灯火互联
管理员
管理员
  • 注册日期2011-07-27
  • 发帖数41778
  • QQ
  • 火币41290枚
  • 粉丝1086
  • 关注100
  • 终身成就奖
  • 最爱沙发
  • 忠实会员
  • 灌水天才奖
  • 贴图大师奖
  • 原创先锋奖
  • 特殊贡献奖
  • 宣传大使奖
  • 优秀斑竹奖
  • 社区明星
阅读:2910回复:0

Win32汇编初探内存补丁GUI技术

楼主#
更多 发布于:2011-12-19 17:43

对于每个Cracker,补丁是再熟悉不过的东西了。补丁的形式大致有两种,一种是文件补丁,一种是内存补丁。两者的根本区别在于文件补丁对程序的部分代码是永久性修改,而内存补丁却是在程序代码映射到内存的时候才做出修改,所以内存补丁的一大好处就是保持了程序的完整性,却能使用到修改后的程序。尤其对现在的一些加壳软件,内存补丁的作用越来越明显。
破解调试用到的OD就附带了一个内存补丁的功能。载入OD,对某个语句进行修改,然后按“Ctrl+P”就可以看到OD的内存补丁管理窗口。我们可以通过键盘上的空格键对补丁进行激活或者禁用。OD这个功能对一些破解新手来说是非常有用的,当对关键跳转不确定的时候,直接用个内存补丁运行一下判断是不是真正的关键跳转,如图1所示。但是OD这个功能还有一点小小的不足,就是每次F9运行后要重新载入,然后再找回那个补丁的位置。一两次还好,如果次数多了也挺烦人的。现在我们就用Win32汇编来实现OD这个功能,要有自己的GUI界面,而且不像OD每补一次就重新载入那么麻烦。
 
初步分析
要修改程序的内存,必须要有足够的权限来打开这个程序的进程,然后才可以通过API函数读写要修改程序的内存数据。确定了总的思路后,我们开始分步来实现。
首先是打开进程,我们可以用CreatProcess函数创建对象程序的进程,然后用ReadProcessMemory和WriteProcessMemory来读取和写入进程的内存地址。界面用输入字符的形式来实现,这里就涉及了字符转16进制数值的问题,我们可以用GetDlgItemText获得字符串,然后进行简单的ASCII码加减来实现转换。
下面简单介绍一下最核心的两个函数,这两个函数都在kernel32.lib输出库里面。ReadProcessMemory函数用来读取指定进程的空间的数据,此空间必须是可以访问的,否则读取操作会失败!函数原型如下。

BOOL ReadProcessMemory(
HANDLE hProcess,
//目标进程句柄,必须要足够的权限才能打开
LPCVOID lpBaseAddress,
//读取数据的起始地址
LPVOID lpBuffer,
//存放数据的缓存区地址
DWORD nSize,
//要读取的字节数
LPDWORD lpNumberOfBytesRead
//实际读取数存放地址
);

BOOL WriteProcessMemory(
HANDLE hProcess,
//要写进程的句柄,也是要足够权限才能写入
LPVOID lpBaseAddress,
//写内存的起始地址
LPVOID lpBuffer,
//写入数据的地址
DWORD nSize,
//要写的字节数
LPDWORD lpNumberOfBytesWritten
//实际写入的字节数
);

编写代码
我们用一个最简单的例子分析一下。test.exe是一个非常简单的测试程序,双击运行就会出现“Sorry”提示,如图2所示。用OD逆向这个程序,我们发现在00401004那里有个je跳转是跳到“Sorry”提示的,如图3所示。如果我们把je改为jnz或者nop掉,就能成功爆破这个程序了。该句是“74 15”,我们只要改为“75 15”或者“90 90”就可以了。分析完最基本的流程,下面是编写核心程序代码,该代码的作用就是创建进程和读写内存的。
  
图2
  
图3

.data?
dbOldByte        db        2 dup(?)
stStartUp        STARTUPINFO <?>
stProcInfo        PROCESS_INFORMATION <?>
PATCH_POSITION        dd        ?
;想爆破的地址
dbPatch                dd        ?
;爆破前的指令
dbPatched        dd        ?
;爆破后的指令

.const
szExecFilename        db        "test.exe",0
;定义文件名
szErrExec        db  "无法装载执行文件",0
szErrVersion        db        "执行文件的版本不正确,无法修正",0

//以下为核心子程序,用来修改内存代码
_ProcMemory        proc
//创建进程
invoke        GetStartupInfo,addr stStartUp
invoke        CreateProcess,offset szExecFilename,0,0,0,0,
NORMAL_PRIORITY_CLASS or CREATE_SUSPENDED,0,0,
offset stStartUp,offset stProcInfo
.if        eax        ;判断eax == 1
;读进程内存并验证内容是否正确
invoke        ReadProcessMemory,stProcInfo.hProcess,PATCH_POSITION,
addr dbOldByte,2,NULL
.if        eax
mov        ax,word ptr dbOldByte
                        
.if        ax == word ptr dbPatch
invoke        WriteProcessMemory,stProcInfo.hProcess,
PATCH_POSITION,addr dbPatched,2,0
invoke        ResumeThread,stProcInfo.hThread
.else
invoke        TerminateProcess,stProcInfo.hProcess,-1
invoke        MessageBox,0,addr szErrVersion,0,
MB_OK or MB_ICONSTOP
.endif

invoke        CloseHandle,stProcInfo.hProcess
invoke        CloseHandle,stProcInfo.dwThreadId
.endif
.else
;不能创建进程时显示出错提示
invoke        MessageBox,NULL,addr szErrExec,NULL,MB_OK or MB_ICONSTOP
.endif
ret
_ProcMemory endp

完成了核心代码之后,就要考虑GUI界面部分了,要由用户输入想要修改的地址和修改的内容。我们先用RadAsm创建一个对话框,添加三个编辑框和一个按钮,如图4所示。首先是限制用户输入的字符串,因为只能输入16进制的字符串,所以我们就用窗体子类化来实现。下面的子程序就是实现窗体子类化的。
szAllowedChar        db        "0123456789ABCDEFabcdef",08h
;定义有效按键,08h为退格键
lpOldProcEdit        dd        ?
//编辑框的新窗体过程
_ProcEdit        proc        uses ebx edi esi hWnd,uMsg,wParam,lParam
        
mov        eax,uMsg
.if        uMsg == WM_CHAR
;只接受我们需要的WM_CHAR字符信息        
mov        eax,wParam
mov        edi,offset szAllowedChar
mov        ecx,sizeof szAllowedChar
repnz        scasb
;当ZF=0或比较结果不相等,且CX/ECX<>0时重复
;使用scasb指令查表
.if        ZERO?        ;判断零标志位是否被置位
.if        al > 9
;判断是否输入字母,因为表中大于9的肯定是字母
and        al,not 20h        
;将字母转换为大写,not为取反。小写从61h开始,大写从41h开始,只要第六位是0,就肯定是大写了
.endif
;CallWindowProc是将WM_CHAR消息转发给主窗体
invoke        CallWindowProc,lpOldProcEdit,hWnd,uMsg,eax,lParam
ret
.endif
        
.else
invoke        CallWindowProc,lpOldProcEdit,hWnd,uMsg,wParam,lParam
ret
.endif
xor        eax,eax
ret

_ProcEdit endp
但是我们有三个输入框,这样要是每个都子类化的话就显得麻烦了,干脆用超类化为这三个输入框建立一个新的类好了。我们用下面的子程序来实现。
//控件超类化,建立新的Edit类,限制输入位数和输入字符
_SuperCla



喜欢0 评分0
游客

返回顶部