本帖最后由 弑神者91511 于 2023-12-17 16:04 编辑
新手第一次写逆向和改造的过程,难免有错误或走弯路的地方,请大神们多多指正。 本文旨在探讨程序编写技术,相关程序均为自已编写代码测试,请勿用于非法目的。
【情境介绍】注:为避免引起歧义,以下为虚拟情境,如有雷同,纯属巧合。
最近拿到某办公程序(x86):victim.exe,自动和服务器联网。在服务器下达通知消息时,会自动跳出提示,播放声音,几秒后停止。 要求播放的声音需要从功放输出,保证范围内所有人能听见。一般来说功放需要24小时不间断开机,但长时间设备容易老化损坏。 需要改造成在有提示声音播放时再自动打开功放电源,播放完毕自动断开功放电源。在程序源代码中增加这个功能也非常容易。 但本文从另一个角度来探讨:如果不修改源代码,能否实现增加功能呢?
【改造思路】 1、逆向分析EXE文件,找出提示开始和结束的代码; 2、编写自己的DLL文件,以供EXE调用实现功能; 3、改造EXE文件(添加DLL导入函数、打补丁调用函数)。
【知识准备】 1、大致了解EXE文件的结构、加载到内存的方式等 2、反汇编与调试 3、DLL的代码编写 4、EXE修改
【使用工具】 查壳工具:DiE 调试工具:OD 编程工具:vs2010(很老了……) 修改工具:StudyPE+、010Editor 记录工具:Programmer's Notepad 抓包工具:Wireshark
【过程记录】 一、逆向分析EXE文件,找出提示开始和结束的代码 1、上手用DiE查了一下,VC编译win32,无壳,studype查为动态基址。这一步并不是很难,如果是遇到加密加壳的,像我这种新手就无能为力了。 2、直接拖入OD,停在OEP(012BFCCC) 如果没有在OEP点一下小人运行到用户代码就行。接着点一下Az搜索字符串,因为字符串包含大量提示信息,很可能找到我们要的东西 3、果然,很快找到VoiceXXX::stop()和VoiceXXX::OnReceiveWarning()之类的信息,应该是写日志用的,字面一看这不就是声音的结束和开始吗,就从这里下手吧。 双击点进去,把地址记录下来,准备打补丁。 4、下面就是刚才那两处的地址 可以看出,push了一个字符串,然后call某个函数。 这种就很好打补丁,因为跳转的机器码和push字符串的机器码一样长(后面再说)。 到这里,第一步分析完成。 012BAE83 | 68 9CEE2C01 | push victim.12CEE9C | 12CEE9C:"VoiceXXX::onReceivedWarning() type:" 012BAE88 | FF15 08A62C01 | call dword ptr ds:[<&public: class QDebug & __thiscall QDebug::operato | ...... 012BA8A1 | 68 68EE2C01 | push victim.12CEE68 | 12CEE68:"VoiceXXX::stop()" 012BA8A6 | FF15 08A62C01 | call dword ptr ds:[<&public: class QDebug & __thiscall QDebug::operato |
二、编写自己的DLL文件,以供EXE调用实现功能 这个可以用你自己喜欢的编写DLL的工具,写两个可以静态调用的函数,为了简单,没有参数,也没返回值。 我用vs2010向导建立了WIN32dll项目,取名叫customsg,过程中最好要选一下“导出符号”以便导出函数 我写了两个导出函数:MsgStart和MsgStop, 在函数里就可以做很多事情了, 比如发送系统消息、给串口发数据、访问某个网址等都行。 这里我采用了插入一个串口继电器,给串口发数据控制电源通断的方式。 //用于导出的函数 // 开始消息,设置dwMsg = 1 CUSTOMSG_API void MsgStart(void) { wMsg = 1; PostMessage(HWND_BROADCAST, nPostMsgID, wMsg, dwMsgVar); SendSerialData(TRUE); //发送命令打开继电器 PostData(strPostUrl, strPostDataStart); } // 停止消息,设置dwMsg = 0 CUSTOMSG_API void MsgStop(void) { wMsg = 0; PostMessage(HWND_BROADCAST, nPostMsgID, wMsg, dwMsgVar); SendSerialData(FALSE);//发送命令关闭继电器 PostData(strPostUrl, strPostDataStop); } //其它函数及具体代码自己实现
然后在项目中添加一个def文件用于导出规范名称的函数,要不然函数名是乱码的 LIBRARY "customsg" EXPORTS MsgStart @1 MsgStop @2
编译成功,你就会得到一堆文件。我们只需要找到.dll这个: customsg.dll
在Debug和Release目录都有,选一个复制到刚才那个victim.exe的目录中,这一步宣告完成。
三、改造EXE文件(添加DLL导入函数、打补丁调用函数) 这一步总体来说有点难度,我也是走了很多弯路才成功,遇到问题多百度可以解决掉大部分 关键在于细致
1、添加DLL导入函数 用StudyPE+打开我的victim.exe,点击“导入”,出现导入dll列表,右键点击任意一个,点击“添加导入函数” 浏览找到自己编写的custmsg.dll,从左侧选取要导入的函数(MsgStart、MsgStop),加到右侧,点击“添加”。 完成后,点击“文件-另存为”,保存为"victim2.exe",DLL导入函数添加完毕。
2、打补丁调用函数 这里最好稍微有点汇编的知识,百度学习一下也行 (1)再次启动OD,打开导入DLL函数的“victim2.exe",运行到用户代码,直接去我们在第一部分找到的地址处 按F2下个断点 方便找 【这里有个问题,就是动态基址,每次程序加载后地址可能会不一样。如果图简单,可以在刚才StudyPE+里顺便点一下“固定基址”就OK,就不会变了】 我这里就以动态基址的方式来改,也没问题,当作一次学习 这里我们把刚才的地址,和刚才记的OEP地址(012BFCCC)减一下 再和现在的OEP地址(00F6FCCC)减一下,就能算出来了 012BAE83 : 012BFCCC-012BAE83=4E49, 00F6FCCC-4E49=00F6AE83 ...... 012BA8A1: 012BFCCC-012BA8A1=542B, 00F6FCCC-542B=00F6A8A1
(2)在CPU窗口 按Ctrl+G输入要找的地址直接过去 00F6AE83 | 68 9CEEF700 | push victim2.F7EE9C | F7EE9C:"VoiceXXX::onReceivedWarning() type:" 00F6AE88 | FF15 08A6F700 | call dword ptr ds:[<&public: class QDebug & __thiscall QDebug::operato | ...... 00F6A8A1 | 68 68EEF700 | push victim2.F7EE68 | F7EE68:"VoiceXXX::stop()" 00F6A8A6 | FF15 08A6F700 | call dword ptr ds:[<&public: class QDebug & __thiscall QDebug::operato |
(3)在代码前后附近找一些空闲空间,放补丁代码 因为程序编译留了许多int3的调试指令,我们需要寻找一大片连续的CC字节 最好在20个以上 按Ctrl+B搜索匹配特征,在十六进制处输入24个连续的CC,按“确定” 运气好,搜索出来了在00F72EB2处有,双击点进去 (4)果然一大片。我们留一两个CC再开始,我从00F72EB5开始写调用MsgStart的代码 右键,二进制 编辑, 前8字节改为 60 FF 15 CC CC CC CC 61 00F72EB5 | 60 | pushad | 00F72EB6 | FF15 CCCCCCCC | call dword ptr ds:[CCCCCCCC] | 00F72EBC | 61 | popad |
这段代码就可以完成调用某个函数的功能了,CCCCCCCC暂时不管,等会儿改成我们自己的函数地址
接着把00F6AE83的5个字节写过来 00F72EBD | 68 9CEEF700 | push victim2.F7EE9C | F7EE9C:"VoiceXXX::onReceivedWarning() type:"
最后加个跳转指令,跳回原来的call那里 这里直接按空格键,输入"jmp 00F6AE88"就行 然后双击jmp那条指令,跳到上面的call,将前面那条指令改成跳转到我们补丁地址 "jmp F72EB5" 这样,就完成了EXE对我的DLL中MsgStart函数的调用框架 梳理一下思路: 在空闲区编写调用DLL函数的代码 -》在EXE中修改一条PUSH指令为JMP到空闲区 -》 空闲区调用DLL函数结束,执行原来的PUSH指令,JMP回去原来地址继续运行
用同样的方法,完成MsgStop函数的调用框架编写 (5)修复调用的DLL函数的地址 首先,到OD中,点击“符号”,选中左侧第一个"victim2.exe",在右边会出现许多它的导入函数。由于我们添加的DLL在最后,因此我们拉到最下面,会找到导入的两个函数: 记住这两个地址,并分别填写到刚才的CCCCCCCC处【注意低字节在前】 02A04C30 =customsg.MsgStart 02A04C34 =customsg.MsgStop 同时记下左上角黑色显示的那个基址【 00F40000】,下面要用到 可以看到,call的函数名称显示正确了 可是不要高兴得太早,因为这是动态基址,下一次运行还会变
(6)我们要计算出它的正常基址,让它再怎么变都能找到。 方法:用函数地址减去上面的基址【00F40000】,再加上正常基址【EXE一般为400000】,用StudyPE+可以查看ImageBase值。 02A04C30-00F40000+400000 =【01EC4C30】 //customsg.MsgStart 02A04C34-00F40000+400000 =【01EC4C34】 //customsg.MsgStop 同样的,上面我们PUSH的那个参数,也是动态地址,也需要减去上面的基址【00F40000】,再加上正常基址【EXE一般为400000】 68 9CEEF700 -》68 【9CEE4300】 68 68EEF700 -》68 【68EE4300】
在OD中,把这4个值对应修改进去 (7)记录数据偏移地址 OD里修改好了,等会儿还要在EXE文件里去修改。在动态基址的情况下,这几个值程序在加载的时候会重新计算,因此要找出这几个值在EXE文件加载时的偏移地址。 方法:数据地址减去基址【00F40000】 将这几个地方都算出来 32EB6 //00F72EB6 | FF15 304CEC01 | call dword ptr ds:[1EC4C30] | 32EBD //00F72EBD | 68 9CEE4300 | push 43EE9C | 32ECA //00F72ECA | FF15 344CEC01 | call dword ptr ds:[1EC4C34] | 32ED1 //00F72ED1 | 68 68EE4300 | push 43EE68 |
由于每个指令前面是操作码FF15或68,后面才是数据,因此要加1到2字节,算出数据的地址: 32EB8 32EBE 32ECC 32ED2
留着备用。
(8)应用补丁到文件 在OD中点右键,点击“补丁”,在出现的对话框中点击“全选” -》 “修补文件”,另存为"victim3.exe" 另存的时候可能会有个提示 “你的补丁和重定向区域重叠”,点YES就行 (9)为我们的补丁增加重定向项目 下载一个最新的010 Editor,打开victim3.exe,会提示你安装EXE模板,点“安装”。 完成后打开的victim3.exe会变得五颜六色,下面也会多出来分析的目录树: 在目录树里找到 RelocTable, 可以看到尺寸大小是78D0H 向下滑动到下面,找到RelocTable的最后一项点一下。可以看到最后一项后面有许多0,这里可以增加我们自己的重定位项。 重定位项的结构可以百度一下。 第1个四字节:VirtualAddress,可以理解为EXE展开到内存中的地址。这个要进行4K对齐,也就是以十六进制1000H为整数倍。 找到第(7)步记录的偏移地址,看到我们的数据都在32EB8到32ED2,因此第1个VirtualAddress填32000H 【00 20 03 00】 第2个四字节:BlockSize,就是这个重定位块的长度。每个数据2字节 乘以四=8字节,再加上VirtualAddress和BlockSize,一共16字节,因此这里填10H 【10 00 00 00】
接下来是具体的重定位项,就是相对VirtualAddress的偏移量 【数据地址减去VirtualAddress】 根据重定位项定义,最高4个二进位值设置为“3”,表示这是个相对地址,加载时需要重新计算
以第一个数据的重定位项为例:数据地址 - VirtualAddress = 32EB8 - 32000 = EB8 最高4个二进制位设为3,合起来就是 3EB8H 【B8 3E】 同理,可算出其它3个数据的重定位项:【BE 3E】【CC 3E】【D2 3E】 紧挨着上面的数据,依次填充进去 : 最后,修改一下重定位表大小,确保我们添加的才能有效 方法:目录树上滑到开头,依次展开 NtHeader -》OptionalHeader -》DataDirArray, 找到一个“BaseRelocationTable”展开,点一下“Size”,看到是【D0 78 00 00】,也就是刚刚我们看到的78D0H 给它加上我们增加的块大小10H,得到78E0H ,所以将上面的数据修改为:【E0 78 00 00】,保存文件或另存为“victim4.exe" (10)大结局 至此,EXE文件的改造工作全部结束。 再次用StudyPE+打开我们改造过后的EXE,查看导入表和重定位表,确认都没有问题。 运行测试,EXE程序工作正常,串口数据发送正常,电源控制正常。
由于技术生疏,边学边改,以上程序前后经历了一周多才完成。 因此记录下来,希望能给初接触EXE文件修改的新手们一点参考。
注:若转载请注明大神论坛来源(本贴地址)与作者信息。
|