2020.03.08-2020.03.15

IDA

枚举

‘View’->‘Open subviews’->‘Enumerations’ 打开枚举窗口,按’Insert’插入新的枚举类型,按’N’添加成员
选中需要重新定义的数据,按’M’后将其转换
8etGNT.md.png

8etJ4U.md.png

FLIRT

库文件快速识别与鉴定技术
在一系列编译器的标准库文件里自动找出调用的函数,如,把’call 406E40’识别为’call strlen’
如果没有自动识别出来,可以强制使用编译器特征文件(xxxx.sig)
‘View’->‘Open subviews’->'Signatures’或’Shift+F5’打开签名窗口,右键’Apply new signature’选择签名文件

(不知道把这部分叫作啥)

可见,可以把004010c9的内容改了让程序显示'OK'

Assmeble修改汇编指令,Apply patches to input file...将修改保存到文件
2.
输入输出等函数可在name窗口中查看

32位软件逆向技术

启动函数

Windows程序执行并不是由WinMain函数开始的,首先执行的是启动函数的相关代码(由编译器生成),完成后才调用WinMain函数

c/c++程序的启动函数作用基本相同,包括 检索指向新进程的命令行指针、检索指向新进程的环境变量指针、全局变量初始化和内存栈初始化等

某程序启动代码(部分)
分析程序的过程中可以略过启动代码,直接将重点放到WinMain函数上

函数

通过call…ret把函数调用和其他跳转指令区别开
直接调用:call 函数首地址
间接调用:call [ eax ] (通过寄存器传递函数地址或动态计算函数地址)

函数的参数

函数传递参数有3种方式:栈方式、寄存器方式、通过全局变量进行隐含参数传递方式
每一种机制与使用的编译语言有关

利用栈传递参数

函数计算结束后,由调用者或函数本身修改栈,使栈恢复原样(平衡栈数据
调用约定:为了实现函数调用而建立的协议(按照什么顺序入栈;由谁来平衡栈…)

  1. c规范(__cdecl)函数按照从右到左的顺序入栈,由调用者负责清除栈(c/c++/mfc(微软基础类库)默认调用约定)
  2. stdcall调用约定按照从右到左传递参数,并由调用的函数在返回前清理传送参数的内存栈
  3. stdcall调用约定是Win32 API采用的约定方式,在Win32 API种也有一些函数采用(__cdecl)调用,如wsprintf

c、c++、pascal 等高级语言的子程序执行过程基本相似:

  1. 调用者将函数执行完毕时应返回的地址和参数压入栈
  2. 子程序通过’ebp 指针+偏移量’对栈中的操作进行寻址
  3. 子程序使用ret或retf返回,此时cpu将eip置为栈中保存的地址

栈的操作对象只能是双操作数(占4个字节)
一个凑行数的图

用ebp存取栈
用ret平衡栈时,在ret指令后加一个操作数,表示在ret指令后给esp加上操作数 如’ret 8’相当于在返回后将esp+8,ret后面的值等于参数个数*4h

enter 和 leave指令可以帮助进行栈的维护

1
2
3
4
5
6
7
8
9
10
11
12
13
14
enter xxxx,0   ;0表示创建xxxx大小的空间来放置局部变量
....
leave
ret 8


enter的作用为:
push ebp
mov ebp,esp
sub esp,xxxx

leave的作用为:
add esp,xxxx
pop ebp

ENTER 有两个操作数:第一个是常数,定义为局部变量保存的堆栈空间字节数;第二个定义了过程的词法嵌套级。
ENTER numbytes, nestinglevel
Numbytes 总是向上舍入为 4 的倍数,以便 ESP 对齐双字边界。Nestinglevel 确定了从主调过程堆栈帧复制到当前帧的堆栈帧指针的个数。

利用寄存器传递参数

绝大多数编译器都遵循fastcall规范
不同的编译器实现的fastcall稍有不同

名称修饰约定

c++编译器会按照某种规则改写每一个入口点的符号名,从而允许同一个名字有多个用法且不破坏链接器。这种技术称为名称改编或名称修饰
在vc++种,函数修饰名由编译类型(c/c++)、函数名、类(class)名、调用约定、返回类型等决定
常见的c和c++编译函数名的修饰

函数的返回值

最常见的是return操作符,还有通过参数 按 传引用方式 返回值、通过全局变量返回值

用return操作符返回值

一般情况下返回值放在eax中,如果超过大小,高32位就会放在edx
对于一个返回两个参数和的子函数:
圈起来的即为存放返回值的过程
对应c语言代码:

1
2
3
4
5
add(int x,int y){
int temp;
temp=x+y;
return temp;
}

通过参数按传引用方式返回值

传递参数的方式有:传值和传引用
传值调用时会建立参数的一份复本,并把它传给调用参数
传引用允许调用函数修改原始变量的值(指针)

1
2
3
4
void max(int *a,int *b){
if(*a<*b)
*a=*b;
}