吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 15258|回复: 171
上一主题 下一主题
收起左侧

[原创] 手把手教你让EXE调用自己写的DLL函数实增加功能

    [复制链接]
跳转到指定楼层
楼主
jackyyue_cn 发表于 2023-12-6 21:15 回帖奖励
新手第一次写逆向和改造的过程,难免有错误或走弯路的地方,请大神们多多指正。
本文旨在探讨程序编写技术,相关程序均为自已编写代码测试,请勿用于非法目的。

【情境介绍】注:为避免引起歧义,以下为虚拟情境,如有雷同,纯属巧合。



最近拿到某办公程序(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字符串的机器码一样长(后面再说)。
到这里,第一步分析完成。
[Asm] 纯文本查看 复制代码
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, 在函数里就可以做很多事情了,
比如发送系统消息、给串口发数据、访问某个网址等都行。
这里我采用了插入一个串口继电器,给串口发数据控制电源通断的方式。
[C++] 纯文本查看 复制代码
//用于导出的函数
// 开始消息,设置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文件用于导出规范名称的函数,要不然函数名是乱码的
[Plain Text] 纯文本查看 复制代码
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输入要找的地址直接过去
[Asm] 纯文本查看 复制代码
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
[Asm] 纯文本查看 复制代码
00F72EB5 | 60                       | pushad                                                                 |
00F72EB6 | FF15 CCCCCCCC            | call dword ptr ds:[CCCCCCCC]                                           |
00F72EBC | 61                       | popad                                                                  |

这段代码就可以完成调用某个函数的功能了,CCCCCCCC暂时不管,等会儿改成我们自己的函数地址

接着把00F6AE83的5个字节写过来
[Asm] 纯文本查看 复制代码
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】

将这几个地方都算出来
[Asm] 纯文本查看 复制代码
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字节,算出数据的地址:
[Plain Text] 纯文本查看 复制代码
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文件修改的新手们一点参考。

免费评分

参与人数 70吾爱币 +64 热心值 +64 收起 理由
zww1 + 1 谢谢@Thanks!
beatone + 1 热心回复!
水木杉 + 1 + 1 热心回复!
笙若 + 1 + 1 谢谢@Thanks!
xgwzz + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Lijienu + 1 我很赞同!
111mojie + 1 + 1 谢谢@Thanks!
iTMZhang + 1 + 1 用心讨论,共获提升!
gs168 + 1 + 1 谢谢@Thanks!
SNSD99 + 1 + 1 收藏,点赞并学习学习!
nwllxxw + 1 用心讨论,共获提升!
BIGSMATER + 1 + 1 谢谢@Thanks!
pgnet + 1 + 1 我很赞同!
抱书人人 + 1 + 1 谢谢@Thanks!
枯头草 + 1 易语言有个永久注入DLL
a1pha + 1 + 1 看的我热泪盈眶,行文缜密的背后是多少人都看不到的钻研和艰辛。行事低调,.
qq1269560093 + 1 + 1 我很赞同!
noheroo + 1 谢谢@Thanks!
liyonxin + 1 我很赞同!
52panggirl + 1 + 1 谢谢@Thanks!
不一般 + 1 + 1 我很赞同!
w8588997 + 1 我很赞同!
Moical + 1 用心讨论,共获提升!
SupSky + 1 + 1 用心讨论,共获提升!
terryyann + 1 + 1 谢谢@Thanks!
SaTang + 1 感谢您的宝贵建议,我们会努力争取做得更好!
wjstnj + 1 + 1 我很赞同!
sjclch + 1 + 1 我很赞同!
Cofei430 + 1 + 1 谢谢@Thanks!
yanyc + 1 + 1 我很赞同!
a3859495 + 1 + 1 谢谢@Thanks!
YunQianQian123 + 1 + 1 谢谢@Thanks!
米修米修ne + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
唐小样儿 + 1 + 1 我很赞同!
success67567381 + 1 我很赞同!
BayMax2911 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
yp17792351859 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Ellis + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
zhixiangwangluo + 1 + 1 谢谢@Thanks!
bullshit + 1 + 1 谢谢@Thanks!
朕来打江山 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
qqycra + 1 + 1 用心讨论,共获提升!
ieusr + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
eijop252023 + 1 + 1 我很赞同!
cy51168 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
wdfdiablo + 1 + 1 用心讨论,共获提升!
Z781287 + 1 谢谢@Thanks!
Chenda1 + 1 + 1 我很赞同!
fix9527 + 1 + 1 谢谢@Thanks!
wengl123 + 1 + 1 谢谢@Thanks!
Wyiyun777 + 1 + 1 热心回复!
UnTracker + 1 + 1 用心讨论,共获提升!
ciker_li + 2 + 1 谢谢@Thanks!
wjlqz + 1 + 1 谢谢@Thanks!
allspark + 1 + 1 用心讨论,共获提升!
wenzijun + 1 + 1 热心回复!
eec + 1 + 1 我很赞同!
sam喵喵 + 1 + 1 谢谢@Thanks!
tqp + 1 + 1 我很赞同!
lalicorne + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
冥界3大法王 + 2 很好!一定我就对二进制忍者进行实操
tianyu925 + 1 我很赞同!
ZiPP + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
wttmm + 1 + 1 谢谢@Thanks!
ouyanghd + 1 + 1 我很赞同!
微笑嘻嘻 + 2 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
nbcty + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
lgc81034 + 1 谢谢@Thanks!
fireshark + 1 + 1 谢谢@Thanks!
rsice + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

来自 131#
 楼主| jackyyue_cn 发表于 2023-12-24 15:35 |楼主
xiaolei1314555 发表于 2023-12-21 13:49
24个CC的特征码么有的话怎么办呢

理论上来说,我觉得只要是在EXE中的可执行的代码段范围内,而且程序加载时不会变动,应该都是可以的。
一般在OD的内存布局中找程序的.text段,从开始地址加上大小,得到段结束的地址,从结束地址往回找到一些全是0的空白区域,应该可以添加代码

如上图:00B11000 + 00142000 = 00C53000
往回找到00C52850左右就全是0了

免费评分

参与人数 1热心值 +1 收起 理由
xiaolei1314555 + 1 热心回复!

查看全部评分

推荐
 楼主| jackyyue_cn 发表于 2023-12-10 22:17 |楼主
少污污 发表于 2023-12-10 19:16
我看到别人在别的Exe上添加按键增加功能。怎么弄的

这个我还没有做过,我觉得思路可能有以下方面,不一定对

在EXE没有加密加壳的情况下
1、使用可编辑EXE资源的编辑器,增加一个按钮资源,然后用OD去修改按钮消息处理的函数代码,加入自己的按钮代码(也可以写DLL来调用)
2、直接在OD中找到窗口初始化之类的地方 用 CreateWindow函数创建按钮,然后同样找到消息处理函数添加按钮的代码

免费评分

参与人数 1热心值 +1 收起 理由
yn12322 + 1 我很赞同!

查看全部评分

推荐
冥界3大法王 发表于 2023-12-12 09:33
看完你的贴子,立刻用Delphi写了一个DLL为了增强Everything的功能:
[Delphi] 纯文本查看 复制代码
library Strong;

uses
  System.SysUtils,
  System.Classes,
  Windows,
  Unit7 in 'Unit7.pas' {Form7};

{$R *.res}

function GetDllPathX: string;
var
  ModuleName: string;
begin
  SetLength(ModuleName, 255);       // 取得Dll自身路径
  GetModuleFileName(HInstance, PChar(ModuleName), Length(ModuleName));
  Result := PChar(ModuleName);
end;

function LoadWindow(): Boolean;    //加载主窗口1 MainForm
begin
  if Form7 = nil then
  begin
    Form7 := TForm7.Create(nil);
    Form7.Visible := not Form7.Visible;
  end
  else
  begin
    Form7 := Form7.Create(nil);
    Form7.Visible := not Form7.Visible;
  end;
end;

exports
  GetDllPathX,
  LoadWindow;

begin
end.

到  
这一步,我的DLL和你的就对不上路子了。

JMP xx修改后,x64dbg提示,指向可执行内存区域。。。
后边就不知该如何操作了。
沙发
bester 发表于 2023-12-8 11:17
1.studype去掉重定位跟动态基址 你可以省点事儿
2.extern "C" 两个函数声明时加这个 ,可以避免C++函数乱码,据说是因为C+有重载
3.然后其实可以直接inline hook 避免手动汇编

点评

不加extern "C"是c++的命名规则,加了之后是c的规则,通过那些"乱码"的修饰名,可以反向分析出导出函数的参数多少和数据类型 参考:https://learn.microsoft.com/zh-cn/cpp/build/reference/decorated-names?view=ms  详情 回复 发表于 2023-12-12 17:47
只把动态基址固定就行了吧,没必要去掉重定位吧?因为动态基址变了,重定位里面的数据肯定跟着也要变,动态基址都固定了,数据也固定下来了,还要必要去掉?  详情 回复 发表于 2023-12-8 15:06

免费评分

参与人数 1热心值 +1 收起 理由
jackyyue_cn + 1 谢谢@Thanks!

查看全部评分

3#
rsice 发表于 2023-12-8 11:57
感谢楼主分享,不知道何时自己也能做到
4#
wasm2023 发表于 2023-12-8 12:29
感谢楼主分享
5#
xiaobaixuepj 发表于 2023-12-8 12:32
感谢大佬分享
6#
 楼主| jackyyue_cn 发表于 2023-12-8 12:36 |楼主
bester 发表于 2023-12-8 11:17
1.studype去掉重定位跟动态基址 你可以省点事儿
2.extern "C" 两个函数声明时加这个 ,可以避免C++函数乱码 ...

感谢指点,又学到了新的思路

我再去看一看inline hook方面的知识
7#
 楼主| jackyyue_cn 发表于 2023-12-8 12:37 |楼主
rsice 发表于 2023-12-8 11:57
感谢楼主分享,不知道何时自己也能做到

有动力,学习就能事半功倍,加油!
8#
朱朱你堕落了 发表于 2023-12-8 15:06
bester 发表于 2023-12-8 11:17
1.studype去掉重定位跟动态基址 你可以省点事儿
2.extern "C" 两个函数声明时加这个 ,可以避免C++函数乱码 ...

只把动态基址固定就行了吧,没必要去掉重定位吧?因为动态基址变了,重定位里面的数据肯定跟着也要变,动态基址都固定了,数据也固定下来了,还要必要去掉?
9#
xyzliuin 发表于 2023-12-8 15:39
谢谢分享,以前思考过这种方式一直没有机会试用,收藏备用
10#
yong3642270 发表于 2023-12-8 16:04
学习了,谢谢楼主分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-21 20:12

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表