CSAPP bomb实验

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修改返回地址搞缓冲区攻击才能触发。
(待补)

上一篇:LeetCode-147-对链表进行插入排序


下一篇:部门树生成 双重for循环代替递归 java