这个程序是我们刚开始学C语言文件操作的一个小程序了,应该很熟悉了,这篇分析文章中大量的运用到了上一篇笔记(分析getchar和while的搭配)所介绍的知识,所以,上一篇算是一个,铺垫吧!
本次分析出现大量的形如此的代码,就是上一篇我们介绍的代码:
0040103F |> \A1 5C904000 mov eax,dword ptr ds:[40905C] ;
00401044 |. 48 dec eax
00401045 |. A3 5C904000 mov dword ptr ds:[40905C],eax
0040104A |. 78 0F js short asm.0040105B ;
0040104C |. A1 58904000 mov eax,dword ptr ds:[409058] ;
00401051 |. 8A18 mov bl,byte ptr ds:[eax] ;
00401053 |. 40 inc eax
00401054 |. A3 58904000 mov dword ptr ds:[409058],eax ;
00401059 |. EB 0F jmp short asm.0040106A
0040105B |> 68 58904000 push asm.00409058 ;
00401060 |. E8 C0020000 call asm.00401325 ;
00401065 |. 83C4 04 add esp,4
00401068 |. 8AD8 mov bl,al
0040106A |> 80FB 23 cmp bl,23 ;
我也写了那么一点注释,不过由于经过上篇的介绍,大家对这样的代码已经有点熟悉了吧,接下来看点注释,不懂的要多思考,上OD独立分析。
程序源代码:
#include "stdio.h"
main()
{
//文件指针,字符,文件名
FILE *fp;
char ch,filename[10];
//打开文件
scanf("%s",filename);
if((fp=fopen(filename,"w"))==NULL)
{
printf("cannot open file/n");
exit(0);
}
//循环接收字符,直到收到#退出
ch=getchar();
while(ch!='#')
{
fputc(ch,fp);//打印在文件
putchar(ch);//打印在屏幕
ch=getchar();//接收字符
}
//释放文件
fclose(fp);
system("pause");
}
OD反汇编代码:
00401000 /$ 83EC 0C sub esp,0C ; malloc->12个字节
00401003 |. 8D4424 00 lea eax,dword ptr ss:[esp] ; eax=12个字节缓冲区首地址
00401007 |. 53 push ebx
00401008 |. 57 push edi
00401009 |. 50 push eax ; 只有本条和下面一条压栈指令是参数传递,上面的两条是保存寄存器
0040100A |. 68 50904000 push asm.00409050 ; ASCII "%s"
0040100F |. E8 09060000 call asm.0040161D ; //这一句执行之后40905c变成了1,尽管我输入的是c:\d.txt
00401014 |. 8D4C24 10 lea ecx,dword ptr ss:[esp+10] ; esp+10H就是esp+16,就是跳过之前压入的4个寄存器,来到12字节缓冲区,就是字符串存放的地方了
00401018 |. 68 4C904000 push asm.0040904C ; 在数据窗口看到是00000077h->119D->ascii"w"
0040101D |. 51 push ecx ; 文件名了
0040101E |. E8 E7050000 call asm.0040160A ; fopen
00401023 |. 8BF8 mov edi,eax ; fopen返回文件指针或错误判断值到eax
00401025 |. 83C4 10 add esp,10 ; 终于舍得恢复4个变量的堆栈了(至此之前压入的参数已经恢复,ebx和edi还没恢复)
00401028 |. 85FF test edi,edi
0040102A |. 75 13 jnz short asm.0040103F ; 跳不过去的话就输出错误信息然后退出程序
0040102C |. 68 38904000 push asm.00409038 ; ASCII "cannot open file/n"
00401031 |. E8 83050000 call asm.004015B9 ; printf
00401036 |. 57 push edi
00401037 |. E8 A8040000 call asm.004014E4 ; exit
0040103C |. 83C4 08 add esp,8
0040103F |> A1 5C904000 mov eax,dword ptr ds:[40905C] ; 又是你!40905C,存放的是字符串的字符数(为什么?我已经在上个笔记说过了,you know,getchar与while的那篇)
00401044 |. 48 dec eax
00401045 |. A3 5C904000 mov dword ptr ds:[40905C],eax
0040104A |. 78 0F js short asm.0040105B ; 这里的运算结果不为负数不是因为没把文件字符串遍历完,而是因为还有一个\n没遍历,所以导致下面最近的一个getchar并不执行,而下面有一个cmp,\n和#自然不同,所以je也没跳转了
0040104C |. A1 58904000 mov eax,dword ptr ds:[409058] ; 将文件名字符串的首地址传给eax
00401051 |. 8A18 mov bl,byte ptr ds:[eax] ; 第一个字节内容给bl
00401053 |. 40 inc eax
00401054 |. A3 58904000 mov dword ptr ds:[409058],eax ; 将第二个字符放到原先的第一个字符的位置去
00401059 |. EB 0F jmp short asm.0040106A
0040105B |> 68 58904000 push asm.00409058 ; 我们貌似没有传参到getchar啊,怎么有push?原来getchar隐含地传递了一个指向字符串缓冲区的指针(注意是二级指针)
00401060 |. E8 C0020000 call asm.00401325 ; getchar
00401065 |. 83C4 04 add esp,4
00401068 |. 8AD8 mov bl,al
0040106A |> 80FB 23 cmp bl,23 ; ascii"23"是#
0040106D |. 74 6F je short asm.004010DE ; 等于#的话就退出了,否则就执行循环体的内容吧
0040106F |. 56 push esi ; 保存esi
00401070 |> 0FBEF3 /movsx esi,bl ; bl扩展成32位送到esi(bl放的可是当前的字符啊)
00401073 |. 57 |push edi ; 还记得吗?00401023地址处我们将文件的指针赋值给edi了:mov edi,eax
00401074 |. 56 |push esi ; 这里又是一个字符,所以呢。。嘿嘿
00401075 |. E8 87020000 |call asm.00401301 ; 所以这里就是fputc函数了!
0040107A |. A1 7C904000 |mov eax,dword ptr ds:[40907C] ; 这里存放的又是字符串长度了
0040107F |. 83C4 08 |add esp,8 ; 咦,忘了恢复堆栈,好了,恢复好了。。
00401082 |. 48 |dec eax
00401083 |. A3 7C904000 |mov dword ptr ds:[40907C],eax
00401088 |. 78 15 |js short asm.0040109F ; 字符串长度为负数,那就去getchar吧
0040108A |. 8B15 78904000 |mov edx,dword ptr ds:[409078]
00401090 |. 881A |mov byte ptr ds:[edx],bl
00401092 |. A1 78904000 |mov eax,dword ptr ds:[409078]
00401097 |. 40 |inc eax
00401098 |. A3 78904000 |mov dword ptr ds:[409078],eax
0040109D |. EB 0E |jmp short asm.004010AD
0040109F |> 68 78904000 |push asm.00409078 ; /Arg2 = 00409078
004010A4 |. 56 |push esi ; |Arg1
004010A5 |. E8 42010000 |call asm.004011EC ; \注意这里可不是getchar,应该是输出字符到屏幕,就就是putchar函数了
004010AA |. 83C4 08 |add esp,8
004010AD |> A1 5C904000 |mov eax,dword ptr ds:[40905C]
004010B2 |. 48 |dec eax
004010B3 |. A3 5C904000 |mov dword ptr ds:[40905C],eax
004010B8 |. 78 0F |js short asm.004010C9
004010BA |. A1 58904000 |mov eax,dword ptr ds:[409058]
004010BF |. 8A18 |mov bl,byte ptr ds:[eax]
004010C1 |. 40 |inc eax
004010C2 |. A3 58904000 |mov dword ptr ds:[409058],eax
004010C7 |. EB 0F |jmp short asm.004010D8
004010C9 |> 68 58904000 |push asm.00409058 ; 我们貌似没有传参到getchar啊,怎么有push?原来getchar隐含地传递了一个指向字符串缓冲区的指针(注意是二级指针)
004010CE |. E8 52020000 |call asm.00401325 ; getchar
004010D3 |. 83C4 04 |add esp,4
004010D6 |. 8AD8 |mov bl,al
004010D8 |> 80FB 23 |cmp bl,23
004010DB |.^ 75 93 \jnz short asm.00401070
004010DD |. 5E pop esi
004010DE |> 57 push edi
004010DF |. E8 B2000000 call asm.00401196 ; fclose(fp);
004010E4 |. 68 30904000 push asm.00409030 ; ASCII "pause"
004010E9 |. E8 12000000 call asm.00401100 ; system("pause");
004010EE |. 83C4 08 add esp,8
004010F1 |. 5F pop edi
004010F2 |. 5B pop ebx
004010F3 |. 83C4 0C add esp,0C
004010F6 \. C3 retn