用GDB工具详细分析SSD4 Exercise 1
此次试验的目的是利用gdb调试工具调试一段程序(此程序要求正确输入四个参数key1,key2,key3,key4从而输出正确的信息)来猜想key1,key2,key3,key4.
先用参数1 2 3 4试探性的运行程序。
先看看process_keys12(&key1, &key2)函数,函数参数key1的值等于主函数里key1的地址。key1所指向的值为主函数里key1的值(第一个密码参数)。显然,这是一个更改某个内存的语句。该内存以主函数里key1的地址为基准,偏移量为key1,即为地址(&key1 key1)。对该地址赋值,其值为主函数里key2的值(第二个密码参数)。
所以,函数process_keys12(*key1,*key2)用来修改某些地址所存放的值。
由
start = (int)(*(((char *) &dummy)));
stride = (int)(*(((char *) &dummy) + 1));
可知extract_message1调用的两个参数start,stride是dummy的第一个字节和第二个字节。看上去跟key1,key2没关系,由此推想调用函数process_keys12(&key1, &key2)改变了dummy的值从而改变start和stride的值。
那么key1和dummy的地址偏移量就是key1的值。Dummy改变后的值就是key2的值。
用gdb查看key1,和dummy的地址如下:
地址相差1个int,因此猜想key1=1;
然后看extract_message1的代码:
char * extract_message1(int start, int stride) {
int i, j, k;
int done = 0;
for (i = 0, j = start + 1; ! done; j++) {
for (k = 1; k < stride; k++, j++, i++) {
if (*(((char *) data) + j) == '/0') {
done = 1;
break;
}
message[i] = *(((char *) data) + j);
}
}
message[i] = '/0';
return message;
}
Start控制data中的起始位置,stride控制一次连续存入message内的个数。
再看data里放的值:
而题目提示message开头是From: 由上面data值可知可能从 10个字符开始,但由于内层循环取出stride-1个字符后会跳过一个字符(两次j++)所以start=9,stride=3
由此求出dummy的值为777(0000001100001001),即key2=777.
将key=1,key2=777代入程序运行入选:
说明key1,key2的值正确
下面接key3,key4的值:
看下面代码,process_keys34被调用了两次,为什么调用两次呢?
我们先把两次process_key34和extract_message1隐藏掉,看执行结果:
得出了正确结果。这说明两次process_key34的执行根本没有改变start和stride的值,也就是说start=9,stride=3可以使函数extract_message2得到正确的结果。
于是我们猜想第一次process_key34的调用改变了函数返回地址,使得程序跳过了代码段:
msg1 = extract_message1(start, stride);
if (*msg1 == '/0') {
process_keys34(&key3, &key4);
而直接执行了extract_message2,因此process_key34将函数的返回地址改变成了第二个process_key34的下一条指令的地址。
由process_key34的代码可知key3为key3和process_key34的返回地址在栈里的地址差值.
在执行process_key34第一条指令之前,用x/20 $esp查看栈的内容:
由上图可知原本的返回地址为0x080486c8
在栈里存储地址与key3差值为-1,因此key3=-1;
用下图可知第二个process_key34的下一条指令地址如下:
地址为0x080486f9;因此差值为0x080486f9-0x080486c8=49;
于是key4=49;
将key1=1,key2=777 key3=-1,key4=49代入程序运行结果如下:
结果正确。因此key1=1,key2=777,key3=-1,key4=49.