CSAPP附带的一个第三章的实验,需要反汇编找出正确的答案,一共需要看懂六个函数
很久之前解了第一个,然后就开始摸了
最近感觉必须要干点什么,然后就又开始解了
参考资料: GDB指令详解
phase_1
用gdb调试时把位于0x402400的字符串取出来就行了,因为是第一题相对好弄。
答案: Border relations with Canada have never been better.
phase_2
第二个是读入六个数。观察发现是一个首项为1,倍数为2的等比数列,填入即可。
答案: 1 2 4 8 16 32
phase_3
第三个是读入两个数。根据第一个数确定第二个数应该是哪个,需要gdb
调试0x402470看switch的地址表,8组答案选一个输入即可。
答案: 0 207 或 1 311 或 2 707 或 3 256 或 4 389 或 5 206 或 6 682 或 7 327
phase_4
第四个仍然是读入两个数。其中第二个数可直接看出为0,第一个数则需要填[0,13]之间
使func4的返回值为0的数,看懂func4就能解了。
(一开始以为是大于14,花了老半天)
答案: 7 0
func4大致代码:
int func4(int a, int b, int c)
{
int f = (c - b) / 2 + b;
// 这个除以2为了用位运算优化对正负数做了分别的处理,因为除法开销太大,但是很难分辨……
if(a == f)
return 0;
if(a < f)
return func4(a, f + 1, c) * 2 + 1;
return func4(a, b, f - 1) * 2;
}
phase_5
第五个是输入一个长度为6的字符串。读入后每个字符都会取低4位转译成0x4024b0对应字母表里的字符,转译后的字符串是“flyers”,取能转译成“flyers”的字符串即可。
答案: 9oNef7(其中一种)
phase_6
第六个仍然是读入六个数,根据读入的六个数重新排列单链表,保证排列后的链表数据从小到大即可。
一是代码量大,一段循环一段循环手动转写成C语言很费劲;
二是用了结构体,不好直接判断寄存器存的是什么数据。
这一关卡了我一个下午,是我太菜了。
答案:4 3 2 1 6 5
附大致的转译程序(欢迎指正):
/* 注:
1. 读六个数的函数直接内联了
2. 这里的nodes的地址不一定是0x6032d0,在程序里的角色相同
3. failed函数即explode_bomb函数
4. 循环判断条件等不一定完全一样,比如有些jne指令为了风格一致就转写成了<了
*/
// gdb debug出的0x6032d0数据
/*
0x6032d0 <node1>: 0x0000014c 0x00000001 0x006032e0 0x00000000
0x6032e0 <node2>: 0x000000a8 0x00000002 0x006032f0 0x00000000
0x6032f0 <node3>: 0x0000039c 0x00000003 0x00603300 0x00000000
0x603300 <node4>: 0x000002b3 0x00000004 0x00603310 0x00000000
0x603310 <node5>: 0x000001dd 0x00000005 0x00603320 0x00000000
0x603320 <node6>: 0x000001bb 0x00000006 0x00000000 0x00000000
*/
typedef struct _node{
int num;
int index;
struct _node *next;
} node;
// 不好意思为方便用了全局变量
node nodes[6];
void initial()
{
nodes[0] = {0x14c, 0x1, nodes+1};
nodes[1] = {0xa8, 0x2, nodes+2};
nodes[2] = {0x39c, 0x3, nodes+3};
nodes[3] = {0x2b3, 0x4, nodes+4};
nodes[4] = {0x1dd, 0x5, nodes+5};
nodes[5] = {0x1bb, 0x6, NULL};
}
void phase_6()
{
int a[6], i, j;
node *b[6];
node *p;
// read_six_numbers
scanf("%d%d%d%d%d%d",a,a+1,a+2,a+3,a+4,a+5);
// ~ 401153
// 两两不同,看了半天循环怎么嵌套的才想起有break这个东西
for(i = 0, j = 0; ; i++)
{
if(a[i] > 6) failed();
if(++j == 6) break;
for(int tmp = j ; tmp < 6 ; tmp++)
{
if(a[tmp] == a[i]) failed();
}
}
// 401153 ~ 40116d
for(i = 0 ; i < 6 ; i++)
{
a[i] = 7 - a[i];
}
// 40116f ~ 4011a9
for(i = 0 ; i < 6 ; i++)
{
if(a[i] > 1)
{
j = 1;
p = nodes;
do
{
p = p->next;
j++;
}while(a[i] != j);
}
else
{
p = nodes;
}
b[i] = p;
}
// 4011ab ~ 4011d2
for(int i = 1 ; i < 6 ; i++)
{
b[i - 1]->next = b[i];
}
b[5]->next = NULL;
// 4011d9 ~
p = b[0];
for(i = 5 ; i != 0 ; i--)
{
if(p->num >= p->next->num){
p = p->next;
}else{
failed();
}
}
}
彩蛋secret_phase
secret_phase在源程序没有任何调用,应该是需要在gdb修改返回地址搞缓冲区攻击才能触发。
(待补)