-
-
[旧帖] IDA工具使用学习_1 0.00雪花
-
发表于: 2012-8-22 00:58 1337
-
1.在PC机上编写一个hello.c的程序如下:
#include <stdio.h>
int MyPrint(int x, int y, char * z)
{
return x+y;
}
int main()
{
int a;
int b;
int c;
int d;
char pTempString[64];
a=4;
b=5;
pTempString[0]='3';
c=MyPrint(a,b,pTempString);
d=c+a;
return 0;
}
2.用VC6.0编译hello.c生成hello.exe.(release版本)。
3.用IDA5.2载入hello.exe,按照默认配置进行分析,结果如下(红色部分为注释):
.text:00401000 ;
.text:00401000 ; +-------------------------------------------------------------------------+
.text:00401000 ; | This file is generated by The Interactive Disassembler (IDA) |
.text:00401000 ; | Copyright (c) 2007 by DataRescue sa/nv, <ida@datarescue.com> |
.text:00401000 ; | Licensed to: Mach EDV Dienstleistungen, Jan Mach, 1 user, adv, 11/2007 |
.text:00401000 ; +-------------------------------------------------------------------------+
.text:00401000 ;
.text:00401000 ; Input MD5 : C26205DEF488E3792611470EB26D47C4
.text:00401000
.text:00401000 ; File Name : C:\Documents and Settings\Administrator\桌面\hello.exe
.text:00401000 ; Format : Portable executable for 80386 (PE)
.text:00401000 ; Imagebase : 400000
.text:00401000 ; Section 1. (virtual address 00001000)
.text:00401000 ; Virtual size : 0000352E ( 13614.)
.text:00401000 ; Section size in file : 00004000 ( 16384.)
.text:00401000 ; Offset to raw data for section: 00001000
.text:00401000 ; Flags 60000020: Text Executable Readable
.text:00401000 ; Alignment : default
.text:00401000 ; OS type : MS Windows
.text:00401000 ; Application type: Executable 32bit
.text:00401000
.text:00401000 .686p
.text:00401000 .mmx
.text:00401000 .model flat
.text:00401000
.text:00401000 ; ===========================================================================
.text:00401000
.text:00401000 ; Segment type: Pure code
.text:00401000 ; Segment permissions: Read/Execute
.text:00401000 _text segment para public 'CODE' use32
.text:00401000 assume cs:_text
.text:00401000 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.text:00401000
.text:00401000 ; =============== S U B R O U T I N E =======================================
.text:00401000
.text:00401000 ; 子函数开始
.text:00401000
.text:00401000 _text_401000 proc near ; CODE XREF: _main+11p
.text:00401000
.text:00401000 arg_0= dword ptr 4
.text:00401000 arg_4= dword ptr 8
.text:00401000
.text:00401000 8B 44 24 08 mov eax, [esp+arg_4]
.text:00401004 8B 4C 24 04 mov ecx, [esp+arg_0] ; 将函数传入的形参分别放到eax和ecx寄存器中
.text:00401008 03 C1 add eax, ecx ; 将2形参相加,丢到eax寄存器中
.text:0040100A C3 retn ; 子函数返回
.text:0040100A
.text:0040100A _text_401000 endp
.text:0040100A
.text:0040100A ; ---------------------------------------------------------------------------
.text:0040100B 90 90 90 90 90 align 10h
.text:00401010
.text:00401010 ; =============== S U B R O U T I N E =======================================
.text:00401010
.text:00401010 ; 主程序开始
.text:00401010
.text:00401010 ; int __cdecl main(int argc, const char **argv, const char *envp)
.text:00401010 _main proc near ; CODE XREF: start+AFp
.text:00401010
.text:00401010 var_40= byte ptr -40h
.text:00401010 argc= dword ptr 4
.text:00401010 argv= dword ptr 8
.text:00401010 envp= dword ptr 0Ch
.text:00401010
.text:00401010 83 EC 40 sub esp, 40h
.text:00401013 8D 44 24 00 lea eax, [esp+40h+var_40] ; 装入有效地址到EAX
.text:00401017 C6 44 24 00 33 mov [esp+40h+var_40], 33h ; 将33H写入某地址中
.text:0040101C 50 push eax ; 从右向左,依次把函数传递的参数压入堆栈
.text:0040101D 6A 05 push 5
.text:0040101F 6A 04 push 4
.text:00401021 E8 DA FF FF FF call _text_401000 ; 调用函数,传入的参数为(4,5,eax)
.text:00401021
.text:00401026 33 C0 xor eax, eax ; eax清零
.text:00401028 83 C4 4C add esp, 4Ch ; 恢复堆栈
.text:0040102B C3 retn ; 主程序结束
.text:0040102B
.text:0040102B _main endp
.text:0040102B
4.详细分析:
首先看主程序的开始部分,__cdecl 标志 说明IDA已经识别出我们使用的是C编译器。_cdecl指明的就是C调用约定。关于C调用约定,它规定:调用方按从右到左的顺序将函数参数放入栈,在被调用的函数完成其操作时,调用方(而不是被调用方)负责从栈中清除参数。
第一条指令: sub esp, 40h
是系统用来预留空间给函数的,这些空间主要是用来存储函数传入的参数和局部变量。另外为什么是40h,其中也是确保栈帧内的特殊的对齐方式。
第二条指令: lea eax, [esp+40h+var_40]
是一条目的地址传送指令.( LEA 装入有效地址. 例: LEA DX,string ;把偏移地址存到DX. )
在主程序之前,有以下几个参量的定义;
var_40= byte ptr -40h
argc= dword ptr 4
argv= dword ptr 8
envp= dword ptr 0Ch
这里要说说IDA为变量自动命名的规则。IDA会根据变量相对于被保存的返回地址的位置,为变量取名。
局部变量位于被保存的返回地址之上,而函数参数则位于被保存的返回地址之下。
局部变量名称以var_为前缀,后面跟一个表示变量与被保存的帧指针之间的距离(以字节为单位)的十六进制后缀。(例如本例的var_40)
函数参数名则以arg_为前缀,后面跟一个表示其与最顶端的参数之间的相对距离的十六进制的后缀。(例如子程序中的arg_0)
另外,如果在程序中找不到一个变量的直接引用(使用),IDA将不会为其生成变量名称。(例如hello.c中的 int d;)
将IDA的反汇编和hello.c源程序对应看:
[esp+40h+var_40] 对应 pTempString 首地址。
_text_401000 对应 MyPrint 函数的入口地址。
add eax, ecx 对应 x+y 指令的执行。
此处有一个地方比较纠结,就是[esp+40h+var_40],这里var_40是相对于ESP而非EBP引用的,而所有符号名称却表示与帧指针EBP的偏移量。因此,要使IDA能够利用符号名称var_40,可能需要一个"修正参数"。地址[esp+var_40]并不正确,因为它等同于[esp-40h],而不是基础机器语言指令指定的[esp]。未来利用符号变量名称,必须添加常量+40h,使上述地址计算正确,从而得到[esp+40h+var_40]。
#include <stdio.h>
int MyPrint(int x, int y, char * z)
{
return x+y;
}
int main()
{
int a;
int b;
int c;
int d;
char pTempString[64];
a=4;
b=5;
pTempString[0]='3';
c=MyPrint(a,b,pTempString);
d=c+a;
return 0;
}
2.用VC6.0编译hello.c生成hello.exe.(release版本)。
3.用IDA5.2载入hello.exe,按照默认配置进行分析,结果如下(红色部分为注释):
.text:00401000 ;
.text:00401000 ; +-------------------------------------------------------------------------+
.text:00401000 ; | This file is generated by The Interactive Disassembler (IDA) |
.text:00401000 ; | Copyright (c) 2007 by DataRescue sa/nv, <ida@datarescue.com> |
.text:00401000 ; | Licensed to: Mach EDV Dienstleistungen, Jan Mach, 1 user, adv, 11/2007 |
.text:00401000 ; +-------------------------------------------------------------------------+
.text:00401000 ;
.text:00401000 ; Input MD5 : C26205DEF488E3792611470EB26D47C4
.text:00401000
.text:00401000 ; File Name : C:\Documents and Settings\Administrator\桌面\hello.exe
.text:00401000 ; Format : Portable executable for 80386 (PE)
.text:00401000 ; Imagebase : 400000
.text:00401000 ; Section 1. (virtual address 00001000)
.text:00401000 ; Virtual size : 0000352E ( 13614.)
.text:00401000 ; Section size in file : 00004000 ( 16384.)
.text:00401000 ; Offset to raw data for section: 00001000
.text:00401000 ; Flags 60000020: Text Executable Readable
.text:00401000 ; Alignment : default
.text:00401000 ; OS type : MS Windows
.text:00401000 ; Application type: Executable 32bit
.text:00401000
.text:00401000 .686p
.text:00401000 .mmx
.text:00401000 .model flat
.text:00401000
.text:00401000 ; ===========================================================================
.text:00401000
.text:00401000 ; Segment type: Pure code
.text:00401000 ; Segment permissions: Read/Execute
.text:00401000 _text segment para public 'CODE' use32
.text:00401000 assume cs:_text
.text:00401000 assume es:nothing, ss:nothing, ds:_data, fs:nothing, gs:nothing
.text:00401000
.text:00401000 ; =============== S U B R O U T I N E =======================================
.text:00401000
.text:00401000 ; 子函数开始
.text:00401000
.text:00401000 _text_401000 proc near ; CODE XREF: _main+11p
.text:00401000
.text:00401000 arg_0= dword ptr 4
.text:00401000 arg_4= dword ptr 8
.text:00401000
.text:00401000 8B 44 24 08 mov eax, [esp+arg_4]
.text:00401004 8B 4C 24 04 mov ecx, [esp+arg_0] ; 将函数传入的形参分别放到eax和ecx寄存器中
.text:00401008 03 C1 add eax, ecx ; 将2形参相加,丢到eax寄存器中
.text:0040100A C3 retn ; 子函数返回
.text:0040100A
.text:0040100A _text_401000 endp
.text:0040100A
.text:0040100A ; ---------------------------------------------------------------------------
.text:0040100B 90 90 90 90 90 align 10h
.text:00401010
.text:00401010 ; =============== S U B R O U T I N E =======================================
.text:00401010
.text:00401010 ; 主程序开始
.text:00401010
.text:00401010 ; int __cdecl main(int argc, const char **argv, const char *envp)
.text:00401010 _main proc near ; CODE XREF: start+AFp
.text:00401010
.text:00401010 var_40= byte ptr -40h
.text:00401010 argc= dword ptr 4
.text:00401010 argv= dword ptr 8
.text:00401010 envp= dword ptr 0Ch
.text:00401010
.text:00401010 83 EC 40 sub esp, 40h
.text:00401013 8D 44 24 00 lea eax, [esp+40h+var_40] ; 装入有效地址到EAX
.text:00401017 C6 44 24 00 33 mov [esp+40h+var_40], 33h ; 将33H写入某地址中
.text:0040101C 50 push eax ; 从右向左,依次把函数传递的参数压入堆栈
.text:0040101D 6A 05 push 5
.text:0040101F 6A 04 push 4
.text:00401021 E8 DA FF FF FF call _text_401000 ; 调用函数,传入的参数为(4,5,eax)
.text:00401021
.text:00401026 33 C0 xor eax, eax ; eax清零
.text:00401028 83 C4 4C add esp, 4Ch ; 恢复堆栈
.text:0040102B C3 retn ; 主程序结束
.text:0040102B
.text:0040102B _main endp
.text:0040102B
4.详细分析:
首先看主程序的开始部分,__cdecl 标志 说明IDA已经识别出我们使用的是C编译器。_cdecl指明的就是C调用约定。关于C调用约定,它规定:调用方按从右到左的顺序将函数参数放入栈,在被调用的函数完成其操作时,调用方(而不是被调用方)负责从栈中清除参数。
第一条指令: sub esp, 40h
是系统用来预留空间给函数的,这些空间主要是用来存储函数传入的参数和局部变量。另外为什么是40h,其中也是确保栈帧内的特殊的对齐方式。
第二条指令: lea eax, [esp+40h+var_40]
是一条目的地址传送指令.( LEA 装入有效地址. 例: LEA DX,string ;把偏移地址存到DX. )
在主程序之前,有以下几个参量的定义;
var_40= byte ptr -40h
argc= dword ptr 4
argv= dword ptr 8
envp= dword ptr 0Ch
这里要说说IDA为变量自动命名的规则。IDA会根据变量相对于被保存的返回地址的位置,为变量取名。
局部变量位于被保存的返回地址之上,而函数参数则位于被保存的返回地址之下。
局部变量名称以var_为前缀,后面跟一个表示变量与被保存的帧指针之间的距离(以字节为单位)的十六进制后缀。(例如本例的var_40)
函数参数名则以arg_为前缀,后面跟一个表示其与最顶端的参数之间的相对距离的十六进制的后缀。(例如子程序中的arg_0)
另外,如果在程序中找不到一个变量的直接引用(使用),IDA将不会为其生成变量名称。(例如hello.c中的 int d;)
将IDA的反汇编和hello.c源程序对应看:
[esp+40h+var_40] 对应 pTempString 首地址。
_text_401000 对应 MyPrint 函数的入口地址。
add eax, ecx 对应 x+y 指令的执行。
此处有一个地方比较纠结,就是[esp+40h+var_40],这里var_40是相对于ESP而非EBP引用的,而所有符号名称却表示与帧指针EBP的偏移量。因此,要使IDA能够利用符号名称var_40,可能需要一个"修正参数"。地址[esp+var_40]并不正确,因为它等同于[esp-40h],而不是基础机器语言指令指定的[esp]。未来利用符号变量名称,必须添加常量+40h,使上述地址计算正确,从而得到[esp+40h+var_40]。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
赞赏
他的文章
看原图
赞赏
雪币:
留言: