Linux下使用objdump+vim+xxd进行反汇编并修改指令

前段时间花了一个星期时间马马虎虎算是对汇编入了门吧(好吧,其它我还是什么都不懂),最近又开始对汇编有点兴趣了,于是想试下反汇编的感觉并尝试自己修改下指令
据说对一个程序反汇编后再修改的方法是以十六进制的方式打开程序,然后再通过一些工具找到相关的位置再用相关工具计算出偏移量等然后再做修改,不过我从来没搞过反汇编,对汇编也是一知半解的样子,所以就没那么专业了,至于能不能直接使用汇编的助记符修改我还不知道,估计应该是有这样的工具吧,不过我没搜索过,所以只尝试下十六进制下以汇编指令的十六进制形式进行修改了,如果有那样的修改方法我希望你能够告诉我,谢谢
在linux下反汇编我们可以使用gdb这个调试器也可以使用objdump这个工具,当然还有其它工具,不过这里我就不说了,然后用十六进制打开文件我们可以使用xxd,我们通过在vim内部调用xxd来对文件进行十六进制模式下修改,当然还有其它的十六进制编辑器,这里我也就不说了,因为我也就是玩玩,毕竟最近还在学习Win32 API(我真蛋疼),下面就从一个简单的程序开始吧

大家先看这个程序,很简单,一个主函数,还有一个jmp函数,正常情况下在编译链接后执行是不可能调用到jmp函数的,但我们要做的就是通过反汇编然后修改指令,让该程序在执行完主函数里的printf函数之后跳转到我们jmp函数的首地址以执行jmp函数

没有采用一些特殊的见不得人的手段的话该程序运行后的效果是这样的
然后我们以二进制形式打开可执行程序文件,vim可以使用-b选项来用二进制形式打开文件
vim -b jmp

可以看到是这个样子的,然后我们使用xxd以十六进制模式对该文件进行编译如上图中的那样
:%!xxd
%表示我们当前的文件,!表示我们要执行一个外部命令(程序)
现在我们反汇编下这个程序
objdump -S -M intel jmp

反汇编后的内容比较多,我们就需要看main函数和jmp函数就够了
现在我们可以看出main函数在调用完printf函数返回后(从这样可以看出是调用了puts),下一步的指令是mov eax,0x0,我们还是先看一下反汇编的程序以及相关参数吧
-S这个其实也可以使用-d,这样反汇编出来后会显示地址,十六进制指令,还有就是对应的汇编代码-M intel表示我们以intel语法反汇编,linux下默认的汇编语法格式是AT&T的,这个我不习惯(其实我不会),好了,我们再继续往下说吧
我们刚刚说到在主函数调用完printf函数返回后的下一条指令是给寄存器eax赋0值,具体什么作用就不研究了,我们只说下我们所关心的问题,那就是跳转问题,我们要让程序跳转到jmp函数的地址处,所以我们要修改这条指令
现在我们先停下来说说我们的指令,我们要跳转到jmp,对应的汇编指令应该类似这样的
jmp short jmp
后面的那个jmp不是汇编指令,是一个函数名,当然是个在c语言中是一个合法的标记符,但在汇编语言中我们不要将标号设置成这样,这个指令经过汇编编译器编译后应该是类似这样的
jmp 80483c4
这里的80483c4是0x080483c4也就是jmp函数的地址
所以我们已经知道了我们的指令是什么,但是怎么用十六进制进行表示呢?
我告诉你这条指令应该类似这样写
e9 d2 ff ff ff
e9是跳转jmp的机器码十六进制表示(这个应该是个段内跳转,而段外跳转应该是ff),后面的d2 ff ff ff表示偏移量,这个是怎么计算出来的呢?好吧首先我还是先说下我是怎么知道段内跳转的机器码十六进制表示为什么是e9吧,其实很简单……..你丫不会自己先写一个jmp指令然后再反汇编啊(我特别邪恶- -),好了下面说下偏移量的问题
首先从反汇编的结果我们可以看出来我们应该向上跳转,所以跳转的偏移值是一个负数,而计算机内部表示有符号数的方法是采用补码,最高位表示符号位,1表示负,0表示正,正数的补码是这个数本身,负数和补码为最高位置1,其它位反转后再加1,那么我们可以从反汇编中的结果中先计算出我们要向上跳转多少

这个程序是用于计算跳转偏移量的,其中a表示我们要修改的指令的首地址加上指令自身所占的内存,b设为我们要跳转到的地址,这样就可以计算出偏移量了,然后我们知道我们是向上跳转的,所以要取得补码,这里我们减过后的数是一个正数,最高位是0所以我们直接对该数取反加1,这样最高位为1,其它位反转加1,刚好得到负数补码形式

通过计算之后我们得到的结果是0xd2ffffff
记住数据在内存中的存储顺序哦,所以我们最终的代码为e9 d2 ff ff ff


这是我们使用xxd打开后的十六进制形式,我们打开我们要修改的指令处,怎么找你应该看得出来吧,使用vim自身的查找功能,很简单的,然后我们就可以进行修改了,将我们的e9 d2 ff ff ff替换掉后面的b8 00 00 00 00(这个还是替换掉好,虽然我这里是先替换然后在后面将先前指令再补上去的,但是这是因为我们的程序后面有大量的nop指令,所以可以覆盖,但是如果没有的话那么如果我们直接在里面添加的话会导致程序一团乱,这个大家自己可以亲身体会下
修改完成后我们再
:%!xxd -r
其它的命令和前面一样,-r参数表示我们修改完后回到上一层,也就是我们刚用二进制形式打开可执行文件的那种状况,当然现在已经是被我们修改过后的程序了,当然不要忘记保存,不保存也是瞎忙活了
现在我们再次执行一下程序看看效果

哈,你看,我们成功了,我们已经成功在执行完主函数第一个printf函数后将程序的流程跳转到jmp函数处执行了,我们通过黑暗手段让程序改变了原来的执行路线

下面我们再反汇编一下这个已经被我们修改过的文件看下,我们可以看出来我们在主函数中调用printf函数指令后的指令正是一个跳转指令,我们可以看到它后面要跳到的地址,正确无误
好了,到这里就全部结束了,如果你有时间去折磨和研究你可以试试修改为其它的指令来达到不同的目的,甚至于搞一些破坏,破解什么的

–阅读次数(47)

发表评论

电子邮件地址不会被公开。

*