new bomblab
1.Border relations with Canada have never been better.
2.1 2 4 8 16 32
- 1 311
- 7 0
- IONEFG
该实验要求用户输入6个不同的字符串,如果任何一个不正确,炸弹就会爆炸。
1.checksec bomblab 来查看保护开启的情况
2.gdb bomb
3.disass main
4.disass phase_1 栈顶抬高sub,传参给esi寄存器。这个是第二个参数,第一个参数在进入phase_1前已经传入了。
0x0000000000400e32 <+146>: call 0x40149e <read_line>
0x0000000000400e37 <+151>: mov rdi,rax
将我们输入的作为第一个参数。rax是前面read的返回值。
(询问明明是64位为什么是esi寄存器而不是rsi寄存器)
0x402400是一个字符串的首地址,在gdb下输入x/s 0x402400显示这个地址处存的东西,这个字符串是”Border relations with Canada have never been better.”
5.x/s 0x402400
6.test eax,eax 基本上和and eax,eax是一样的,但是test不会改变eax的结果,只是改变flag寄存器的状态。(flag寄存器又是什么?就是进位标志、零标志、溢出标志)当eax为0的时候,令zf=0.
7.je 是一个跳转指令,看zf标志(零标志)
8.返回看eax为0的条件,实际上就是两个字符相等返回0,不相等返回1.
2.disass 段2
前面rax也就是输入的字符开头地址传给rdi作为第一个参数,后面把
保存调用者信息,开辟栈帧,把rsp作为指针传给函数,进入read siz函数中
读进来六个数,rsp rsp +0x4 rsp+0x14 rsp +0x10 rsp+0xc rsp+0x8
10.在phase2下断点,运行,随意输入值。
aaaa 输入以后查看rax是什么值,可以vmmap看下 这个地址是bss段,输入存储在了bss段
六个及六个以上也行。
输入以后step进入函数,可以看见有读六个数函数。
下个断电待会step进去看看
read里面有个scanf函数 可以看到要求的是6个整数。
C 库函数 int sscanf(const char *str, const char *format, …) 从字符串读取格式化输入,如果成功,该函数返回成功匹配和赋值的个数。
jg是什么意思?
Jump if greater。
JG 前>后
这个read number的作用就是看你是不是写了6及6个以上的整数进来,并且将它们放置在栈上。
然后重新运行 ,我们看看栈空间变成什么样了。
dword ptr【rsp】是四个字节 --> 00000001
输入1 2 3 4 5 6,直接next 过了cmp。
发现没有引发任何一个炸弹,无论是数量炸弹还是第一个参数必须为1的炸弹。
disass直接分析汇编。
栈中
这个就是1 2 3 4 5 6 四个字节一个参数
右边是低地址,左边是高地址。 %d是有符号整型数,也就是int 四个字节。
接下来跳转到+52去了
lea指令可以用来将一个内存地址直接赋给目的操作数
一开始第二个参数的地址赋值给rbx;
然后设置栈底rbp;next看下rbp在哪了,在f8这里。
第一次:跳转到+27,rbx=第二个参数地址,dword ptr[rbx-0x4] 第一个参数地址中的数据,赋值给eax;
intel汇编和at&t汇编有些区别
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I1U4y6h9-1631186649285)(https://i.imgur.com/zgp14CG.png)]
eax里面是1,下一个add指令,相加然后赋值给eax,赋值给左边的。
rbx=第二个参数,与这个第一个参数的两倍进行对比。
je,如果相等则跳转,跳到+41这里,把rbx的地址值加了4,再对比rbx和rbp,这个是啥意思呢,就是看rbx到没到rbp这个地方,你看它下面有个jne,意思是不相等则跳转,你看他跳到+27了,这个进入循环了。
第二轮:
把第二轮讲了才好看出规律;
rbx=第三个参数,eax=2+2(rbx-0x4,就是第二个参数=2)=4 ,和第三个数不相等了,直接爆炸了。
1 2 4 8 16 32
我们进行一个思路的梳理。
首先第一个参数必须得是1,每次循环都会将后一个参数和前一个参数的两倍进行对比,不相等就会爆炸。1 2 4 8 16 32
第一轮:rbx=第二个参数,第二个参数去比两倍的前一个参数,这个结束+4;
3+4
4+4
5+4
6+4
当我们是第六个参数的时候,下一个+4就是rbp的位置了。
3 disass 段3
给5分钟看下题目汇编,思考一下
1.可以发现,在进入每个段的时候,输入的数据的首地址都会赋值给rdi寄存器,作为第一个参数。
2.随便输入一个aaaa,查看第一个进入的函数,这个函数和段2那个函数是一样的,会返回参数个数。有两个%d 意味着得输入两个整型数
3.重来输入1 2,记得要在段三加断点。
4.发现确实躲过了第一个炸弹点
JA(jump above)大于则转移到目标指令执行。 第一个参数要小于7,+8是这个1所在的地方。取四个字节。
后面看这个数要和311相比 那就填上311
回过头来再看地址跳转,x/20gx 0x402470 发现这个就是个switch表,rax输入的不同,后面eax的值也不一样,刚才我们输入的1,对应的值就是311,这组有多组解。
4.disass 段4
继续输入aaaa 动态调试。在段4下断点
进入段四函数,发现又有固定参数数目的限制吗,还是得是整数。
jbe:即当低于或等于时转移 等于14或者小于14
func4的功能直接用ida查看。我们需要它返回0
a3=0xe=14
a2=0
a1=第一个参数
解析这段反汇编出来的伪代码,v3=14-0)/2+0=7
a1是我们输入的第一个参数,我们可以看到一个最清晰的路线,就是令a1=7,不用进入迭代,直接返回result( 0 );
5.disass 段5
看段5的反汇编,加入了canary。
开头的fs:0x28还有后面的xor rax和fs:0x28就是标志。
介绍一下canary。
放在栈的rsp+0x18的位置。
调用string_length,返回值必须是6,不然炸弹爆炸。这个string就有点像输入字符换了,猜它应该是计算字符串的长度的。进去查看,确实是查看字符的个数的,条件是+16这个,前面的add rdx,1就是不停后移,直到输入的落空。
让他们输入五个a,试试看rax是多少,一步一步跟着。
jmp无条件跳转,直接给eax赋值为0;
再无条件跳转到41
+41到+74是一个循环,得循环6次
字符与数字做与运算是字符的ascii码与数字做与运算
重新输入abcdef,查看循环中的变化过程。
一开始,rax是0(因为112初始化了),rdx是rdi这个参数传进来的,代表字符串的起始位置。
BYTE PTR []取一个字节,
应该是把a放入了ecx,是的,0x61
第二句是把0x61(刚好八位)放入栈开头的最低位
x/20gx 0x7fffffffdef0来查看内存(rsp指向的),发现确实61在最后一个字节。
下一句把61赋值给了rdx
再下一句and 0x61,0xf edx变成了1.
0 0 0 0| 1 1 1 1
6 | 0 0 0 1
然后用这个and出来的值取加0x4024b0这个地址,根据edx的偏移值取对应的字符放进edx。再把这个字符放到栈上。
这个rax会从0到5,也就是会放到rsp+0x10] 到[rsp+0x15],我们看被拿出的是a,也就是edx为1的时候,拿出的是a,2应该是接下去的值。
和0xf与的特点是什么,
00001111
前四位是多少都没用,都会变成0,后四位最小为1最大为f,取决于你输入的值是多少,意思就是,你的最后四位是1,那与的结果就是1,是2.结果就是二,是f结果就是f。
取完6个数,依次被放在栈上,可以看下最后栈的布局。
f00,这一串就是最后的结果。,可以看到低位到高位依次是转换出来的结果。(有点像密码)
跳出循环,看到有个string对比函数,和之前的一样,如果和0x40245e里面的字符串不匹配就炸弹爆炸。
一个参数是rsp+0x10也就是我们转换出来的放置的地址,另外一个就是内存中本来有的字符串。
x/s 0x40245e 查看字符串的值。
发现是flyers。
将这几个字符依次和刚才的做对比,发现f在标号为9的地方。
依次是9 f e 5 6 7 然后去找ascii码后四位是这几个的字符输入就能成功。