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被执行了,而且正确地显示了“感谢您使用正版软件”消息框,这说明补丁程序产生效果了。现在来分析一下补丁程序的实