首页
社区
课程
招聘
[原创]开局一场直播,我踏上了逆向还原的征程 一
发表于: 2025-3-16 17:40 1666

[原创]开局一场直播,我踏上了逆向还原的征程 一

2025-3-16 17:40
1666

起点

最近在哔哩哔哩观看了钱老师(逆向老钱)关于逆向还原程序的直播,这场直播让我深受启发,仿佛打开了一扇通往新世界的大门。通过观看钱老师的讲解以及他分析的历史录屏,我对逆向还原这一领域产生了浓厚的兴趣,并决心深入学习和实践。作为一名初学者,我深知这条路上还有许多需要探索的地方,因此希望能得到各位前辈的指导与帮助,期待与大家共同进步!
话不多说,由此开始吧。

NO.1

以下代码是此次逆向还原程序的源代码。

1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main(int argc, char* argv[])
{
int a=20;
int b=30;
int c=b-a;
printf("%d\r\n",c);
return 0;
}

静态分析(编译器选用VC6以及VS2010)

注意:分析时需要删除pdb符号文件

Debug

用IDA打开VC6 Debug版本的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
.text:0040B750 55 push ebp
.text:0040B751 8B EC mov ebp, esp
.text:0040B753 83 EC 4C sub esp, 4Ch
开辟栈空间
.text:0040B756 53 push ebx
.text:0040B757 56 push esi
.text:0040B758 57 push edi
.text:0040B759 8D 7D B4 lea edi,
[ebp+var_4C]
.text:0040B75C B9 13 00 00 00 mov ecx, 13h
.text:0040B761 B8 CC CC CC CC mov eax,
0CCCCCCCCh
.text:0040B766 F3 AB rep stosd
用IDA打开 VS2010 Debug版本的程序
保存异变寄存器以及将栈空间全部初始化为C
.text:0040B768 C7 45 FC 14 00 00 00 mov [ebp+var_4],
14h
.text:0040B76F C7 45 F8 1E 00 00 00 mov [ebp+var_8],
1Eh
.text:0040B776 8B 45 F8 mov eax,
[ebp+var_8]
.text:0040B779 2B 45 FC sub eax,
[ebp+var_4]
.text:0040B77C 89 45 F4 mov [ebp+var_C],
eax
.text:0040B77F 8B 4D F4 mov ecx,
[ebp+var_C]
.text:0040B782 51 push ecx
.text:0040B783 68 1C 00 42 00 push offset Format
; "%d\r\n"
.text:0040B788 E8 D3 58 FF FF call _printf
栈操作
.text:0040B788
.text:0040B78D 83 C4 08 add esp, 8
.text:0040B790 33 C0 xor eax, eax
.text:0040B792 5F pop edi
.text:0040B793 5E pop esi
.text:0040B794 5B pop ebx
.text:0040B795 83 C4 4C add esp, 4Ch
.text:0040B798 3B EC cmp ebp, esp
.text:0040B79A E8 41 59 FF FF call __chkesp
.text:0040B79A
.text:0040B79F 8B E5 mov esp, ebp
.text:0040B7A1 5D pop ebp
.text:0040B7A2 C3 retn
恢复栈并返回

用IDA打开 VS2010 Debug版本的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
.text:00411380 55 push ebp
.text:00411381 8B EC mov ebp, esp
.text:00411383 81 EC E4 00 00 00 sub esp, 0E4h
开辟栈空间
.text:00411389 53 push ebx
.text:0041138A 56 push esi
.text:0041138B 57 push edi
.text:0041138C 8D BD 1C FF FF FF lea edi,
[ebp+var_E4]
.text:00411392 B9 39 00 00 00 mov ecx, 39h
.text:00411397 B8 CC CC CC CC mov eax,
0CCCCCCCCh
.text:0041139C F3 AB rep stosd
保存异变寄存器以及将栈空间填充为C
.text:0041139E C7 45 F8 14 00 00 00 mov [ebp+var_8],
14h
.text:004113A5 C7 45 EC 1E 00 00 00 mov [ebp+var_14],
1Eh
.text:004113AC 8B 45 EC mov eax,
[ebp+var_14]
.text:004113AF 2B 45 F8 sub eax,
[ebp+var_8]
.text:004113B2 89 45 E0 mov [ebp+var_20],
eax
.text:004113B5 8B F4 mov esi, esp
.text:004113B7 8B 45 E0 mov eax,
[ebp+var_20]
.text:004113BA 50 push eax
.text:004113BB 68 3C 57 41 00 push offset Format
; "%d\r\n"
.text:004113C0 FF 15 B0 82 41 00 call ds:printf
栈操作
.text:004113C0
.text:004113C6 83 C4 08 add esp, 8
.text:004113C9 3B F4 cmp esi, esp
.text:004113CB E8 5C FD FF FF call
栈内操作解释
二者相同,就只解释一种了。
j___RTC_CheckEsp
.text:004113CB
.text:004113D0 33 C0 xor eax, eax
.text:004113D2 5F pop edi
.text:004113D3 5E pop esi
.text:004113D4 5B pop ebx
.text:004113D5 81 C4 E4 00 00 00 add esp, 0E4h
.text:004113DB 3B EC cmp ebp, esp
.text:004113DD E8 4A FD FF FF call
j___RTC_CheckEsp
.text:004113DD
.text:004113E2 8B E5 mov esp, ebp
.text:004113E4 5D pop ebp
.text:004113E5 C3 retn
.text:004113E5
恢复栈并返回

