MyException - 我的异常网
当前位置:我的异常网» C++ » OD一段简略的C++代码

OD一段简略的C++代码

www.MyException.Cn  网友分享于:2013-12-29  浏览:6次
OD一段简单的C++代码

开始学习逆向,从熟悉的js、php跳跃到汇编,有点小凌乱 ...

 

先来OD一段简单的C++代码:

#include <string.h>
#include <windows.h>
#include <stdio.h>

int funcA(int a, int b);

void main()
{
	int a;
	int b;
	int n;

	a = strlen("abc");
	b = 10;
	n = funcA(a, b);
}

int funcA(int a, int b)
{
	int c;
	char sBuff[10];

	c = (a + b) * a * b;

	sprintf(sBuff, "%d", c);
	MessageBox(NULL, sBuff, NULL, MB_OK);
	return c;
}
 

编译环境:VC6 ,release

 

这段代码逻辑上很简单,一共两个函数,main和funcA,funcA会在main中被调用一次。main函数的入口会被编译成call 00401000,00401000是虚拟的偏移量。虚拟地址也可写作“段:偏移量”的形式 ,一般来说可以不用关注段,因为同一个程序在不同的系统下,段值可能不相同。但是偏移量却是有着一些共性,对于同一个程序的某条指令,在不同系统中它的偏移量一般是相同的。按照VS的默认设置,编译出的exe文件基地址是00400000,dll文件基地址是10000000。

 

 

先来直接在main的入口处下断,然后逐步跟进。

 

00401154  |.  50                       push    eax
00401155  |.  FF35 0C994000  push    dword ptr [40990C]
0040115B  |.  FF35 08994000  push    dword ptr [409908]
00401161  |.  E8 9AFEFFFF       call    00401000

00401166  |.  83C4 0C             add     esp, 0C

 

这是一个典型的函数调用,只不过调的是main罢了。三个参数压栈,然后call,完了消栈,格式很清晰。

main中的汇编代码如下:

 

00401000  /$  57                       push    edi
00401001  |.  BF 30704000       mov     edi, 00407030                    ;  ASCII "abc"
00401006  |.  83C9 FF               or      ecx, FFFFFFFF
00401009  |.  33C0                    xor     eax, eax
0040100B  |.  F2:AE                   repne   scas byte ptr es:[edi]
0040100D  |.  F7D1                    not     ecx
0040100F  |.  49                        dec     ecx
00401010  |.  6A 0A                   push    0A
00401012  |.  51                        push    ecx
00401013  |.  E8 08000000       call    00401020                             ;  该处调用funcA函数
00401018  |.  83C4 08              add     esp, 8
0040101B  |.  5F                        pop     edi
0040101C  \.  C3                       retn

 

strlen函数并没有向funcA一样被编译为另外一个call,而是直接编译在了main函数之中。strlen的实现非常之精妙:

这是strlen()在VC优化编译模式下编译后的代码:

00401000  /$  57                       push    edi      ;  因为后面repne的时候要发生更改,所以先暂存edi

00401006  |.  83C9 FF               or      ecx, FFFFFFFF
00401009  |.  33C0                    xor     eax, eax
0040100B  |.  F2:AE                   repne   scas byte ptr es:[edi]
0040100D  |.  F7D1                    not     ecx
0040100F  |.  49                        dec     ecx

 

edi是指向字符串“abc”的指针,repne   scas byte ptr es:[edi] 指令会扫描字符串“abc”(在内存中为 61 62 63 00),从字母a开始,一直到字符串结束符,一共4个字符。

 

REPNE :用在CMPS、SCAS指令前,每执行一次串操作指令ECX减1,并判断ZF标志是否为1,只要CX=0或ZF=1,则重复执行结束。

SCAS :将附加段中的字节或字内容与AL/AX寄存器内容进行比较,根据比较的结果设置标志,每次比较后修改EDI寄存器的值,使之指向下一个元素。

 

