How to study C && ASM Code(3)
来源:互联网 发布:淘宝几单才有2颗心 编辑:程序博客网 时间:2024/04/26 06:48
Download:
http://www.cisrg.cn/doc/lesson3.rar
==www.cisrg.cn.==
How to study C && ASM Code(3)
|=---------------=[ 运算符、表达式的逆向 ]=------------------------=|
|=-----------------------------------------------------------------=|
|=---------------=[ 7all<7all7_at_163.com> ]=----------------------=|
|=-----------------------------------------------------------------=|
|=---------------=[ 版权所有:www.cisrg.cn ]=-----------------------=|
--[ Let's Go
一个C程序由运算符、表达式组成,表达式还包括一些循环语句。C的运算符比较多,
也比较有意思,而且其定义没有C++那般的无耻,所以这可能是有些老道的程序员喜欢
C而不喜欢C++的原因之一。一般来说C的程序都比较的随意,随意的结果就是允许程序
员自己实现更多的东西,这种随意的结果使得很多的代码狂成了C的偏执者。有那么一
段时间,本人徘徊在C、C++、JAVA与C#之间,直到某个月黑人静的晚上,我逆向了C、
C++的代码后就再也没有碰过C++,虽然偶尔也会写MFC的代码,但基本上MFC程序的核心
代码我都试图摆脱MFC的封装类。如果不是人懒惰的原因,我想我会直接拿SDK来写界面
程序的。那个月黑人静的夜晚,是什么原因要我放弃了C++呢?原因是比较简单的,我
发现我拿函数封装实现某个功能的C代码,与用类来实现同样功能的C++代码相比,其汇
编代码相差很大,这里我的意思是说C++逆向后的汇编代码要比C的多了那么十几行,同
样的Release模式编译,实现同样功能的代码居然会相差这么多!虽然现在电脑运行速度
快了很多,很难想象,如果一个超过2W行的代码使用C++书写的话,其汇编代码要比C多
多少?做为一个只有小学文化的程序员,我在考虑的差不多头皮发麻的那一瞬间,毅然
的抛弃了C++:)当然这里仅仅是个人的观点,毕竟C++还有众多的FANS存在,我可不想走
在大街上都被别人扔臭鸡蛋。
--[ 运算符
如果您不知道C的运算符是啥子东东,您可以去论坛的FAQ那个板块提问,如果在那里有
人给您了回答,但是您还是不理解运算符是啥东东,那么您可以给我发封EMail过来,我
会找我身边对C非常熟悉的同事给您一个很专业的答案,如果您在看到我同事的回答后还
是不知道运算符是啥东东,那么您可以选择这样的一条汇编语句,以表达我们对您的崇拜
之情。汇编语句是:
mov eax, 黄河
jmp eax
C的运算符有赋值运算符(=)、算数运算符、其它运算符,我们该小节会简单的说明下
这些运算符,本文的内容也会针对这些运算符进行我们的逆向过程。
A: 赋值运算符
例如: int i = 9;即把整数9赋值给int类型的变量i,这里需要注意的是符号=,不是真正
等于的意思,而是赋值的意思。虽然也可以称呼为等于,但在C里面的标准却是赋值。
B: 算数运算符
+、-、*、/、%、++、--,我们这里直接书写一段C代码来演示下这些运算符,当然后面会
直接拿这个代码进行我们可爱的逆向。
[code]
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i = 9;
int j = 8;
i = i + j;
printf("i+j = %d/n", i);
i = i - j;
printf("i-j = %d/n", i);
i = i * j;
printf("i*j = %d/n", i);
i = i / j;
printf("i/j = %d/n", i);
i = i % j;
printf("i%%j = %d/n", i);
i = i++;
printf("i++ = %d/n", i);
i = i--;
printf("i-- = %d/n", i);
exit(0);
}
[/code]
C: 其它运算符
sizeof是个很有意思的运算符,sizeof以字节为单位返回其操作数的大小,
sizeof、(类型)
[code]
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i = 1;
char *ptr = "abc";
char str[12] = "aaa";
float flt;
ptr = (char *)malloc(8);
printf("sizeof(i) = %d/n", sizeof(i));
printf("sizeof(*ptr) = %d/n", sizeof(ptr));
printf("sizeof(str) = %d/n", sizeof(str));
printf("sizeof(flt) = %d/n", sizeof(flt));
exit(0);
}
[/code]
小节:运算符的概念是很容易理解的,看过C的朋友都知道,这里也就不再加以
累赘的说明。在下面的章节我们会针对这两个代码进行逆向。
--[ 算数运算符的逆向
按照上面的那个算数运算符的例子代码来看,该代码最后打印的结果会是:
i+j = 17
i-j = 9
i*j = 72
i/j = 9
i%j = 1
i++ = 2
i-- = 1
下面我们来剖析下汇编代码是如何完成这个工作的:)下面的代码是关于例子1的
汇编代码实现。
[code]
6: int i = 9;
//把整数9放到ebp-4的位置,前面的文章曾详细的讲解过这种类型的赋值
0040D798 mov dword ptr [ebp-4],9
7: int j = 8;
//把整数8放到ebp-8的位置
0040D79F mov dword ptr [ebp-8],8
8: i = i + j;
/*这里的add指令在汇编里为加法指令*/
0040D7A6 mov eax,dword ptr [ebp-4]--|->实现了i+j的操作
0040D7A9 add eax,dword ptr [ebp-8]--|->而后把相加的值
0040D7AC mov dword ptr [ebp-4],eax--|->赋值给变量i
9: printf("i+j = %d/n", i);
0040D7AF mov ecx,dword ptr [ebp-4]
0040D7B2 push ecx
0040D7B3 push offset string "i+j = %d/n" (00422fe8)
0040D7B8 call printf (004012c0)
0040D7BD add esp,8
10: i = i - j;
/*这里的sub指令在汇编里为减法指令*/
0040D7C0 mov edx,dword ptr [ebp-4]--|->实现了i-j的操作
0040D7C3 sub edx,dword ptr [ebp-8]--|->而后把相减的值
0040D7C6 mov dword ptr [ebp-4],edx--|->赋值给变量i
11: printf("i-j = %d/n", i);
0040D7C9 mov eax,dword ptr [ebp-4]
0040D7CC push eax
0040D7CD push offset string "i-j = %d/n" (00422fdc)
0040D7D2 call printf (004012c0)
0040D7D7 add esp,8
12: i = i * j;
/*这里的imul指令在汇编里为乘法指令*/
0040D7DA mov ecx,dword ptr [ebp-4]-|->实现了i*j的操作
0040D7DD imul ecx,dword ptr [ebp-8]-|->而后把相乘的值
0040D7E1 mov dword ptr [ebp-4],ecx-|->赋值给变量i
13: printf("i*j = %d/n", i);
0040D7E4 mov edx,dword ptr [ebp-4]
0040D7E7 push edx
0040D7E8 push offset string "i*j = %d/n" (00422fd0)
0040D7ED call printf (004012c0)
0040D7F2 add esp,8
14: i = i / j;
/*
这里的idiv指令在汇编里为除法指令,可能大家注意到了,在idiv指令前
增加了cdq指令,cdq指令会把EAX寄存器中的符号位扩展到EDX中,可能
大家猜测到这里会做什么了:)
如果这里修改为mov dword ptr [ebp-4],edx。那么就是把进行%运算后
的数值赋值给变量i。不过即使你不去主动的使用cdq指令,Intel的处理
办法也会自动把除法的余数放到edx上。
*/
0040D7F5 mov eax,dword ptr [ebp-4]-|->实现了i/j的操作
0040D7F8 cdq -|->而后把相除的值
0040D7F9 idiv eax,dword ptr [ebp-8]-|->赋值给变量i
0040D7FC mov dword ptr [ebp-4],eax-|
15: printf("i/j = %d/n", i);
0040D7FF mov eax,dword ptr [ebp-4]
0040D802 push eax
0040D803 push offset string "i/j = %d/n" (00422040)
0040D808 call printf (004012c0)
0040D80D add esp,8
16: i = i % j;
/*
正如前面我们猜测的结果,cdq指令会把eax的有符号位扩展到edx上,
此时对变量i的赋值已经不是 mov [ebp-4],eax,而是mov [ebp-4],edx
此时被扩展到edx上的值就是取模后的数值:)
*/
0040D810 mov eax,dword ptr [ebp-4]-|->实现了i%j的操作
0040D813 cdq -|->而后把取模的值
0040D814 idiv eax,dword ptr [ebp-8]-|->赋值给变量i
0040D817 mov dword ptr [ebp-4],edx-|->
17: printf("i%%j = %d/n", i);
0040D81A mov ecx,dword ptr [ebp-4]
0040D81D push ecx
0040D81E push offset string "i%%j = %d/n" (00422034)
0040D823 call printf (004012c0)
0040D828 add esp,8
18: i = i++;
/*
i++ = i + 1;所以这里会采取add eax,1,其它与上面我们的解释一致。
*/
0040D82B mov edx,dword ptr [ebp-4]
0040D82E mov dword ptr [ebp-4],edx
0040D831 mov eax,dword ptr [ebp-4]
0040D834 add eax,1
0040D837 mov dword ptr [ebp-4],eax
19: printf("i++ = %d/n", i);
0040D83A mov ecx,dword ptr [ebp-4]
0040D83D push ecx
0040D83E push offset string "i++ = %d/n" (00422028)
0040D843 call printf (004012c0)
0040D848 add esp,8
20: i = i--;
/*
i-- = i - 1;所以这里会采取sub eax,1,其它与上面我们的解释一致。
*/
0040D84B mov edx,dword ptr [ebp-4]
0040D84E mov dword ptr [ebp-4],edx
0040D851 mov eax,dword ptr [ebp-4]
0040D854 sub eax,1
0040D857 mov dword ptr [ebp-4],eax
21: printf("i-- = %d/n", i);
0040D85A mov ecx,dword ptr [ebp-4]
0040D85D push ecx
0040D85E push offset string "i-- = %d/n" (0042201c)
0040D863 call printf (004012c0)
0040D868 add esp,8
22: exit(0);
0040D86B push 0
0040D86D call exit (00401130)
23: }
[/code]
通过上面我们对汇编语言的理解,我们基本上可以把这个代码从C代码转换为
汇编代码了。至于怎么详细的实现了这个转换过程,这里就不再累赘了,前面
的文章比较详细的说明了整个的逆向流程。
汇编代码实现的与C代码相同的程序:)
[code]
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i = 9;
int j = 8;
printf("i = 9/nj = 8/nStart.../n/n");
/*实现i+j的汇编代码*/
__asm{
mov eax,i;
add eax,j;
mov i,eax;
}
printf("i+j = %d/n", i);
/*实现i-j的汇编代码*/
__asm{
mov eax,i;
sub eax,j;
mov i,eax;
}
printf("i-j = %d/n", i);
/*实现i*j的汇编代码*/
__asm{
mov eax,i;
imul eax,j;
mov i,eax;
}
printf("i*j = %d/n", i);
/*实现i/j的汇编代码*/
/*__asm{
xor edx,edx;//该段代码使用了div指令完成,其用法与idiv类似.
mov eax,i; //至于为什么需要xor edx,edx呢?那是因为被除数达到了双精度值
mov ecx,j; //所以不能用符号扩展,而只能将高16位送0
div ecx;
mov i,eax;
}*/
/*
大家仔细观察下这里的汇编代码与上面反编译的汇编代码有啥区别?
上面的指令为idiv eax,dword ptr [ebp-8],而实际情况是idiv这个指令,
在Intel的声明中只支持一个操作数而已,令人头疼的问题,但这里也不可能
是伪指令,如果是的话,按照常理编译器应该认识我的语法...
可怜的程序员又被MS给爽了一把,窃喜ing...
*/
__asm{
xor edx,edx;
mov eax,i;
mov ecx,j
//cdq;
idiv ecx;
mov i,eax;
}
printf("i/j = %d/n", i);
/*实现i%j的汇编代码*/
__asm{
xor edx,edx;
mov eax,i;
mov ebx,j;
//cdq;
idiv ebx;
mov i,edx;
}
printf("i%%j = %d/n", i);
/*实现i++的汇编代码*/
__asm{
mov eax,i;
add eax,0x01;
mov i,eax;
}
printf("i++ = %d/n", i);
/*实现i--的汇编代码*/
//请自行书写,当家庭作业吧:)
exit(0);
}
[/code]
--[ 其它运算符的逆向
可爱的sizeof是我们的第一个选择,写代码写多了,于是遇到了这样的一个
问题,关于把变量置空的问题。我一般置空的办法为:
char str[12];
memset(&str, 0, sizeof(&str));这样的情况下数组一般可以被我置空,但是
那天同事突然感觉我这种置空的办法很不可思议,于是认真的考虑了下这个问题。
同事当时给了我另外一种memset置空的方法,搞的我也是莫名其妙......,今天
算是把这个问题给彻底的搞定了:)不过不知道自己这么想到底对还是不对?
memset函数原型
函数名: memset
功 能: 设置s中的所有字节为ch, s数组的大小由n给定
用 法: void *memset(void *s, char ch, unsigned n);
首先,sizeof(&str)的值肯定是12个字节的,虽然我这里使用了取地址,但是
取来取去还是str所指向的内存空间,道理还是相同的。
其次,调用memset函数时,会把&str的内存空间置空,但仔细考虑下的话,str
取地址的话,不正与&str是相同的嘛?
最后,证明我的这边办法是可行的。不过,也得出一个结论,不是学计算机专业
难免会存在很多的弊端。
为了更好的书写这里的代码,我把上面的代码简单的修改了下,修改后的C
代码为:
[code]
int main()
{
printf("sizeof(int) = %d/n", sizeof(int));
printf("sizeof(char *) = %d/n", sizeof(char *));
printf("sizeof(char) = %d/n", sizeof(char));
printf("sizeof(float) = %d/n", sizeof(float));
exit(0);
}
[/code]
得到的汇编代码为:
[code]
6: printf("sizeof(int) = %d/n", sizeof(int));
0040D708 push 4 //sizeof直接返回了int类型的长度
0040D70A push offset string "sizeof(i) = %d/n" (00423004)
0040D70F call printf (00401230)
0040D714 add esp,8
7: printf("sizeof(char *) = %d/n", sizeof(char *));
0040D717 push 4 //sizeof直接返回了char *类型的长度
0040D719 push offset string "sizeof(*ptr) = %d/n" (00422fcc)
0040D71E call printf (00401230)
0040D723 add esp,8
8: printf("sizeof(char) = %d/n", sizeof(char));
0040D726 push 1 //sizeof直接返回了char类型的长度
0040D728 push offset string "sizeof(str) = %d/n" (00422fb8)
0040D72D call printf (00401230)
0040D732 add esp,8
9: printf("sizeof(float) = %d/n", sizeof(float));
0040D735 push 4 //sizeof直接返回了float类型的长度
0040D737 push offset string "sizeof(flt) = %d/n" (0042201c)
0040D73C call printf (00401230)
0040D741 add esp,8
10: exit(0);
0040D744 push 0
0040D746 call exit (004010a0)
[/code]
因此,我们没有必要去逆向sizeof到底做了啥事情,他只是处理了返回的字节大小。
但在实际编程中,sizeof却是如此的重要,极少的C代码会不使用sizeof。当然也有人
会说自己写C就没有使用过sizeof,那样的话,的确得好好的佩服一下了。
--] 总结
运算符、表达式多么美好的组合,有了这些就可以象码砖一样的建个大楼出来,
不过在你学会了这些的同时,你已经朝程序员的旅程迈进了一步了。对了,不要
以为别人喊你是程序员你就开心的不得了,那其实是在骂你而已:),最起码国内
的程序员是这样的。
- How to study C && ASM Code(3)
- How to study C && ASM Code(1)
- How to study C && ASM Code(2)
- How to study C && ASM Code(6)
- How to study C && ASM Code(4)
- How To Study C Programming Language
- C Language Study - how to use '#'
- How to inline ASM in C (gcc)
- How to study Linux
- How to study.
- How to Study .NET?
- how to study jquery
- lua study -- how to call lua function use C language
- how to change the machine code to ASM by Using OllyDbg
- how to study java good
- (转)how to study rootkit
- How to study foreign language
- asm source code note 1.8_没有原文的asm to c
- 周末八大处之游
- bumpmap
- 有家拉
- 工作的第一天
- 论二战东线作战中德国战胜苏联的可能性
- How to study C && ASM Code(3)
- How to study C && ASM Code(6)
- Qmail控制文件详解
- How to study C && ASM Code(4)
- Enterprise Library2.0(1):Data Access Application Block
- MFC学习点滴三:句柄,指针,对象,资源的ID
- VC6打开临时文件,导致特权执行漏洞
- Enterprise Library2.0(2):Logging Application Block
- 有点想kill掉......