WIN32汇编语言教程:第13章 进程控制 · 13.3 进程调试(4)

2. 一个内存补丁例子

与这个例子相关的文件放在所附光盘的Chapter13\Patch1目录中,其中Test.asm是将要被修改的目标程序,这个程序中只有几句代码:

               .const

szErr      db    '对不起,你使用的是盗版软件!',0

szOK          db    '感谢您使用正版软件!',0

szCaption  db    '谢谢',0

             ...

               .code

start:

               xor    eax,eax

               .if    eax

                       invoke MessageBox,NULL,addr szOK,addr szCaption,MB_OK

               .else

                       invoke MessageBox,NULL,addr szErr,NULL,\

                       MB_OK or MB_ICONSTOP

               .endif

             ...

在程序中模拟测试序列号的过程,当eax返回0的时候,显示“对不起,你使用的是盗版软件”,否则显示“感谢您使用正版软件”,为了简化程序,程序用了一句xor eax,eax指令使eax永远为0,所以显示的总是“盗版软件......”。我们的目标就是编写一个内存补丁程序,让它去调用Test.exe并修改测试代码,这样就可以跳过“反盗版”测试。

要对某个软件进行内存补丁,就必须首先对它进行分析,这样才能知道该往目标进程的什么地方写入什么数据。这方面的工作属于软件跟踪的课题,在本书中暂不涉及,有兴趣的读者可以另外研究。对于上面这个简单的Test.exe文件,采用静态分析的方法,用W32Dasm将它反汇编,就可以得到下面的代码(在实际的使用中,就是仁者见仁、智者见智了,读者可以用自己擅长的方法分析目标文件):

:00401000 33C0         xor eax, eax

:00401002 0BC0         or eax, eax          ;.if eax

:00401004 7415         je 0040101B

:00401006 6A00         push 00000000

:00401008 6840204000       push 00402040

:0040100D 682C204000       push 0040202C

:00401012 6A00         push 00000000

:00401014 E819000000       call 00401032          ;“正版”MessageBox

:00401019 EB10         jmp 0040102B

:0040101B 6A10         push 00000010         ;.else

:0040101D 6A00         push 00000000

:0040101F 6810204000       push 00402010

:00401024 6A00         push 00000000

:00401026 E807000000       call 00401032          ;MessageBox

分析这段代码就可以发现,只要将00401004h地址的je指令略过就可以让程序执行“正版”逻辑,在内存补丁程序中可以使用两个nop指令代替je指令,nop指令的机器码为90h,为单字节指令,而je指令有两个字节,所以需要两个nop指令。

读者可能还有一个问题:反汇编的时候显示地址是00401004h,难道执行的时候实际装入的地址也是这个吗?对于exe程序来说,这是肯定的,因为不同进程的地址空间是隔离的,不会有其他东西占用这部分地址;对于DLL来说就不一定了,因为一个exe文件可能装入多个DLL,当两个DLL的默认装入地址相同时,有一个肯定会被重新定位到其他地方,所以对DLL进行静态反汇编得到的地址不一定是正确的,在这种情况下可以扫描目标进程的整个空间来找到DLL的实际装入位置。

分析目标代码得到要补丁的地址和该地址处的新旧机器码以后,就可以写出下面的补丁程序了,程序的汇编源文件为Patch1.asm:

       .386

       .model flat, stdcall

       option casemap :none  ; case sensitive

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

;     Include 数据

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

include        windows.inc

include        user32.inc

include        kernel32.inc

includelib     user32.lib

includelib     kernel32.lib

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

PATCH_POSITION equ    00401004h

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

;     数据段

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

                 .data?

dbOldBytes     db     2 dup (?)

stStartUp      STARTUPINFO            <?>

stProcInfo     PROCESS_INFORMATION    <?>

 

                 .const

dbPatch        db     74h,15h

dbPatched      db     90h,90h

szExecFilename db     'Test.exe',0

szErrExec     db    '无法装载执行文件!',0

szErrVersion       db    '执行文件的版本不正确,无法修正!',0

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

                  .code

Start:

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

; 创建进程

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

                 invoke GetStartupInfo,addr stStartUp

                 invoke  CreateProcess,offset szExecFilename,NULL,NULL,NULL,\

                         NULL,NORMAL_PRIORITY_CLASS or CREATE_SUSPENDED,0,0,\

                         offset stStartUp,offset stProcInfo

                 .if eax

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

; 读进程内存并验证内容是否正确

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

          invoke ReadProcessMemory,stProcInfo.hProcess,PATCH_POSITION,\

                 addr dbOldBytes,2,NULL

          .if    eax

                 mov    ax,word ptr dbOldBytes

                 .if    ax ==  word ptr dbPatch

                         invoke WriteProcessMemory,stProcInfo.hProcess,\

                                 PATCH_POSITION,addr dbPatched,2,NULL

                         invoke ResumeThread,stProcInfo.hThread

                 .else

                         invoke TerminateProcess,stProcInfo.hProcess,-1

                         invoke MessageBox,NULL,addr szErrVersion,\

                                 NULL,MB_OK or MB_ICONSTOP

                 .endif

         .endif

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

                         invoke CloseHandle,stProcInfo.hProcess

                         invoke CloseHandle,stProcInfo.hThread

                 .else

                         invoke MessageBox,NULL,addr szErrExec,NULL,\

                                 MB_OK or MB_ICONSTOP

                 .endif

                 invoke ExitProcess,NULL

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

                 end    Start

编译链接后执行Patch1.exe程序就会发现,Test.exe被执行了,而且正确地显示了“感谢您使用正版软件”消息框,这说明补丁程序产生效果了。现在来分析一下补丁程序的实

上页:第13章 进程控制 · 13.3 进程调试(3) 下页:第13章 进程控制 · 13.3 进程调试(5)

第13章 进程控制

版权所有 © 云南伯恩科技 证书:粤ICP备09170368号