反汇编是个精细活,PEDIY更是精细活中的精细活.这是我第一次PEDIY的最大感受.
第一次做PEDIY,在找切入跳转点和堆栈平衡上,反反复复折腾了几十次,历经两个半晚上,总算是成功了,整理了一下,拿出来跟初学PEDIY的朋友们一起交流一下,大牛飘过吧.
基本情况:
软件名称:手机控件电脑关机
软件功能:此程序通过邮件方式远程控件电脑关机(没亲自测试)
PEDIY原因:此程序启动后每次都必须输入邮箱地址及邮箱密码,为了更加方便使用,所以打算以INI形式保存邮箱地址及密码,在程序启动时通过读取INI文件来自动加载邮箱地址及邮箱密码(朋友提出来的要求,呵,以前没玩过PEDIY,所以就顺便练一下手).
介绍完了,就正式开工吧…
1.在程序目录里新建一个INI文件,内容如下:
[section]
user=yjphskf2004@163.com
pass=lzwx
2.需要用到两个API函数.
读取INI所需要的API: GetPrivateProfileStringA
将读出值写入编辑框的API:SendMessageA
OD载入程序,右键-查找-查找当前模块中的名称,没有找到GetPrivateProfileStringA这个API函数,于是用PETOOLS添加此输入表API函数,保存.
3.增加区段.
经过几次的折腾发现原有的.text代码段空间不够,于是又用PETOOLS工具增加了一个区段,详细过程不啰嗦了.区段地址:0058500,大小3000(随便设的),如图:
4.在新的区段里写入需要用到的字符串,如图
地址 字符串
00585000 .\config.ini
00585030 section
00585060 user
00585090 pass
5.找代码跳转点:
这里折腾了我很久,做之前翻了一些以前PEDIY方面的文章,几乎都是在消息循环处找位置跳转处理的(因为都是加个按钮加个菜单什么的,在消息循环处处理确实比较合适),但是这个不一样,这个要求我们在程序启动,界面显示之前就得处理.找来找去.终于找了个个人觉得比较合适的地方,大家都知道窗口创建的过程是RegisterClassEx-> CreateWindow-> ShowWindow-> UpdateWindow,经过比较,发现CreateWindow,也就是编辑框控件创建完毕的时机跳转比较合适,看一下此函数的参数:
HWND CreateWindow(LPCTSTR lpClassName,
LPCTSTR lpWindowName,
DWORD dwStyle,
int x,
int y,
int nWidth,
int nHeight,
HWND hWndParent,
HMENU hMenu, //控件ID(窗口标识)
HINSTANCE hInstance,
LPVOID lpParam
);
其中hMenu为控件ID,可以用来判断当前创建的是哪个窗口(控件),而CreateWindow的返回值就是当前创建的窗口的句柄,刚好满足了我们的要求,具体思路如下:
在CreateWindow创建窗口完毕的时机,跳到我们的代码处理处,判断当前窗口是否是邮箱名编辑框,是则读取INI文件中邮箱名的配置信息,并以消息的形式发送给邮箱名编辑框,不是则判断当前窗口是否是密码编辑框,是则读取INI文件中密码的配置信息,不是则跳回原来代码处继续往下执行.
于是,OD载入程序,下CreateWindowExA断点,成功断下,堆栈窗口上右键-反汇编窗口跟随,到如下位置
0046D24C |. E8 F8FEFFFF call sjkzdngj.0046D149
0046D251 |. FF75 D0 push [local.12] ; /lParam
0046D254 |. FF75 D4 push [local.11] ; |hInst
0046D257 |. FF75 D8 push [local.10] ; |hMenu
0046D25A |. FF75 DC push [local.9] ; |hParent
0046D25D |. FF75 E0 push [local.8] ; |Height
0046D260 |. FF75 E4 push [local.7] ; |Width
0046D263 |. FF75 E8 push [local.6] ; |Y
0046D266 |. FF75 EC push [local.5] ; |X
0046D269 |. FF75 F0 push [local.4] ; |Style
0046D26C |. FF75 F4 push [local.3] ; |WindowName
0046D26F |. FF75 F8 push [local.2] ; |Class
0046D272 |. FF75 FC push [local.1] ; |ExtStyle
0046D275 |. FF15 78B54700 call dword ptr ds:[<&USER32.CreateWindow>; \CreateWindowExA //停在这里
0046D27B |. 8BF8 mov edi,eax //从这行开始改成跳向我们读取配置的代码,修改后的代码见下面
0046D27D |. E8 13FFFFFF call sjkzdngj.0046D195
0046D282 |. 85C0 test eax,eax
0046D284 |. 75 0A jnz Xsjkzdngj.0046D290
0046D286 |. 8B06 mov eax,dword ptr ds:[esi]
0046D288 |. 8BCE mov ecx,esi
6.写入代码:
在0046d27b处开始修改代码,跳转到我们的代码处理的位置.下面直接贴代码:
跳转指令:
修改前:
0046D24C |. E8 F8FEFFFF call sjkzdngj.0046D149
0046D251 |. FF75 D0 push [local.12] ; /lParam
0046D254 |. FF75 D4 push [local.11] ; |hInst
0046D257 |. FF75 D8 push [local.10] ; |hMenu
0046D25A |. FF75 DC push [local.9] ; |hParent
0046D25D |. FF75 E0 push [local.8] ; |Height
0046D260 |. FF75 E4 push [local.7] ; |Width
0046D263 |. FF75 E8 push [local.6] ; |Y
0046D266 |. FF75 EC push [local.5] ; |X
0046D269 |. FF75 F0 push [local.4] ; |Style
0046D26C |. FF75 F4 push [local.3] ; |WindowName
0046D26F |. FF75 F8 push [local.2] ; |Class
0046D272 |. FF75 FC push [local.1] ; |ExtStyle
0046D275 |. FF15 78B54700 call dword ptr ds:[<&USER32.CreateWindow>; \CreateWindowExA
0046D27B |. 8BF8 mov edi,eax //从这行开始改成跳向我们读取配置的代码,修改后的代码见下面
0046D27D |. E8 13FFFFFF call sjkzdngj.0046D195
0046D282 |. 85C0 test eax,eax
0046D284 |. 75 0A jnz Xsjkzdngj.0046D290
0046D286 |. 8B06 mov eax,dword ptr ds:[esi]
0046D288 |. 8BCE mov ecx,esi
修改后:
0046D24C . E8 F8FEFFFF call 35.0046D149
0046D251 . FF75 D0 push dword ptr ss:[ebp-0x30] ; /lParam
0046D254 . FF75 D4 push dword ptr ss:[ebp-0x2C] ; |hInst
0046D257 . FF75 D8 push dword ptr ss:[ebp-0x28] ; |hMenu
0046D25A . FF75 DC push dword ptr ss:[ebp-0x24] ; |hParent
0046D25D . FF75 E0 push dword ptr ss:[ebp-0x20] ; |Height
0046D260 . FF75 E4 push dword ptr ss:[ebp-0x1C] ; |Width
0046D263 . FF75 E8 push dword ptr ss:[ebp-0x18] ; |Y
0046D266 . FF75 EC push dword ptr ss:[ebp-0x14] ; |X
0046D269 . FF75 F0 push dword ptr ss:[ebp-0x10] ; |Style
0046D26C . FF75 F4 push dword ptr ss:[ebp-0xC] ; |WindowName
0046D26F . FF75 F8 push dword ptr ss:[ebp-0x8] ; |Class
0046D272 . FF75 FC push dword ptr ss:[ebp-0x4] ; |ExtStyle
0046D275 . FF15 78B54700 call dword ptr ds:[<&USER32.CreateWindow>; \CreateWindowExA
0046D27B .- E9 807F1100 jmp 35.00585200 //这里跳向读取配置的代码(00585200)
0046D280 90 nop
0046D281 90 nop
0046D282 90 nop
0046D283 90 nop
0046D284 . 75 0A jnz X35.0046D290 //过了读取配置的代码后,跳回这里继续往下走
0046D286 . 8B06 mov eax,dword ptr ds:[esi]
0046D288 . 8BCE mov ecx,esi
=================================================================
配置读取的代码(这一段是写在我们新加的区段里的):
00585200 60 pushad ; 入栈
00585201 83FB 6E cmp ebx,0x6E ; 比较是否是用户名编辑框(6E是控件ID,用spy++可以得到)
00585204 74 21 je X35.00585227 ; 是则跳
00585206 83FB 78 cmp ebx,0x78 ; 比较是否是密码编辑框(6E是控件ID,用spy++可以得到)
00585209 74 61 je X35.0058526C ; 是则跳
0058520B 90 nop
0058520C 90 nop
0058520D 90 nop
0058520E 90 nop
0058520F 90 nop
00585210 90 nop
00585211 90 nop
00585212 90 nop
00585213 61 popad ; 出栈
00585214 90 nop
00585215 8BF8 mov edi,eax ; 补上跳转指令覆盖的代码
00585217 E8 797FEEFF call 35.0046D195 ; 补上跳转指令覆盖的代码
0058521C 85C0 test eax,eax ; 补上跳转指令覆盖的代码
0058521E - E9 6180EEFF jmp 35.0046D284 ; 跳回原来的地方继续往下执行
00585223 90 nop
00585224 90 nop
00585225 90 nop
00585226 90 nop
00585227 A3 00515800 mov dword ptr ds:[0x585100],eax ; 句柄保存到585100处
0058522C 68 00505800 push 35.00585000 ; ASCII ".\config.ini"
00585231 6A 64 push 0x64
00585233 68 C0505800 push 35.005850C0
00585238 68 C0505800 push 35.005850C0
0058523D 68 60505800 push 35.00585060 ; ASCII "user"
00585242 68 30505800 push 35.00585030 ; ASCII "section"
00585247 FF15 31815800 call dword ptr ds:[<&kernel32.GetPrivate>; 读取用户名配置
0058524D 68 C0505800 push 35.005850C0
00585252 6A 00 push 0x0
00585254 6A 0C push 0xC
00585256 FF35 00515800 push dword ptr ds:[0x585100]
0058525C FF15 DCB54700 call dword ptr ds:[<&USER32.SendMessageA>; 把读出来的值以消息方式发送到用户名编辑框
00585262 ^ EB AF jmp X35.00585213 ; 跳
00585264 0000 add byte ptr ds:[eax],al
00585266 0000 add byte ptr ds:[eax],al
00585268 0000 add byte ptr ds:[eax],al
0058526A 0000 add byte ptr ds:[eax],al
0058526C A3 00515800 mov dword ptr ds:[0x585100],eax ; 句柄保存到585100处
00585271 68 00505800 push 35.00585000 ; ASCII ".\config.ini"
00585276 6A 64 push 0x64
00585278 68 C0505800 push 35.005850C0
0058527D 68 C0505800 push 35.005850C0
00585282 68 90505800 push 35.00585090 ; ASCII "pass"
00585287 68 30505800 push 35.00585030 ; ASCII "section"
0058528C FF15 31815800 call dword ptr ds:[<&kernel32.GetPrivate>; 读取密码配置
00585292 68 C0505800 push 35.005850C0
00585297 6A 00 push 0x0
00585299 6A 0C push 0xC
0058529B FF35 00515800 push dword ptr ds:[0x585100]
005852A1 FF15 DCB54700 call dword ptr ds:[<&USER32.SendMessageA>; 把读出来的值以消息方式发送到密码编辑框
005852A7 ^ E9 67FFFFFF jmp 35.00585213 ; 跳
OK,到目前为止.所有的工作都已经完成,OD保存好文件后,测试,一切正常.
关键的只有两点,一是要找准代码跳转点,二是写代码的时候要注意堆栈平衡.思路最重要.
附件因为比较大.就不上传了.
因工作原因,少有时间来看雪逛了,今天难得,趁着朋友给的这程序, 发个文章上来跟初学的朋友交流一下.水平有限,有啥不当之处,还请各位指正.
PS:现在论坛怎么不能直接贴图上来了呢.印象中以前是可以滴...
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!