这里需要扫描4次,每次扫描的字符与eax中的低八8位即00相比较,如果相同,则ZF标志位变成1,扫描结束;如果不同,则ecx减1并继续扫描。因此需要事先将eax置0,并且ecx置为-1。扫描完之后的ecx变为FFFFFFFB。取反后变成00000004,这是包含了字符串结束标志的长度,因此还需要减1.

 

随后将10、3入栈,准备调用funcA函数。函数的调用基本上都满足_stdcall约定 ,即参数从右向左,顺次压栈,函数调用的返回值保存在eax函数中。funcA的汇编码如下:

 

虽然有点长,但是代码还是很好理解的。

开始的两句mov是去栈中取操作数3、10,并且分别存放到eax、ecx中。

随后,sub esp,0xC是将栈顶向上移动,从而开辟出一块空的区域,可以用作预留用。

 

0040102C  |.  8D3408        lea esi,dword ptr ds:[eax+ecx]
0040102F  |.  0FAFF0        imul esi,eax
00401032  |.  0FAFF1        imul esi,ecx

这三句完成了 c = (a + b) * a * b ,运算结果的值保存在esi中。

随后调用的MessageBox的过程暂时忽略。

因为最后需要返回c的值,因此在retn之前,需要执行一句mov eax,esi。

 

这里的call调用都没有被编译成经典的:

PUSH ebp
MOV ebp esp

······

这个无所谓,值得注意的是需要确保栈的平衡性,即入栈一定要与消栈相对应。

 

 

 

1 楼 RednaxelaFX 2011-04-28  
driftcloudy 写道
这里的call调用都没有被编译成经典的:
PUSH ebp
MOV ebp esp

这跟编译参数相关。开了FPO的话就不使用EBP作为frame pointer,就省下了这段代码。
引用
/Oy[-] enable frame pointer omission
2 楼 driftcloudy 2011-04-28  
RednaxelaFX 写道
这跟编译参数相关。开了FPO的话就不使用EBP作为frame pointer,就省下了这段代码。


喔哦~果然是这样...感谢指点
实验了一下,加上 /Oy- 进行编译,立马变成了我熟悉的形式
引用

push    ebp
mov     ebp, esp
sub     esp, 0C
mov     eax, dword ptr [ebp+8]
……
mov     esp, ebp
pop     ebp
retn


而 /Oy 的时候是
引用

mov     eax, dword ptr [esp+4]
mov     ecx, dword ptr [esp+8]
sub     esp, 0C
……
add     esp, 0C
retn


嘿嘿,现在回忆起来,原来你早就玩OD了,可惜当时我一窍不通...石化 >_<
3 楼 RednaxelaFX 2011-04-28  
driftcloudy 写道
嘿嘿,现在回忆起来,原来你早就玩OD了,可惜当时我一窍不通...石化 >_<

想当年啊诶诶…

文章评论

亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
总结2014中国互联网十大段子
总结2014中国互联网十大段子
程序员应该关注的一些事儿
程序员应该关注的一些事儿
每天工作4小时的程序员
每天工作4小时的程序员
鲜为人知的编程真相
鲜为人知的编程真相
漫画:程序员的工作
漫画:程序员的工作
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
编程语言是女人
编程语言是女人
60个开发者不容错过的免费资源库
60个开发者不容错过的免费资源库
如何成为一名黑客
如何成为一名黑客
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
我的丈夫是个程序员
我的丈夫是个程序员
为什么程序员都是夜猫子
为什么程序员都是夜猫子
10个调试和排错的小建议
10个调试和排错的小建议
旅行,写作,编程
旅行,写作,编程
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
那些争议最大的编程观点
那些争议最大的编程观点
中美印日四国程序员比较
中美印日四国程序员比较
程序员必看的十大电影
程序员必看的十大电影
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
一个程序员的时间管理
一个程序员的时间管理
老程序员的下场
老程序员的下场
程序员和编码员之间的区别
程序员和编码员之间的区别
我是如何打败拖延症的
我是如何打败拖延症的
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
程序员都该阅读的书
程序员都该阅读的书
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
程序员的鄙视链
程序员的鄙视链
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
代码女神横空出世
代码女神横空出世
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有