最近听了老外的一个关于secure coding program的培训,感觉挺有意思。网上找了本相关的技术书仔细研究下,做点笔记。
***** 小实验
书的1.4节介绍了一个在Windows下的代码的*****的实验,利用虚拟机+ubuntu16重现一下:
code
实验的code如下,代码逻辑很简单,验证用户的输入密码,如果密码与预定义的PASSWORD相同,验证通过:
#include <stdio.h>
#include <string.h>
#define PASSWORD "1234567"
int verify_password(char *password)
{
int tmp;
tmp = strcmp(password, PASSWORD);
return tmp;
}
int main()
{
int valid_flag = 0;
char password[1024];
while(1)
{
printf("Please input password: ");
scanf("%s", password);
valid_flag = verify_password(password);
if(valid_flag)
{
printf("incorrect password!\n\n");
}
else
{
printf("Congratulation! You have passed the verification!\n");
break;
}
}
return 0;
}
编译成可执行文件:
gcc test.c -o test
执行试试,确实如我们预期:
[email protected]:~/Desktop$ ./test
Please input password: 123
incorrect password!
Please input password: 1234567
Congratulation! You have passed the verification!
*****
按照书本的教程,我们尝试来黑掉它。
1 反汇编
祭出反汇编神器objdump:
objdump -d test
来看一看main函数的汇编代码,我们主要的目的是找到**if(valid_flag)**这条判断语句
0804853c <main>:
804853c: 8d 4c 24 04 lea 0x4(%esp),%ecx
8048540: 83 e4 f0 and $0xfffffff0,%esp
8048543: ff 71 fc pushl -0x4(%ecx)
8048546: 55 push %ebp
8048547: 89 e5 mov %esp,%ebp
8048549: 51 push %ecx
804854a: 81 ec 14 04 00 00 sub $0x414,%esp
8048550: 65 a1 14 00 00 00 mov %gs:0x14,%eax
8048556: 89 45 f4 mov %eax,-0xc(%ebp)
8048559: 31 c0 xor %eax,%eax
804855b: c7 85 f0 fb ff ff 00 movl $0x0,-0x410(%ebp)
8048562: 00 00 00
8048565: 83 ec 0c sub $0xc,%esp
8048568: 68 78 86 04 08 push $0x8048678
804856d: e8 4e fe ff ff call 80483c0 <[email protected]>
8048572: 83 c4 10 add $0x10,%esp
8048575: 83 ec 08 sub $0x8,%esp
8048578: 8d 85 f4 fb ff ff lea -0x40c(%ebp),%eax
804857e: 50 push %eax
804857f: 68 94 86 04 08 push $0x8048694
8048584: e8 77 fe ff ff call 8048400 <[email protected]>
8048589: 83 c4 10 add $0x10,%esp
804858c: 83 ec 0c sub $0xc,%esp
804858f: 8d 85 f4 fb ff ff lea -0x40c(%ebp),%eax
8048595: 50 push %eax
8048596: e8 80 ff ff ff call 804851b <verify_password>
804859b: 83 c4 10 add $0x10,%esp
804859e: 89 85 f0 fb ff ff mov %eax,-0x410(%ebp)
80485a4: 83 bd f0 fb ff ff 00 cmpl $0x0,-0x410(%ebp)
80485ab: 74 12 je 80485bf <main+0x83>
80485ad: 83 ec 0c sub $0xc,%esp
80485b0: 68 97 86 04 08 push $0x8048697
80485b5: e8 26 fe ff ff call 80483e0 <[email protected]>
80485ba: 83 c4 10 add $0x10,%esp
80485bd: eb a6 jmp 8048565 <main+0x29>
80485bf: 83 ec 0c sub $0xc,%esp
80485c2: 68 ac 86 04 08 push $0x80486ac
80485c7: e8 14 fe ff ff call 80483e0 <[email protected]>
80485cc: 83 c4 10 add $0x10,%esp
80485cf: 90 nop
80485d0: b8 00 00 00 00 mov $0x0,%eax
80485d5: 8b 55 f4 mov -0xc(%ebp),%edx
80485d8: 65 33 15 14 00 00 00 xor %gs:0x14,%edx
80485df: 74 05 je 80485e6 <main+0xaa>
80485e1: e8 ea fd ff ff call 80483d0 <[email protected]>
80485e6: 8b 4d fc mov -0x4(%ebp),%ecx
80485e9: c9 leave
80485ea: 8d 61 fc lea -0x4(%ecx),%esp
80485ed: c3 ret
80485ee: 66 90 xchg %ax,%ax
Hmmm, 看不懂汇编。。。不要紧,这段代码是我们自己实现的,刚开始学习的时候可以根据我们的需求重新编译,我们将这条跳转语句改成if(!valid_flag),然后重新生成一个test1的可执行文件,对这个可执行文件也进行反汇编,然后比较下两个反汇编的main函数有什么差异:
gcc test.c -o test1
objdump -d test1
比较后发现,只有下面的je跳转语句变成了jne,也就是74变成了75.
80485ab: 75 12 jne 80485bf <main+0x83>
okay,我们找到了if语句在test中的位置是0x80485ab,linux的应用程序的基地址是0x8048000,所以if语句在test中的地址是0x5ab,用bless将可执行程序test以二进制格式打开(Ubuntu 下安装bless的命令为 sudo apt-get install bless),找到0x5ab,该处的数据确实是74 12,将其改成75 12后保存:
再执行test,发现原来正常的PASSWORD变成了非法,而其他非法的密码变成了可以正常认证的密码了:
[email protected]:~/Desktop$ ./test
Please input password: 1234567
incorrect password!
Please input password: 123
Congratulation! You have passed the verification!
总结
与原文中存在比较大的差异,原文是使用windows和一些工具进行*****,很多工具我也没有用过,尽量用自己知道或者常用的工具来进行复现一下。 ?