freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

0

1

2

3

4

5

6

7

8

9

逆向分析教程(四)——打补丁
dopromax 2022-01-05 10:23:52 228240
所属地 上海

该系列文章以《逆向工程核心原理》为原型,调整其中对于新手需要耗费大量时间解决之处,更方便于新手入门。也同时解决互联网上扫描件电子书版本的阅读障碍(如截图模糊等)。

前言

代码逆向分析中,“打补丁”操作是不可或缺的重要主题。利用“打补丁”技术不仅可以修复已有程序的BUG,还可以向程序中添加新功能。“打补丁”的对象可以是文件、内存,还可以是程序的代码、数据等。本示例中,我们将使用“打补丁”技术把HelloWorld.exe程序消息窗口显示的“Hello World!”字符串更改为其他字符串。

我们后面会经常用到“打补丁”技术

请记住,我们的目标是把消息对话框中显示的“Hello World!”字符串更改为其他字符串。前面我们已经查找到了调用MessageBoxW的部分和“Hello World!”字符串的地址,这已经算成功了一半。

按Ctrl+F2快捷键重新调试,并使调试流运行到main函数的起始地址处(401000)。在401000地址处按F2键设置断点,再按F9执行程序。main()函数的地址401000被用作“大本营”(40104F)后第一个“前进营”。

修改字符串的两种方法

我们先介绍2种简单的修改字符串的方法。

1.直接修改字符串缓冲区(buffer)

2.在其他内存区域生成新字符串并传递给消息函数。

这两种方法各有优缺点,我们详细的来了解一下。

1.直接修改字符串缓冲区

MessageBoxW函数的字符串参数“Hello World!”保存在地址4092A0处的一段缓冲区,只要修改这段内容,就可以修改MessageBoxW函数显示出的字符串。在Dump窗口中按Ctrl+G快捷键执行Go to命令,在弹出窗口中输入4092A0进入字符串缓冲区。然后使用鼠标选中4092A0地址处的字符串,按Ctrl+E快捷键打开编辑窗口。

从图中可以看出,Unicode形式的“Hello World!”字符串占据的区域为4092A0~4092B0 (Unicode编码中用2个字节表示1个字罗马母)。用新字符串覆盖写该区域。

如果新字符串大于原有字符串,执行覆盖操作时可能损坏字符串后面的数据,所以一定要小心。特别是字符串后面有非常重要的数据时,覆盖操作导致数据损坏就会引发程序内存引用错误。

在弹出的编辑器窗口Unicode文本框中输入“Hello bylibrary!”字符串

unicode字符串必须以NULL结束,它占据2个字节(添加NULL时不能直接在unicode文本框中进行,而要在HEX项目中添加)。

更改后的字符串“Hello bylibrary!”的长度要比原字符串“Hello World!”更长一些。原字符串后一般会存在某些有意义的数据,使用更长的字符串覆盖原字符串时,数据可能会遭到损坏,这是十分危险的。之所以上面我们要采用更长的字符串覆盖,一方面是为了打广告,一方面是为了更好的向大家演示,实际操作中不建议这样做。

我们再返回main()函数,(还记得第一个前进营吧。)

虽然指令保持不变,但原字符串已经被新字符串取代,用作MessageBoxW()函数的参数,并且参数的地址仍为4092A0,只是该地址空间中的内容(字符串)发生了改变。按F9运行程序后,将弹出消息窗口,可以看到显示出新字符串。

以上就是直接更改字符串缓冲区来修改的方法。这种方法的优点就是使用起来十分简单,但缺点是它对新字符串的长度有限制,新字符串的长度不应比原字符串长。

可执行文件保存字符串时一般会给字符串多留出一些空间,第二张图中的HelloWorld.exe程序就是如此。所以,如果你的运气足够好,使用更长的字符串覆盖原字符串时,即使原字符串后面的部分空间被侵占,程序仍然能正常运行。但是我们不建议大家这样做,随着这些不安定因素逐渐增多,整个系统的稳定性最终会遭到破坏。请记住,我们是解决问题的人,而不是制造麻烦的。


保存更改到可执行文件

上面的调试中,我们通过修改字符串缓冲区更改了程序显示的消息内容,但是这种更改只是暂时的,终止调试(结束Hello World进程)后,程序中的原字符串仍然没有改变,如果想把这种更改永久保存下来,就要把更改后的程序另保存为一个可执行文件。

我们选中更改后的“Hello bylibrary”字符串,单机鼠标右键,在弹出的菜单中选择Copy to execuable 菜单,打开如下所示的HEX窗口

在弹出的Hex窗口中单机鼠标右键,选择Save file菜单,在Save file as对话框中输入文件名“Hello Reversing.exe”后保存为.exe可执行文件。然后运行该文件,弹出消息窗口,显示的字符串已经变为“Hello bylibrary”。

2.在其他内存区域新建字符串并传递给消息函数