栈内操作解释

二者相同,就只解释一种了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
.text:0041139E C7 45 F8 14 00 00 00 mov [ebp+var_8],
14h
14h(20)赋给局部变量a
.text:004113A5 C7 45 EC 1E 00 00 00 mov [ebp+var_14],
1Eh
1Eh(30)赋给局部变量B
.text:004113AC 8B 45 EC mov eax,
[ebp+var_14]
将局部变量B的值赋给eax寄存器
.text:004113AF 2B 45 F8 sub eax,
[ebp+var_8]
使用减法指令,使得变量B-A,将结果存在eax寄存器中
.text:004113B2 89 45 E0 mov [ebp+var_20],
eax
将eax的值赋给局部变量C
比较
从Debug版本的代码来看,VC6和VS2010在栈内操作上差别不大。
主要差别在于恢复栈并返回时使用了运行时检查函数的次数不同。
Release
用IDA打开VC6 Release版本的程序
.text:004113B5 8B F4 mov esi, esp
将当前 ESP 的值复制到 ESI。这通常用来保存当前栈的位置,以便后续操作中使
用,并不会改变栈帧的结构(栈帧依旧由 EBP 固定)。
.text:004113B7 8B 45 E0 mov eax,
[ebp+var_20]
.text:004113BA 50 push eax
取出C的值存入栈中
.text:004113BB 68 3C 57 41 00 push offset Format
; "%d\r\n"
取出格式符号存入栈中
.text:004113C0 FF 15 B0 82 41 00 call ds:printf
调用printf函数输出

比较

从Debug版本的代码来看,VC6和VS2010在栈内操作上差别不大。
主要差别在于恢复栈并返回时使用了运行时检查函数的次数不同。

Release

用IDA打开VC6 Release版本的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
.text:004113B5 8B F4 mov esi, esp
将当前 ESP 的值复制到 ESI。这通常用来保存当前栈的位置,以便后续操作中使
用,并不会改变栈帧的结构(栈帧依旧由 EBP 固定)。
.text:004113B7 8B 45 E0 mov eax,
[ebp+var_20]
.text:004113BA 50 push eax
取出C的值存入栈中
.text:004113BB 68 3C 57 41 00 push offset Format
; "%d\r\n"
取出格式符号存入栈中
.text:004113C0 FF 15 B0 82 41 00 call ds:printf
调用printf函数输出
在VC6代码块中,只在恢复局部变量后、cmp ebp, esp 后调用一次
__chkesp 来检查栈指针是否恢复正确。
VS2010代码块则在两个阶段进行了检查:
先在 add esp, 8 后通过 cmp esi, esp 并调用
j___RTC_CheckEsp,这可能用于验证某个保存的寄存器(如 esi)
或调用者参数的正确性;
然后,在恢复完局部空间后,通过 cmp ebp, esp 再次调用
j___RTC_CheckEsp 检查栈帧基址与栈指针是否一致。

Release

用IDA打开VC6 Release版本的程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.text:00401000 6A 0A push 0Ah
.text:00401002 68 30 60 40 00 push offset Format
; "%d\r\n"
用IDA打开VS2010 Release版本的程序
比较
VC6以及VS2010直接优化了程序的计算、赋值过程。直接将计算好的常量直
接作为参数传递
拓展
以下为如今编译器在release模式下所会采用的优化方式
.text:00401007 E8 14 00 00 00 call _printf
.text:00401007
.text:0040100C 83 C4 08 add esp, 8
.text:0040100F 33 C0 xor eax, eax
.text:00401011 C3 retn

用IDA打开VS2010 Release版本的程序

1
2
3
4
5
6
7
8
.text:00401000 6A 0A push 0Ah
.text:00401002 68 F4 20 40 00 push offset Format
; "%d\r\n"
.text:00401007 FF 15 A0 20 40 00 call ds:printf
.text:00401007
.text:0040100D 83 C4 08 add esp, 8
.text:00401010 33 C0 xor eax, eax
.text:00401012 C3 retn

