WIN32汇编语言教程:第13章 进程控制 · 13.4 进程的隐藏(4)

 

                 call      @F

                  @@:

               pop    ebx

               sub    ebx,offset @B

;********************************************************************

                 invoke _ZeroMemory,addr @stWndClass,sizeof @stWndClass

                 _invoke [ebx + _lpLoadCursor],0,IDC_ARROW

                mov    @stWndClass.hCursor,eax

                 push      [ebx + _hInstance]

                 pop    @stWndClass.hInstance

                 mov    @stWndClass.cbSize,sizeof WNDCLASSEX

                 mov    @stWndClass.style,CS_HREDRAW or CS_VREDRAW

                 lea    eax,[ebx + offset _ProcWinMain]

                 mov    @stWndClass.lpfnWndProc,eax

                 mov    @stWndClass.hbrBackground,COLOR_WINDOW + 1

                 lea    eax,[ebx + offset _szClassName]

                 mov    @stWndClass.lpszClassName,eax

                 lea    eax,@stWndClass

                 _invoke [ebx + _lpRegisterClassEx],eax

;********************************************************************

; 建立并显示窗口

;********************************************************************

                 lea    eax,[ebx + offset _szClassName]

                 lea    ecx,[ebx + offset _szCaptionMain]

                 _invoke [ebx + _lpCreateWindowEx],WS_EX_CLIENTEDGE,eax,ecx,\

                       WS_OVERLAPPEDWINDOW,\

                       100,100,600,400,\

                       NULL,NULL,[ebx + _hInstance],NULL

                 mov    [ebx + _hWinMain],eax

                 _invoke [ebx + _lpShowWindow],[ebx + _hWinMain],SW_SHOWNORMAL

               _invoke [ebx + _lpUpdateWindow],[ebx + _hWinMain]

;********************************************************************

; 消息循环

;********************************************************************

                 .while TRUE

                       lea    eax,@stMsg

                       _invoke [ebx + _lpGetMessage],eax,NULL,0,0

                         .break .if eax == 0

                       lea    eax,@stMsg

                         _invoke [ebx + _lpTranslateMessage],eax

                         lea eax,@stMsg

                       _invoke [ebx + _lpDispatchMessage],eax

                 .endw

                 ret

 

_WinMain          endp

REMOTE_CODE_END       equ this byte

REMOTE_CODE_LENGTH    equ offset REMOTE_CODE_END - offset REMOTE_CODE_START

;####################################################################

在上面的代码中,所有对API函数的调用被换成了对函数入口地址的调用,因为入口地址被存放在全局变量中,所以要用call [ebx + XXXX]的格式调用以解决重定位问题,但是这样的话就无法使用invoke伪指令了。

因为用一大堆的push指令来压入参数太麻烦,笔者写了一个宏来自动压入参数,宏的名称定为_invoke,宏定义存放在Macro.inc文件中。文件的内容是:

;####################################################################

; 将参数列表的顺序翻转

;####################################################################

reverseArgs    macro  arglist:VARARG

                local  txt,count

   

         txt    TEXTEQU <>

          count  = 0

          for    i,<arglist>

                count  = count + 1

                txt    TEXTEQU @CatStr(i,<!,>,<%txt>)

         endm

          if    count GT 0

                txt    SUBSTR txt,1,@SizeStr(%txt)-1

          endif

         exitm  txt

endm

;####################################################################

; 建立一个类似于 invoke 的 Macro

;####################################################################

_invoke       macro  _Proc,args:VARARG

                local  count

   

          count  = 0

%     for    i,< reverseArgs( args ) >

               count  = count + 1

                 push      i

          endm

         call      dword ptr _Proc   

   

endm

;####################################################################

远程代码中的线程函数是_RemoteThread,在这里程序首先获取要用到的API函数的地址,所有API函数的名称被放到了一系列相连的字符串中,最后以一个附加的0结束,这样可以很方便地通过循环来处理它们。

要获取函数地址必须使用LoadLibrary,GetProcAddress和GetModuleHandle函数,但这些函数地址又从哪里得到呢(这就好像一个“先有鸡,还是先有蛋”的问题),幸亏这些函数都存在于Kernel32库中,Kernel32.dll库文件和User32.dll,Gdi32.dll一样,都是最常用的库,在不同的进程中,系统会将它们装入到同样的地址中,所以对于它们来说,在本地进程中获取的地址可以用在远程线程中。

完成获取API函数地址的工作后,就可以调用_WinMain函数来创建窗口了,注意在_WinMain函数的后面不能使用ExitProcess函数来结束进程,这样会将整个Explorer.exe结束掉,必须使用ret指令来结束线程。

_WinMain函数和其他的相关代码改编自第04章中的FirstWindow.asm程序,只不过是将程序中所有涉及全局变量的指令全部改成了以ebx为基址的指令而已。另外,在所有的子程序的开始处,都加上了call/pop/sub这3句用来计算偏移差的指令。

完成远程线程的代码后,现在来看如何将这段代码装载到目标进程中,装载代码存放在RemoteThread.asm中:

                 .386

                 .model flat, stdcall

                 option casemap :none

;####################################################################

; Include 文件定义

;####################################################################

include       windows.inc

include       user32.inc

includelib    user32.lib

include       kernel32.inc

includelib    kernel32.lib

include       Macro.inc

;####################################################################

; 数据段

;####################################################################

                .data  ?

1pLoadLibrary  dd     ?

1pGetProcAddress       dd ?

1pGetModu1eHandle  dd ?

dwProcessID    dd     ?

dwThreadID     dd     ?

hProcess           dd     ?

lpRemoteCode      dd    ?

dwTemp        dd    ?

上页:第13章 进程控制 · 13.4 进程的隐藏(3) 下页:第13章 进程控制 · 13.4 进程的隐藏(5)

第13章 进程控制

版权所有 © 中山市飞娥软件工作室 证书:粤ICP备09170368号