今天分析了两三个程序,连连受挫!
其中有一个程序是这样的:让用户输入一个字符串,统计字符串中字母,数字,空格,其他字符的个数并分别输出,源码:
#include <stdio.h>
#include <stdlib.h>
main()
{
//字符 英文字母字符个数,空格字符个数,数字字符个数,其他字符个数
char c;
int letters=0,space=0,digit=0,others=0;
printf("please input some characters\n");
//输入为\n就结束循环
while((c=getchar())!='\n')
{
if(c>='a'&&c<='z'||c>='A'&&c<='Z')//如果输入为a-z或A-Z字符
letters++;
else if(c==' ') //如果输入为空格字符
space++;
else if(c>='0'&&c<='9') //如果是数字字符
digit++;
else//如果是其他字符
others++;
}
printf("all in all:char=%d space=%d digit=%d others=%d\n",letters,space,digit,others);//输出结果
}
再看看OD反汇编我就晕死了:(注:先不要看下面这一段,否则会打击你自信心。。)
00401000 53 push ebx
00401001 55 push ebp
00401002 56 push esi
00401003 57 push edi
00401004 68 60604000 push asm.00406060 ; ASCII "please input some characters
"
00401009 33ED xor ebp,ebp
0040100B 33DB xor ebx,ebx
0040100D 33FF xor edi,edi
0040100F 33F6 xor esi,esi
00401011 E8 0C020000 call asm.00401222
00401016 83C4 04 add esp,4
00401019 A1 84604000 mov eax,dword ptr ds:[406084]
0040101E 48 dec eax
0040101F A3 84604000 mov dword ptr ds:[406084],eax
00401024 78 11 js short asm.00401037
00401026 8B0D 80604000 mov ecx,dword ptr ds:[406080] ; asm.00406E40
0040102C 8A01 mov al,byte ptr ds:[ecx]
0040102E 41 inc ecx
0040102F 890D 80604000 mov dword ptr ds:[406080],ecx
00401035 EB 0D jmp short asm.00401044
00401037 68 80604000 push asm.00406080
0040103C E8 4F000000 call asm.00401090
00401041 83C4 04 add esp,4
00401044 3C 0A cmp al,0A
00401046 74 28 je short asm.00401070
00401048 3C 61 cmp al,61
0040104A 7C 04 jl short asm.00401050
0040104C 3C 7A cmp al,7A
0040104E 7E 08 jle short asm.00401058
00401050 3C 41 cmp al,41
00401052 7C 07 jl short asm.0040105B
00401054 3C 5A cmp al,5A
00401056 7F 03 jg short asm.0040105B
00401058 45 inc ebp
00401059 ^ EB BE jmp short asm.00401019
0040105B 3C 20 cmp al,20
0040105D 75 03 jnz short asm.00401062
0040105F 43 inc ebx
00401060 ^ EB B7 jmp short asm.00401019
00401062 3C 30 cmp al,30
00401064 7C 07 jl short asm.0040106D
00401066 3C 39 cmp al,39
00401068 7F 03 jg short asm.0040106D
0040106A 47 inc edi
0040106B ^ EB AC jmp short asm.00401019
0040106D 46 inc esi
0040106E ^ EB A9 jmp short asm.00401019
00401070 56 push esi
00401071 57 push edi
00401072 53 push ebx
00401073 55 push ebp
00401074 68 30604000 push asm.00406030 ; ASCII "all in all:char=%d space=%d digit=%d others=%d
"
00401079 E8 A4010000 call asm.00401222
0040107E 83C4 14 add esp,14
00401081 5F pop edi
00401082 5E pop esi
00401083 5D pop ebp
00401084 5B pop ebx
00401085 C3 retn
-----------------------------------------------------------
00401016 83C4 04 add esp,4
到这来的时候我还能说句,so far ,so good
之后的代码真的是一头雾水了,虽然知道那连续的jg jmp跳转正是嵌套的 if else,但却早已经被类似下面的这些代码击溃了我的自信心了,所以下面的代码自然:无眼睇啦!
00401019 A1 84604000 mov eax,dword ptr ds:[406084]
0040101E 48 dec eax
0040101F A3 84604000 mov dword ptr ds:[406084],eax
00401024 78 11 js short asm.00401037
00401026 8B0D 80604000 mov ecx,dword ptr ds:[406080]
然后我放下这个程序,又拿起另外一个程序来分析,谁知道又是上面的这些鬼代码,不知所云,但是至此我已经找到一个共同点了,就是这些我分析不通的程序都有这样的一个程序框架:
while((c=getchar())!='\n')
{
跳转………
跳过来………
又跳过去………
又跳回来…………
再跳过去………………
………………………………
}
看来产生那些我看不懂的汇编代码的罪归祸首就是while((c=getchar())!='\n')
而下面的函数体中的跳转只是作了一个干扰我的思路的东西
于是我把函数体全部删去,于是源码成了这样:
//字符 英文字母字符个数,空格字符个数,数字字符个数,其他字符个数
char c;
int letters=0,space=0,digit=0,others=0;
printf("please input some characters\n");
//输入为\n就结束循环
while((c=getchar())!='\n')
{
}
printf("end");//输出结果
}
再反汇编分析一下,情况就明朗多了!
00401000 68 34604000 push asm.00406034 ; ASCII "please input some characters
"
00401005 E8 D8010000 call asm.004011E2 ; 输出字符串
0040100A 83C4 04 add esp,4 ; 恢复堆栈
0040100D A1 5C604000 mov eax,dword ptr ds:[40605C] ; 可见此处为字符串的字符个数
00401012 48 dec eax
00401013 A3 5C604000 mov dword ptr ds:[40605C],eax ; 字符个数减1
00401018 78 11 js short asm.0040102B ; 如果是字符个数是负数的话就要去getchar了
0040101A 8B0D 58604000 mov ecx,dword ptr ds:[406058] ; 莫非是将getchar存放字符的那片缓冲区首地址赋值给ecx?事实证明是的
00401020 8A01 mov al,byte ptr ds:[ecx] ; 取第一个字符到al
00401022 41 inc ecx ; ecx++
00401023 890D 58604000 mov dword ptr ds:[406058],ecx ; 把第二个字符放到第一个字符那里
00401029 EB 0D jmp short asm.00401038 ; 如果js跳转语句执行时,S为0,即运算结果为正数时,将导致执行到此处,即跳过了getchar()
0040102B 68 58604000 push asm.00406058
00401030 E8 1B000000 call asm.00401050 ; getchar()
00401035 83C4 04 add esp,4
00401038 3C 0A cmp al,0A ; 上次的笔记已经说过了,0A就是换行符 呵呵
0040103A ^ 75 D1 jnz short asm.0040100D ; 不是换行符就给我继续循环下去!
0040103C 68 30604000 push asm.00406030 ; ASCII "end"
00401041 E8 9C010000 call asm.004011E2 ; 输出end
00401046 59 pop ecx
00401047 C3 retn
总结下:
我怎么知道40605C地址处为字符个数呢??因为在OD里看的。。。我输入了abcde,这个地址在数据窗口中就显示了5,输入ab就显示2,所以嘛,搞逆向就要心静,注意哪些数据可疑的,值得关注的,通常要两只眼睛看四个个窗口,you know,汇编代码窗口,寄存器窗口,堆栈窗口,数据窗口,当然,那个注释窗口也挺重要的,不过它只跟踪了当前执行指令的一些数据情况