解题思路:
由dummy=1,可以知道dummy的地址已经被确定,dummy的值也被确定。结合:
start= (int)(*(((char *) &dummy)));
stride = (int)(*(((char *) &dummy)+ 1));
观察提取函数extract_message1和extract_message2,知道start是开始破解的第一个字符 的位置,而stride是破解时跳跃字符的最大间隔。
运行到此处,如果dummy的值没有改变,则start和 stride都是固定值,start=1,stride=0,显然是不可能的。因为data的数据为:
再结合破译出来前几个字符为:From:
可以知道start=8或9,stride=2或3.所以可以知道dummy的值在运行到此处时已经被改变了。显然只有:
voidprocess_keys12 (int * key1, int * key2)
{
*((int *) (key1 + *key1)) = *key2;
}
该函数的调用导致了dummy值的改变,且dummy=key2。所以:(key1 + *key1)是dummy的地址,又由:
int dummy = 1;
int start, stride;
int key1, key2, key3, key4;
char * msg1, * msg2;
key3 = key4 = 0;
显然知道key1=3,这是因为这几个变量的地址是连续的。
接着来解决key2.又前面可以知道start=8或9,stride=2或3.在watch窗口中给start和stride依次赋值(8,2)、(8,3)、(9,2)、(9,3),观察提取出来的msg1,只有当start=9,sride=3的时候,msg1的前面几个字符为:From:
Dummy的地址为:0x0018ff44,所以地址0x0018ff44处的值为:09,地址0x0018ff45处的值为:03,地址0x0018ff46处的值为:00,地址0x0018ff47处的值为:00。
所以dummy的值为:dummy=0x00000309=777
接下来解决key3和key4.看一下面的代码段:
start = (int)(*(((char *) &dummy)));
stride = (int)(*(((char *) &dummy) +1));
if (key3 != 0 && key4 != 0)
{
process_keys34(&key3, &key4);
//执行该函数后,则跳过下一程序段
}
msg1 = extract_message1(start, stride);
if (*msg1 == '\0')
{
process_keys34(&key3, &key4);
msg2 = extract_message2(start, stride);
printf("%s\n", msg2);
}
由于start=9,stide=3,所以:msg1 = extract_message1(start, stride);没有执行,如果执行了则*msg1!=’\0’。进一步可以知道:
voidprocess_keys34 (int * key3, int * key4)
{
*(((int*)&key3) + *key3) += *key4;
}
该函数的调用,使该函数的返回地址改变了,所以:(((int *)&key3) +*key3)是该函数的返回地址,又由编译原理中的相关知识可以知道函数的返回地址和函数参数的地址是连续的,并且返回地址为参数地址小1。函数返回地址比key3小于1,所以key3=-1.
但是该函数的返回地址到底返回到哪里去了呢???
其实,这是很简单的。下面有第二次调用process_keys34,显然第一次调用process_keys34的返回地址就是第二次调用该函数的返回地址。不然的话,程序陷入了死循环,或者是msg2 =extract_message2(start, stride);跳过,这两种显然都是不可能的。
key4的值就是这两次调用process_keys34的返回地址的差值。运行到第一次调用process_keys34的地方,打开反汇编(disassembly)窗口:
所以:key4=0x004013BF-0x00491392=45