如果要用“Hello bylibrary world!!!”替换原字符串“Hello World!”,上述方法就不适用了。此时我们就要换一个方法。

按ctrl+F2我们来重新调试,再按F9运行,由于之前我们在main()函数的起始位置处(401000)设置了断点,所以调试流自动转到main()函数处。再看一下main()函数。

401007处有一条PUSH 004092A0命令,它把4092A0地址处的“Hello World”字符串以参数形式传递给MessageBoxW()函数。

向MessageBoxW()函数传递字符串参数时,传递的是字符串所在区域的首地址。如果改变了字符串地址,消息框就会显示变更后的字符串。在内存的某个区域新建一个长字符串,并把新字符串的首地址传递给MessageBoxW()函数,可以认为传递的是完全不同的字符串地址

上面这样的做法看起来是没有问题的,但是我们需要考虑另外一个问题:“应该在哪块区域创建新字符串呢?”,要解决这个问题,我们又要留坑了,这个需要在后面了解到PE文件格式以及虚拟地址结构相关知识才行。

我们在方法1中修改的字符串地址为4092A0,我们在dump窗口查看该部分。向下拖动滑动条,相应内存区域由NULL填充(NULL padding)结束。

这部分就是程序中未使用的NULL填充区域。

如果找不到你肯定又忘了ctrl+G这玩意了,4092A0,往下拖

应用程序被加载到内存时有一个最小的内存分配大小,一般为1000。即使程序运行时只占用100内存,它被加载到内存时仍然会分到1000左右的内存,这些内存一部分被程序占用,其余部分为空余区域,全部被填充为NULL。

我们最好把这个地方当作字符串缓冲区并传递给MessageBoxW函数,用快捷键Ctrl+E向结尾部分适当位置(比如409F50)写入新字符串(“Hello bylibrary!!!”)即可。

但是如果只是进行上面的操作,我们时没有办法更改消息框中的字符串的。既然我们已经新建了缓冲区,接下来就应该把新的缓冲区地址(409F50)作为参数传递给MessageBoxW函数。所以,我们需要在代码窗口中使用汇编命令修改代码。

我们把光标移至401007处,按空格键或者双击打开Assemble窗口。

在打开的窗口中输入“push 409F50”,地址409F50为新字符串“Hello bylibrary!!!”字符串。

用户可以在Assemble窗口中输入任何想要输入的汇编指令,输入当时就能在代码中体现出来,也可以被执行。这种“在运行过程中动态修改进程代码”的方式正是调试最强大的功能之一。

在OllyDbg中我们按F9运行程序,弹出消息窗口,可以看到字符串已经被修改了。

如果我们把修改后的代码重新保存为程序文件,可以发现程序无法正常运行,这是由409F50这一地址引起的。可执行文件被加载到内存并以进程形式运行时,文件并非原封不动的被载入内存,而是要遵循一定的规则进行。在这个过程中,通常进程的内存是存在的,但是相应的文件偏移并不存在。比如,我们上面的例子中,与内存409F50对应的文件偏移就不存在,所以修改后的程序无法正常运行。如果还是有疑惑,还是因为我们还没有掌握PE文件格式的相关知识的原因,这些问题我们可以以后再解决。

Q&A

经过前面四章的学习,有三个问题需要顺便给大家解答一下。

A.快捷键F4与F9的区别

两个都是“运行”命令,F9为Run(运行),F4为Run to Cursor(运行到光标处),F9是运行整个程序的命令,而F4仅运行到当前光标所在位置,可以把F4看作断点与F9命令的组合。

B.什么是启动函数

启动函数(Stubcode)不是用户编写的代码,而是编译器任意添加的代码。编译程序时,不同编译器会根据自身特点添加不同启动函数,特别是EP代码区域中存在着许多启动函数,它们也被称为启动代码(StartUp code)。调试程序时,我们不需要仔细分析这些启动函数,但是初学者有必要分清程序中哪些是启动函数,哪些是用户代码。希望大家调试时多看一看这些代码,熟悉后就能轻松区分。

C.到底什么是PE文件,为什么要等到后面才讲解?如果不懂得PE文件是否就无法调试?

PE是Portable Executable的简称,它是Windows操作系统下的可执行文件的格式,主要包含了对文件规格的描述,代码逆向分析技术的初学者学习它会感到非常吃力、无趣。所以我们并没有在前面详细讲解,更重要的是先让大家感受到调试的乐趣,然后再一点点地学习。此外,如果不了解PE文件结构的相关知识,将无法进行高级调试。

# 逆向工程 # 逆向分析 # 逆向技术 # 逆向基础 # 逆向工程核心原理
本文为 dopromax 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
逆向分析系列教程
dopromax LV.5
  • 32 文章数
  • 344 关注者
JWT身份验证相关安全问题
2022-08-07
HTTPS单双向认证流程详解与联想
2022-03-08
逆向分析教程(七)——栈
2022-03-02
文章目录