- UID
- 34504
注册时间2007-8-16
阅读权限8
最后登录1970-1-1
初入江湖
该用户从未签到
|
回想一下,从毕业后到现在,学编程有三年了,这三年来学了很多的东西,网络安全的知识面算是有了,可是却发现没有一样擅长的,于是乎打算找一样专攻,想来想去还是觉得逆向分析比较好,吸星大法嘛,哈哈.
这篇文章我个人感觉是非常简单的逆向分析的文章,所以高手就不要见笑啊.当然它还是需要一定的基础的,比如什么叫入栈,什么叫出栈,什么叫栈顶,如果这些都不清楚的话,估计还是会看不懂.之所以抽时间写下来,一是为了记录一下自已的成长过程,二是为了把学习结果总结一下和大家分享.
OK,废话不多说了,文章开始.
; ========================================================================
; 作者:∮明天去要饭
; Blog: http://hi.baidu.com/kgdiwss
; 论坛: http://bbs.80dnst.com
; ========================================================================
源代码:
#include <stdio.h>
int main(int argc, char *argv[])
{
for(int i = 0; i < 10; i++)
{
printf("i = %d\n", i);
}
return 0;
}
用VC6.0的cl.exe编译,命令如下:
cl.exe for1.cpp
就可以得到for1.exe了(见附件)
然后用 IDA5.0.0.879 反汇编后的代码:
.text:00401000 ; int __cdecl main(int argc,const char **argv,const char *envp)
; 从 00401000 这一行代码可以看出来, c的缺省入栈方式是 __cdecl ,即调用者调整堆栈 .
; char *argv[] 变成了 char **argv ,说明 argv 是一个指针的指针
; 反汇编后还多了一个参数: const char *envp
.text:00401000 _main proc near ; CODE XREF: start+AF↓p
.text:00401000
.text:00401000 var_4 = dword ptr -4 ; 这个变量偏移是-4,即在基地址上方.在基址上方的比较可能是局部变量
; 这里把内存用高址和低址来表示,内存地址从下往上依次减小
; 上方表示比基地址小,下方表示比基址大,暂时可以这么理解.
; 它的长度是4,至于这个变量用来干什么暂时不太清楚,跳过去
.text:00401000 argc = dword ptr 8 ; 参数:argc,偏移为8,表示在基址的下方,下面两个变量也是在基址下方
.text:00401000 argv = dword ptr 0Ch ; 参数:argv,偏移为0ch(即十进制的12),所以argv的长度是12-8=4,这和指针的长度是4字节相符.
.text:00401000 envp = dword ptr 10h ; 比原代码多出来的变量,偏移为10h(即16),所以它的长度是16-12=4.
.text:00401000 ; ---------------------------------------------------------------------------
; 以下代码保存main函数运行前的环境
; 基地址入栈,函数运行完要pop出来以恢复原来的运行环境.
; ---------------------------------------------------------------------------
.text:00401000 push ebp ; 基地址入栈,main函数运行完后会pop出来的
.text:00401001 mov ebp, esp ; ebp指向基址入栈后的顶栈,此时ebp是main函数地址的开始
.text:00401003 push ecx ; 计数器入栈
.text:00401004 mov [ebp+var_4], 0 ; 初始化变量 var_4 为0,即[ebp-4]位置处置0
.text:0040100B jmp short loc_401016
.text:0040100B
.text:0040100D ; ---------------------------------------------------------------------------
.text:0040100D
.text:0040100D loc_40100D: ; CODE XREF: _main+2D↓j
.text:0040100D mov eax, [ebp+var_4] ; [ebp-4]处的值赋给eax
.text:00401010 add eax, 1 ; eax值加1
.text:00401013 mov [ebp+var_4], eax ; 将加1后的结果赋回给[ebp-4]
.text:00401013
.text:00401016 ; ---------------------------------------------------------------------------
.text:00401016 loc_401016: ; CODE XREF: _main+B↑j
.text:00401016 cmp [ebp+var_4], 0Ah ; 判断[ebp-4]处的值 与 0A(即10) 的大小
.text:0040101A jge short loc_40102F ; 大于或等于10时跳到loc_40102F处,即退出程序
.text:0040101A
.text:0040101C mov ecx, [ebp+var_4] ; 如果小于10则将值赋给计数器ecx
.text:0040101F push ecx ; 值入栈
.text:00401020 push offset s_ID ; 字符串 "i = %d\n" 入栈
.text:00401025 call sub_401035 ; 对比一下源码 sub_401035 应该是printf的地址
; 分析到这里我们可以确定, var_4 就是源代码中的变量i
.text:00401025
.text:0040102A add esp, 8 ; 调用了 printf 函数后,要调整一下堆栈以恢复当前函数的堆栈
; 从这里可以看出来__cdecl方式是调用者调整堆栈
; 如果是__stdcall方式的话,这句话就会在printf函数的堆栈中完成
.text:0040102D jmp short loc_40100D ; 跳到 loc_40100D 继续下一轮的比较
.text:0040102D
.text:0040102F ; ---------------------------------------------------------------------------
; 以下代码恢复main函数运行前的堆栈
.text:0040102F ; ---------------------------------------------------------------------------
.text:0040102F loc_40102F: ; CODE XREF: _main+1A↑j
.text:0040102F xor eax, eax
.text:00401031 mov esp, ebp
.text:00401033 pop ebp
.text:00401034 retn
.text:00401034
.text:00401034 _main endp
怎么样,是不是很简单啊,分析的过程倒是很简单,不过要写成文章倒是花了我不少时间.
如果你发现有什么错误请告诉我,非常感谢。
如果有什么疑问,也欢迎到『80王朝』论坛来提,我会抽时间回复的。
提示:
不知道为什么我排好版后的文章发到这来版面就不整齐了,而且颜色也没了,如果看不太清楚可以到这里看(有颜色标记应该容易看懂点):
http://b0d.5d6d.com/viewthread.p ... &extra=page%3D1
[ 本帖最后由 kgdiwss 于 2007-11-21 10:40 编辑 ] |
-
-
for1.rar
114.65 KB, 下载次数: 4, 下载积分: 飘云币 -2 枚
评分
-
查看全部评分
|