汇编与反汇编
汇编与反汇编的区别
phase_1 比较字符串是否相同
二进制炸弹
常见汇编指令详解
AT&T 格式Linux 汇编语法格式
AT&T(GAS)汇编指令小集
ESP和EBP指针寄存器
汇编指令
步骤一: 反编译
将二进制可执行文件变为汇编语言
objdump -d bomb > bomb.s
反编译命令 objdump -d
步骤二:调试
执行二进制可执行文件
gdb bomb
gdb命令
gdb调试命令 全面
一些gdb简单命令
一步一步学调试——gdb命令小结
gdb layout使用
断点
设置断点 b 运行 r 退出ctrl+d 或 quit
x/3i $pc显示3条指令(3为示范,数字可选)
display result
display相当于添加监听变量,每一次run后都会给出result的值。而print就对应着IDE中的鼠标停留时显示变量的值。
$pc 指向当前程序运行地址
display/i $pc 跟踪显示命令
1.查看display设置的自动显示的信息
info display 。
2.删除自动显示,dnums意为所设置好了的自动显式的编号。如果要同时删除几个,编号可以用空格分隔,如果要删除一个范围内的编号,可以用减号表示(如:2-5)
undisplay dnums…
deletedisplay dnums…
3.disable和enalbe不删除自动显示的设置,而只是让其失效和恢复。
disabledisplaydnums…
enabledisplaydnums…
(gdb)(e)x(amine)
语法:
x/
n选择从当前地址向后显示几个
f是显示格式,还有s字符串和i整型
u表示从当前地址往后请求的字节数,如果不指定的话,GDB默认是4个bytes。u参数可以用下面的字符来代替,b表示单字节,h表示双字节,w表示四字节,g表示八字节。当我们指定了字节长度后,GDB会从指内存定的内存地址开始,读写指定字节,并把其当作一个值取出来。
例子:
x/3uh 0x54320表示,从地址0x54320读取,h表示以双字节为单位,3表示三个单位,u表示十六进制
问题:
解决过程:
问题原因查找
通过命令 readelf -l 地址 | grep interpreter 查找原因
原因为:/lib/ld-linux.so.2
解释:系统为64位,缺少32位依赖,无法运行32位程序
解决方案:
64位ubuntu16.04兼容32位程序
安装如下三个依赖:
sudo apt-get install libc6:i386
sudo apt-get install lib32stdc++6
sudo apt-get install lib32z1
gdb调试
设置断点 b 地址
查看断点 info br
删除断点 单个删除 delete 序号 如delete 1 多个删除 delete 范围序号 如delete 1-3
phase1 比较字符串
由上面代码,主要关注strings_not_equal,就是比较字符串是否相同:那应该有一个样例与所输入的进行比较,因此压栈操作引起了我的注意,查看其内容并输入,发现第一个成功破解。
phase2
* LEA指令
lea 7(%edx, %edx,4), %eax ==> 将寄存器%eax的值置为 5 * %edx + 7.
base(offset, index, i) 计算方法为base + offset + index * i
phase2
phase_2 循环 寻找数字间的逻辑
可以看出第一个爆炸点是比较第一个数字与第四个数字大小,相等才不会爆炸。
由上图可以得知,由ebp地址开始每四个字节存储一个数字,ebp-0x38为第一个数字,ebp-0x2c为第四个数字。
lea -0xc(%ebp),%eax incl (%eax)
由分析可知 ebp-0xc为计数器 ,假设用N表示,每次循环后加一
分析第一个红框炸弹点:
mov -0xc(%ebp),%eax
mov -0xc(%ebp),%edx
简单分析逻辑,首先eax和edx赋值为N
mov -0x38(%ebp,%eax,4),%eax
cmp -0x2c(%ebp,%edx,4),%eax
六个数字存储用数组A[6]表示,因为每4字节存储一数字,每次N增加一,其相当于向后挪一位数,从第1个和第4个开始对比,遍历到所有数字。
-0x38(%ebp,%eax,4)为ebp+4N-0x38就相当A[0+N]
0x2c(%ebp,%edx,4)为ebp+4N-0x2c就相当A[3+N],
对比A[0+N]与A[3+N],不等则爆炸。
分析最下面红框第二个爆炸点:
mov -0x38(%ebp,%eax,4),%edx
lea -0x10(%ebp),%eax
add %edx,(%eax)
ebp-0x10 存储每次累加的和
cmpl $0x0,-0x10(%ebp) jne 80489ee call 80487de
和为零则爆炸
简易逻辑如下:
phase_3
相当于switch函数。
分析第一个爆炸点
首先随意输入了4个数字
通过观察sscanf运行前后的内存状态及观察内容,发现其功能是读取两个数字,存储在ebp-0x4和ebp-0x8位置,并用eax存储其个数。这也就是第一个爆炸点要求的输入的个数大于1。
分析第二、三个爆炸点
首先有cmp可知要求第一个数字小于7,否则爆炸。
接下来读取第一个数字到eax中,并进行逻辑左移,移两位,就相当于扩大4倍。例如,0x1逻辑左移两位后变为0x100,就由1变为了4。由于每四字节存储一个数字,就是每四个地址存储一个数字。并且mov 0x8049060(%eax),%eax可翻译为eax=0x8049060+4*第一个数字。可以看出我们输入的一个数字对应一个地址。
通过观察由0x8049060开始的内存内容及代码段地址,可发现他们之间的关联。输入的第一个数字决定跳转的位置,第一个数字范围0-7。
跳转的代码作用:存入数字。
之后代码作用:见下图,使存入的数字并与第二个数字进行比较,相同则安全,不同则保证。注意上面是16进制数,输入时要转换为10进制数。
举例:如第一个数输入1,则对应0x1b7,换为10进制为439.
phase_4 递归函数
可以看出,只读取了一个数字,读取到ebp-0x8,eax计数为1。因此应该输入一个数字。
介绍下面拆除前,首先分析函数调用及返回,调用时将该函数的下一行代码地址压入栈,上图就应该是0x8048b0b,函数结束应该执行下条语句,也就是地址0x8048b0b。
由上面可知,ebp-0x8是我们所输入的数字。但随着fun4的调用,我们数字的存储位置为ebp+0x8
因此上面的循环可以解释为每次,每次该数字减1,并压栈,下次继续取出直至减到0才能继续。
如果不为0,则继续进入func函数,就会在下一行代码留下个标记点,如果输入的数字为3,则留下3次标记点,直至减至0才开始向下进行,由代码可知0时eax赋值为1。
接下来分析橘黄框,edx=8*eax;eax=edx-eax;结果就为eax=7*eax 每次变为原来的7倍;
已知0时eax=1,之后开始恢复标记点;1时eax=7;2时eax=7*7;3时eax=7*7*7;
这就是我们熟悉的递归函数。
拆除爆炸点:
cmpl $0x1cb91,-0x4(%ebp) 不等则爆炸;
0x1cb91变为十进制数为117649 为7的6次幂,因此输入的数字应为6
phase_5
看到string_length很自然想到比较字符长度,同时根据以上实验的经验,一般eax寄存器用来计数,事实证明,本关也是。因此第一个爆炸点就是要求字符串长度为6,不等则爆炸。
分析第二段代码,这是一个循环,可以看出循环六次,可以联系与输入的字符串有些关系。可以看出红框的add语句和黄框的mov语句,都是向下取值,其中ebp+0x8及0x804a620引起我们的注意;接下来可以看出绿框,ebp-0x8是存储累加值的,这个值就是下个爆炸点的判断;ebp-0x4是存储计数的。
查看ebp+0x8存储的内容为一地址,继续查看改地址就是我们所输入的字符串,因此add操作相当将字符串中的内容逐个取出,并存入eax进行下一步操作。
通过观察0x804a620,我们可以联想到数组,每个位置存储固定的数字。
mov 0x804a620(,%eax,4),%edx可翻译为edx=(0x804a620+4*eax)
这个可以看出我们输入的字符串的内容,影响着取出的内容,他们之间是一一对应关系,比如输入0取出的就是0x00000002等等,由于数字只能为0-9取出的范围4*eax为0-36;考虑到16进制,可以输入字母a-f,这样就扩大了范围。
之后lea -0x8(%ebp),%eax add %edx,(%eax) 是将取出内容进行累加,并存到ebp-0x8中;
分析最后一个爆炸点,要求累加的和等于0x25,也就是37;
综合考虑输入字符串有个数6个限制,观察对应的数组,相加的和等于37;因此可以有多种组合,列举其中一种:033355
phase_6
首先看代码,好多循环。。。
第一个为两层循环,目的是:是输入的6个数各不相同,且范围是1-6;否则爆炸。
上面两个压栈的地址,引起我们注意ebp-0x38及ebp+0x8,验证可知,是存储输入6个数的位置。
因为此次测试输入的为123456,因此一一对应。
输入 -0x58存储内容 相对于值 大小
1 0x0804a69c 0xce 6
2 0x0804a690 0x3c8 1
3 0x0804a684 0x106 5
4 0x0804a678 0x21f 3
5 0x0804a66c 0x276 2
6 0x0804a660 0x158 4
可以看出,每个数对应一个地址,存储在ebp-0x58;且每个地址对应一个值,按值的大小排序,输入顺序应该为 2 5 4 6 3 1
第三个循环目的是将输入所对应的地址串联起来,就是第一个对应的地址+0x8存储下一个数对应的地址,如上图。
可以看出,这个循环是比较大小,前一个数所对应地址存储的值大于后一个,则安全;
根据前面分析输入范围为1-6,且输入各值不同;按照所对应地址的值从大到小排序,输入顺序应为2 5 4 6 3 1.