比较

VC6以及VS2010直接优化了程序的计算、赋值过程。直接将计算好的常量直
接作为参数传递

拓展一 编译器优化方式

以下为如今编译器在release模式下所会采用的优化方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
常量折叠(Constant Folding)与传播(Constant Propagation):
在编译期间计算所有可以确定的常量表达式,将常量值直接替换到代码中。
死代码消除(Dead Code Elimination):
移除那些永远不会被执行或其结果不会被使用的代码片段。
函数内联(Function Inlining):
将小函数的代码嵌入到调用处,减少函数调用开销。
WinDBG
循环优化:
包括循环展开(Loop Unrolling)、循环不变代码外提(Loop Invariant Code
Motion)和循环融合等,以减少循环控制带来的开销。
寄存器分配优化(Register Allocation):
将变量尽可能分配到寄存器中,减少内存访问次数,提高运行速度。
公共子表达式消除(Common Subexpression Elimination):
合并重复计算的表达式,避免重复求值。
尾递归优化(Tail-Call Optimization):
对尾递归进行优化,转化为迭代,从而节省栈空间。
指令调度与短视优化(Peephole Optimization):
对小范围内的指令序列进行重排和合并,生成更高效的机器码。
矢量化(Vectorization):
利用 SIMD 指令集,把标量运算转换为并行的矢量运算(如果硬件支持)。
跨过程优化(Interprocedural Optimization, IPO / LTO):
在函数边界内进行优化,甚至整个程序级别的优化(如链接时优化 Link Time
Optimization)。
配置导向优化(Profile Guided Optimization, PGO):
基于实际运行时的数据,优化热路径和分支预测(需要额外的运行数据)。

拓展二 WinDBG

常用命令(后续每次更新会加一些拓展内容)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
- 列出当前寄存器环境
0:000> r
eax=00000000 ebx=00000003 ecx=10e70000 edx=00000000 esi=004000d8
edi=003ec000
eip=7717ceac esp=0019fa44 ebp=0019fa70 iopl=0 nv up ei pl
zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b
efl=00000246
ntdll!LdrInitShimEngineDynamic+0x6dc:
7717ceac cc int 3
k - 查看栈列表(kb 显示栈函数以及函数参数)
0:000> k
# ChildEBP RetAddr
WARNING: Stack unwind information not available. Following frames
may be wrong.
00 0019fa70 7714303f ntdll!LdrInitShimEngineDynamic+0x6dc
01 0019fca8 77134556 ntdll!WinSqmAddToStreamEx+0x1bdf
02 0019fcf8 7713446b ntdll!LdrInitializeThunk+0x136
03 0019fd08 77134430 ntdll!LdrInitializeThunk+0x4b
04 0019fd10 00000000 ntdll!LdrInitializeThunk+0x10
p - 单步步过
0:000> p
eax=00000000 ebx=00000003 ecx=10e70000 edx=00000000 esi=004000d8
edi=003ec000
eip=7717cead esp=0019fa44 ebp=0019fa70 iopl=0 nv up ei pl
zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b
efl=00000244
ntdll!LdrInitShimEngineDynamic+0x6dd:
7717cead eb07 jmp
ntdll!LdrInitShimEngineDynamic+0x6e6 (7717ceb6)
t - 单步步入
0:000> t
eax=00000000 ebx=00000003 ecx=10e70000 edx=00000000 esi=004000d8
edi=003ec000
eip=7717ceb6 esp=0019fa44 ebp=0019fa70 iopl=0 nv up ei pl
zr na pe nc
Windows知识拓展
用户态和内核态
用户态(User Mode)和内核态(Kernel Mode)是两种不同的 CPU 运行
模式,它们主要用于区分普通应用程序和操作系统核心功能的执行权限。
简而言之:
用户态运行普通应用程序,权限受限,不能直接访问硬件和关键系统资源。
内核态运行操作系统核心代码,具有最高权限,可以直接控制硬件和管理系
统资源。
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b
efl=00000246
ntdll!LdrInitShimEngineDynamic+0x6e6:
7717ceb6 c745fcfeffffff mov dword ptr [ebp-4],0FFFFFFFEh
ss:002b:0019fa6c=00000000
.cls - 清屏

今天就先写到这里吧!这是我的第一篇文章,但绝不会是最后一篇。作为一名初学者,我深知自己还有许多不足,因此非常期待能与各位前辈和同好交流学习。如果文章中有任何疏漏或不妥之处,恳请大家不吝指正。愿我们共同进步,在技术的道路上越走越远!


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回