Win32汇编初探内存补钉GUI技术[网络技术]
本文“Win32汇编初探内存补钉GUI技术[网络技术]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
文/图 广东产业大学物理学院 BoXer[ICY]===================================
关于每个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类,限制输入位数和输入字符
_SuperClass proc
LOCAL @stWC:WNDCLASSEX ;@stWC是一个WNDCLASSEX构造
mov @stWC.cbSize,sizeof @stWC
invoke GetClassInfoEx,NULL,addr szEditClass,addr @stWC
;获得Edit控件的类构造
push@stWC.lpfnWndProc
poplpOldProcEdit
mov@stWC.lpfnWndProc,offset _ProcEdit
;调用_ProcEdit子程序的要求
pushhInstance
pop@stWC.hInstance
;将hInstance字段设置为自己的实例句柄
mov@stWC.lpszClassName,offset szEditClass
invokeRegisterClassEx,addr @stWC
;注册新类,但类名还是用Edit
ret
_SuperClass endp
注册新类名的时刻,假如是自己编写的rc文件,可以别的起其他名字.但是在RadAsm只能再用回本来的类名Edit,这样就把本来的类给覆盖了,改成我们新的类.
最后我们要做的,就是把字符串转化为16进制数值便可以了.由下面的子程序实现这一功效.
;得到要改正的地址转换为数值
_ProcNum proc nDialog:dword
LOCAL@szBuffer[512]:byte
invokeGetDlgItemText,hWinMain,nDialog,addr @szBuffer,sizeof @szBuffer
leaesi,@szBuffer ;把偏移地址存到esi
cld ;清方向标志位 DF=0
xoreax,eax ;清空eax,便利下面做or运算
.whileTRUE
movzxecx,byte ptr [esi]
incesi
.break.if!ecx
;举行非操作,假如ecx为0就跳出循环
.ifcl > '9' ;比9大的是字母
subcl,37h ;是字母就减37h转化为16进制数值
.else
subcl,30h ;是数字就减30h转换为16进制数值
.endif
shleax,4 ;相当于乘16,因为是16进制,eax=eax*16
or al,cl ;或运算,相当于加cl,eax=eax+cl就是最后想要的数值了
.endw
ret
_ProcNum endp
最后,通过窗体主程序对各个子程序实现调用便可以了.不过这里要注意一点,参数入栈的时刻,我们定义的stdCall是从右到左入栈的,但是我们调用_ProcNum子程序得到的16进制数值是从左到右的,所以赋值前要先调高位.下面是窗体主程序的代码.
;窗体主程序
_ProcMain proc uses ebx edi esi hWnd,uMsg,wParam,lParam
LOCALBuffer
moveax,uMsg
.ifuMsg == WM_CLOSE
invokeEndDialog,hWnd,0
.elseifuMsg == WM_INITDIALOG
moveax,hWnd
movhWinMain,eax;hWinMain保存全局句柄
;下面是限制输入框输入的字符数目
invokeSendDlgItemMessage,hWnd,ID_ADDR,EM_LIMITTEXT,8,0
invokeSendDlgItemMessage,hWnd,ID_OLD,EM_LIMITTEXT,4,0
invokeSendDlgItemMessage,hWnd,ID_NEW,EM_LIMITTEXT,4,0
.elseifuMsg == WM_COMMAND
moveax,wParam
.ifax == ID_ADDR
invoke_ProcNum,ID_ADDR
;调用字符串转换16进制数值子程序
movPATCH_POSITION,eax
;将后果保存在 PATCH_POSITION
.elseifax == ID_OLD
invoke_ProcNum,ID_OLD
;下面是高低位交换,得到的数值是次序,而放进仓库是逆序,所以要换位
xorebx,ebx
movbh,al
movbl,ah
movdword ptr dbPatch,ebx
;dbPatch保存爆破前的指令
.elseifax == ID_NEW
invoke_ProcNum,ID_NEW
xorebx,ebx
movbh,al
movbl,ah
movdword ptr dbPatched,ebx
;dbPatched保存爆破指令
.elseif ax == ID_OK
invoke_ProcMemory
.endif
.else
moveax,FALSE
ret
.endif
moveax,TRUE
ret
_ProcMain endp
start:
invokeGetModuleHandle,NULL
movhInstance,eax
invoke_SuperClass ;将Edit框超类化
invokeDialogBoxParam,hInstance,ID_MAIN,NULL,offset _ProcMain,NULL
invokeExitProcess,0
endstart
成果展示
将以上代码用RadAsm编译运行,可以看到如图4所示的效果.到此,探究OD的内存补钉和写出其GUI界面就基本上完成了.当然,这只是一个简单到不得了的小程序,功效限制也非常的多,剩下的就由我们敬爱的读者施展充分的想象来不断扩大这个小软件了!说不定下一个用纯汇编写的keymake就是你写的哦!
以上是“Win32汇编初探内存补钉GUI技术[网络技术]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |