前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >反汇编与二进制分析的一些基本知识

反汇编与二进制分析的一些基本知识

作者头像
望月从良
发布2021-03-27 18:05:11
2.3K0
发布2021-03-27 18:05:11
举报
文章被收录于专栏:Coding迪斯尼Coding迪斯尼

在程序届有一句名言:如果你能读懂汇编,一切程序对你来说就是开源。所以要抵达黑客层次,不熟练的掌握反汇编分析技巧那是不可能的。本节我们看看一些反汇编的工具和相关技巧,后续我们再看看一些高级方法该怎么用。

常用的反汇编工具一般需要执行三个步骤,1,加载要反汇编的二进制文件;2,从二进制文件中找到所有机器指令;3,将指令转换为汇编语句;通常第2步是一个难点,由于机器指令与通常的二进制数值无异,因此很容易把不是指令的数值认为是机器指令。为了尽可能降低步骤2的错误,反汇编算法常采用两种模式,分别是线性反汇编和递归反汇编。

线性反汇编其实就是从头走到尾,将所有二进制数值都认为是机器指令,然后将其转换为汇编语句,我们常用的Objdum就是如此。这当然会产生问题,很显然不可能所有二进制数值都是机器指令,因此这种做法容易将原本是数据的数值看做是指令。由此会带来两种错误,一种是将数值转换成无效机器指令,一种更糟糕,数值正好对应了某条机器指令,于是给后面的分析带来巨大的干扰,如下图所示:

左边是正确的反汇编,其中前4字节是数据,如果将前4字节看成指令那就好把他们认为是mov, pop这种指令,由于解读错误,接下来又产生了一系列错误指令,例如接下来的add, mov,最可怕的是错误反汇编的add指令其实是将数据和指令混合在了一起,这样的话就严重影响了后续指令的解读。

最右边的指令对应反汇编从字节20开始视作指令,于是把4字节数据中的后4节也就是20,5c,00,55解读成指令and [rax+rax*1+0x55], bl。对于恶意程序而言,他们会特意在代码中穿插一些数据,这样就能干扰反汇编工具,使得安全人员很难对其进行准确的分析。

接下来我们看看递归反汇编。它的基本思路是寻找程序的控制流,它首先从main等程序入口着手,然后先是线性反汇编,如果遇到jump等指令,它就会跳到jump对应的地址继续反汇编。这种情况也容易出问题,因为程序的控制流很难追踪,因为很多跳转其实是隐性跳转,也就是这种跳转不会在二进制文件中给出具体地址,需要在运行时才能确认具体地址,如下图所示:

上图中 jmp BB1中的BB1是显性地址,而call eax就是隐性地址,其中[fptr+ecx]对应f1的地址,那么此时只有代码运行起来才能知道eax的值,要不然在静态情况下,反汇编工具基本上无法确定eax对应的具体数值,因此它就很难跳到给定地址进行反汇编。

我们看一个反汇编的实例:

左边是一段c语言代码,右边是对应的反汇编指令,左边C语言的逻辑很简单,它遍历一个数组,检测每个元素值,如果它满足一些条件就直接忽略,满足一些条件就返回它对应下标,满足另一些条件就调用fatal函数后退出,然而对应右边反汇编的指令就很难理解其逻辑。右边汇编语言采用一种叫”跳转表“的方法,在地址4438f9处的指令,mov rax, [rax8+0x49e840],其中0x49e840对应表的入口地址,rax8对应表的偏移,读取的数值就是要跳转的地址。在这种情况下,递归式反编译就相当难做,因为你根本不知道 [rax*8+0x49e840]这个地方会出现什么数值,而且数值会根据程序的运行状态不断改变。

由于静态反汇编面临一系列困难,因此我们需要动态反汇编的帮助。它的基本思路是将代码运行起来,在运行中设置断点,然后从暂停处进行反汇编,就像前面我们用过的那样。这种办法也有缺陷,那就是无法做到分支的充分覆盖。显然在linux上,最常用的动态反汇编莫过于使用gdb了,我们看一个例子,启动linux系统,然后执行gdb /bin/ls 也就是动态反汇编ls命令程序,然后执行命令info files,该命令会显示在运行ls程序时有多少文件被打开读取,目前也就只有ls对应可执行文件被加载,

其中entry point表示ls对应可执行文件被加载到内存后的入口,也就是从改地址对应的指令开始执行,于是在0x409a0处设置断点就能让ls程序在一开始执行时就暂停。于是我们执行命令b *0x409a0,然后执行命令set pagination off, set logging on,set loggin redirect on,前者作用是让gdb不要一下子将太多信息输出到控制台,只要输出几行就行,后者是让gdb将运行过程中的信息输出到文件gdb.txt,然后执行命令run,一运行程序就里面暂停住。

暂停时执行指令display/i $pc,它的意思是让gdb将当前要执行的指令输出到gdb.txt,接着输入命令while 1,它的意思是让gdb逐条指令执行,并将执行的指令输入到gdb.txt,直到所有指令执行完毕程序退出为止。过一会使用ctrl+c退出,然后执行命令quit退出gdb,此时我们使用命令wc -l gdb.txt可以发现它已经包含几万条内容。使用head -n 20 gdb.txt可以打印出前20条,使用正则表达式对文件内容进行过滤:

代码语言:javascript
复制
egrep '^=> 0x[0-9a-f]+:' gdb.txt | head -n 20

动态反汇编只能让我们解析程序运行经过的指令,如果代码中包含逻辑炸弹,也就是某些恶意指令只能在某个时间点过后才执行,那么动态反汇编就找不出这些恶意代码。而且动态反汇编会让程序运行的速度变慢,因此有些恶意程序甚至会监控它自身运行速度,一旦发现自己执行慢了就会探测到它正在被动态分析。

综合而言,我们必须静态和动态分析结合着用,后面我们还会看到更多高级技巧。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-03-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Coding迪斯尼 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档