修改邻接变量的方法对代码环境限制比较多,更通用、更强大的方法是修改 EBP、返回地址等状态值。
为了方便调试,修改之前的代码如下:
1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 5 #define PASSWORD "1234567" 6 7 int verify_password(char *password) 8 { 9 int authenticated=0x03050709; 10 char buffer[8]; // add local buf to be overflowed 11 authenticated=strcmp(password,PASSWORD); 12 strcpy(buffer, password); // overflow here 13 return authenticated; 14 } 15 16 int main() 17 { 18 int valid_flag=0; 19 char password[1024]; 20 if(!freopen("password.txt","r",stdin)) // 非打印字符不便于从console输入,故 redirect stdin 21 //FILE *fp; 22 //if(!(fp=fopen("password.txt","rw+"))) 23 { 24 printf("file open error! "); 25 exit(0); 26 } 27 scanf("%s",password); 28 //fscanf(fp,"%s",password); 29 printf("password input: %s ",password); 30 valid_flag=verify_password(password); 31 if(valid_flag){ 32 printf("Incorrect password! "); 33 } 34 else 35 { 36 printf("Congratulation! You have passed the verification! "); 37 } 38 //fclose(fp); 39 return 0; 40 }
在 password.txt 中存储内容为 abcdefg 时,OD 调试 exp_me.exe,执行完第 12 行 strcpy 后的栈帧如下图所示:
如上图,此时栈帧中 authenticated 的值(0x0012FAE8)为 1,表示验证未通过。EBP : 0x0012FAEC,前栈帧 EBP : 0x0012FF48,返回地址 : 0x0040ECD8。
1 0040ECCC |. 8D95 FCFBFFFF LEA EDX,DWORD PTR SS:[EBP-404] 2 0040ECD2 |. 52 PUSH EDX 3 0040ECD3 |. E8 2D23FFFF CALL exp_me.00401005 4 0040ECD8 |. 83C4 04 ADD ESP,4 5 0040ECDB |. 8945 FC MOV DWORD PTR SS:[EBP-4],EAX 6 0040ECDE |. 837D FC 00 CMP DWORD PTR SS:[EBP-4],0 7 0040ECE2 |. 74 0F JE SHORT exp_me.0040ECF3 8 0040ECE4 |. 68 AC404200 PUSH OFFSET exp_me.??_C@_0BG@GFGB@Incorr>; /format = "Incorrect password! 9 10 " 11 0040ECE9 |. E8 F225FFFF CALL exp_me.printf ; printf 12 0040ECEE |. 83C4 04 ADD ESP,4 13 0040ECF1 |. EB 0D JMP SHORT exp_me.0040ED00 14 0040ECF3 |> 68 6C404200 PUSH OFFSET exp_me.??_C@_0DD@FPBB@Congra>; /format = "Congratulation! You have passed the verification! 15 16 " 17 0040ECF8 |. E8 E325FFFF CALL exp_me.printf ; printf 18 0040ECFD |. 83C4 04 ADD ESP,4 19 0040ED00 |> 33C0 XOR EAX,EAX 20 0040ED02 |. 5F POP EDI 21 0040ED03 |. 5E POP ESI
从上面的反汇编代码可以看到,0x0040ECD8 处的代码对应源码第 30 行后恢复栈帧处,如果能将此时的返回地址覆盖为 0x0040ECF3,就能跳过错误验证流程,直接执行源码中第 36 行的 else 分支,进入验证正确后的执行流程。
用 UltraEdit 将 password.txt 修改为如下内容:
注意,CPU 为 Little_Endian 模式,最后的四字节返回地址(0x0040ECF3)要反写成 E3 EC 40 00
在返回地址之前的前栈帧地址 0x0012FF48 不能写为 48 FF 12 00,因为这里的 00 会被 scanf 当作截断符中止读取,导致内容读取不够,覆盖返回地址失败(只能覆盖到 EBP)。
修改 password.txt 后,OD 调试结果如下:
紧接着出现如下错误,因为 EBP 覆盖成了错误的 0xAA12FF48 而不是 0x0012FF48,导致栈帧不平衡。
这个问题留着以后再学习吧,今天到此了,睡觉去(2014-3-31 23:31:55)。