# 滴水 reverse

阿里云盘分享 (aliyundrive.com)

视频教程_免费高速下载 | 百度网盘 - 分享无限制 (baidu.com)

[0404-NCK 逆向课程滴滴水网络逆向破解基础初级中级就业班工具完整教程_免费高速下载 | 百度网盘 - 分享无限制 (baidu.com)](https://pan.baidu.com/s/1EtNCSIjDdmL5Fus_z2ckSw#list/path=%2F0404-NCK 逆向课程滴滴水网络逆向破解基础初级中级就业班工具完整教程 %2FV-1309:D 水网络逆向破解 基础 %2B 中级 %2B 就业班合集(完结 )&parentPath=%2F)

Revival_S_滴水逆向三期,C 语言入门,数据结构 - CSDN 博客

逆向开发学习笔记_lygl35 的博客 - CSDN 博客

PE 文件结构

任何一个在 Windows 上运行的可执行文件都要遵守一定的格式,这个格式就是 PE 文件结构,比如:exe, dIl, 部分 sys

img

此处可使用 PEID 或 ExeinfoPE 进行代替

image-20221210120028888

image-20221210132659775

32 位计算机最大内存 4G,寻址宽度 32 位

image-20221210141922512

image-20221210141955667

push esp-4 , 压栈,EIP +4

call EIP 跳转,esp-4 压栈

ret 将栈中的地址弹出到 EIP,esp-4

image-20221211125947066

image-20221211130508986

image-20221211131806741

image-20221211133036307

image-20221211133000935

arr [6] 是 ebp+4,放的是函数返回值的地址,修改了返回值在函数返回时会自动执行

image-20221211133428315

全局变量在 pe 头的数据中,局部变量在堆栈中

image-20221211135158527

image-20221211135229931

image-20221211134515487

image-20221211134557068

# 汇编

# 进制

进制的本质是查数

image-20221212151456236

image-20221212151324431

image-20221212151333052

# 数据宽度

4 位宽度表示

image-20221213152958630

image-20221213153742328

8 位宽度表示

image-20221213153933311

image-20221213154014302

16 位宽度表示

32 位宽度表示

image-20221213154324319

6、几个重要的计量单位:

BYTE 字节 8BIT

WORD 字 16BIT 2 字节

DWORD 双字 32BIT 4 字节

or

image-20221213154728965

and

image-20221213155024628

xor

image-20221213155128086

not

2+3

image-20221210132659775

image-20221213160855262

image-20221213160948087

# 寄存器

mov ebx,0x123456789 //ebx=23456789 忽略高位,1 是高位

image-20221213171205765

image-20221213171554801

image-20221213174336225

image-20221213175435630

image-20221213182649856

image-20221213183047619

我们可以打补丁或者其他方式拓展内存

# 内存

image-20221213183647234

栈中每四个内存单元一组

image-20221213184806456

数据窗口中以小端存储以 db 查看时

image-20221214203828764

image-20221214203909142

image-20221214204249791

0012FFDC = E4

0012FFDD = 7C

在数据窗口是小端在前

image-20221214205211991

image-20221214205617926

image-20221214210429166

image-20221214210441256

image-20221214210742628

# 堆栈

image-20221214213244308

image-20221214213607192

image-20221214214015040

image-20221214214825379

1
2
3
4
5
6
7
8
9
10
push imm    //esp-4
push al //不能push8位
push ax //esp-2
push eax //esp-4
push dword ptr ds:[12FFDC] //esp-4
push word ptr ds:[12FFDC] //esp-2
push byte ptr ds:[12FFDC] //不能push 8位内存

pushad //将所有通用寄存器入栈
popad

# JCC

image-20221219205236604

image-20221219205328411

image-20221219210046713

只计算最低有效字节中的 1 的个数

image-20221219210317802

1
2
3
看当前数据宽度/2 中间的位是否进位
12345678 看5是否产生进位
16位,8位一样

image-20221219210949437

test 和 xor 的区别

image-20221219211306372


image-20221219212150517

image-20221219213621597

image-20221219213712037

image-20221219213903993

1
2
adc al,cl
al = al + cl + 进位标志carry(1)

image-20221219214113901

Borrow

image-20221219214341294

image-20221219214413526

image-20221219215219369

1
2
3
movs stos  移动方向由D标志位决定 
D = 0 +
D = 1 -

image-20221219215520140

# JCC

image-20221228171843970

image-20221228184206472

image-20221228184559423

image-20221228184754864

test 可以判断寄存器中值是否为 0

call 执行时首先将 call 的下一条命令入栈,通过当前地址 + 当前指令长度,指令长度通过硬编码确定,此时 esp-4,作为 call ret 之后的下一条执行,EIP 变为 call 的地址

ret 时将栈顶的返回地址给 EIP,esp+4

因此一个函数在被调用时,堆栈栈顶就是他返回后执行的下一条指令

# 堆栈图

定位到 winMain

image-20221230210156563

有如下函数

1
2
3
4
00401085  |.  6A 04         push    0x4
00401087 |. 6A 03 push 0x3
00401089 |. E8 77FFFFFF call 00401005
0040108E |. 83C4 08 add esp,0x8
1
2
00401005   $ /E9 16000000   jmp     add
0040100A $ |E9 51000000 jmp main
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
00401020 >/> \55            push    ebp
00401021 |. 8BEC mov ebp,esp
00401023 |. 83EC 48 sub esp,0x48
00401026 |. 53 push ebx
00401027 |. 56 push esi
00401028 |. 57 push edi
00401029 |. 8D7D B8 lea edi,[local.18]
0040102C |. B9 12000000 mov ecx,0x12
00401031 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401036 |. F3:AB rep stos dword ptr es:[edi]
00401038 |. 8B45 08 mov eax,[arg.1]
0040103B |. 8945 FC mov [local.1],eax
0040103E |. 8B4D 0C mov ecx,[arg.2]
00401041 |. 894D F8 mov [local.2],ecx
00401044 |. 8B45 FC mov eax,[local.1]
00401047 |. 0345 F8 add eax,[local.2]
0040104A |. 5F pop edi
0040104B |. 5E pop esi
0040104C |. 5B pop ebx
0040104D |. 8BE5 mov esp,ebp
0040104F |. 5D pop ebp
00401050 \. C3 retn

image-20221230235920026

image-20221230235929733

image-20221230235938501

注意 1:

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
0018FE94     00000001
0018FE98 0018FF48
0018FE9C 00000000
0018FEA0 7EFDE000
0018FEA4 CCCCCCCC
0018FEA8 CCCCCCCC
0018FEAC CCCCCCCC
0018FEB0 CCCCCCCC
0018FEB4 CCCCCCCC
0018FEB8 CCCCCCCC
0018FEBC CCCCCCCC
0018FEC0 CCCCCCCC
0018FEC4 CCCCCCCC
0018FEC8 CCCCCCCC
0018FECC CCCCCCCC
0018FED0 CCCCCCCC
0018FED4 CCCCCCCC
0018FED8 CCCCCCCC
0018FEDC CCCCCCCC
0018FEE0 CCCCCCCC
0018FEE4 00000004
0018FEE8 00000003
0018FEEC 0018FF48
0018FEF0 0040108E add.0040108E
0018FEF4 00000003
0018FEF8 00000004
0018FEFC 00000000

这是 add esp, 0x8 之后的堆栈,并不是将他清空了,只是改变了 esp,垃圾数据还在那

注意 2:

CCCCCCCC 是 int3 的硬编码

注意 3:

esp+8 是外部传进来的参数

esp-4 是函数内部的局部变量

esp+4 是函数返回的地址

注意 4:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int add(int a, int b)
{
int c = a;
int d = b;
return c + d;
}

int main(int argc, char* argv[])
{
printf("Hello World!\n");
add(3,4);
return 0;
}
//这是函数代码

注意 5:

add esp,0x8 是 cdecl 的外平栈

ret 8 是 stdcall 的内平栈

注意 6

1
2
3
4
lea     edi,dword ptr ss:[ebp-0x48]
mov ecx,0x12
mov eax,0xCCCCCCCC
rep stos dword ptr es:[edi]

的作用是重复 12 次,将 CCCCCCCC 的值不停填充到 ebp-48 的位置,每填充一次 edi+4,edi+4 或 - 4 由 df 标志位决定,df=0 + df=1 -

# 函数

计算机的函数,是一个固定的一个程序段,或称其为一个子程序,它东可以实现固定运算功能的同时还带有一入口和一个出口,所谓的入口,就是函数所带的各个参数,我们可以通过这个入口,把函数的参数值代入子程序,供计算机处理,所谓出口,就是指函数的计算结果,也称为返回值,在计算机求得之后,由此口带回给调用它的程序。

image-20230101174907969

函数传参包括寄存器传参 (比如 this 指针) 和内存传参

函数返回结果可以通过寄存器返回 (一般为 EAX) 或者内存

Windows 堆栈特点

1. 现进后出

2. 向低地址扩展

windows 中的堆栈,是一块普通的内存,主要用来存储一些临时的数据和参数等可以把 Windows 中的堆栈想象成是一个公用的书箱,函数就像是使用箱子的人函数在执行的时候,会用到这个书箱,把一些数据存到里面,但用完的时候一定要记得把书拿走,否则会乱的,也就是说,你放进去几本书,走的时候也要拿走几本书,这个就是堆栈平衡.

# C 语言

# VC6.0

快捷键

F7 编译

F5 执行

shift + F5 结束

image-20230103212249828

函数名、参数名、变量名的命名规则:只能包含字母、数字和_且不能以数字开头.

函数返回值

1. 无参数,无返回值的格式形式

1
2
3
4
void fun()
{

}

当函数没有参数时不需要提升和降低堆栈

2. 有参数,无返回值函数格式

1
2
3
4
void fn(int a,double b)
{

}

裸函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void __declspec(naked) func1()
{
__asm
{
ret
}
}
void func()
{

}
int main(int argc, char* argv[])
{

}
int __declspec(naked) func1(int x, int y)
{

}


调用裸函数中没有代码,没有ret,不能正常返回,会报错
需要手动加内联汇编中ret返回

调用约定

1
2
3
__cdecl			从右至左入栈							调用者清理栈					默认调用约定
__stdcall 从右至左入栈 自身清理堆栈 winapi调用方式
__fastcall ecx/edx传送前两个,从右至左入栈 自身清理堆栈,如果参数大于两个,小于两个不用清理

函数入口

1
2
3
4
5
main(int 1, char * * 0x005210d8) line 23
mainCRTStartup() line 206 + 25 bytes
KERNEL32! 74d433ca()
NTDLL! 76f59ed2()
NTDLL! 76f59ea5()

更改入口点

image-20230104194840789

1
2
3
4
5
6
7
8
9
10
11
12
main 函数被调用前要先调用的函数如下:
GetVersion()
_heap_init()
GetCommandLineA()
_crtGetEnvironmentStringsA()
_setargv(
_setenvp()
_cinit()

以上函数都会在mainCRTStartup中初始化

mainret = main(__argc,__argv,__environ);
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
00401180 >/$  55            push    ebp
00401181 |. 8BEC mov ebp,esp
00401183 |. 6A FF push -0x1
00401185 |. 68 38214200 push 00422138
0040118A |. 68 30424000 push _except_handler3 ; SE 处理程序安装
0040118F |. 64:A1 0000000>mov eax,dword ptr fs:[0]
00401195 |. 50 push eax
00401196 |. 64:8925 00000>mov dword ptr fs:[0],esp
0040119D |. 83C4 F0 add esp,-0x10
004011A0 |. 53 push ebx
004011A1 |. 56 push esi
004011A2 |. 57 push edi
004011A3 |. 8965 E8 mov [local.6],esp
004011A6 |. FF15 4CA14200 call dword ptr ds:[<&KERNEL32.GetVers>; kernel32.GetVersion
004011AC |. A3 707C4200 mov dword ptr ds:[_osver],eax
004011B1 |. A1 707C4200 mov eax,dword ptr ds:[_osver]
004011B6 |. C1E8 08 shr eax,0x8
004011B9 |. 25 FF000000 and eax,0xFF
004011BE |. A3 7C7C4200 mov dword ptr ds:[_winminor],eax
004011C3 |. 8B0D 707C4200 mov ecx,dword ptr ds:[_osver]
004011C9 |. 81E1 FF000000 and ecx,0xFF
004011CF |. 890D 787C4200 mov dword ptr ds:[_winmajor],ecx
004011D5 |. 8B15 787C4200 mov edx,dword ptr ds:[_winmajor]
004011DB |. C1E2 08 shl edx,0x8
004011DE |. 0315 7C7C4200 add edx,dword ptr ds:[_winminor]
004011E4 |. 8915 747C4200 mov dword ptr ds:[_winver],edx
004011EA |. A1 707C4200 mov eax,dword ptr ds:[_osver]
004011EF |. C1E8 10 shr eax,0x10
004011F2 |. 25 FFFF0000 and eax,0xFFFF
004011F7 |. A3 707C4200 mov dword ptr ds:[_osver],eax
004011FC |. 6A 00 push 0x0
004011FE |. E8 BD2D0000 call _heap_init
00401203 |. 83C4 04 add esp,0x4
00401206 |. 85C0 test eax,eax
00401208 |. 75 0A jnz short 00401214
0040120A |. 6A 1C push 0x1C
0040120C |. E8 CF000000 call fast_error_exit
00401211 |. 83C4 04 add esp,0x4
00401214 |> C745 FC 00000>mov [local.1],0x0
0040121B |. E8 A0270000 call _ioinit
00401220 |. FF15 48A14200 call dword ptr ds:[<&KERNEL32.GetComm>; [GetCommandLineA
00401226 |. A3 04964200 mov dword ptr ds:[_acmdln],eax
0040122B |. E8 70250000 call __crtGetEnvironmentStringsA
00401230 |. A3 487C4200 mov dword ptr ds:[_aenvptr],eax
00401235 |. E8 56200000 call _setargv
0040123A |. E8 011F0000 call _setenvp
0040123F |. E8 1C1B0000 call _cinit
00401244 |. 8B0D 8C7C4200 mov ecx,dword ptr ds:[_environ]
0040124A |. 890D 907C4200 mov dword ptr ds:[__initenv],ecx
00401250 |. 8B15 8C7C4200 mov edx,dword ptr ds:[_environ]
00401256 |. 52 push edx
00401257 |. A1 847C4200 mov eax,dword ptr ds:[__argv]
0040125C |. 50 push eax
0040125D |. 8B0D 807C4200 mov ecx,dword ptr ds:[__argc]
00401263 |. 51 push ecx
00401264 |. E8 A1FDFFFF call 0040100A
00401269 |. 83C4 0C add esp,0xC
0040126C |. 8945 E4 mov [local.7],eax
0040126F |. 8B55 E4 mov edx,[local.7]
00401272 |. 52 push edx ; /status
00401273 |. E8 281B0000 call exit ; \exit
00401278 |. 8B45 EC mov eax,[local.5]
0040127B |. 8B08 mov ecx,dword ptr ds:[eax]
0040127D |. 8B11 mov edx,dword ptr ds:[ecx]
0040127F |. 8955 E0 mov [local.8],edx
00401282 |. 8B45 EC mov eax,[local.5]
00401285 |. 50 push eax
00401286 |. 8B4D E0 mov ecx,[local.8]
00401289 |. 51 push ecx
0040128A |. E8 A11C0000 call _XcptFilter
0040128F |. 83C4 08 add esp,0x8
00401292 \. C3 retn

# 数据类型

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
基本类型:   	  整数类型
浮点类型
整数类型: char short int(32) long(32)

char i = 0x12345678;
mov byte ptr [ebp-4],78h

short k = 0x12345678 k = 5678

整数类型 unsigned signed
unsigned char a = 0xFF; //不写char默认int
char b = 0xFF;
在内存中都是FF,但使用时有符号-1,无符号255
类型转化,比较大小

编译器对于有符号和无符号的jcc编译结果不一样
jz je

浮点类型 float double
float 存储方式
符号位 指数部分 尾数部分
31-30 30-22 22-0
double 存储方式
符号位 指数部分 尾数部分
63-62 62-51 51-0

将一个 float 型转化为内存存储格式的步骤为:

1、先将这个实数的绝对值化为二进制格式
2、将这个二进制格式实数的小数点左移或右移 n 位,直到小数点移动到第一个有效数字的右边。

3、从小数点右边第一位开始数出二十三位数字放入第 22 到第 0 位。

4、如果实数是正的,则在第 31 位放入 “0”,否则放入 “1”。

5、如果 n 是左移得到的,说明指数是正的,第 30 位放入 “1”。如果 n 是右移得到的或 n=0,则第 30 位放入 0

6、如果 n 是左移得到的,则将 n 减去 1 后化为二进制,并在左边加 “0” 补足七位,放入第 29 到第 23 位。

如果 n 是右移得到的或 n=0,则将 n 化为二进制后在左边加 “0” 补足七位,再各位求反,再放入第 29 到第 23 位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
8.25
1.转为二进制数,整数部分除二取余,倒序排列,整数部分乘二取整,正序排列
1000.01
2.科学计数法,左移+右移-
1.00001*(2^3)
3.存放尾数,加起来一共23位,不够用0补,补在最后
00001000000000000000000
4.符号位正0负1
0
5.指数部分,第一位为位移方向,左移1,右移0.剩下七位是指数二进制-1的值 (或者使用127+x转为二进制填充 )
10000010
6.拼接
01000001000001000000000000000000
41040000
1
2
3
4
5
6
7
8
9
10
11
12
13
0.25

0.01 -> 1.0
1 * (2^(-2)) 右移
FD = -2-1 = -3

0
0 1111101
00000000000000000000000

00111110100000000000000000000000

3E800000

尾数部分长度决定精确度,比如 8.4 的小数部分无限循环

比如 float 精确到 24 位 (尾数 + 科学计数法最前面的 0 或 1),可以精确到小数点后 6 位

# 编码

1
2
3
4
5
6
7
通过两个ASCII表示一个汉字,即为GB2312

一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用至0xF7,后面一个字(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。

一个char占一个字节,放不下gb2312
gb2312每个字节最高位都是1

if

1
2
if-else  /  if - if else else 
只会执行其中一种分支

# 程序内存

1
2
3
4
5
6
7
8
9
10
代码区  可读可执行

堆栈 参数,局部变量,临时数据

堆 动态申请,大小可变,可读可写

全局变量 可读可写,程序运行时分配

常量区 只读

全局变量的特点:
1、全局变量在程序编译完成后地址就已经确定下来了,只要程序启动,全局变量就已经存是否有值取决于声明时是否给定了初始值,如果没有,默认为 0

2、全局变量的值可以被所有函数所修改,里面存储的是最后一次修改的值.

3、全局变量所占内存会一直存在,知道整个进程结束.

4、全局变量的反汇编识别:

MOV 寄存器,byte/word/dword ptr ds: [0x12345678]

通过寄存器的宽度,或者 byte/word/dword 来判断全局变量的宽度

全局变量就是基址

局部变量的特点

1、局部变量在程序编译完成后并没有分配固定的地址.

2、在所属的方法没有被调用时,局部变量并不会分配内存地址,只有调用时才在堆栈中分配内存.

3、当局部变量所属的方法执行先毕后,局部变量所占用的内存将变成垃圾

4、局部变量只能在方法内部使用,函数 A 无法使用函数 B 的局部变量.

5、局部变量的反汇编识别:

判断参数个数

观察调用处的代码

1
2
push 3
call

找到堆栈平衡的代码

1
2
3
4
5
call
add esp,0C

或者函数内部
ret 0xC

1、不考虑 ebp、 esp

2、只找给别人赋值的寄存器 eax/ecx/edx/ebx/esi/edi

3、找到以后追查其来源,如果,该寄存器中的值不是在函数内存赋值的,那一定是传进来的参数.

公式一:寄存器 + ret4 = 参数个数

公式二:寄存器+[ebp+8] +[ebp+0x] = 参数个数

# if 语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int g_n = 10;
void func(int x, int y)
{
if (x>y)
{
g_n = x;
}
}

mov eax,dword ptr [ebp+8]
cmp eax,dword ptr [ebp+0x0C]
jle func+29h(401049)
mov ecx,dword ptr [ebp+8]
mov dword ptr [424a30],ecx
pop edi 401049

在反汇编中 jcc 的代码和 if 中的判断条件是相反的

还原反汇编

image-20230110133105437

函数内部功能分析:

1、分析参数:[ebp+8] : x [ebp+0Ch] :Y

2、分析局部变量

3、分析全局变量
mov dword ptr 004225c4,ecx

4、功能分析
eax, dword ptr [ebp+8]
cmp eax, dword ptr [ebp+0Ch]

将参数 x 存到到 EAX 中,然后比较 EAX。与参数 Y 的大小如果 x<=Y 那么跳转到 00401059 的位置

否则,将 X 的值存储到全局变量中

1
2
3
4
5
6
7
8
9
10
11
12
int a
int func(int x, int y)
{
int var1 = a;
if(x<=y)
{
y = var1 + y;
a = y;
}
ret a

}

if 会有影响寄存器的语句,然后 JCC

if-else 中会有向下跳的 jmp,通过 jmp 跳过 else 之后的代码

image-20230110141055410

1、当每个条件跳转指令要跳转的地址前面都有 jmp 指令

2、这些 jmp 指令跳转的地址都是一样的

3、如果某个分支没有条件判断,则为 else 部分中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "stdafx.h"

void HelloWorld()
{
printf("helloworld");
getchar();
}

int main(int argc, char* argv[])
{
int arr[5] = {1,2,3,4,5};
arr[6] = (int)HelloWorld;
return 0;
}

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
004010C0 >/> \55            push    ebp
004010C1 |. 8BEC mov ebp,esp
004010C3 |. 83EC 54 sub esp,0x54
004010C6 |. 53 push ebx
004010C7 |. 56 push esi
004010C8 |. 57 push edi
004010C9 |. 8D7D AC lea edi,[local.21]
004010CC |. B9 15000000 mov ecx,0x15
004010D1 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
004010D6 |. F3:AB rep stos dword ptr es:[edi]
004010D8 |. C745 EC 01000>mov dword ptr ss:[ebp-0x14],0x1
004010DF |. C745 F0 02000>mov [local.4],0x2
004010E6 |. C745 F4 03000>mov [local.3],0x3
004010ED |. C745 F8 04000>mov [local.2],0x4
004010F4 |. C745 FC 05000>mov dword ptr ss:[ebp-0x4],0x5
004010FB |. C745 04 05104>mov dword ptr ss:[ebp+0x4],00401005
00401102 |. 33C0 xor eax,eax
00401104 |. 5F pop edi
00401105 |. 5E pop esi
00401106 |. 5B pop ebx
00401107 |. 8BE5 mov esp,ebp
00401109 |. 5D pop ebp
0040110A \. C3 retn


0018FF28 CCCCCCCC
0018FF2C CCCCCCCC
0018FF30 CCCCCCCC
0018FF34 CCCCCCCC
0018FF38 CCCCCCCC
0018FF3C CCCCCCCC
0018FF40 CCCCCCCC
0018FF44 CCCCCCCC
0018FF48 /0018FF88 ebp
0018FF4C |004015F9 返回到 88888.<ModuleEntryPoint>+0E9 来自 88888.0040100A

0018FF28 CCCCCCCC
0018FF2C CCCCCCCC
0018FF30 CCCCCCCC
0018FF34 00000001
0018FF38 00000002
0018FF3C 00000003
0018FF40 00000004
0018FF44 00000005
0018FF48 0018FF88 ebp
0018FF4C 00401005 88888.00401005

00401005 . /E9 16000000 jmp HelloWorld
0040100A $ |E9 B1000000 jmp main


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include "stdafx.h"

void Func()
{
int i;
int arr[5] = {0};

for(i=0;i<=5;i++)
{
arr[i]=0;
printf("hello world");
}
}

int main(int argc, char* argv[])
{
Func();
return 0;
}

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
55
56
57
58
59
60
61
62
63
64
65
66
004010C0 >/> \55            push    ebp
004010C1 |. 8BEC mov ebp,esp
004010C3 |. 83EC 40 sub esp,0x40
004010C6 |. 53 push ebx
004010C7 |. 56 push esi
004010C8 |. 57 push edi
004010C9 |. 8D7D C0 lea edi,[local.16]
004010CC |. B9 10000000 mov ecx,0x10
004010D1 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
004010D6 |. F3:AB rep stos dword ptr es:[edi]
004010D8 |. E8 32FFFFFF call 0040100F
004010DD |. 33C0 xor eax,eax
004010DF |. 5F pop edi
004010E0 |. 5E pop esi
004010E1 |. 5B pop ebx
004010E2 |. 83C4 40 add esp,0x40
004010E5 |. 3BEC cmp ebp,esp
004010E7 |. E8 E4030000 call _chkesp
004010EC |. 8BE5 mov esp,ebp
004010EE |. 5D pop ebp
004010EF \. C3 retn

00401005 . /E9 16000000 jmp Func
0040100A $ |E9 B1000000 jmp main
0040100F /$ |E9 0C000000 jmp Func

00401020 >|> \55 push ebp
00401021 |. 8BEC mov ebp,esp
00401023 |. 83EC 58 sub esp,0x58
00401026 |. 53 push ebx
00401027 |. 56 push esi
00401028 |. 57 push edi
00401029 |. 8D7D A8 lea edi,[local.22]
0040102C |. B9 16000000 mov ecx,0x16
00401031 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401036 |. F3:AB rep stos dword ptr es:[edi]
00401038 |. C745 E8 00000>mov [local.6],0x0
0040103F |. 33C0 xor eax,eax
00401041 |. 8945 EC mov [local.5],eax
00401044 |. 8945 F0 mov [local.4],eax
00401047 |. 8945 F4 mov [local.3],eax
0040104A |. 8945 F8 mov [local.2],eax
0040104D |. C745 FC 00000>mov [local.1],0x0
00401054 |. EB 09 jmp short 0040105F
00401056 |> 8B4D FC /mov ecx,[local.1]
00401059 |. 83C1 01 |add ecx,0x1
0040105C |. 894D FC |mov [local.1],ecx
0040105F |> 837D FC 05 cmp [local.1],0x5
00401063 |. 7F 1A |jg short 0040107F
00401065 |. 8B55 FC |mov edx,[local.1]
00401068 |. C74495 E8 000>|mov dword ptr ss:[ebp+edx*4-0x18],0>
00401070 |. 68 1C304200 |push 0042301C ; /format = "hello world"
00401075 |. E8 D6030000 |call printf ; \printf
0040107A |. 83C4 04 |add esp,0x4
0040107D |.^ EB D7 \jmp short 00401056
0040107F |> 5F pop edi
00401080 |. 5E pop esi
00401081 |. 5B pop ebx
00401082 |. 83C4 58 add esp,0x58
00401085 |. 3BEC cmp ebp,esp
00401087 |. E8 44040000 call _chkesp
0040108C |. 8BE5 mov esp,ebp
0040108E |. 5D pop ebp
0040108F \. C3 retn

mov dword ptr ss:[ebp+edx*4-0x18],0 在最后一次循环将ebp-4 = i变为了0,循环条件永远满足

总结:

声明变量就是告诉计算机,我要用一块内存,你给我留着,宽度和存储格式有数据类型决定.

计算机什么时候把这块内存给你,取决于变量的作用范围,如果是全局变量,在程序编译完就已经分配了空间,如果是局所在的程序被调用的时候,才会分配空间.

全局变量如果不赋初始值,默认是 0,但局部变量在使用前一定要赋初值.

类型转换

1
2
3
4
5
6
7
8
9
movsx   ;先符号扩展再传送
mov al,0x0FF
movsx cx,al 11111111 11111111
movsx al,80
movsx cx,al

movzx
mov al,0x0ff
movzx cx,al
1
2
3
int -> short 转换时高位丢失,即从低位开始截取
int a = 0x12345678;
short b = a; b = 5678

表达式

特点一:

表达式无论多么复杂,都只有一个结果

特点二:

只有表达式,可以编译通过,但并不生成代码,需要与赋值或者其他流程控制语句一起组合的时候才有意义

特点三:

当表达式中存在不同宽度的变量时,结果将转换为宽度最大的那个

1
2
3
char a = 10;
int b = 20;
printf("%d",a+b); ;a+b为int类型

特点四:

当表达式中同时存在有符号和无符号数的时候,表达式的结构将转换为无符号数.

输出无符号或有符号根据输出的格式 % d 或 % u 决定

int x = a == b; ;sete setne

sete cmp 相等时 set 寄存器为 1

if(a) == if(a != 0)

if(!a) == if(a == 0)

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
if (x>1 && x>2 && x>3)
{
printf("sb")
}


if (x>1 || x>2 || x>3)
{
printf("sb")
}


00401020 >/> \55 push ebp
00401021 |. 8BEC mov ebp,esp
00401023 |. 83EC 44 sub esp,0x44
00401026 |. 53 push ebx
00401027 |. 56 push esi
00401028 |. 57 push edi
00401029 |. 8D7D BC lea edi,[local.17]
0040102C |. B9 11000000 mov ecx,0x11
00401031 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401036 |. F3:AB rep stos dword ptr es:[edi]
00401038 |. C745 FC 05000>mov [local.1],0x5
0040103F |. 837D FC 01 cmp [local.1],0x1
00401043 |. 7E 19 jle short 0040105E
00401045 |. 837D FC 02 cmp [local.1],0x2
00401049 |. 7E 13 jle short 0040105E
0040104B |. 837D FC 03 cmp [local.1],0x3
0040104F |. 7E 0D jle short 0040105E
00401051 |. 68 1C304200 push 0042301C ; /format = "sb"
00401056 |. E8 F5030000 call printf ; \printf
0040105B |. 83C4 04 add esp,0x4
0040105E |> 837D FC 01 cmp [local.1],0x1
00401062 |. 7F 0C jg short 00401070
00401064 |. 837D FC 02 cmp [local.1],0x2
00401068 |. 7F 06 jg short 00401070
0040106A |. 837D FC 03 cmp [local.1],0x3
0040106E |. 7E 0D jle short 0040107D
00401070 |> 68 1C304200 push 0042301C ; /format = "sb"
00401075 |. E8 D6030000 call printf ; \printf
0040107A |. 83C4 04 add esp,0x4
0040107D |> 33C0 xor eax,eax
0040107F |. 5F pop edi
00401080 |. 5E pop esi
00401081 |. 5B pop ebx
00401082 |. 83C4 44 add esp,0x44
00401085 |. 3BEC cmp ebp,esp
00401087 |. E8 44040000 call _chkesp
0040108C |. 8BE5 mov esp,ebp
0040108E |. 5D pop ebp
0040108F \. C3 retn


C 语言没有 bool,C++ 才有

1
2
3
4
5
if (x = 0)   //不执行
if (x = 2) //执行
int a = 3;
if (a == 3) //执行

1
2
3
4
5
6
7
8
9
1、将两个变量的值交换.
2、将一个数组中的数倒序输出.
3、找出数组里面最大的值,并返回
4、将数组所有的元素相加,将结果返回
5、将两个等长数组相同位置的值相加,存储到另外一个等长的数组中
6、写一个函数int prime(int x),如果x是素数返回值为1,否则返回0。
7、俩俩比较数组的值,将最大的一个存储到数组的最后一个位置
8、编写程序实现一个冒泡排序的算法.

# 循环语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
do...while   性能最高的循环
while
for

int length = 10
int m = 0
int arr[10] = { }
while (m < length - 1)
{
for (int i = 1; i < length-1-m;i++)
{
swap(arr[i],arr[i+1]);
}
m++;
}

返回值

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
如果返回值只有8位,使用al返回
ax
eax

long long 在vc6中对应的是__int64

__int64 Function()
{
__int64 x = 0x1234567890;
return x;

}

00401020 >/> \55 push ebp
00401021 |. 8BEC mov ebp,esp
00401023 |. 83EC 48 sub esp,0x48
00401026 |. 53 push ebx
00401027 |. 56 push esi
00401028 |. 57 push edi
00401029 |. 8D7D B8 lea edi,[local.18]
0040102C |. B9 12000000 mov ecx,0x12
00401031 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401036 |. F3:AB rep stos dword ptr es:[edi]
00401038 |. C745 F8 90785634 mov [local.2],0x34567890
0040103F |. C745 FC 12000000 mov [local.1],0x12
00401046 |. 8B45 F8 mov eax,[local.2]
00401049 |. 8B55 FC mov edx,[local.1]
0040104C |. 5F pop edi
0040104D |. 5E pop esi
0040104E |. 5B pop ebx
0040104F |. 8BE5 mov esp,ebp
00401051 |. 5D pop ebp
00401052 \. C3 retn

参数传递

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
void func(char a,char b,char c)
{

}

int main(int argc, char* argv[])
{
char a = 1,b=2,c=3;
func(a,b,c);
return 0;
}

0040D758 |. C645 FC 01 mov byte ptr ss:[ebp-0x4],0x1
0040D75C |. C645 F8 02 mov byte ptr ss:[ebp-0x8],0x2
0040D760 |. C645 F4 03 mov byte ptr ss:[ebp-0xC],0x3
0040D764 |. 8A45 F4 mov al,byte ptr ss:[ebp-0xC]
0040D767 |. 50 push eax
0040D768 |. 8A4D F8 mov cl,byte ptr ss:[ebp-0x8]
0040D76B |. 51 push ecx
0040D76C |. 8A55 FC mov dl,byte ptr ss:[ebp-0x4]
0040D76F |. 52 push edx
0040D770 |. E8 9A38FFFF call 0040100F
0040D775 |. 83C4 0C add esp,0xC

虽然定义的是char,但在压栈的时候还是会按照四字节的压,所以没法节约空间

本机尺寸:本机32位,那么对32位的数据支持最好

编译器遵守这个规则

传输数据按照本机尺寸传,用还是按照原来大小用

传参的时候如果是<int的整数,一律使用int类型

参数传递的本质:将上层函数的变量,或者表达式的值“复制一份”,传递给下层函数. 可以理解为[ebp-4] 和 [ebp+8]的区别

局部变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
空函数会分配40h的缓冲区,如果使用局部变量会在40h的基础上增加,  只适用于vc6-debug

void func()
{
char a = 1;
char b = 2;
}
此时缓冲区分配了48,因为本机尺寸

1、小于32位的局部变量,空间在分配时,按32位分配.
2、使用时按实际的宽度使用.
3、不要定义char/short类型的局部变量.
4、参数与局部变量没有本质区别,都是局部变量,都在栈中
5、完全可以把参数当初局部变量使用

参数在执行前分配,局部变量在执行时分配,本质一样
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
char a[3] = {1,2,3};
0040D740 >/> \55 push ebp
0040D741 |. 8BEC mov ebp,esp
0040D743 |. 83EC 44 sub esp,0x44
0040D746 |. 53 push ebx
0040D747 |. 56 push esi
0040D748 |. 57 push edi
0040D749 |. 8D7D BC lea edi,[local.17]
0040D74C |. B9 11000000 mov ecx,0x11
0040D751 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
0040D756 |. F3:AB rep stos dword ptr es:[edi]
0040D758 |. C645 FC 01 mov byte ptr ss:[ebp-0x4],0x1
0040D75C |. C645 FD 02 mov byte ptr ss:[ebp-0x3],0x2
0040D760 |. C645 FE 03 mov byte ptr ss:[ebp-0x2],0x3
0040D764 |. 5F pop edi
0040D765 |. 5E pop esi
0040D766 |. 5B pop ebx
0040D767 |. 8BE5 mov esp,ebp
0040D769 |. 5D pop ebp
0040D76A \. C3 retn

char a[4] = {1,2,3,4};
0040D740 >/> \55 push ebp
0040D741 |. 8BEC mov ebp,esp
0040D743 |. 83EC 44 sub esp,0x44
0040D746 |. 53 push ebx
0040D747 |. 56 push esi
0040D748 |. 57 push edi
0040D749 |. 8D7D BC lea edi,[local.17]
0040D74C |. B9 11000000 mov ecx,0x11
0040D751 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
0040D756 |. F3:AB rep stos dword ptr es:[edi]
0040D758 |. C645 FC 01 mov byte ptr ss:[ebp-0x4],0x1
0040D75C |. C645 FD 02 mov byte ptr ss:[ebp-0x3],0x2
0040D760 |. C645 FE 03 mov byte ptr ss:[ebp-0x2],0x3
0040D764 |. C645 FF 04 mov byte ptr ss:[ebp-0x1],0x4
0040D768 |. 5F pop edi
0040D769 |. 5E pop esi
0040D76A |. 5B pop ebx
0040D76B |. 8BE5 mov esp,ebp
0040D76D |. 5D pop ebp
0040D76E \. C3 retn


数组声明是需要常量来提升堆栈,使用时[]内可以是变量

char i[3] //缓冲区分配40+4
char i[4] //缓冲区分配40+4
short i[4] //缓冲区分配40+4*2

//缓冲区分配是按照本机宽度位数的倍数分配的,参考char[3] 和 char[4]


void func()
{
int x = 1;
int y = 2;
int r;
int arr[10] = {1,2,3,4,5,6,7,8,9,0};

r = arr[1];
r = arr[x];
r = arr[x+y];
r = arr[x*2+y];
}


0040D770 >/> \55 push ebp
0040D771 |. 8BEC mov ebp,esp
0040D773 |. 83EC 74 sub esp,0x74 ;0x74-0x40=0x34=52/4=13 = 10 + 1 + 1 + 1
0040D776 |. 53 push ebx
0040D777 |. 56 push esi
0040D778 |. 57 push edi
0040D779 |. 8D7D 8C lea edi,[local.29]
0040D77C |. B9 1D000000 mov ecx,0x1D
0040D781 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
0040D786 |. F3:AB rep stos dword ptr es:[edi]

0040D788 |. C745 FC 01000000 mov [local.1],0x1
0040D78F |. C745 F8 02000000 mov [local.2],0x2

0040D796 |. C745 CC 01000000 mov [local.13],0x1 ;低地址在上
0040D79D |. C745 D0 02000000 mov [local.12],0x2
0040D7A4 |. C745 D4 03000000 mov [local.11],0x3
0040D7AB |. C745 D8 04000000 mov [local.10],0x4
0040D7B2 |. C745 DC 05000000 mov [local.9],0x5
0040D7B9 |. C745 E0 06000000 mov [local.8],0x6
0040D7C0 |. C745 E4 07000000 mov [local.7],0x7
0040D7C7 |. C745 E8 08000000 mov [local.6],0x8
0040D7CE |. C745 EC 09000000 mov [local.5],0x9
0040D7D5 |. C745 F0 00000000 mov [local.4],0x0

; r = arr[1];
0040D7DC |. 8B45 D0 mov eax,[local.12] ; 反汇编中写的是定值,直接取值
0040D7DF |. 8945 F4 mov [local.3],eax ;r = arr[1] = 2;

; r = arr[x];
0040D7E2 |. 8B4D FC mov ecx,[local.1] ; ecx = 1 0x34 = 52
0040D7E5 |. 8B548D CC mov edx,dword ptr ss:[ebp+ecx*4-0x34] ; edx = 4-34=ebp-0x30=[local.12]=2
0040D7E9 |. 8955 F4 mov [local.3],edx ; r = 2

0040D7EC |. 8B45 FC mov eax,[local.1] ; 1
0040D7EF |. 0345 F8 add eax,[local.2] ; eax = 1+2 = 3
0040D7F2 |. 8B4C85 CC mov ecx,dword ptr ss:[ebp+eax*4-0x34] ; ecx = [ebp-0x28] = [local.10] = 4
0040D7F6 |. 894D F4 mov [local.3],ecx ; r = 4

0040D7F9 |. 8B55 FC mov edx,[local.1] ; edx = 1
0040D7FC |. 8B45 F8 mov eax,[local.2] ; eax = 2
0040D7FF |. 8D0C50 lea ecx,dword ptr ds:[eax+edx*2] ; ecx = 4
0040D802 |. 8B548D CC mov edx,dword ptr ss:[ebp+ecx*4-0x34] ; edx = [local.9] = 5
0040D806 |. 8955 F4 mov [local.3],edx ; r= 5
0040D809 |. 5F pop edi
0040D80A |. 5E pop esi
0040D80B |. 5B pop ebx
0040D80C |. 8BE5 mov esp,ebp
0040D80E |. 5D pop ebp
0040D80F \. C3 retn

[ebp+ecx*4-0x34] 每次减去0x34的原因,0x34 = 52,是该函数内所有局部变量一共分配的空间
ecx的值即为数组下标,去对应数组的下标即可取到对应的值,注意下标从0开始
short ecx * 2


int a[10] = {1,2,3,4,5} //后面会用0补

二维数组在汇编中和一维数组存储方式一样

int a[3][4] = {[1,2,3,4,5],[],[]} //编译报错
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; //正常执行
多维数组只是用户使用方便,编译后的结果都一样
int a[][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; //编译通过,编译器四个一组分配
int a[3][] = {1,2,3,4,5,6,7,8,9,10,11,12}; //编译错误

int arr[5][12] = {}
arr[1][7] = arr[1*12+7];
int arr[5][4][3] = arr[1*4*3 + 2*3 + 1]
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
桶排序
1.找数组中最大的,创建max+1个数的数组
2.下标对应改为出现的次数

void dui()
{
int a[10] = {9,8,7,7,6,6,3,3,1,1};

int max = a[0];
for (int p = 1; p < 10; p++)
{
if (a[p]>max)
{
max = a[p];
}
}
printf("%d\n",max);

int b[max] = {0};
for (int i = 0; i <= max-1; i++)
{
b[a[i]]++;
}
for (i = 0; i <= max-1; i++)
{
int k = b[i];
if (k > 0)
{
for (int m = k; m>0; m--)
{
printf("%d ",i);
}

}
}
printf("\n");

}

int main(int argc, char* argv[])
{
dui();
return 0;
}

image-20230113171701402

image-20230114173623044

# 结构体

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
struct B
{
float x;
float y;
float z;
}

struct A
{
int m_up;
char m_Name[32];
int m_Num;
...
B zuobiao;
};

A aa;
// aa + 4 //m_up


A 通过一级偏移找到B zuobiao
zuobiao通过二级偏移找到xyz

结构体在创建时不分配空间
A aa; 此时分配空间
aa.fXpos = 2.1;

结构体在定义的时候,除了自身以外,可以存储任何类型
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
struct AA
{
int m;
int n;
}

struct st
{
int a;
short b;
char c;
int arr[10];
AA aa;
};

st x;

void func()
{
x.a = 10;
x.b = 20;
x.c = 30;
x.arr[0] = 100;
x.aa.m = 22;
}
void func2()
{
int a1 = x.a;
int y1 = x.arr[0];
int y2 = x.aa.m;
}
st func()
{
st s;
s.a = 1;
s.b = 2;
s.c = 3;
return s;
}

数组和结构体判断

等宽连续 - 数组

连续不等宽 - 大概率结构体

但结构体中都是相同类型的变量也可以逆成数组

结构体尽量定义为全局变量,减少复制次数,尽量不要当参数和返回值

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// 结构体作为全局变量
struct st
{
int a;
short b;
char c;
int arr[10];
};

st x;
void func()
{
x.a = 10;
x.b = 20;
x.c = 30;
x.arr[0] = 1;
x.arr[1] = 2;
}

void func2()
{
int a1 = x.a;
int b1 = x.b;
int c1 = x.c;
}

int main(int argc, char* argv[])
{
func();
func2();
return 0;
}

00401030 >/> \55 push ebp
00401031 |. 8BEC mov ebp,esp
00401033 |. 83EC 40 sub esp,0x40
00401036 |. 53 push ebx
00401037 |. 56 push esi
00401038 |. 57 push edi
00401039 |. 8D7D C0 lea edi,[local.16]
0040103C |. B9 10000000 mov ecx,0x10
00401041 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401046 |. F3:AB rep stos dword ptr es:[edi]
00401048 |. C705 507C4200>mov dword ptr ds:[0x427C50],0xA
00401052 |. 66:C705 547C4>mov word ptr ds:[0x427C54],0x14
0040105B |. C605 567C4200>mov byte ptr ds:[0x427C56],0x1E
00401062 |. C705 587C4200>mov dword ptr ds:[0x427C58],0x1
0040106C |. C705 5C7C4200>mov dword ptr ds:[0x427C5C],0x2
00401076 |. 5F pop edi
00401077 |. 5E pop esi
00401078 |. 5B pop ebx
00401079 |. 8BE5 mov esp,ebp
0040107B |. 5D pop ebp
0040107C \. C3 retn

00401090 >/> \55 push ebp
00401091 |. 8BEC mov ebp,esp
00401093 |. 83EC 4C sub esp,0x4C
00401096 |. 53 push ebx
00401097 |. 56 push esi
00401098 |. 57 push edi
00401099 |. 8D7D B4 lea edi,[local.19]
0040109C |. B9 13000000 mov ecx,0x13
004010A1 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
004010A6 |. F3:AB rep stos dword ptr es:[edi]
004010A8 |. A1 507C4200 mov eax,dword ptr ds:[0x427C50]
004010AD |. 8945 FC mov [local.1],eax
004010B0 |. 0FBF0D 547C42>movsx ecx,word ptr ds:[0x427C54]
004010B7 |. 894D F8 mov [local.2],ecx
004010BA |. 0FBE15 567C42>movsx edx,byte ptr ds:[0x427C56]
004010C1 |. 8955 F4 mov [local.3],edx
004010C4 |. 5F pop edi
004010C5 |. 5E pop esi
004010C6 |. 5B pop ebx
004010C7 |. 8BE5 mov esp,ebp
004010C9 |. 5D pop ebp
004010CA \. C3 retn


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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
//结构体作为局部变量
struct Pointer
{
float x_pos;
float y_pos;
float z_pos;
};

struct st
{
int a;
short b;
char c;
int arr[10];
Pointer p1;
};


void func()
{
st x;
x.a = 10;
x.b = 20;
x.c = 30;
x.arr[0] = 1;
x.arr[1] = 2;

x.p1.x_pos = 2.1;

}


int main(int argc, char* argv[])
{
func();
return 0;
}

00401020 >/> \55 push ebp
00401021 |. 8BEC mov ebp,esp
00401023 |. 83EC 7C sub esp,0x7C
00401026 |. 53 push ebx
00401027 |. 56 push esi
00401028 |. 57 push edi
00401029 |. 8D7D 84 lea edi,[local.31]
0040102C |. B9 1F000000 mov ecx,0x1F
00401031 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401036 |. F3:AB rep stos dword ptr es:[edi]
00401038 |. C745 C4 0A000000 mov dword ptr ss:[ebp-0x3C],0xA
0040103F |. 66:C745 C8 1400 mov word ptr ss:[ebp-0x38],0x14
00401045 |. C645 CA 1E mov byte ptr ss:[ebp-0x36],0x1E
00401049 |. C745 CC 01000000 mov dword ptr ss:[ebp-0x34],0x1
00401050 |. C745 D0 02000000 mov dword ptr ss:[ebp-0x30],0x2
00401057 |. C745 F4 66660640 mov dword ptr ss:[ebp-0xC],0x40066666
0040105E |. 5F pop edi
0040105F |. 5E pop esi
00401060 |. 5B pop ebx
00401061 |. 8BE5 mov esp,ebp
00401063 |. 5D pop ebp
00401064 \. C3 retn


//当结构体中变量不等宽时,分空间不再按照本机宽度分
//局部变量int+short+char 还是按照12字节分
//结构体中变量等宽时和局部变量一样

struct st
{
int a;
int b;
int c;
};


void func()
{
st x;
x.a = 10;
x.b = 20;
x.c = 30;

}

void func2()
{
int a = 10;
int b = 20;
int c = 30;
}

void func3()
{
int arr[3] = {10,20,30};
}


int main(int argc, char* argv[])
{
func();
func2();
func3();
return 0;
}

func
00401020 >/> \55 push ebp
00401021 |. 8BEC mov ebp,esp
00401023 |. 83EC 4C sub esp,0x4C
00401026 |. 53 push ebx
00401027 |. 56 push esi
00401028 |. 57 push edi
00401029 |. 8D7D B4 lea edi,[local.19]
0040102C |. B9 13000000 mov ecx,0x13
00401031 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401036 |. F3:AB rep stos dword ptr es:[edi]
00401038 |. C745 F4 0A000000 mov [local.3],0xA
0040103F |. C745 F8 14000000 mov [local.2],0x14
00401046 |. C745 FC 1E000000 mov [local.1],0x1E
0040104D |. 5F pop edi
0040104E |. 5E pop esi
0040104F |. 5B pop ebx
00401050 |. 8BE5 mov esp,ebp
00401052 |. 5D pop ebp
00401053 \. C3 retn

func2
00401080 >/> \55 push ebp
00401081 |. 8BEC mov ebp,esp
00401083 |. 83EC 4C sub esp,0x4C
00401086 |. 53 push ebx
00401087 |. 56 push esi
00401088 |. 57 push edi
00401089 |. 8D7D B4 lea edi,[local.19]
0040108C |. B9 13000000 mov ecx,0x13
00401091 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401096 |. F3:AB rep stos dword ptr es:[edi]
00401098 |. C745 FC 0A000000 mov [local.1],0xA
0040109F |. C745 F8 14000000 mov [local.2],0x14
004010A6 |. C745 F4 1E000000 mov [local.3],0x1E
004010AD |. 5F pop edi
004010AE |. 5E pop esi
004010AF |. 5B pop ebx
004010B0 |. 8BE5 mov esp,ebp
004010B2 |. 5D pop ebp
004010B3 \. C3 retn

func3
004106F0 >/> \55 push ebp
004106F1 |. 8BEC mov ebp,esp
004106F3 |. 83EC 4C sub esp,0x4C
004106F6 |. 53 push ebx
004106F7 |. 56 push esi
004106F8 |. 57 push edi
004106F9 |. 8D7D B4 lea edi,[local.19]
004106FC |. B9 13000000 mov ecx,0x13
00410701 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00410706 |. F3:AB rep stos dword ptr es:[edi]
00410708 |. C745 F4 0A000> mov [local.3],0xA
0041070F |. C745 F8 14000> mov [local.2],0x14
00410716 |. C745 FC 1E000> mov [local.1],0x1E
0041071D |. 5F pop edi
0041071E |. 5E pop esi
0041071F |. 5B pop ebx
00410720 |. 8BE5 mov esp,ebp
00410722 |. 5D pop ebp
00410723 \. C3 retn


结构体可以作为参数和返回值

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
struct st
{
int a;
short b;
char c;
};

void func(st x)
{

}

int main(int argc, char* argv[])
{
st x;
x.a = 1;
x.b = 2;
x.c = 3;
func(x);
return 0;
}


00401040 55 push ebp
00401041 8B EC mov ebp,esp
00401043 83 EC 48 sub esp,48h
00401046 53 push ebx
00401047 56 push esi
00401048 57 push edi
00401049 8D 7D B8 lea edi,[ebp-48h]
0040104C B9 12 00 00 00 mov ecx,12h
00401051 B8 CC CC CC CC mov eax,0CCCCCCCCh
00401056 F3 AB rep stos dword ptr [edi]
00401058 C7 45 F8 01 00 00 00 mov dword ptr [ebp-8],1
0040105F 66 C7 45 FC 02 00 mov word ptr [ebp-4],offset main+23h (00401063)
00401065 C6 45 FE 03 mov byte ptr [ebp-2],3
00401069 8B 45 FC mov eax,dword ptr [ebp-4]
0040106C 50 push eax
0040106D 8B 4D F8 mov ecx,dword ptr [ebp-8]
00401070 51 push ecx
00401071 E8 A3 FF FF FF call @ILT+20(func) (00401019)
00401076 83 C4 08 add esp,8
00401079 33 C0 xor eax,eax
0040107B 5F pop edi
0040107C 5E pop esi
0040107D 5B pop ebx
0040107E 83 C4 48 add esp,48h
00401081 3B EC cmp ebp,esp
00401083 E8 E8 00 00 00 call __chkesp (00401170)
00401088 8B E5 mov esp,ebp
0040108A 5D pop ebp
0040108B C3 ret

ebp-4
0018FF44 02 00 03 CC 88 FF 18
ebp-8
0018FF40 01 00 00 00 02 00 03
第二次push的时候把short和char一起push了


struct st
{
int a;
int b;
int c;
int d;
int e;
};

void func(st x)
{

}

int main(int argc, char* argv[])
{
st x;
x.a = 1;
x.b = 2;
x.c = 3;
x.d = 4;
x.e = 5;
func(x);
return 0;
}


00401040 55 push ebp
00401041 8B EC mov ebp,esp
00401043 83 EC 54 sub esp,54h
00401046 53 push ebx
00401047 56 push esi
00401048 57 push edi
00401049 8D 7D AC lea edi,[ebp-54h]
0040104C B9 15 00 00 00 mov ecx,15h
00401051 B8 CC CC CC CC mov eax,0CCCCCCCCh
00401056 F3 AB rep stos dword ptr [edi]
00401058 C7 45 EC 01 00 00 00 mov dword ptr [ebp-14h],1
0040105F C7 45 F0 02 00 00 00 mov dword ptr [ebp-10h],2
00401066 C7 45 F4 03 00 00 00 mov dword ptr [ebp-0Ch],3
0040106D C7 45 F8 04 00 00 00 mov dword ptr [ebp-8],4
00401074 C7 45 FC 05 00 00 00 mov dword ptr [ebp-4],5
0040107B 83 EC 14 sub esp,14h
0040107E B9 05 00 00 00 mov ecx,5
00401083 8D 75 EC lea esi,[ebp-14h]
00401086 8B FC mov edi,esp
00401088 F3 A5 rep movs dword ptr [edi],dword ptr [esi]
0040108A E8 8A FF FF FF call @ILT+20(func) (00401019)
0040108F 83 C4 14 add esp,14h
00401092 33 C0 xor eax,eax
00401094 5F pop edi
00401095 5E pop esi
00401096 5B pop ebx
00401097 83 C4 54 add esp,54h
0040109A 3B EC cmp ebp,esp
0040109C E8 CF 00 00 00 call __chkesp (00401170)
004010A1 8B E5 mov esp,ebp
004010A3 5D pop ebp
004010A4 C3 ret

此时传参和push原理一样,此时先把所有堆栈提升,再填充提升的堆栈
结构体传参通过堆栈传参

结构体作为返回值

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
00401040 55                   push        ebp
00401041 8B EC mov ebp,esp
00401043 83 EC 70 sub esp,70h
00401046 53 push ebx
00401047 56 push esi
00401048 57 push edi
00401049 8D 7D 90 lea edi,[ebp-70h]
0040104C B9 1C 00 00 00 mov ecx,1Ch
00401051 B8 CC CC CC CC mov eax,0CCCCCCCCh
00401056 F3 AB rep stos dword ptr [edi]
00401058 8D 45 D0 lea eax,[ebp-30h]
0040105B 50 push eax
0040105C E8 BD FF FF FF call @ILT+25(func3) (0040101e)
00401061 83 C4 04 add esp,4

00401064 8B 08 mov ecx,dword ptr [eax]
00401066 89 4D E0 mov dword ptr [ebp-20h],ecx
00401069 8B 50 04 mov edx,dword ptr [eax+4]
0040106C 89 55 E4 mov dword ptr [ebp-1Ch],edx
0040106F 8B 48 08 mov ecx,dword ptr [eax+8]
00401072 89 4D E8 mov dword ptr [ebp-18h],ecx
00401075 8B 50 0C mov edx,dword ptr [eax+0Ch]
00401078 89 55 EC mov dword ptr [ebp-14h],edx
0040107B 8B 45 E0 mov eax,dword ptr [ebp-20h]
0040107E 89 45 F0 mov dword ptr [ebp-10h],eax
00401081 8B 4D E4 mov ecx,dword ptr [ebp-1Ch]
00401084 89 4D F4 mov dword ptr [ebp-0Ch],ecx
00401087 8B 55 E8 mov edx,dword ptr [ebp-18h]
0040108A 89 55 F8 mov dword ptr [ebp-8],edx
0040108D 8B 45 EC mov eax,dword ptr [ebp-14h]
00401090 89 45 FC mov dword ptr [ebp-4],eax
00401093 33 C0 xor eax,eax
00401095 5F pop edi
00401096 5E pop esi
00401097 5B pop ebx
00401098 83 C4 70 add esp,70h
0040109B 3B EC cmp ebp,esp
0040109D E8 CE 00 00 00 call __chkesp (00401170)
004010A2 8B E5 mov esp,ebp
004010A4 5D pop ebp
004010A5 C3 ret



004106F0 55 push ebp
004106F1 8B EC mov ebp,esp
004106F3 83 EC 50 sub esp,50h
004106F6 53 push ebx
004106F7 56 push esi
004106F8 57 push edi
004106F9 8D 7D B0 lea edi,[ebp-50h]
004106FC B9 14 00 00 00 mov ecx,14h
00410701 B8 CC CC CC CC mov eax,0CCCCCCCCh
00410706 F3 AB rep stos dword ptr [edi]
00410708 C7 45 F0 01 00 00 00 mov dword ptr [ebp-10h],1
0041070F 66 C7 45 F4 02 00 mov word ptr [ebp-0Ch],offset func+23h (00410713)
00410715 C6 45 F6 03 mov byte ptr [ebp-0Ah],3
00410719 C7 45 F8 04 00 00 00 mov dword ptr [ebp-8],4
00410720 C7 45 FC 05 00 00 00 mov dword ptr [ebp-4],5

00410727 8B 45 08 mov eax,dword ptr [ebp+8]
0041072A 8B 4D F0 mov ecx,dword ptr [ebp-10h]
0041072D 89 08 mov dword ptr [eax],ecx
0041072F 8B 55 F4 mov edx,dword ptr [ebp-0Ch]
00410732 89 50 04 mov dword ptr [eax+4],edx
00410735 8B 4D F8 mov ecx,dword ptr [ebp-8]
00410738 89 48 08 mov dword ptr [eax+8],ecx
0041073B 8B 55 FC mov edx,dword ptr [ebp-4]
0041073E 89 50 0C mov dword ptr [eax+0Ch],edx
00410741 8B 45 08 mov eax,dword ptr [ebp+8]

00410744 5F pop edi
00410745 5E pop esi
00410746 5B pop ebx
00410747 8B E5 mov esp,ebp
00410749 5D pop ebp
0041074A C3 ret

总结:如果不用指针,别声明结构体为局部变量,别把结构体当做返回值和参数


sizeof

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
sizeof(char);

char arr1[10]
short arr2[10] //20
int arr3[10] //40

sizeof(arr1) //10
sizeof(arr1[10]) //1

struct s1
{
char a;
int b;
char c;
}; //12
a 0 0 0
b b b b
c 0 0 0
struct s2
{
int c;
char a;
char b;
}; //8
c c c c
a b 0 0

sizeof(s1); //12
sizeof(s2); //8

对齐参数:结构体中参数以几字节对齐,取值1,2,4,8 默认为8
#pragma pack(1)
struct ha
{
int a;
__int64 b;
char c;
};
#pragma pack()
sizeof(ha) //13

如果这个值比结构体成员的sizeof值小,那么该成员的偏移量应该以此值为准,即是说,结构体成员的偏移量应该取二者的最小值.

#pragma pack(2)
struct ha
{
int a;
__int64 b;
char c;
};
#pragma pack()
sizeof(ha) //14

#pragma pack(4)
struct ha
{
int a;
__int64 b;
char c;
};
#pragma pack()
sizeof(ha) //16

#pragma pack(4)
struct ha
{
int a;
__int64 b;
char c;
};
#pragma pack()
sizeof(ha) //24

image-20230122135557244

image-20230122140313752

image-20230122140354031

1
2
3
4
5
6
对齐原则
原则一:数据成员对齐规则。结构的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储).
原则二:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。
原则三:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b, b里有char,int,double等元素,那b应该从8的整数倍开始存储.)
原则四:对齐参数如果比结构体成员的sizeof值小,该成员的偏移量应该以此值为准.也就是说,结构体成员的偏移量应该取二者的最小值.

image-20230122141122230

image-20230122142241459

取结构体中变量最大的和默认相比,取小的

image-20230122142505891

按照数据型由小到大的顺序进行书写

image-20230122142714801

嵌套结构体按照嵌套的结构体中变量最大的是对齐宽度,但结构体还是按照原来大小累加

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
struct S1
{
//16
char c;
double i;
};

struct S3
{
//32
char c1;
S1 s;
char c2;
char c3;
};

struct S4
{
//40
char c1;
S1 s;
char c2;
double c3;
};

struct S5
{
//16
int c1;
char c2[10];
};



int main(int argc, char* argv[])
{
printf("%d %d %d %d %d\n",sizeof(S1),sizeof(S3),sizeof(S4),sizeof(S5),sizeof(1));
return 0;
}

# typedef

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
typedef为C语言的关键字,作用是为一种数据类型定义一个新名字。这里的数据类型包括内部数据类型(int, char等)和自定

typedef unsigned char BYTE;

typedef int vector[10];
vector v;
v[0] = 1;

typedef struct student
{
char a[10];
}stu;
main()
{
stu s;
s.a[0] = 'a';
s.a[1] = 'b';
s.a[2] = 'c';
s.a[3] = '\0'; //s.a[3] = 0; \0和0都是结束符号
printf("%s",s.arr); //abc

}

stu 是 student结构体的别名,两个都可以使用


#include "string.h"
strcpy(s.arr,"china");
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
struct nPos
{
float x;
float y;
float z;
};

struct Monster
{
int id;
char name[32];
int Hp;
int blue;
nPos pos;
};

Monster mon[10]; //结构体数组

void value(int n,char *name,int hp,int blue, float x,float y,float z)
{
mon[i].name = name;
mon[i].pos.x = x;
}

void find(int x)
{
for(int i=0;i<=9;i++)
{
if (mon[i].id == x)
{
printf();
}
}
}

int main()
{

return 0;
}

# Switch

image-20230123104349398

case 后面必须是常量表达式

case 后常量表达式的值不能一样

switch 后面表达式必须为整数

没有 break 时会顺序向下执行,直到遇到 break

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
55
56
57
58
59
60
61
62
void func(int x)
{
switch(x)
{
case 1:
printf("1");
break;
case 2:
printf("2");
break;
case 3:
printf("3");
break;
case 4:
printf("4");
break;
default:
printf("5");
break;
}
}

int main(int argc, char* argv[])
{
func(2); //2
return 0;
}

----------------------------------------------------------------

void func(int x)
{
switch(x)
{
case 1:
printf("1");

case 2:
printf("2");

case 3:
printf("3");

case 4:
printf("4");
break;
default:
printf("5");
break;
}
}

int main(int argc, char* argv[])
{
func(2); //234
return 0;
}


//表达式都不成立执行default,default可以省略但不建议


switch 反汇编分析

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
void func(int x)
{
switch(x)
{
case 1:
printf("1");
break;
case 2:
printf("2");
break;
case 3:
printf("3");
break;
default:
printf("5");
break;
}
}

int main(int argc, char* argv[])
{
func(2);
return 0;
}


00401020 55 push ebp
00401021 8B EC mov ebp,esp
00401023 83 EC 44 sub esp,44h
00401026 53 push ebx
00401027 56 push esi
00401028 57 push edi
00401029 8D 7D BC lea edi,[ebp-44h]
0040102C B9 11 00 00 00 mov ecx,11h
00401031 B8 CC CC CC CC mov eax,0CCCCCCCCh
00401036 F3 AB rep stos dword ptr [edi]

00401038 8B 45 08 mov eax,dword ptr [ebp+8]
0040103B 89 45 FC mov dword ptr [ebp-4],eax
0040103E 83 7D FC 01 cmp dword ptr [ebp-4],1
00401042 74 0E je func+32h (00401052)

00401044 83 7D FC 02 cmp dword ptr [ebp-4],2
00401048 74 17 je func+41h (00401061)
0040104A 83 7D FC 03 cmp dword ptr [ebp-4],3
0040104E 74 20 je func+50h (00401070)
00401050 EB 2D jmp func+5Fh (0040107f)
00401052 68 28 20 42 00 push offset string "1" (00422028)
00401057 E8 E4 00 00 00 call printf (00401140)
0040105C 83 C4 04 add esp,4
0040105F EB 2B jmp func+6Ch (0040108c)
00401061 68 24 20 42 00 push offset string "2" (00422024)
00401066 E8 D5 00 00 00 call printf (00401140)
0040106B 83 C4 04 add esp,4
0040106E EB 1C jmp func+6Ch (0040108c)
00401070 68 20 20 42 00 push offset string "4" (00422020)
00401075 E8 C6 00 00 00 call printf (00401140)
0040107A 83 C4 04 add esp,4
0040107D EB 0D jmp func+6Ch (0040108c)
0040107F 68 1C 20 42 00 push offset string "5" (0042201c)
00401084 E8 B7 00 00 00 call printf (00401140)
00401089 83 C4 04 add esp,4
0040108C 5F pop edi
0040108D 5E pop esi
0040108E 5B pop ebx
0040108F 83 C4 44 add esp,44h
00401092 3B EC cmp ebp,esp
00401094 E8 27 01 00 00 call __chkesp (004011c0)
00401099 8B E5 mov esp,ebp
0040109B 5D pop ebp
0040109C C3 ret


//分支条件比较少时<4和if-else反汇编类似

//分支大于4时在内存中生成大表 [edx*4+4010B1h]通过寻址

void func(int x)
{
switch(x)
{
case 1:
printf("1");
break;
case 2:
printf("2");
break;
case 3:
printf("3");
break;
case 4:
printf("4");
break;
default:
printf("5");
break;
}
}

int main(int argc, char* argv[])
{
func(2);
return 0;
}

00401020 55 push ebp
00401021 8B EC mov ebp,esp
00401023 83 EC 44 sub esp,44h
00401026 53 push ebx
00401027 56 push esi
00401028 57 push edi
00401029 8D 7D BC lea edi,[ebp-44h]
0040102C B9 11 00 00 00 mov ecx,11h
00401031 B8 CC CC CC CC mov eax,0CCCCCCCCh
00401036 F3 AB rep stos dword ptr [edi]
00401038 8B 45 08 mov eax,dword ptr [ebp+8]
0040103B 89 45 FC mov dword ptr [ebp-4],eax
0040103E 8B 4D FC mov ecx,dword ptr [ebp-4]
00401041 83 E9 01 sub ecx,1
00401044 89 4D FC mov dword ptr [ebp-4],ecx
00401047 83 7D FC 03 cmp dword ptr [ebp-4],3
0040104B 77 46 ja $L539+0Fh (00401093)
0040104D 8B 55 FC mov edx,dword ptr [ebp-4]
00401050 FF 24 95 B1 10 40 00 jmp dword ptr [edx*4+4010B1h]
$L533:
00401057 68 2C 20 42 00 push offset string "1" (0042202c)
0040105C E8 DF 00 00 00 call printf (00401140)
00401061 83 C4 04 add esp,4
00401064 EB 3A jmp $L539+1Ch (004010a0)
$L535:
00401066 68 28 20 42 00 push offset string "2" (00422028)
0040106B E8 D0 00 00 00 call printf (00401140)
00401070 83 C4 04 add esp,4
00401073 EB 2B jmp $L539+1Ch (004010a0)
$L537:
00401075 68 24 20 42 00 push offset string "3" (00422024)
0040107A E8 C1 00 00 00 call printf (00401140)
0040107F 83 C4 04 add esp,4
00401082 EB 1C jmp $L539+1Ch (004010a0)
$L539:
00401084 68 20 20 42 00 push offset string "4" (00422020)
00401089 E8 B2 00 00 00 call printf (00401140)
0040108E 83 C4 04 add esp,4
00401091 EB 0D jmp $L539+1Ch (004010a0)
00401093 68 1C 20 42 00 push offset string "5" (0042201c)
00401098 E8 A3 00 00 00 call printf (00401140)
0040109D 83 C4 04 add esp,4
004010A0 5F pop edi
004010A1 5E pop esi
004010A2 5B pop ebx
004010A3 83 C4 44 add esp,44h
004010A6 3B EC cmp ebp,esp
004010A8 E8 13 01 00 00 call __chkesp (004011c0)
004010AD 8B E5 mov esp,ebp
004010AF 5D pop ebp
004010B0 C3 ret


//case顺序交换不影响大表的生成,即可以是无序的

image-20230123113122801

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
//只要case连续且数目足够就会生成大表
//case连续没有空缺
void func(int x)
{
switch(x)
{
case 301:
printf("1");
break;
case 302:
printf("2");
break;
case 303:
printf("3");
break;
case 304:
printf("4");
break;
case 305:
printf("5");
break;
case 306:
printf("6");
break;
case 307:
printf("7");
break;
case 308:
printf("8");
break;
case 309:
printf("9");
break;
case 310:
printf("A");
break;
default:
printf("sb");
break;
}
}


0040D7D0 55 push ebp
0040D7D1 8B EC mov ebp,esp
0040D7D3 83 EC 44 sub esp,44h
0040D7D6 53 push ebx
0040D7D7 56 push esi
0040D7D8 57 push edi
0040D7D9 8D 7D BC lea edi,[ebp-44h]
0040D7DC B9 11 00 00 00 mov ecx,11h
0040D7E1 B8 CC CC CC CC mov eax,0CCCCCCCCh
0040D7E6 F3 AB rep stos dword ptr [edi]
0040D7E8 8B 45 08 mov eax,dword ptr [ebp+8]
0040D7EB 89 45 FC mov dword ptr [ebp-4],eax
0040D7EE 8B 4D FC mov ecx,dword ptr [ebp-4]
0040D7F1 81 E9 2D 01 00 00 sub ecx,12Dh
0040D7F7 89 4D FC mov dword ptr [ebp-4],ecx
0040D7FA 83 7D FC 09 cmp dword ptr [ebp-4],9
0040D7FE 0F 87 A6 00 00 00 ja $L551+0Fh (0040d8aa)
0040D804 8B 55 FC mov edx,dword ptr [ebp-4]
0040D807 FF 24 95 C8 D8 40 00 jmp dword ptr [edx*4+40D8C8h]
$L533:
0040D80E 68 C0 2F 42 00 push offset string "1" (00422fc0)
0040D813 E8 28 39 FF FF call printf (00401140)
0040D818 83 C4 04 add esp,4
0040D81B E9 97 00 00 00 jmp $L551+1Ch (0040d8b7)
$L535:
0040D820 68 BC 2F 42 00 push offset string "2" (00422fbc)
0040D825 E8 16 39 FF FF call printf (00401140)
0040D82A 83 C4 04 add esp,4
0040D82D E9 85 00 00 00 jmp $L551+1Ch (0040d8b7)
$L537:
0040D832 68 B8 2F 42 00 push offset string "3" (00422fb8)
0040D837 E8 04 39 FF FF call printf (00401140)
0040D83C 83 C4 04 add esp,4
0040D83F EB 76 jmp $L551+1Ch (0040d8b7)
$L539:
0040D841 68 B4 2F 42 00 push offset string "4" (00422fb4)
0040D846 E8 F5 38 FF FF call printf (00401140)
0040D84B 83 C4 04 add esp,4
0040D84E EB 67 jmp $L551+1Ch (0040d8b7)
$L541:
0040D850 68 64 2F 42 00 push offset string "5" (00422f64)
0040D855 E8 E6 38 FF FF call printf (00401140)
0040D85A 83 C4 04 add esp,4
0040D85D EB 58 jmp $L551+1Ch (0040d8b7)
$L543:
0040D85F 68 3C 21 42 00 push offset string "6" (0042213c)
0040D864 E8 D7 38 FF FF call printf (00401140)
0040D869 83 C4 04 add esp,4
0040D86C EB 49 jmp $L551+1Ch (0040d8b7)
$L545:
0040D86E 68 2C 20 42 00 push offset string "7" (0042202c)
0040D873 E8 C8 38 FF FF call printf (00401140)
0040D878 83 C4 04 add esp,4
0040D87B EB 3A jmp $L551+1Ch (0040d8b7)
$L547:
0040D87D 68 28 20 42 00 push offset string "8" (00422028)
0040D882 E8 B9 38 FF FF call printf (00401140)
0040D887 83 C4 04 add esp,4
0040D88A EB 2B jmp $L551+1Ch (0040d8b7)
$L549:
0040D88C 68 24 20 42 00 push offset string "9" (00422024)
0040D891 E8 AA 38 FF FF call printf (00401140)
0040D896 83 C4 04 add esp,4
0040D899 EB 1C jmp $L551+1Ch (0040d8b7)
$L551:
0040D89B 68 20 20 42 00 push offset string "A" (00422020)
0040D8A0 E8 9B 38 FF FF call printf (00401140)
0040D8A5 83 C4 04 add esp,4
0040D8A8 EB 0D jmp $L551+1Ch (0040d8b7)
0040D8AA 68 1C 20 42 00 push offset string "sb" (0042201c)
0040D8AF E8 8C 38 FF FF call printf (00401140)
0040D8B4 83 C4 04 add esp,4
0040D8B7 5F pop edi
0040D8B8 5E pop esi
0040D8B9 5B pop ebx
0040D8BA 83 C4 44 add esp,44h
0040D8BD 3B EC cmp ebp,esp
0040D8BF E8 FC 38 FF FF call __chkesp (004011c0)
0040D8C4 8B E5 mov esp,ebp
0040D8C6 5D pop ebp
0040D8C7 C3 ret

0040D8C8 0E D8 40 00 .谸.
0040D8CC 20 D8 40 00 谸.
0040D8D0 32 D8 40 00 2谸.
0040D8D4 41 D8 40 00 A谸.
0040D8D8 50 D8 40 00 P谸.
0040D8DC 5F D8 40 00 _谸.
0040D8E0 6E D8 40 00 n谸.
0040D8E4 7D D8 40 00 }谸.
0040D8E8 8C D8 40 00 屫@.
0040D8EC 9B D8 40 00 涁@.


//case不连续且差值不大
//如果中间出现不连续的case会用default补大表空缺
//空缺的地方会用case补,下面的两个0040D886
0040D8A4 0E D8 40 00 .谸.
0040D8A8 86 D8 40 00 嗀@.
0040D8AC 86 D8 40 00 嗀@.
0040D8B0 1D D8 40 00 .谸.
0040D8B4 2C D8 40 00 ,谸.
0040D8B8 3B D8 40 00 ;谸.
0040D8BC 4A D8 40 00 J谸.
0040D8C0 59 D8 40 00 Y谸.
0040D8C4 68 D8 40 00 h谸.
0040D8C8 77 D8 40 00 w谸.



//如果case不连续且差值过大 使用大表加小表
void func(int x)
{
switch(x)
{
case 301:
printf("1");
break;
case 308:
printf("8");
break;
case 309:
printf("9");
break;
case 310:
printf("A");
break;
default:
printf("sb");
break;
}
}


0040D7D0 55 push ebp
0040D7D1 8B EC mov ebp,esp
0040D7D3 83 EC 44 sub esp,44h
0040D7D6 53 push ebx
0040D7D7 56 push esi
0040D7D8 57 push edi
0040D7D9 8D 7D BC lea edi,[ebp-44h]
0040D7DC B9 11 00 00 00 mov ecx,11h
0040D7E1 B8 CC CC CC CC mov eax,0CCCCCCCCh
0040D7E6 F3 AB rep stos dword ptr [edi]
0040D7E8 8B 45 08 mov eax,dword ptr [ebp+8]
0040D7EB 89 45 FC mov dword ptr [ebp-4],eax
0040D7EE 8B 4D FC mov ecx,dword ptr [ebp-4]
0040D7F1 81 E9 2D 01 00 00 sub ecx,12Dh
0040D7F7 89 4D FC mov dword ptr [ebp-4],ecx
0040D7FA 83 7D FC 09 cmp dword ptr [ebp-4],9
0040D7FE 77 4E ja $L539+0Fh (0040d84e)
0040D800 8B 45 FC mov eax,dword ptr [ebp-4]
0040D803 33 D2 xor edx,edx
0040D805 8A 90 80 D8 40 00 mov dl,byte ptr (0040d880)[eax] //内存0040d880中偏移量为[eax]的值
0040D80B FF 24 95 6C D8 40 00 jmp dword ptr [edx*4+40D86Ch]
$L533:
0040D812 68 2C 20 42 00 push offset string "1" (0042202c)
0040D817 E8 24 39 FF FF call printf (00401140)
0040D81C 83 C4 04 add esp,4
0040D81F EB 3A jmp $L539+1Ch (0040d85b)
$L535:
0040D821 68 28 20 42 00 push offset string "8" (00422028)
0040D826 E8 15 39 FF FF call printf (00401140)
0040D82B 83 C4 04 add esp,4
0040D82E EB 2B jmp $L539+1Ch (0040d85b)
$L537:
0040D830 68 24 20 42 00 push offset string "9" (00422024)
0040D835 E8 06 39 FF FF call printf (00401140)
0040D83A 83 C4 04 add esp,4
0040D83D EB 1C jmp $L539+1Ch (0040d85b)
$L539:
0040D83F 68 20 20 42 00 push offset string "A" (00422020)
0040D844 E8 F7 38 FF FF call printf (00401140)
0040D849 83 C4 04 add esp,4
0040D84C EB 0D jmp $L539+1Ch (0040d85b)
0040D84E 68 1C 20 42 00 push offset string "sb" (0042201c)
0040D853 E8 E8 38 FF FF call printf (00401140)
0040D858 83 C4 04 add esp,4
0040D85B 5F pop edi
0040D85C 5E pop esi
0040D85D 5B pop ebx
0040D85E 83 C4 44 add esp,44h
0040D861 3B EC cmp ebp,esp
0040D863 E8 58 39 FF FF call __chkesp (004011c0)
0040D868 8B E5 mov esp,ebp
0040D86A 5D pop ebp
0040D86B C3 ret


0040D86C 12 D8 40 00 .谸.
0040D870 21 D8 40 00 !谸.
0040D874 30 D8 40 00 0谸.
0040D878 3F D8 40 00 ?谸.
0040D87C 4E D8 40 00 N谸.
0040D880 00 04 04 04 ....
0040D884 04 04 04 01 ....
0040D888 02 03 CC CC ..烫
//中间的04就是代替大表中的default
//如果空缺的太多会生成一张小表来替代大表中default填充,变为大表加小表



//差值过大
void func(int x)
{
switch(x)
{
case 1301:
printf("1");
break;
case 5308:
printf("8");
break;
case 10309:
printf("9");
break;
case 20310:
printf("A");
break;
default:
printf("sb");
break;
}
}

0040D7D0 55 push ebp
0040D7D1 8B EC mov ebp,esp
0040D7D3 83 EC 44 sub esp,44h
0040D7D6 53 push ebx
0040D7D7 56 push esi
0040D7D8 57 push edi
0040D7D9 8D 7D BC lea edi,[ebp-44h]
0040D7DC B9 11 00 00 00 mov ecx,11h
0040D7E1 B8 CC CC CC CC mov eax,0CCCCCCCCh
0040D7E6 F3 AB rep stos dword ptr [edi]
0040D7E8 8B 45 08 mov eax,dword ptr [ebp+8]
0040D7EB 89 45 FC mov dword ptr [ebp-4],eax
0040D7EE 81 7D FC 45 28 00 00 cmp dword ptr [ebp-4],2845h
0040D7F5 7F 1D jg func+44h (0040d814)
0040D7F7 81 7D FC 45 28 00 00 cmp dword ptr [ebp-4],2845h
0040D7FE 74 3D je func+6Dh (0040d83d)
0040D800 81 7D FC 15 05 00 00 cmp dword ptr [ebp-4],515h
0040D807 74 16 je func+4Fh (0040d81f)
0040D809 81 7D FC BC 14 00 00 cmp dword ptr [ebp-4],14BCh
0040D810 74 1C je func+5Eh (0040d82e)
0040D812 EB 47 jmp func+8Bh (0040d85b)
0040D814 81 7D FC 56 4F 00 00 cmp dword ptr [ebp-4],4F56h
0040D81B 74 2F je func+7Ch (0040d84c)
0040D81D EB 3C jmp func+8Bh (0040d85b)
0040D81F 68 2C 20 42 00 push offset string "1" (0042202c)
0040D824 E8 17 39 FF FF call printf (00401140)
0040D829 83 C4 04 add esp,4
0040D82C EB 3A jmp func+98h (0040d868)
0040D82E 68 28 20 42 00 push offset string "8" (00422028)
0040D833 E8 08 39 FF FF call printf (00401140)
0040D838 83 C4 04 add esp,4
0040D83B EB 2B jmp func+98h (0040d868)
0040D83D 68 24 20 42 00 push offset string "9" (00422024)
0040D842 E8 F9 38 FF FF call printf (00401140)
0040D847 83 C4 04 add esp,4
0040D84A EB 1C jmp func+98h (0040d868)
0040D84C 68 20 20 42 00 push offset string "A" (00422020)
0040D851 E8 EA 38 FF FF call printf (00401140)
0040D856 83 C4 04 add esp,4
0040D859 EB 0D jmp func+98h (0040d868)
0040D85B 68 1C 20 42 00 push offset string "sb" (0042201c)
0040D860 E8 DB 38 FF FF call printf (00401140)
0040D865 83 C4 04 add esp,4
0040D868 5F pop edi
0040D869 5E pop esi
0040D86A 5B pop ebx
0040D86B 83 C4 44 add esp,44h
0040D86E 3B EC cmp ebp,esp
0040D870 E8 4B 39 FF FF call __chkesp (004011c0)
0040D875 8B E5 mov esp,ebp
0040D877 5D pop ebp
0040D878 C3 ret


//用case最大的-case最小的,得出的范围称为k,如果要判断的值-case最小的值不在范围k之内,直接default
//edx 存放的是a 与case 中最小值的差值,而eax*4+地址存放的是每个case的地址

image-20230123120353631

# 循环对比分析

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
void func2()
{
for (int i=0;i<=10;i++)
{
printf("%d\n",i);
}
}


0040D920 55 push ebp
0040D921 8B EC mov ebp,esp
0040D923 83 EC 44 sub esp,44h
0040D926 53 push ebx
0040D927 56 push esi
0040D928 57 push edi
0040D929 8D 7D BC lea edi,[ebp-44h]
0040D92C B9 11 00 00 00 mov ecx,11h
0040D931 B8 CC CC CC CC mov eax,0CCCCCCCCh
0040D936 F3 AB rep stos dword ptr [edi]

0040D938 C7 45 FC 00 00 00 00 mov dword ptr [ebp-4],0 ;int i = 0;
0040D93F EB 09 jmp func2+2Ah (0040d94a)
0040D941 8B 45 FC mov eax,dword ptr [ebp-4]
0040D944 83 C0 01 add eax,1
0040D947 89 45 FC mov dword ptr [ebp-4],eax
0040D94A 83 7D FC 0A cmp dword ptr [ebp-4],0Ah ;i<=10
0040D94E 7F 13 jg func2+43h (0040d963) ;i>10,ret
0040D950 8B 4D FC mov ecx,dword ptr [ebp-4] ;i<=10
0040D953 51 push ecx ;ecx = i
0040D954 68 C4 2F 42 00 push offset string "%d\n" (00422fc4)
0040D959 E8 E2 37 FF FF call printf (00401140)
0040D95E 83 C4 08 add esp,8
0040D961 EB DE jmp func2+21h (0040d941)
0040D963 5F pop edi
0040D964 5E pop esi
0040D965 5B pop ebx
0040D966 83 C4 44 add esp,44h
0040D969 3B EC cmp ebp,esp
0040D96B E8 50 38 FF FF call __chkesp (004011c0)
0040D970 8B E5 mov esp,ebp
0040D972 5D pop ebp
0040D973 C3 ret


void func3()
{
int i = 0;
while(i<=10)
{
printf("%d\n",i);
i++;
}
}

0040D980 55 push ebp
0040D981 8B EC mov ebp,esp
0040D983 83 EC 44 sub esp,44h
0040D986 53 push ebx
0040D987 56 push esi
0040D988 57 push edi
0040D989 8D 7D BC lea edi,[ebp-44h]
0040D98C B9 11 00 00 00 mov ecx,11h
0040D991 B8 CC CC CC CC mov eax,0CCCCCCCCh
0040D996 F3 AB rep stos dword ptr [edi]

0040D998 C7 45 FC 00 00 00 00 mov dword ptr [ebp-4],0
0040D99F 83 7D FC 0A cmp dword ptr [ebp-4],0Ah
0040D9A3 7F 1C jg func3+41h (0040d9c1)
0040D9A5 8B 45 FC mov eax,dword ptr [ebp-4]
0040D9A8 50 push eax
0040D9A9 68 C4 2F 42 00 push offset string "%d\n" (00422fc4)
0040D9AE E8 8D 37 FF FF call printf (00401140)
0040D9B3 83 C4 08 add esp,8
0040D9B6 8B 4D FC mov ecx,dword ptr [ebp-4]
0040D9B9 83 C1 01 add ecx,1
0040D9BC 89 4D FC mov dword ptr [ebp-4],ecx
0040D9BF EB DE jmp func3+1Fh (0040d99f)
0040D9C1 5F pop edi
0040D9C2 5E pop esi
0040D9C3 5B pop ebx
0040D9C4 83 C4 44 add esp,44h
0040D9C7 3B EC cmp ebp,esp
0040D9C9 E8 F2 37 FF FF call __chkesp (004011c0)
0040D9CE 8B E5 mov esp,ebp
0040D9D0 5D pop ebp
0040D9D1 C3 ret



void func4()
{
int i = 0;
do
{
printf("%d\n");
i++;
}while(i<=10);
}

0040D980 55 push ebp
0040D981 8B EC mov ebp,esp
0040D983 83 EC 44 sub esp,44h
0040D986 53 push ebx
0040D987 56 push esi
0040D988 57 push edi
0040D989 8D 7D BC lea edi,[ebp-44h]
0040D98C B9 11 00 00 00 mov ecx,11h
0040D991 B8 CC CC CC CC mov eax,0CCCCCCCCh
0040D996 F3 AB rep stos dword ptr [edi]

0040D998 C7 45 FC 00 00 00 00 mov dword ptr [ebp-4],0
0040D99F 83 7D FC 0A cmp dword ptr [ebp-4],0Ah
0040D9A3 7F 1C jg func3+41h (0040d9c1)
0040D9A5 8B 45 FC mov eax,dword ptr [ebp-4]
0040D9A8 50 push eax
0040D9A9 68 C4 2F 42 00 push offset string "%d\n" (00422fc4)
0040D9AE E8 8D 37 FF FF call printf (00401140)
0040D9B3 83 C4 08 add esp,8
0040D9B6 8B 4D FC mov ecx,dword ptr [ebp-4]
0040D9B9 83 C1 01 add ecx,1
0040D9BC 89 4D FC mov dword ptr [ebp-4],ecx
0040D9BF EB DE jmp func3+1Fh (0040d99f)
0040D9C1 5F pop edi
0040D9C2 5E pop esi
0040D9C3 5B pop ebx
0040D9C4 83 C4 44 add esp,44h
0040D9C7 3B EC cmp ebp,esp
0040D9C9 E8 F2 37 FF FF call __chkesp (004011c0)
0040D9CE 8B E5 mov esp,ebp
0040D9D0 5D pop ebp
0040D9D1 C3 ret

# 指针

总结:
1、带有 * 的变量类型的标准写法: 变量类型* 变量名

2、任何类型都可以带 * 加上 * 以后是新的类型

3、* 可以是任意多个

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
//赋值
struct Stu
{
int a;
int b;
};

void func()
{
char* x;
short* y;
int* z;
Stu* stu;
int** a;

x = (char*)1;
y = (short*)2;
z = (int*)3;
stu = (Stu*)4;
a = (int**)5;
}

0040D740 55 push ebp
0040D741 8B EC mov ebp,esp
0040D743 83 EC 54 sub esp,54h
0040D746 53 push ebx
0040D747 56 push esi
0040D748 57 push edi
0040D749 8D 7D AC lea edi,[ebp-54h]
0040D74C B9 15 00 00 00 mov ecx,15h
0040D751 B8 CC CC CC CC mov eax,0CCCCCCCCh
0040D756 F3 AB rep stos dword ptr [edi]
0040D758 C7 45 FC 01 00 00 00 mov dword ptr [ebp-4],1
0040D75F C7 45 F8 02 00 00 00 mov dword ptr [ebp-8],2
0040D766 C7 45 F4 03 00 00 00 mov dword ptr [ebp-0Ch],3
0040D76D C7 45 F0 04 00 00 00 mov dword ptr [ebp-10h],4
0040D774 C7 45 EC 05 00 00 00 mov dword ptr [ebp-14h],5



//带*类型变量赋值必须使用完整写法
//不加家多少个*,宽度永远是4字节
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
//++/--加多少取决于类型减去一个*之后的数据宽度
void func2()
{
char* x;
short* y;
int* z;

x = (char*)1;
y = (short*)2;
z = (int*)3;

x++;
y++;
z++;
//2,4,7

printf("%d %d %d\n",x,y,z);

char** x1;
short** y1;
int** z1;

x1 = (char**)1;
y1 = (short**)2;
z1 = (int**)3;

x1++;
y1++;
z1++;

printf("%d %d %d\n",x1,y1,z1);
//5,6,7
}

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
	char* x;
short* y;
int* z;

x = (char*)100;
y = (short*)100;
z = (int*)100;

x+=5;
y+=5;
z+=5;

printf("%d %d %d\n",x,y,z); //105 110 120


char**** x;
short**** y;
int**** z;

x = (char****)100;
y = (short****)100;
z = (int****)100;

x+=5;
y+=5;
z+=5;

printf("%d %d %d\n",x,y,z); //120,120,120
总结:
1.带*类型的变量可以加、减一个整数,但不能乘或者除.
2.带*类型变量与其他整数相加或者相减时:
//带*类型变量+N=带*类型变量+N(去掉一个*后类型的宽度)
//带*类型变量一N=带*类型变量–N(去掉一个*后类型的宽度)

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
	char**** x;
char**** y;

x = (char****)200;
y = (char****)100;

int a = x-y;

printf("%d \n",a); //25

char* x;
char* y;

x = (char*)200;
y = (char*)100;

int a = x-y; //*相减结果为int

printf("%d \n",a); //100

short* x;
short* y;

x = (short*)200;
y = (short*)100;

int a = x-y;

printf("%d \n",a); //50


总结:
1、两个类型相同的带*类型的变量可以进行减法操作
2、想减的结果要除以去掉一个*的数据的宽度.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//带*的类型可以做大小比较


short**** x;
short**** y;

x = (short****)200;
y = (short****)100;

if(x>y)
printf("1"); //1
else
printf("2");

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
char** arr[10]    //40
0040D740 55 push ebp
0040D741 8B EC mov ebp,esp
0040D743 83 EC 68 sub esp,68h //0x68-0x40=0x28=40/4=10


struct Student
{
int x;
int y;
};

Student**** s;
s = (Student****)100;
s++; //104
s += 2; //112
s -= 3; //100
s += 2.5; //pointer on left; needs integral value on right

Student**** s1;
Student**** s2;
int x;
s1 = (Student****)200;
s2 = (Student****)100;
x = s1-s2; //25

Student* s;
s = (Student*)100;
s++; //108
s+=2; //124
s-=3; //100

Student* s1;
Student* s2;
int x;
s1 = (Student*)200;
s2 = (Student*)100;
x=s1-s2 //12

void func2()
{
#if 0
Student**** s;
s = (Student****)100;
s++;
printf("%d\n",s);
s += 2;
printf("%d\n",s);
s -= 3;
printf("%d\n",s);

Student**** s1;
Student**** s2;
int x;
s1 = (Student****)200;
s2 = (Student****)100;
x = s1-s2;
printf("%d\n",x);

Student* s;
s = (Student*)100;
s++;
printf("%d\n",s);
s+=2;
printf("%d\n",s);
s-=3;
printf("%d\n",s);

Student* s1;
Student* s2;
int x;
s1 = (Student*)200;
s2 = (Student*)100;
x=s1-s2;
printf("%d\n",x);

#endif
}
Student**** 4
Student* 8

Student s;
int x = (Studnet)s; //x


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
struct Student
{
int x;
int y;
};
Student s;
int m = 10;
s = (Student)x; //不能转,int不能和结构体类型互相转换


int* x;
Student* y;
x = (int*) 10;
y = (Student*)x; //可以转

int* x;
Student y;
x = (Studnet*)y; //不能转

&是地址符,类型是其后面的类型加一个“*”,任何变量都可以使用来获取地址,但不能用在常量上。
&可以取任何一个变量的地址
&a 的类型是a的类型+*



char***** a = (char*****)10;
&a = char******

image-20230125132226542

image-20230125133522040

image-20230221165556550

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int  x=10;
int* px = &x;
char x1 = *px;
*x == x的类型减去一个*

变量类型本来带*才能加*

int arr[5] = {1,2,3,4,5}; //开辟8个字节缓冲区
int* x = &arr[0]; //第一个变量的地址
int* px = arr; //两种等价的写法

for(int i = 0; i<5;i++)
{
printf("%d ",arr[i]);
printf("%d ",*(px+i));
}
//px是int*类型,运算时去掉一个*,变为int类型,偏移时每次+4找到数组中对应位置,*在解引用
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
void func3()
{
char a = 10;
short b = 20;
int c = 30;

char* pa = &a;
short* pb = &b;
int* pc = &c;

char** ppa = &pa;
short** ppb = &pb;
int** ppc = &pc;

}

0040D740 55 push ebp
0040D741 8B EC mov ebp,esp
0040D743 83 EC 64 sub esp,64h
0040D746 53 push ebx
0040D747 56 push esi
0040D748 57 push edi
0040D749 8D 7D 9C lea edi,[ebp-64h]
0040D74C B9 19 00 00 00 mov ecx,19h
0040D751 B8 CC CC CC CC mov eax,0CCCCCCCCh
0040D756 F3 AB rep stos dword ptr [edi]

0040D758 C6 45 FC 0A mov byte ptr [ebp-4],0Ah
0040D75C 66 C7 45 F8 14 00 mov word ptr [ebp-8],offset func3+20h (0040d760)
0040D762 C7 45 F4 1E 00 00 00 mov dword ptr [ebp-0Ch],1Eh
0040D769 8D 45 FC lea eax,[ebp-4]
0040D76C 89 45 F0 mov dword ptr [ebp-10h],eax
0040D76F 8D 4D F8 lea ecx,[ebp-8]
0040D772 89 4D EC mov dword ptr [ebp-14h],ecx
0040D775 8D 55 F4 lea edx,[ebp-0Ch]
0040D778 89 55 E8 mov dword ptr [ebp-18h],edx
0040D77B 8D 45 F0 lea eax,[ebp-10h]
0040D77E 89 45 E4 mov dword ptr [ebp-1Ch],eax
0040D781 8D 4D EC lea ecx,[ebp-14h]
0040D784 89 4D E0 mov dword ptr [ebp-20h],ecx
0040D787 8D 55 E8 lea edx,[ebp-18h]
0040D78A 89 55 DC mov dword ptr [ebp-24h],edx
0040D78D 5F pop edi
0040D78E 5E pop esi
0040D78F 5B pop ebx
0040D790 8B E5 mov esp,ebp
0040D792 5D pop ebp
0040D793 C3 ret


void func4()
{
int p = 10;
int******* p7;
int****** p6;
int***** p5;
int**** p4;
int*** p3;
int** p2;
int* p1;

p1 = &p;
p2 = &p1;
p3 = &p2;
p4 = &p3;
p5 = &p4;
p6 = &p5;
p7 = &p6;
}


0040D870 55 push ebp
0040D871 8B EC mov ebp,esp
0040D873 83 EC 60 sub esp,60h
0040D876 53 push ebx
0040D877 56 push esi
0040D878 57 push edi
0040D879 8D 7D A0 lea edi,[ebp-60h]
0040D87C B9 18 00 00 00 mov ecx,18h
0040D881 B8 CC CC CC CC mov eax,0CCCCCCCCh
0040D886 F3 AB rep stos dword ptr [edi]

0040D888 C7 45 FC 0A 00 00 00 mov dword ptr [ebp-4],0Ah
0040D88F 8D 45 FC lea eax,[ebp-4]
0040D892 89 45 E0 mov dword ptr [ebp-20h],eax
0040D895 8D 4D E0 lea ecx,[ebp-20h]
0040D898 89 4D E4 mov dword ptr [ebp-1Ch],ecx
0040D89B 8D 55 E4 lea edx,[ebp-1Ch]
0040D89E 89 55 E8 mov dword ptr [ebp-18h],edx
0040D8A1 8D 45 E8 lea eax,[ebp-18h]
0040D8A4 89 45 EC mov dword ptr [ebp-14h],eax
0040D8A7 8D 4D EC lea ecx,[ebp-14h]
0040D8AA 89 4D F0 mov dword ptr [ebp-10h],ecx
0040D8AD 8D 55 F0 lea edx,[ebp-10h]
0040D8B0 89 55 F4 mov dword ptr [ebp-0Ch],edx
0040D8B3 8D 45 F4 lea eax,[ebp-0Ch]
0040D8B6 89 45 F8 mov dword ptr [ebp-8],eax
0040D8B9 5F pop edi
0040D8BA 5E pop esi
0040D8BB 5B pop ebx
0040D8BC 8B E5 mov esp,ebp
0040D8BE 5D pop ebp
0040D8BF C3 ret


void fun()
{
int arr[5] = {1,2,3,4,5};
for (int k = 0;k<5;k++)
{
*(arr+k) = 5 - k;
}
for (int i = 0;i<5;i++)
{
printf("%d\n",*(arr+i));
}

}

image-20230126121202168

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
char arr[] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x07,0x09,
0x00,0x20,0x10,0x03,0x03,0x0C,0x00,0x00,0x44,0x00,
0x00,0x33,0x00,0x47,0x0C,0x0E,0x00,0x0D,0x00,0x11,
0x00,0x00,0x00,0x02,0x64,0x00,0x00,0x00,0x0A,0x00,
0x00,0x00,0x64,0x10,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x02,0x00,0x74,0x0F,0x41,0x00,0x00,0x00,
0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x0A,0x00,
0x00,0x02,0x74,0x0F,0x41,0x00,0x06,0x08,0x00,0x00,
0x00,0x00,0x00,0x64,0x00,0x0F,0x00,0x00,0x0D,0x00,
0x00,0x00,0x23,0x00,0x00,0x64,0x00,0x00,0x64,0x00
};

void FindBloodAddr()
{
int i = 0, length = sizeof(arr)/sizeof(arr[0]);
while(i<length - 4)
{
int* p = (int*)(arr+i);
if(*p == 0x64)
{
printf("[%X] = %d\n",p,*p);
}
i++;
}
}

int main(int argc, char* argv[])
{
FindBloodAddr();
return 0;
}


void FindNum()
{
char* add = data;

printf("%X\n",add);
for(int i=0; i<sizeof(data)/sizeof(char)-4; i++)
{
//*(add+i)
if(*(int*)(add+i) == 0x64)
{
printf("%d\n",i);
printf("[%X]=%d\n", (int*)(add+i), *(int*)(add+i));
}
}
}

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
char names[] = "ABCDE";
==
char arr[6] = {'a','b','b','d','e','0'};
printf("%s\n",arr )

代码区
堆栈区
全局变量区 - 可读可写
常量区 - 可读不可写


char* x = "China"; //char*指向的字符串不能改,因为存储在常量区,char*的地址可以改
char y[] = "China"; //全局区

void func()
{
*(x+1) = "A"; //不能修改常量区 0xC0000005
x = "abx";

y[1] = 'A'; //常量区的内容复制到局部变量中
}

void func()
{
char* x = "China"; //china在常量区,x是china在常量区的首地址
char y[] = "China"; //china在常量区,被拷贝到栈中
*(x+1) = "A"; //0xC0000005

y[1] = 'A'; //修改栈中数据
}


int strlen(char* s) //返回值是s的长度,不包括\0
{
int ret = 0;
while(*(s) != 0)
{

ret++;
s++;
}
return ret;
}

char* strcpy(char* dest, char* src)
{
char* ret = dest;
while(*(dest++) = *(src)++);
return ret;

}

char* strcat(char* dest, char* src)
{
char* ret = dest;
while(*dest != 0)
{
dest++;
}
while(*dest++ = *src++);
return ret;
}

int strcmp(char*s1, char* s2) //一样返回0,不一样返回1
{
while(*s1!=0 && *s2!=0)
{
if(*s1++ != *s2++)
{
return false;
}
}
return true;
}

char* FindRoleNameAddr(char* pData, char* pRoleName) //返回值为指针的为指针函数
{
}


//返回值为带*类型的函数为指针函数


#include "stdafx.h"
#include "string.h"

char data[] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x07,0x09,
0x00,0x20,0x10,0x03,0x03,0x0C,0x00,0x00,0x44,0x00,
0x00,0x33,0x00,0x47,0x0C,0x0E,0x00,0x0D,0x00,0x11,
0x00,0x00,0x00,0x02,0x64,0x00,0x00,0x00,0x0A,0x00,
0x00,0x00,0x64,0x10,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x02,0x00,0x74,0x0F,0x41,0x00,0x00,0x00,
0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x0A,0x00,
0x00,0x02,0x57,0x4F,0x57,0x00,0x06,0x08,0x00,0x00,
0x00,0x00,0x00,0x64,0x00,0x0F,0x00,0x00,0x0D,0x00,
0x00,0x00,0x23,0x00,0x64,0x00,0x00,0x00,0x64,0x00
};

int strlen(char* s)
{
int len = 0;
while(*s != 0)
{
len++;
s++;
}
return len;
}

char* strcpy(char* dest, char* src)
{
char* ret = dest; //需要先保存首地址,后续dest指针会随着循环向后移,但返回时还是返回首地址
while((*dest++) = (*src++)); //这是赋值语句,0也会被复制,先复制在判断
/*
while(*src != 0)
{
*dest = *src;
src++;
dest++;
}
*/
return ret;
}

char* strcat(char* dest, char* src)
{
char* ret = dest;
while(*dest != 0)
{
dest++;
}
while(*dest != 0)
{
*dest = *src;
dest++;
src++;
}
return ret;
}

int strcmp(char* s1, char* s2)
{
while(*s1 !=0 || *s2 != 0)
{
if(*s1 == *s2)
{
s1++;
s2++;
continue;
}
else
{
return 1;
}
}
return 0;
}

char* FindRoleNameAddr(char* pData, char* pRoleName)
{
int total = sizeof(data)/sizeof(data[0]);
int length = strlen(pRoleName);
for(int i=0; i<=total-length; i++)
{
if(*(pData+i) == 0x57)
{
if(strncmp(&pData[i],pRoleName,3) == 0)
return &pData[i];
}
}
return NULL;


}

int main(int argc, char* argv[])
{

char arr2[] = "WOW";
printf("%X\n",data);
printf("%X\n",FindRoleNameAddr(data, arr2));
return 0;
}

image-20230126151524978

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
//指针数组
int*** arr[5];

char a1 = 'A';
char* p1 = &a1;

char* arr = "china";
printf("%s\n",arr); //china

char arr[10] = {'c','h','i','n','a'};
printf("%s\n",arr); //china
0040D7B8 C6 45 F4 63 mov byte ptr [ebp-0Ch],63h
0040D7BC C6 45 F5 68 mov byte ptr [ebp-0Bh],68h
0040D7C0 C6 45 F6 69 mov byte ptr [ebp-0Ah],69h
0040D7C4 C6 45 F7 6E mov byte ptr [ebp-9],6Eh
0040D7C8 C6 45 F8 61 mov byte ptr [ebp-8],61h
0040D7CC 33 C0 xor eax,eax
0040D7CE 89 45 F9 mov dword ptr [ebp-7],eax
0040D7D1 88 45 FD mov byte ptr [ebp-3],al


char arr[5] = {'c','h','i','n','a'};
printf("%s\n",arr); //china烫虉

char arr[5] = {'c','h','i','n','a'};
printf("%c\n",arr); //@

char arr[6] = {'c','h','i','n','a','\0'};
printf("%s\n",arr); //china


char* arr[] = {"if","or","while","for"};

char* p1 = "if"
char* p2 = "for";
char* p3 = "while";
char* p4 = "switch";
char* keyword[] = {p1,p2,p3,p4};

for(int i=0;i<4;i++)
{
printf("%s\n",*(arr+i));
}


image-20230224153206988

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
//结构体指针
struct Stu
{
int a;
int b;
int c;
}

Stu* stu;
stu = (Stu*)100;
stu++; //+12

Stu* Stu1 = (Stu*)20;
int x = stu - stu1;


Stu s;
s.a = 10;
s.b = 20;
s.c = 30;
Stu *ss = &s;
ss->a = 100;


int x = 10;
Stu* s = (Stu*)&x;
printf("%d %d %d",s->a,s->b,s->c); //10,x,x x取决于内存中有什么
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int x1 = 1;
int x2 = 2;
int x3 = 3;

int* arr[3];
arr[0] = &x1;
arr[1] = &x2;
arr[2] = &x3;

char* p1 = "if"
char* p2 = "for";
char* p3 = "while";
char* p4 = "switch";
char* keyword[] = {p1,p2,p3,p4};

for(int i=0;i<4;i++)
{
printf("%s\n",*(arr+i));
}

image-20230127133346425

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
typedef struct TagPlayer
{
int id;
int level;
}Player;

char data[] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x07,0x09,
0x00,0x20,0x10,0x03,0x03,0x0C,0x00,0x00,0x44,0x00,
0x00,0x33,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,
0x00,0x00,0x00,0x02,0x64,0x00,0x00,0x00,0x0A,0x00,
0x00,0x00,0x64,0x10,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x02,0x00,0x74,0x0F,0x41,0x00,0x00,0x00,
0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x0A,0x00,
0x00,0x02,0x57,0x4F,0x57,0x00,0x06,0x08,0x00,0x00,
0x00,0x00,0x00,0x64,0x00,0x0F,0x00,0x00,0x0D,0x00,
0x00,0x00,0x23,0x00,0x64,0x00,0x00,0x00,0x64,0x00
};

void fun()
{
char* haha = data;
for(int i=0; i<sizeof(data)/sizeof(data[0])-sizeof(Player); i++)
{
Player* hehe = (Player*)(haha+i);
if(hehe->id == 0x01 && hehe->level == 0x08)
{
printf("%X\ %d \n",&(hehe->id),hehe->id);
}
}
}

int main(int argc, char* argv[])
{
fun();
return 0;
}
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
55
56
57
58
59
60
61
*(p1+x)  = p1[x]
两种写法一样

char** p2;
*(*(p2+1)) +4
*(*(p2+1)+1) +4+1
*(*(p2+2)+3) == p2[2][3] +8+3

char*** p3;
*(*(*(p3+1)+2)+3) == p3[1][2][3] +4+8+3

//结构体指针
Student* s;

//数组指针
int (*px)[5];
printf("%d\n",sizeof(px)); //4

//赋值
px = ((*px)[5])10;

//*px 指向 int[5]
px++; +20

int arr[16] = {1,2,3,4,5,6,7,8,9,10,11};
int (*px)[2];
px = (int (*px)[2])arr;
*px == &arr[0]
*(*px) == arr[0]
*(*(px)) = 1
*(*(px+1)+1) == 4 +8byte+4byte 1+3
*(*(px+3)+3) == 4 +3*8byte+3*4byte 3*2+3
//px 和 *px 里面的值一样
//但运算时宽度不一样

//指针数组
int* p[5];
//这个数组的所有元素都是指针类型
//数组指针
int (*p)[5];
//这个指针存放着一个数组的首地址,或者说这个指针指向一个数组的首地址。

//int (*p)[5] 可以不指向一个数组

int (*px)[5];
*(*(px+2)+2) 2*4*5+2*4

//二维数组指针
char (*px)[2][3];
*(*(*(px+2)+3)+4) 2*3*2+3*3

char (*px)[2][3][2];
px = (char (*px)[2][3][2])code;
*(*(*(*(px+2)+3)+1)+4)
2*2*3*2*1 + 3*3*2*1 + 1*2*1 + 4*1

int (*pFun)(int,int) //sizeof() = 4 是返回值为int,有两个int参数
pFun++ //illegal 因为宽度不确定
int Function(int a,int b) { }
pFun = Function;
int sb = pFun(1,2);

image-20230225163935045

image-20230225172241524

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
int main(int argc, char* argv[])
{
int arr[] = {1,2,3,4,5,6,7};
int (*px)[5];
px = (int (*)[5])arr;

printf("%X %X\n",*(px+1)[2],px[1][2]);
return 0;
}

0040D768 C7 45 E4 01 00 00 00 mov dword ptr [ebp-1Ch],1
0040D76F C7 45 E8 02 00 00 00 mov dword ptr [ebp-18h],2
0040D776 C7 45 EC 03 00 00 00 mov dword ptr [ebp-14h],3
0040D77D C7 45 F0 04 00 00 00 mov dword ptr [ebp-10h],4
0040D784 C7 45 F4 05 00 00 00 mov dword ptr [ebp-0Ch],5
0040D78B C7 45 F8 06 00 00 00 mov dword ptr [ebp-8],6
0040D792 C7 45 FC 07 00 00 00 mov dword ptr [ebp-4],7
0040D799 8D 45 E4 lea eax,[ebp-1Ch] &arr[0]
0040D79C 89 45 E0 mov dword ptr [ebp-20h],eax
0040D79F 8B 4D E0 mov ecx,dword ptr [ebp-20h]
0040D7A2 8B 51 1C mov edx,dword ptr [ecx+1Ch]
0040D7A5 52 push edx
0040D7A6 8B 45 E0 mov eax,dword ptr [ebp-20h]
0040D7A9 8B 48 3C mov ecx,dword ptr [eax+3Ch]
0040D7AC 51 push ecx
0040D7AD 68 1C 20 42 00 push offset string "%d %d %d\n" (0042201c)
0040D7B2 E8 09 39 FF FF call printf (004010c0)
0040D7B7 83 C4 0C add esp,0Ch

int main(int argc, char* argv[])
{
int arr[] = {1,2,3,4,5,6,7};
int (*px)[5];
px = (int (*)[5])arr;
for(int i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
{
printf("%d\n",*(*(px)+i));
}
return 0;
}

image-20230130142624937

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int (*px)[2];
int (*py)[2][3];
char (*pz)[2];
char (*pk)[2][3];

*(*(px+0)+0) 03020100
*(*(px+1)+0) 20000907
*(*(px+2)+3) 1100
*(*(*(py+1)+2)+3) 01
*(*(pz+2)+3) 07




1
2
3
4
5
6
7
8
9
int *(pFunc)(int,int)
sizeof = 4

int Function(int x, int y)
{}

int (*pFun)(int,int);
pFun = Function;
int x= pFun(1,2);

image-20230130145723603

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
55
56
57
int Func(int x,int y)
{
return x*y;
}

0040D770 55 push ebp
0040D771 8B EC mov ebp,esp
0040D773 83 EC 40 sub esp,40h
0040D776 53 push ebx
0040D777 56 push esi
0040D778 57 push edi
0040D779 8D 7D C0 lea edi,[ebp-40h]
0040D77C B9 10 00 00 00 mov ecx,10h
0040D781 B8 CC CC CC CC mov eax,0CCCCCCCCh
0040D786 F3 AB rep stos dword ptr [edi]
0040D788 8B 45 08 mov eax,dword ptr [ebp+8]
0040D78B 0F AF 45 0C imul eax,dword ptr [ebp+0Ch]
0040D78F 5F pop edi
0040D790 5E pop esi
0040D791 5B pop ebx
0040D792 8B E5 mov esp,ebp
0040D794 5D pop ebp
0040D795 C3 ret
55


unsigned char Func[] = {
0x55,
0x8B, 0xEC,
0x83, 0xEC, 0x40,
0x53,
0x56,
0x57,
0x8D, 0x7D, 0xC0,
0xB9, 0x10, 0x00, 0x00, 0x00,
0xB8, 0xCC, 0xCC, 0xCC, 0xCC,
0xF3, 0xAB,
0x8B, 0x45, 0x08,
0x0F, 0xAF, 0x45, 0x0C,
0x5F,
0x5E,
0x5B,
0x8B, 0xE5,
0x5D,
0xC3
};


int main(int argc, char* argv[])
{
int (*p)(int,int);
p = (int (*)(int,int))&Func;
int x = p(3,4);
printf("%d\n",x);
return 0;
}

# 位运算

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
算术移位
SAL/SAR Reg/Mem,CL/Imm

SAL:算术左移,补0
SAR:算术右移,补符号位

逻辑位移
SHL 补0
SHR 补0

循环位移
ROL
ROR

带进位的循环移位指令
RCL
RCR

2&3 = 2
2|3 = 3
~2 = fd
2^3 = 1

int x
x >> rol
x << shr

# define

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
替换
编译
连接

typedef只能给类型起别名
define可以给任何起别名

define定义的变量不在常量区,在替换的环节直接替换为

#define PI 3.14 //无参数宏
#define MAX(A,B) ((A)>(B)?(A):(B)) //无堆栈调用
#define \ 定义多行

//避免重复包含
#if !define(ZZZ)
#define ZZZ
...
#endif


//stdafx.h

#if !defined(AFX_STDAFX_H__7058845C_68A9_4312_A9CE_A08FAB5CE1F2__INCLUDED_)
#define AFX_STDAFX_H__7058845C_68A9_4312_A9CE_A08FAB5CE1F2__INCLUDED_

#if _MSC_VER > 1000
#pragma once //pragma对编译器兼容不好,如果版本>1000才使用pragma once ,否则就使用上面的方法
#endif // _MSC_VER > 1000

#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers

#include <stdio.h>

# malloc

1
2
3
4
5
6
7
8
9
10
11
#include <stdlib.h>
#include <malloc.h>
"" 先到当前目录,<>先到安装目录

int* ptr;
ptr = (int*)malloc(sizeof(int)*128); //void* malloc(size_t size) void*类型不确定,用的时候强转
if (ptr == NULL) return 0; //无法分配内存的时候返回NULL
memset(ptr,0,sizeof(int)*128); //初始化分配的内存空间
*(ptr) = 1;
free(ptr); //释放堆空间
ptr = NULL;
1
2
3
4
fopen
fseek 设置文件指针
ftell 文件大小
fclose

image-20230201143200063

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
#include "stdafx.h"
#include "stdlib.h"
#include "malloc.h"
#include <string.h>

int main(int argc, char* argv[])
{
FILE *fp = NULL;
fp = fopen("C:\\Windows\\System32\\notepad.exe","rb");

if(fp == NULL) return -1;

fseek(fp,0,SEEK_END);

int len = ftell(fp);



//printf("%d\n",len);
char* ptr;
ptr = (char*)malloc(len);
if(ptr == NULL) return -1;
memset(ptr,0,len);

int c;
while(1)
{
c = fgetc(fp);
if(feof(fp)) break;
printf("%c", c);
}

printf("%X\n",ptr);

free(ptr);
ptr = NULL;
fclose(fp);

return 0;

}

Revival_S_滴水逆向三期,C 语言入门,数据结构 - CSDN 博客

# PE

# PE 结构

1
2
3
4
5
6
7
8
sys dll exe 4D5A开头 MZ的ASCII

PE结构是分节的
1.节省硬盘空间,内存中的空隙会更大(任何exe都有自己的4G空间,2G程序,2G操作系统,通过指针访问低2G,通过驱动访问高2G)
2.对齐:早期程序硬盘对齐200H,内存对齐1000H.现在大部分都是1000H字节对齐,增加读写速度
3.节省内存,不在复制全部节


image-20230203140027163

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
DOS头
WORD e_magic MZ判断是否是可执行文件
DWORD e_lfanew 从文件开始算,偏移e_lfanew个字节就是PE文件开始

NT头
Machine
NumberOfSections
TimeDateStamp
SizeOfOptionalHeader
Characteristics

可选PE头
Magic
SizeOfCode
SizeOfInitializedData
SizeOfUnInitializedData
AddressOfEntryPoint
BaseOfCode
BaseOfData
ImageBase
SectionAlignment;
FileAlignment
SizeOfImage
SizeOfHeaders
CheckSum
SizeOfStackReverse
SizeOfStackCommit
SizeOfHeapReverse
SizeOfHeapCommit
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//x64 regedit.exe
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; 5A4D // Magic number MZ标记用于判断是否是可执行文件
WORD e_cblp; 0090 // Bytes on last page of file
WORD e_cp; 0003 // Pages in file
WORD e_crlc; 0000 // Relocations
WORD e_cparhdr; 0004 // Size of header in paragraphs
WORD e_minalloc; 0000 // Minimum extra paragraphs needed
WORD e_maxalloc; FFFF // Maximum extra paragraphs needed
WORD e_ss; 0000 // Initial (relative) SS value
WORD e_sp; 00B8 // Initial SP value
WORD e_csum; 0000 // Checksum
WORD e_ip; 0000 // Initial IP value
WORD e_cs; 0000 // Initial (relative) CS value
WORD e_lfarlc; 0040 // File address of relocation table
WORD e_ovno; 0000 // Overlay number
WORD e_res[4]; 0000 0000 0000 0000 // Reserved words
WORD e_oemid; 0000 // OEM identifier (for e_oeminfo)
WORD e_oeminfo; 0000 // OEM information; e_oemid specific
WORD e_res2[10]; 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 // Reserved words
DWORD e_lfanew; 0000 00F8 // File address of new exe header PE头相对于文件的偏移,定位PE文件
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
//0x40



typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; 00004550
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
//E0+14+4=0xF8

typedef struct _IMAGE_FILE_HEADER {
WORD Machine; 8664 //程序运行的CPU型号,0x0任何处理器,0x14C 386及后续处理器
WORD NumberOfSections; 0007 //文件中节的总数,要新增或者合并节,修改值
DWORD TimeDateStamp; AC6AAA87 //时间戳:文件创建时间,编译器填写,可以用于判断map文件生成时间
DWORD PointerToSymbolTable; 00000000
DWORD NumberOfSymbols; 00000000
WORD SizeOfOptionalHeader; 00F0 //可选PE头大小,32位默认E0H,64位默认F0,大小可以自定义
WORD Characteristics; 0022 //可执行文件为10F,每位有不同含义
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
//0x14


typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; 020B //10B 32位下PE 20B 64位下pe
BYTE MajorLinkerVersion; 0E
BYTE MinorLinkerVersion; 14
DWORD SizeOfCode; 00023400 //所有 节的和,必须是FileAlignment的整数倍,编译器填的没用
DWORD SizeOfInitializedData; 00079400 //已初始化数据大小的和必须是FileAlignment的整数倍,编译器填的没用
DWORD SizeOfUninitializedData; 00000000 //未初始化数据大小的和必须是FileAlignment的整数倍,编译器填的没用
DWORD AddressOfEntryPoint; 00023520 //程序入口,但在内存中真正的入口在imagebase+OEP,一个exe中包含多个pe文件,且exe有独立空间
DWORD BaseOfCode; 00001000 //代码开始的基址
DWORD BaseOfData; 40000000 //数据开始的基址
DWORD ImageBase; 00000100 //内存镜像地址,内存中数据是哪里开始的
DWORD SectionAlignment; 00001000 //内存对齐
DWORD FileAlignment; 00000002 //文件对齐
WORD MajorOperatingSystemVersion; 000A
WORD MinorOperatingSystemVersion; 0000
WORD MajorImageVersion; 000A
WORD MinorImageVersion; 0000
WORD MajorSubsystemVersion; 000A
WORD MinorSubsystemVersion; 0000
DWORD Win32VersionValue; 00000000
DWORD SizeOfImage; 000A2000 //内存中整个PE文件的映射的尺寸,必须是FileAlignment的整数倍
DWORD SizeOfHeaders; 00000400 //所有头+节表按照文件对齐后的大小
DWORD CheckSum; 0005D8CD
WORD Subsystem; 0020
WORD DllCharacteristics; C160
DWORD SizeOfStackReserve; 00080000 //初始化时保留的堆栈大小
DWORD SizeOfStackCommit; 00000000
DWORD SizeOfHeapReserve; 00004000
DWORD SizeOfHeapCommit; 00000000
DWORD LoaderFlags; 00100000
DWORD NumberOfRvaAndSizes; 00000000 //目录项目数
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
//0xE0
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
//Unpacker.exe
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE header
WORD e_magic; 5A4D // Magic number MZ标记用于判断是否是可执行文件
WORD e_cblp; 0090 // Bytes on last page of file
WORD e_cp; 0003 // Pages in file
WORD e_crlc; 0000 // Relocations
WORD e_cparhdr; 0004 // Size of header in paragraphs
WORD e_minalloc; 0000 // Minimum extra paragraphs needed
WORD e_maxalloc; FFFF // Maximum extra paragraphs needed
WORD e_ss; 0000 // Initial (relative) SS value
WORD e_sp; 00B8 // Initial SP value
WORD e_csum; 0000 // Checksum
WORD e_ip; 0000 // Initial IP value
WORD e_cs; 0000 // Initial (relative) CS value
WORD e_lfarlc; 0040 // File address of relocation table
WORD e_ovno; 0000 // Overlay number
WORD e_res[4]; 0000 0000 0000 0000 // Reserved words
WORD e_oemid; 0000 // OEM identifier (for e_oeminfo)
WORD e_oeminfo; 0000 // OEM information; e_oemid specific
WORD e_res2[10]; 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 // Reserved words
DWORD e_lfanew; 0000 0108 // File address of new exe header PE头相对于文件的偏移,定位PE文件
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
//0x40



typedef struct _IMAGE_NT_HEADERS {
DWORD Signature; 00004550
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
//

typedef struct _IMAGE_FILE_HEADER {
WORD Machine; 014C //程序运行的CPU型号,0x0任何处理器,0x14 386及后续处理器
WORD NumberOfSections; 0004 //文件中节的总数,要新增或者合并节,修改值
DWORD TimeDateStamp; 491D4B09 //时间戳:文件创建时间,编译器填写
DWORD PointerToSymbolTable; 00000000
DWORD NumberOfSymbols; 00000000
WORD SizeOfOptionalHeader; 00E0 //可选PE投大小,32位默认E0H,64位默认F0,大小可以自定义
WORD Characteristics; 010F //可执行文件尾10F
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
//0x14


typedef struct _IMAGE_OPTIONAL_HEADER {
WORD Magic; 10B //10B32位下PE 20B64位下pe
BYTE MajorLinkerVersion; 06
BYTE MinorLinkerVersion; 00
DWORD SizeOfCode; 11000 //所有代码节的和,必须是FileAlignment的整数倍,编译器填的没用
DWORD SizeOfInitializedData; 2F000 //已初始化数据大小的和必须是FileAlignment的整数倍,编译器填的没用
DWORD SizeOfUninitializedData; 00000000 //未初始化数据大小的和必须是FileAlignment的整数倍,编译器填的没用
DWORD AddressOfEntryPoint; 10D4F //程序入口
DWORD BaseOfCode; 00001000 //代码开始的基址
DWORD BaseOfData; 12000 //数据开始的基址
DWORD ImageBase; 400000 //内存镜像地址
DWORD SectionAlignment; 00001000 //内存对齐
DWORD FileAlignment; 00001000 //文件对齐
WORD MajorOperatingSystemVersion; 0004
WORD MinorOperatingSystemVersion; 0000
WORD MajorImageVersion; 0000
WORD MinorImageVersion; 0000
WORD MajorSubsystemVersion; 0004
WORD MinorSubsystemVersion; 0000
DWORD Win32VersionValue; 00000000
DWORD SizeOfImage; 41000 //内存中整个PE文件的映射的尺寸,必须是FileAlignment的整数倍,即拉伸后大小,imagebuffer
DWORD SizeOfHeaders; 1000 //所有头+节表按照文件对齐后的大小,严格按照FileAlignment对齐
DWORD CheckSum; 00000000 //每两个字节相加,存到checksum,存不下自然溢出
WORD Subsystem; 0002
WORD DllCharacteristics; 0000
DWORD SizeOfStackReserve; 100000 //初始化时保留的堆栈大小
DWORD SizeOfStackCommit; 1000
DWORD SizeOfHeapReserve; 100000
DWORD SizeOfHeapCommit; 1000
DWORD LoaderFlags; 00000000
DWORD NumberOfRvaAndSizes; 00000010 //目录项目数
IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
//E0

image-20230203150643470

image-20230204150314850

将文件读取到内存中,此时称 FileBuffer,如果内存对齐和文件对齐一样,那么不拉伸,但还是不能直接加载

如果两者不一样,需要先在内存中进行拉伸,此事成为 image buffer

image buffer 从 imagebase 开始

imagebase 用于模块对齐和保护指针

image-20230301170240621

正常情况下 OEP 在 text 代码段中,OEP 真正的位置 = OEP + imagebase

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include "stdafx.h"
#include "stdlib.h"
#include "malloc.h"
#include <string.h>
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <Windows.h>


#define FILEPATH "C:\\Users\\shinya\\Desktop\\Unpacker.exe"

LPVOID ReadPELoader()
{
int len = 0;
FILE *fp = NULL;
LPVOID pFileBuffer = NULL;

fp = fopen(FILEPATH, "rb");

if (fp == NULL)
{
printf("fp = NULL");
return NULL;
}

fseek(fp, 0, SEEK_END);

len = ftell(fp);

fseek(fp, 0, SEEK_SET);

pFileBuffer = malloc(len);
if (!pFileBuffer)
{
printf("ptr = NULL");
fclose(fp);
return NULL;
}

size_t reader = fread(pFileBuffer, len, 1, fp);

if (!reader)
{
printf("reader= 0");
free(pFileBuffer);
pFileBuffer = NULL;
fclose(fp);

}

printf("%s\n", pFileBuffer);
fclose(fp);
return pFileBuffer;


}

void PrintNTHeader()
{
LPVOID pFileBuffer = NULL;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pPEHeader = NULL;
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;

pFileBuffer = ReadPELoader();

if (!pFileBuffer)
{
printf("文件读取失败");
return;
}


if (*((PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
{
printf("不是有效mz标识");
free(pFileBuffer);
return;
}
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;

printf("MZ标志:%X\n", pDosHeader->e_magic);
printf("PE偏移:%X\n", pDosHeader->e_lfanew);

//判断是否是有效的PE标志
if (*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE标志\n");
free(pFileBuffer);
return;
}

//打印NT头
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);

printf("NT:%X\n", pNTHeader->Signature);
pPEHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4);

printf("PE:%X\n", pPEHeader->Machine);
printf("节的数量:%X\n", pPEHeader->NumberOfSections);
printf("SizeOfOptionalHeader:%X\n", pPEHeader->SizeOfOptionalHeader);

//可选PE头
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + IMAGE_SIZEOF_FILE_HEADER);
printf("Optional_PE:%X\n", pOptionHeader->Magic);

free(pFileBuffer);
}


int main(int argc, char* argv[])
{
PrintNTHeader();

return 0;

}

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
LPVOID OpenFile(char* FilePath)
{
FILE* fp;
fp = fopen(FilePath,"rb");

if(fp == NULL) return NULL;

fseek(fp,0,SEEK_END);

//获得文件指针长度
int len = ftell(fp);

fseek(fp,0,SEEK_SET);

//开辟堆区
LPVOID ptr;
ptr = (LPVOID)malloc(len);
if(ptr == NULL)
{
cout<< "ptr == NULL" << "\n";
free(ptr);
fclose(fp);
return NULL;
}

//初始化
memset(ptr,0,len);

//读文件
size_t reader = fread(ptr,len,1,fp);

if(reader == NULL)
{
cout<< "reader == NULL" << "\n";
free(ptr);
fclose(fp);
}

fclose(fp);

return ptr;
}

void ResolvePEHeader(char* FilePath)
{
LPVOID pFileBuffer = (LPVOID)OpenFile(FilePath);

if(pFileBuffer == NULL)
{
printf("pFileBuffer == NULL\n");
return;
}

PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;



if (*(PWORD)pDosHeader != IMAGE_DOS_SIGNATURE)
{
printf("not a valid mz signature\n");
free(pFileBuffer);
return;
}
printf("valid mz signature\n");
printf("---------DOS header---------\n");
printf("DOS header: e_magic=%X\n",pDosHeader->e_magic);
printf("DOS header: e_lfanew=%X\n",pDosHeader->e_lfanew);


pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
printf("---------NT header---------\n");
printf("NT header: Signature=%X\n",pNTHeader->Signature);


pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
printf("---------File header---------\n");
printf("File header: Machine=%X\n",pFileHeader->Machine);
printf("File header: NumberOfSections=%X\n",pFileHeader->NumberOfSections);
printf("File header: TimeDateStamp=%X\n",pFileHeader->TimeDateStamp);
printf("File header: SizeOfOptionalHeader=%X\n",pFileHeader->SizeOfOptionalHeader);
printf("File header: Characteristics=%X\n",pFileHeader->Characteristics);


pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
printf("---------Optional header---------\n");
printf("Optional header: Magic=%X\n",pOptionHeader->Magic);
printf("Optional header: SizeOfCode=%X\n",pOptionHeader->SizeOfCode);
printf("Optional header: AddressOfEntryPoint=%X\n",pOptionHeader->AddressOfEntryPoint);
printf("Optional header: BaseOfCode=%X\n",pOptionHeader->BaseOfCode);
printf("Optional header: BaseOfData=%X\n",pOptionHeader->BaseOfData);
printf("Optional header: ImageBase=%X\n",pOptionHeader->ImageBase);
printf("Optional header: SectionAlignment=%X\n",pOptionHeader->SectionAlignment);
printf("Optional header: FileAlignment=%X\n",pOptionHeader->FileAlignment);
printf("Optional header: SizeOfImage=%X\n",pOptionHeader->SizeOfImage);
printf("Optional header: SizeOfHeaders=%X\n",pOptionHeader->SizeOfHeaders);
printf("Optional header: CheckSum=%X\n",pOptionHeader->CheckSum);


free(pFileBuffer);
}

void test()
{
char* FilePath = "C:\\Users\\shinya\\Desktop\\Unpacker.exe";
ResolvePEHeader(FilePath) ;
}
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
union Test     //类型
{
char x;
int y;
};
sizeof取成员变量中最大的

1、联合体的成员是共享内存空间的中
2、联合体的内存空间大小是联合体成员中对内存空间大小要求最大的空间大小
3、联合体最多只有一个成员有效

Test test;
test.y = 0x12345678;
printf("%X\n",test.x); //0x78
printf("%X\n",test.y); //0x12345678

union
{
char x;
int y;
}Testhaha; //变量
Testhaha.y = 0x12345678;
printf("%d",Testhaha) //0x12345678

结构体也可以这么写
struct
{
int x;
int y;
}shabi;

# 节表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
typedef struct _IMAGE_SECTION_HEADER {
BYTE Name[IMAGE_SIZEOF_SHORT_NAME];// 节表名称,如“.text” ,如果不是以\0结尾,系统会截取8个字节长度处理
//IMAGE_SIZEOF_SHORT_NAME=8,创建长度为9的数组,最后以0结尾,不要用char*
union {
// 共用体,也叫联合体,在一个“联合”内可以定义多种不同的数据类型,
//一个被说明为该“联合”类型的变量中,允许装入该“联合”所定义的任何一种数据,这些数据共享同一段内存,
//以达到节省空间的目的。union变量所占用的内存长度等于最长的成员的内存长度。
DWORD PhysicalAddress; //当前节的名称 , 占8字节
DWORD VirtualSize; //内存中文件对齐大小
} Misc; //对齐前的真实尺寸,可以不准确,没有程序照样运行,misc可以大于sizeofrawdata
DWORD VirtualAddress; //在内存中的偏移地址 +imageOfBase=真正的位置
DWORD SizeOfRawData; //当前节在文件中对齐后的大小 也就是节点的大小
DWORD PointerToRawData; //当前节在文件中的偏移 也就是文件的开始位置,一定是文件对齐的整数倍
DWORD PointerToRelocations; // 调试相关
DWORD PointerToLinenumbers;// 调试相关
WORD NumberOfRelocations;// 调试相关
WORD NumberOfLinenumbers;// 调试相关
DWORD Characteristics; //文件属性,比如该节数据属性是否为可执行属性,都在这里面
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

image-20230205140709262

image-20230302175633767

MISC = a - b

image-20230205140950190

image-20230205143439207

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
标志(属性块) 常用特征值对照表:

[值:00000020h] [IMAGE_SCN_CNT_CODE // Section contains code.(包含可执行代码)]
[值:00000040h] [IMAGE_SCN_CNT_INITIALIZED_DATA // Section contains initialized data.(该块包含已初始化的数据)]
[值:00000080h] [IMAGE_SCN_CNT_UNINITIALIZED_DATA // Section contains uninitialized data.(该块包含未初始化的数据)]
[值:00000200h] [IMAGE_SCN_LNK_INFO // Section contains comments or some other type of information.]
[值:00000800h] [IMAGE_SCN_LNK_REMOVE // Section contents will not become part of image.]
[值:00001000h] [IMAGE_SCN_LNK_COMDAT // Section contents comdat.]
[值:00004000h] [IMAGE_SCN_NO_DEFER_SPEC_EXC // Reset speculative exceptions handling bits in the TLB entries for this section.]
[值:00008000h] [IMAGE_SCN_GPREL // Section content can be accessed relative to GP.]
[值:00500000h] [IMAGE_SCN_ALIGN_16BYTES // Default alignment if no others are specified.]
[值:01000000h] [IMAGE_SCN_LNK_NRELOC_OVFL // Section contains extended relocations.]
[值:02000000h] [IMAGE_SCN_MEM_DISCARDABLE // Section can be discarded.]
[值:04000000h] [IMAGE_SCN_MEM_NOT_CACHED // Section is not cachable.]
[值:08000000h] [IMAGE_SCN_MEM_NOT_PAGED // Section is not pageable.]
[值:10000000h] [IMAGE_SCN_MEM_SHARED // Section is shareable(该块为共享块).]
[值:20000000h] [IMAGE_SCN_MEM_EXECUTE // Section is executable.(该块可执行)]
[值:40000000h] [IMAGE_SCN_MEM_READ // Section is readable.(该块可读)]
[值:80000000h] [IMAGE_SCN_MEM_WRITE // Section is writeable.(该块可写)]

手动画图列数据

写程序解析节表

使用 notepad

1
2
3
4
5
6
7
8
9
10
11
12
13
DOS头 00h-40h
40H-F7 垃圾数据
F8-FB signature
FC-10F 标准PE头 NumberOfSection = 7 sizeofoptionalheader = 0xF0
110H-1FF 可选PE头
200-227 节表1 .text 025E00 400
228-24F 节表2 .rdata 9A00 26200
250-277 节表3 .data 0E00 2FC00
278-29F 节表4 .pdata 1200 30A00
2A0-2C7 节表5 .didat 0200 31C00
2C8-2ef 节表6 .rsrc 0C00 31E00
2f0-317 节表7 .reloc 0400 32A00

Unpacker.exe

1
2
3
4
5
6
7
8
9
10
11
12
13
DOS头 00h-39h
垃圾数据 40h-107h
NT头 108h-
file header 10Ch-11f
optional header 120-1ff
section
200 - 227
.text
misc 10AF0
VA 1000
soraw 11000
ptrd 1000
char 60000020
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);
while(pFileHeader->NumberOfSections > 0)
{
unsigned char name[9] = {0};
for(int i=0;i<8;i++)
{
name[i] = pSectionHeader->Name[i];
}
printf("---------SECTION_HEADER---------\n");
printf("Section header: Name=%s\n", name);
printf("Section header: Misc=%X\n", pSectionHeader->Misc);
printf("Section header: VirtualAddress=%X\n", pSectionHeader->VirtualAddress);
printf("Section header: SizeOfRawData=%X\n", pSectionHeader->SizeOfRawData);
printf("Section header: PointerToRawData=%X\n", pSectionHeader->PointerToRawData);
printf("Section header: Characteristics=%X\n", pSectionHeader->Characteristics);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
pFileHeader->NumberOfSections--;
}

image-20230303182144938

sizeofheader = 所有头 + 节表 文件对齐后大小

MISC 可能大于 sizeofrawdata,含有未初始化的数据,比如 char arr [1000]; 文件中不分配,但内存中分配

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
FileBuffer -> ImageBuffer

1.分配新的内存,分配sizeofimage大小,初始化为0 (FileBuffer是按照文件大小分的,即ftell得出的尺寸)

----接下来分块移动到内存中

2.复制sizeofheaders,注意内存和文件的对齐,即内存中headers之后不一定直接是节

3.循环复制节,从pintertorawdata开始,复制sizeofrawdata大小,复制到virtualaddress

ImageBuffer -> NewBuffer
1.开辟最后一个节的pointertorawdaata + sizeofrawdata

2.复制头

3.把virtualaddress 复制到 pointertorawdata ,大小为sizeofrawdata

空闲节添加代码
1/读到filebuffer
2.到imagebuffer
3.判断空闲区是否够
4.判断加哪 imagebase + VA + MISC
5.复制shellcode
6.修正E8 x-imagebuffer+imagebase
7.修正E9
8.修改OEP
9.image -> nnew
10.存盘

如果不往代码区里填,需要改属性,判断拉伸后剩余空间是否够
在任意节里添加,封装函数
属性通过 | 取并集

image-20230206140901033

用函数写

1
2
3
4
5
6
7
8
9
DWORD ReadPEFile(IN LPSTR lpszFile, OUT LPVOID* pFileBuffer); //失败返回0,否则实际读取大小

DWORD CopyFileBufferToImageBuffer(IN LPVOID PFileBuffer, OUT LPVOID* pImageBuffer); //失败返回0,否则返回复制大小

...

bool MemeryToFile(IN LPVOID pMemBuffer, IN SIZE_T size, OUT LPSTR lpszFile);

DWORD RvaToFileOffset(IN LPVOID pFileBuffer, IN DWORD dwRva); //dwrva rva的值,返回转换后的rva的值,是吧返回0

2. 将 RVA 转为 FOA RVA 相对偏移地址就是下面的 1234,即内存中相对的偏移

FOA 文件中偏移,即下面的 634

imagebuffer 中并不能运行,还需要做一些别的工作

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
imagebuffer中为501234的节在文件中什么位置 ,malloc返回500000
imagebuffer开始的地址时malloc分的地址

1.用501234-500000 = 1234

2.循环判断1234在那个节中,大于第n个节virtualaddress,小于第n个节的virtualaddress+misc.virtualsize

3.1234-第n个节表 = 1234 - 1000 = 234,即距离当前节开始的偏移

4.pointertorawdata + 234 = 400 + 234

DWORD RvaToFileOffset(IN LPVOID pFileBuffer, IN char* FilePath, DWORD dwRva)
{


PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);

printf("pFileBuffer: %X\n", pFileBuffer); //450068
printf("DWRVA: %X\n", dwRva);
DWORD MemOffset = (dwRva - (DWORD)pFileBuffer); //45C29C - 450068 = C234
printf("MemOffset: %X\n", MemOffset);
int NumOfSec = 0;


DWORD thisVA = 0;
DWORD thisFile = 0;
while (pFileHeader->NumberOfSections > 0)
{
if ((DWORD)MemOffset > (DWORD)pSectionHeader->VirtualAddress && (DWORD)MemOffset < (DWORD)pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize)
{
thisVA = pSectionHeader->VirtualAddress;
thisFile = pSectionHeader->PointerToRawData;
printf("thisVA:%X\n",thisVA); //C000
break;
}
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
pFileHeader->NumberOfSections--;
NumOfSec++;
}

printf("NumOfSec:%d\n", NumOfSec+1);


DWORD thisoffset = MemOffset - thisVA; //C234-C000 = 234;

printf("thisoffset:%X\n",thisoffset);

DWORD FilePos = thisoffset + thisFile; // 234 + AC00 = AE34

printf("FilePos:%X\n",FilePos);

return FilePos;
}

void test()
{
//char* FilePath = "C:\\Windows\\System32\\notepad.exe";
char* FilePath = "C:\\Users\\shinya\\Desktop\\Unpacker.exe";
LPVOID pFileBuffer = (LPVOID)OpenFile(FilePath);

//ResolvePEHeader(pFileBuffer, FilePath);
RvaToFileOffset(pFileBuffer, FilePath,((DWORD)pFileBuffer+0x19543));
}

image-20230303212608618

# 代码节空白区添加代码

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
12:       MessageBox(0,0,0,0);
004011A8 8B F4 mov esi,esp
004011AA 6A 00 push 0
004011AC 6A 00 push 0
004011AE 6A 00 push 0
004011B0 6A 00 push 0
004011B2 FF 15 E4 F2 43 00 call dword ptr [__imp__MessageBoxA@16 (0043f2e4)]

75EBFD1E 8B FF mov edi,edi
75EBFD20 55 push ebp
75EBFD21 8B EC mov ebp,esp
75EBFD23 6A 00 push 0
75EBFD25 FF 75 14 push dword ptr [ebp+14h]
75EBFD28 FF 75 10 push dword ptr [ebp+10h]
75EBFD2B FF 75 0C push dword ptr [ebp+0Ch]
75EBFD2E FF 75 08 push dword ptr [ebp+8]
75EBFD31 E8 A0 FF FF FF call 75EBFCD6
75EBFD36 5D pop ebp
75EBFD37 C2 10 00 ret 10h

call E8
jmp E9




7650FD1E > 8BFF mov edi,edi
7650FD20 55 push ebp
7650FD21 8BEC mov ebp,esp
7650FD23 6A 00 push 0x0
7650FD25 FF75 14 push dword ptr ss:[ebp+0x14]
7650FD28 FF75 10 push dword ptr ss:[ebp+0x10]
7650FD2B FF75 0C push dword ptr ss:[ebp+0xC]
7650FD2E FF75 08 push dword ptr ss:[ebp+0x8]
7650FD31 E8 A0FFFFFF call MessageBoxExA
7650FD36 5D pop ebp
7650FD37 C2 1000 retn 0x10

760634D0 <user32.MessageB | 8BFF | mov edi,edi |
760634D2 | 55 | push ebp |
760634D3 | 8BEC | mov ebp,esp |
760634D5 | 833D 948C0876 00 | cmp dword ptr ds:[76088C94],0 |
760634DC | 74 22 | je user32.76063500 |
760634DE | 64:A1 18000000 | mov eax,dword ptr fs:[18] |
760634E4 | BA 9C910876 | mov edx,user32.7608919C |
760634E9 | 8B48 24 | mov ecx,dword ptr ds:[eax+24] |
760634EC | 33C0 | xor eax,eax |
760634EE | F0:0FB10A | lock cmpxchg dword ptr ds:[edx],ecx |
760634F2 | 85C0 | test eax,eax |
760634F4 | 75 0A | jne user32.76063500 |
760634F6 | C705 008D0876 01000000 | mov dword ptr ds:[76088D00],1 |
76063500 | 6A FF | push FFFFFFFF |
76063502 | 6A 00 | push 0 |
76063504 | FF75 14 | push dword ptr ss:[ebp+14] |
76063507 | FF75 10 | push dword ptr ss:[ebp+10] |
7606350A | FF75 0C | push dword ptr ss:[ebp+C] |
7606350D | FF75 08 | push dword ptr ss:[ebp+8] |
76063510 | E8 3B020000 | call <user32.MessageBoxTimeoutA> |
76063515 | 5D | pop ebp |
76063516 | C2 1000 | ret 10 |

E8 3B020000 | call <user32.MessageBoxTimeoutA>
E9 ???????? | jmp ????????

跳转的地址 = E8这条指令下一行地址 + X X = 跳转的地址 - E8这条指令下一行地址
跳转的地址 = E8当前的地址 + 5 + X X = 跳转的地址 - (E8的地址 + 5)


6A 00 6A 00 6A 00 6A 00 E8 00 00 00 00 E9 00 00 00 00

ImageBase = 400000
400000 + 10D4F = 410D4F
410D4F-411b02
760634D0 - 411afd
11af0

1.将节中多余部分改写为6A 00 6A 00 6A 00 6A 00 E8 00 00 00 00 E9 00 00 00 00
2.计算E8 和 E9之后的地址
跳转的地址 = E8这条指令下一行地址 + X
E8 = 760634D0 - 411afd (411afd为E9的地址,就是E8的下一条地址)
E9 = 410D4F - 411b02 (imagebase+OEP)-(imagebase+E9下一条地址)
3.更改OEP为我们代码开始的地址 ,不需要加imagebase

程序开始时先执行我们的代码,执行结束后在由我们写的jmp跳转到OEP,执行正常的程序
MessageBox = 76E534D0
E9 = 11afd
76E534D0 - (400000+11afd) = 76A419D3
E9->next = 11b02
410D4F - 411b02


MessageBox = 75BDFD1E
E9 = b5fd
75BDFD1E - 100b5fd = 74BD 4721
E9->next = b602
3689 - b602 =FFFF 8087
1
2
3
4
5
6
7
8
9
10
11
创建新节
1.新增一个节
2.在新节后面填充一个节大小的00
3.修改PE头中节的数量
4.修改sizeofimage
5.原有数据后新增内存对齐整数倍的一个节
6.修改节表

notepad中节表后面存在非零的有用数据,所以需要把nt,file,option,节表提升抬升,提升到垃圾数据中

如果提升也不能用就需要扩大节,只能扩大最后一个节
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
扩大节
1.拉伸到内存 -> filebuffer -> imagebuffer
2.分配一块显得空间,newimagebuffer,大小为 sizeofimage + x
3.将最后一个接的sizeofrawdata和virtualsize 改为N N=内存对齐后尺寸+新增大小
3.修改sizeofimage大小
扩大节:

1、拉伸到内存(只是逻辑上,实际代码操作并不需要这一步),需要注入的代码为 ShellCode

2、分配一块新的空间:SizeOfImage + sizeof ( ShellCode)

3、扩大节,修改最后一个节的 SizeOfRawData 和 VirtualSize

OrgSecSize = max ( SizeOfRawData 或 VirtualSize内存对齐后的值 )

∵ 有些初始化数据未全部写入文件,VirtualSize 可能比 SizeOfRawData 大,必须保证添加的代码不影响到原程序

VirtualSize = OrgSecSize + sizeof ( ShellCode)

SizeOfRawData = ( OrgSecSize + sizeof(ShellCode) ) 按文件对齐后的值

4、修改SizeOfImage大小

SizeOfImage = (最后一节 VirtualAddress + VirtualSize) 按内存对齐后的值


合并节 可以节省节表
0.拉伸到内存
1.修改sectionnumber
2.将第一个节内存大小,文件大小改成一样
VirtualSize=sizeofrawdata=sizeofimage-virtualaddress[0]=VA[-1]+max{sizeofraw,VSize}-headers(内存对齐后)
3.virtualaddress不改 pointertorawdata 不改
4.该属性
1
2
3
4
扩大最后一个节
合并所有节
输出全部目录
返回对齐后大小 Align(int x, int y);

image-20230307192218718

1
2
3
4
5
6
image_directory 
记录编译器生成的数据在哪里

#define IIAGE_NUMBEROF_DIRECTORY_ENTRIES 16
分别是:导出表、导入表、资源表、异常信息表、安全证书表、重定位表、调试信息表、版权所以表、全局指针表TLS表、加载配置表、绑定导入表、IAT表、延迟导入表、COM信息表最后一个保留未使用。

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
//关于节表的所有实验:
//千万不要做黑盒!!!
DWORD ReadPEFile(IN LPSTR lpszFile, OUT LPVOID* pFileBuffer)
{
FILE* fp;
fp = fopen((LPSTR)lpszFile, "rb");

if(fp == NULL) return -1;

fseek(fp,0,SEEK_END);

//获得文件指针长度
int len = ftell(fp);

fseek(fp,0,SEEK_SET);

//开辟堆区
LPVOID ptr = NULL;
ptr = (LPVOID)malloc(len);
if(ptr == NULL)
{
cout<< "ptr == NULL" << "\n";
free(ptr);
fclose(fp);
return NULL;
}

//初始化
memset(ptr,0,len);

//读文件
size_t reader = fread(ptr,len,1,fp);

if(reader == NULL)
{
cout<< "reader == NULL" << "\n";
free(ptr);
fclose(fp);
return NULL;
}

//传出参数复制
*pFileBuffer = ptr;
ptr = NULL;

fclose(fp);

return len;
}


void ResolvePEHeader(IN LPVOID pFileBuffer)
{

if(pFileBuffer == NULL)
{
printf("pFileBuffer == NULL\n");
return;
}

//定义PE结构
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;


//判断MZ标记
if (*(PWORD)pDosHeader != IMAGE_DOS_SIGNATURE)
{
printf("not a valid mz signature\n");
free(pFileBuffer);
return;
}
//printf("valid mz signature\n");
//DOS头
printf("---------DOS header---------\n");
printf("DOS header: e_magic=%X\n",pDosHeader->e_magic);
printf("DOS header: e_lfanew=%X\n",pDosHeader->e_lfanew);

//NT header
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
printf("---------NT header---------\n");
printf("NT header: Signature=%X\n",pNTHeader->Signature);


//file header
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
printf("---------File header---------\n");
printf("File header: Machine=%X\n",pFileHeader->Machine);
printf("File header: NumberOfSections=%X\n",pFileHeader->NumberOfSections);
printf("File header: TimeDateStamp=%X\n",pFileHeader->TimeDateStamp);
printf("File header: SizeOfOptionalHeader=%X\n",pFileHeader->SizeOfOptionalHeader);
printf("File header: Characteristics=%X\n",pFileHeader->Characteristics);

//optional header
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
printf("---------Optional header---------\n");
printf("Optional header: Magic=%X\n",pOptionHeader->Magic);
printf("Optional header: SizeOfCode=%X\n",pOptionHeader->SizeOfCode);
printf("Optional header: AddressOfEntryPoint=%X\n",pOptionHeader->AddressOfEntryPoint);
printf("Optional header: BaseOfCode=%X\n",pOptionHeader->BaseOfCode);
printf("Optional header: BaseOfData=%X\n",pOptionHeader->BaseOfData);
printf("Optional header: ImageBase=%X\n",pOptionHeader->ImageBase);
printf("Optional header: SectionAlignment=%X\n",pOptionHeader->SectionAlignment);
printf("Optional header: FileAlignment=%X\n",pOptionHeader->FileAlignment);
printf("Optional header: SizeOfImage=%X\n",pOptionHeader->SizeOfImage);
printf("Optional header: SizeOfHeaders=%X\n",pOptionHeader->SizeOfHeaders);
printf("Optional header: CheckSum=%X\n",pOptionHeader->CheckSum);

//section header
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);
while(pFileHeader->NumberOfSections > 0)
{
//防止在name=8个字符时产生越界
unsigned char name[9] = {0};
for(int i=0;i<8;i++)
{
name[i] = pSectionHeader->Name[i];
}
//section header print
printf("---------SECTION_HEADER---------\n");
printf("Section header: Name=%s\n", name);
printf("Section header: Misc=%X\n", pSectionHeader->Misc);
printf("Section header: VirtualAddress=%X\n", pSectionHeader->VirtualAddress);
printf("Section header: SizeOfRawData=%X\n", pSectionHeader->SizeOfRawData);
printf("Section header: PointerToRawData=%X\n", pSectionHeader->PointerToRawData);
printf("Section header: Characteristics=%X\n", pSectionHeader->Characteristics);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
pFileHeader->NumberOfSections--;
}

free(pFileBuffer);
}


DWORD RvaToFileOffset(IN LPVOID pFileBuffer, DWORD dwRva)
{
LPVOID pImageBuffer = NULL;
//从filebuffer - imagebuffer
DWORD copySizeFromFile = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);



//define pe structure
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);

#if 0
PIMAGE_SECTION_HEADER pSectionTemp = pSectionHeader;
if (dwRva <= pOptionHeader->SizeOfHeaders)
return (DWORD)dwRva;
else
{
for (int n = 0; n < pFileHeader->NumberOfSections; n++, pSectionTemp++)
{ //判断 : 文件对齐+文件偏移>file_panyi>文件偏移 (即是在文件的哪个节中)
if ((dwRva >= pSectionTemp->VirtualAddress) && (dwRva < pSectionTemp->VirtualAddress + pSectionTemp->Misc.VirtualSize))
{
return dwRva - pSectionTemp->VirtualAddress + pSectionTemp->PointerToRawData;
}
}
}
return 0;
#endif
#if 1
//printf("pFileBuffer: %X\n", pFileBuffer); //450068
//printf("DWRVA: %X\n", dwRva);
//DWORD MemOffset = (dwRva - (DWORD)pImageBuffer); //45C29C - 450068 = C234,内存中的rva
DWORD MemOffset = (dwRva);
//printf("MemOffset: %X\n", MemOffset);

//当前RVA在哪个节中
int NumOfSec = 0;
//RVA所在节的VirtualAddress
DWORD thisVA = 0;
//RVA所在节的PointerToRawData
DWORD thisFile = 0;
//距离当前节开始的便宜
DWORD thisoffset = 0;
//在文件中的FOA
DWORD FilePos = 0;
//判断在哪个节的条件
while (pFileHeader->NumberOfSections > 0)
{
if ((DWORD)MemOffset > (DWORD)pSectionHeader->VirtualAddress && (DWORD)MemOffset < (DWORD)pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize)
{
thisVA = pSectionHeader->VirtualAddress;
thisFile = pSectionHeader->PointerToRawData;
//printf("thisVA:%X\n",thisVA); //C000
break;
}
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
pFileHeader->NumberOfSections--;
NumOfSec++;
}

//printf("NumOfSec:%d\n", NumOfSec+1);

thisoffset = MemOffset - thisVA; //C234-C000 = 234;

//printf("thisoffset:%X\n",thisoffset);

FilePos = thisoffset + thisFile; // 234 + AC00 = AE34

//printf("FilePos:%X\n",FilePos);

return FilePos;
#endif
}


DWORD CopyFileBufferToImageBuffer(IN LPVOID pFileBuffer, OUT LPVOID* pImageBuffer)
{

/*
void *memset(void *s,int c,size_t n)
将已开辟内存空间 s 的首 n 个字节的值设为值 c(给空间初始化)
void *memcpy(void *dest, const void *src, size_t n);
用来将src地址处的内容拷贝n个字节的数据至目标地址dest指向的内存中去 ,函数返回指向dest的指针
*/

//定义PE header
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);


//计算共复制了多少
DWORD TotalSize = 0;
//为imagebuffer开辟堆空间
LPVOID pTempImageBuffer = NULL;
pTempImageBuffer = (LPVOID)malloc(pOptionHeader->SizeOfImage);
if (pTempImageBuffer == NULL)
{
cout << "为imagebuffer申请内存失败";
return NULL;
}

//初始化
memset(pTempImageBuffer, 0, pOptionHeader->SizeOfImage);

//复制headers , 从filebuffer 到 imagebuffer
memcpy(pTempImageBuffer, pFileBuffer, pOptionHeader->SizeOfHeaders);
TotalSize += pOptionHeader->SizeOfHeaders;

//文件和内存对齐不一样的情况
//if (pOptionHeader->FileAlignment < pOptionHeader->SectionAlignment)
//{
// int memcal = pOptionHeader->SizeOfHeaders / pOptionHeader->SectionAlignment; //if header in file = 1234 , 1234/1000 , then 1000*2 in image
// pImageBuffer += memcal * pOptionHeader->SectionAlignment;
// //memset(pImageBuffer + pOptionHeader->SizeOfHeaders, 0, memcal * pOptionHeader->SectionAlignment);
//}

//循环复制节表
//while (pFileHeader->NumberOfSections > 0)
//{
// memcpy((LPVOID)((DWORD)pTempImageBuffer + pSectionHeader->VirtualAddress), (LPVOID)((DWORD)pFileBuffer + pSectionHeader->PointerToRawData), pSectionHeader->SizeOfRawData);
// pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
// TotalSize += pSectionHeader->SizeOfRawData;
// pFileHeader->NumberOfSections--;
//}

for (size_t i = 0; i < pFileHeader->NumberOfSections; i++)
{
memcpy((LPVOID)((DWORD)pTempImageBuffer + pSectionHeader[i].VirtualAddress), (LPVOID)((DWORD)pFileBuffer + pSectionHeader[i].PointerToRawData), pSectionHeader[i].SizeOfRawData);
TotalSize += pSectionHeader[i].SizeOfRawData;
}

*pImageBuffer = pTempImageBuffer;
//return TotalSize;
return TotalSize;
}


DWORD CopyImageBufferToNewBuffer(LPVOID pImageBuffer, LPVOID *pNewBuffer)
{

//LPVOID pFileBuffer = (LPVOID)OpenFile();
//pMemBuffer = CopyFileBufferToImageBuffer(pFileBuffer);
//LPVOID pFileBuffer = NULL;
//pMemBuffer = CopyFileBufferToImageBuffer(pFileBuffer,&pFileBuffer);

//定义PE header
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);

//为newbuffer开辟堆空间
int secNum = pFileHeader->NumberOfSections; //节的数量
DWORD pointerdata = 0; //当前节的PointerToRawData
DWORD sizedata = 0; //当前节的SizeOfRawData
//DWORD FileAlig = pOptionHeader->FileAlignment; //文件对齐

//计算最后一个节所在位置+大小,用于后续开辟空间
/*
while (secNum > 0)
{
if (secNum == 1)
{
pointerdata = pSectionHeader->PointerToRawData;
sizedata = pSectionHeader->SizeOfRawData;
//cout << pointerdata << " " << sizedata << "\n";
}
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
secNum--;
}
*/
for(int m=0;m<pFileHeader->NumberOfSections;m++)
{
if(m == pFileHeader->NumberOfSections-1)
{
pointerdata = pSectionHeader[m].PointerToRawData;
sizedata = pSectionHeader[m].SizeOfRawData;
}
else
{
continue;
}
}

//开辟内存
*pNewBuffer = (LPVOID)malloc(pointerdata + sizedata);
if (*pNewBuffer == NULL)
{
cout << "为imagebuffer申请内存失败\n";
return NULL;
}

//初始化
memset(*pNewBuffer, 0, pointerdata + sizedata);

//复制headers , 从filebuffer 到 imagebuffer
memcpy(*pNewBuffer, pImageBuffer, pOptionHeader->SizeOfHeaders);

//复制section
for (size_t i = 0; i < pFileHeader->NumberOfSections; i++)
{
memcpy((LPVOID)((DWORD)(*pNewBuffer) + pSectionHeader[i].PointerToRawData), (LPVOID)((DWORD)pImageBuffer + pSectionHeader[i].VirtualAddress), pSectionHeader[i].SizeOfRawData);
//memcpy((LPVOID)((DWORD)(*pNewBuffer) + pSectionHeader[i].PointerToRawData), (LPVOID)((DWORD)pImageBuffer + pSectionHeader[i].VirtualAddress), pSectionHeader[i].SizeOfRawData);
}
return pointerdata + sizedata;

}

bool MemeryToFile(IN LPVOID pMemBuffer, IN size_t size, OUT LPSTR lpszFile)
{
FILE *fp = NULL;
fp = fopen(lpszFile, "wb+");
if (fp == NULL)
{
return false;
}
fwrite(pMemBuffer, size, 1, fp);
fclose(fp);
fp = NULL;
return true;
}

void TestAddCodeInCodeSec(IN LPSTR lpszFile, OUT LPSTR lpszOutFile)
{
LPVOID pFileBuffer = NULL;
LPVOID pImageBuffer = NULL;
LPVOID pNewBuffer = NULL;
//从filebuffer - imagebuffer
DWORD fileLen = ReadPEFile(lpszFile, &pFileBuffer);
DWORD copySizeFromFile = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);


PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);

//cout << sizeof(shellcode)/sizeof(shellcode[0]) << "\n";

//判断空闲区大小是否足够添加shellcode
if(pSectionHeader->SizeOfRawData - pSectionHeader->Misc.VirtualSize < sizeof(shellcode)/sizeof(shellcode[0]))
{
cout << "空闲区不够!!\n";
free(pFileBuffer);
free(pImageBuffer);
return;
}

//加shellcode的位置
PBYTE addPos = (PBYTE)((DWORD)pImageBuffer + pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize);

//复制shellcode

//cout << (PBYTE)addPos << "\n";
memcpy(addPos ,shellcode, sizeof(shellcode)/sizeof(shellcode[0]));

//修正E8
//DWORD e8 = MessageAddr - (pOptionHeader->ImageBase + ((DWORD)(addPos+0xD) - (DWORD)pImageBuffer));
DWORD e8 = MessageAddr - (pOptionHeader->ImageBase + pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize +8 +5);
printf("%X\n",e8);
memcpy(addPos+9,&e8,sizeof(DWORD));

//修正E9
//DWORD e9 = pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint - (pOptionHeader->ImageBase + ((DWORD)(addPos+0xD+5) - (DWORD)pImageBuffer));
DWORD e9 = pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint - (pOptionHeader->ImageBase + pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize + 13 + 5);
printf("%X\n",e9);
memcpy(addPos+14,&e9,sizeof(DWORD));

//修正OEP
//pOptionHeader->AddressOfEntryPoint = (DWORD)addPos - (DWORD)pImageBuffer;
pOptionHeader->AddressOfEntryPoint = pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize;

DWORD size = CopyImageBufferToNewBuffer(pImageBuffer, &pNewBuffer);

bool isSucc = MemeryToFile(pNewBuffer, size, lpszOutFile);
if (isSucc == true) cout << "Success\n";
else cout << "woshishabi\n";
}

void TestAddCodeInAnySec(DWORD NumOfSec, IN LPSTR lpszFile, OUT LPSTR lpszOutFile)
{
LPVOID pFileBuffer = NULL;
LPVOID pImageBuffer = NULL;
LPVOID pNewBuffer = NULL;
//从filebuffer - imagebuffer
DWORD fileLen = ReadPEFile(lpszFile, &pFileBuffer);
DWORD copySizeFromFile = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);


PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);

//cout << sizeof(shellcode)/sizeof(shellcode[0]) << "\n";
if(NumOfSec > pFileHeader->NumberOfSections)
{
cout << "超出最大节数量\n";
return;
}

//判断空闲区大小是否足够添加shellcode

if(pSectionHeader[NumOfSec-1].SizeOfRawData - pSectionHeader[NumOfSec-1].Misc.VirtualSize < sizeof(shellcode)/sizeof(shellcode[0]))
{
cout << "空闲区不够!!\n";
free(pFileBuffer);
free(pImageBuffer);
return;
}

//加shellcode的位置
PBYTE addPos = (PBYTE)((DWORD)pImageBuffer + pSectionHeader[NumOfSec-1].VirtualAddress + pSectionHeader[NumOfSec-1].Misc.VirtualSize);
//PVOID codeBegin = (PBYTE)((pFileOptionHeader->SizeOfHeaders) + (DWORD)ImageBuffer + pFileSectionHeader->Misc.VirtualSize);
//复制shellcode

//cout << (PBYTE)addPos << "\n";
memcpy(addPos ,shellcode, sizeof(shellcode)/sizeof(shellcode[0]));

//修正E8
DWORD e8 = MessageAddr - (pOptionHeader->ImageBase + pSectionHeader[NumOfSec-1].VirtualAddress + pSectionHeader[NumOfSec-1].Misc.VirtualSize +8 +5);
//DWORD e8 = MessageAddr - (pOptionHeader->ImageBase + ((DWORD)(addPos+0xD) - (DWORD)pImageBuffer));
//DWORD e8 = ((DWORD)MessageAddr - (pOptionHeader->ImageBase + ((DWORD)((DWORD)addPos + 0xD) - (DWORD)pImageBuffer)));
printf("%X\n",e8);
memcpy(addPos+9,&e8,sizeof(DWORD));

//修正E9
//DWORD e9 = (pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint) - (pOptionHeader->ImageBase + ((DWORD)(addPos+0xD+5) - (DWORD)pImageBuffer));
//DWORD e9 = (pOptionHeader->AddressOfEntryPoint+pOptionHeader->ImageBase-(pOptionHeader->ImageBase + 18) + (DWORD)pImageBuffer);
DWORD e9 = pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint - (pOptionHeader->ImageBase + pSectionHeader[NumOfSec-1].VirtualAddress + pSectionHeader[NumOfSec-1].Misc.VirtualSize + 13 + 5);
printf("%X\n",e9);
memcpy(addPos+14,&e9,sizeof(DWORD));

//修正OEP
//pOptionHeader->AddressOfEntryPoint = (DWORD)addPos - (DWORD)pImageBuffer;
//pOptionHeader->AddressOfEntryPoint = (DWORD)addPos;
pOptionHeader->AddressOfEntryPoint = pSectionHeader[NumOfSec-1].VirtualAddress + pSectionHeader[NumOfSec-1].Misc.VirtualSize;


pSectionHeader[NumOfSec-1].Characteristics = pSectionHeader[NumOfSec-1].Characteristics | pSectionHeader[0].Characteristics;

DWORD size = CopyImageBufferToNewBuffer(pImageBuffer, &pNewBuffer);

bool isSucc = MemeryToFile(pNewBuffer, size, lpszOutFile);
if (isSucc == true) cout << "Success\n";
else cout << "woshishabi\n";
}

void TestAddSec(IN LPSTR lpszFile, IN DWORD AddNumber, OUT LPSTR lpszOutFile)
{
LPVOID pFileBuffer = NULL;
LPVOID pImageBuffer = NULL;
LPVOID pNewBuffer = NULL;
//从filebuffer - imagebuffer
DWORD fileLen = ReadPEFile(lpszFile, &pFileBuffer);
DWORD copySizeFromFile = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);


PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);


//判断空闲区大小是否足够添加节表

if ((DWORD)pImageBuffer + pOptionHeader->SizeOfHeaders - pDosHeader->e_lfanew - IMAGE_SIZEOF_FILE_HEADER - pFileHeader->SizeOfOptionalHeader - pFileHeader->NumberOfSections * IMAGE_SIZEOF_SECTION_HEADER < 2 * IMAGE_SIZEOF_SECTION_HEADER)
{
cout << "headers中空闲区不够!!\n";
free(pFileBuffer);
free(pImageBuffer);
return;
}

//把节表中第一个节复制到节表中最后一个节
//计算开始复制的位置和第一个节的位置
LPVOID SecBegin = (LPVOID)((DWORD)pImageBuffer + pDosHeader->e_lfanew + IMAGE_SIZEOF_FILE_HEADER + pFileHeader->SizeOfOptionalHeader + 4);
//LPVOID MyBegin = (LPVOID)((DWORD)pImageBuffer + pDosHeader->e_lfanew + IMAGE_SIZEOF_FILE_HEADER + pFileHeader->SizeOfOptionalHeader + pFileHeader->NumberOfSections * IMAGE_SIZEOF_SECTION_HEADER);
LPVOID MyBegin = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader+(pFileHeader->NumberOfSections)*IMAGE_SIZEOF_SECTION_HEADER);

memcpy(MyBegin, SecBegin, IMAGE_SIZEOF_SECTION_HEADER);

//在新增节表后面再加IMAGE_SIZEOF_SECTION_HEADER个0
LPVOID ZeroBegin = (PIMAGE_SECTION_HEADER)MyBegin + 1;
memset(ZeroBegin, 0, 40);

//修改NumberOfSections
pFileHeader->NumberOfSections += 1;

//新增节,NewSize为新增节的大小
//LPVOID codeBegin = (LPVOID)((DWORD)pImageBuffer + pSectionHeader[pFileHeader->NumberOfSections - 1].VirtualAddress + pSectionHeader[pFileHeader->NumberOfSections - 1].SizeOfRawData);
//memset(codeBegin, 1, AddNumber);

//修改SizeOfImage
pOptionHeader->SizeOfImage += ((AddNumber / pOptionHeader->SectionAlignment) + 1)* pOptionHeader->SectionAlignment;

//修改节表
/*
pSectionHeader[pFileHeader->NumberOfSections - 1].Name[0] = (BYTE)"s";
pSectionHeader[pFileHeader->NumberOfSections - 1].Name[1] = (BYTE)"b";
pSectionHeader[pFileHeader->NumberOfSections - 1].Name[2] = (BYTE)"s";
pSectionHeader[pFileHeader->NumberOfSections - 1].Name[3] = (BYTE)"b";
pSectionHeader[pFileHeader->NumberOfSections - 1].Name[4] = (BYTE)"s";
pSectionHeader[pFileHeader->NumberOfSections - 1].Name[5] = (BYTE)"b";
pSectionHeader[pFileHeader->NumberOfSections - 1].Name[6] = (BYTE)"s";
pSectionHeader[pFileHeader->NumberOfSections - 1].Name[7] = (BYTE)"b";
*/
pSectionHeader[pFileHeader->NumberOfSections - 1].Misc.VirtualSize = AddNumber;
pSectionHeader[pFileHeader->NumberOfSections - 1].VirtualAddress = pSectionHeader[pFileHeader->NumberOfSections - 2].VirtualAddress + pSectionHeader[pFileHeader->NumberOfSections - 2].SizeOfRawData;
DWORD num = (AddNumber / pOptionHeader->SectionAlignment) + 1;
pSectionHeader[pFileHeader->NumberOfSections - 1].SizeOfRawData = ((AddNumber/pOptionHeader->SectionAlignment)+1) * pOptionHeader->SectionAlignment;
pSectionHeader[pFileHeader->NumberOfSections - 1].Characteristics = pSectionHeader[0].Characteristics;
pSectionHeader[pFileHeader->NumberOfSections - 1].PointerToRawData = pSectionHeader[pFileHeader->NumberOfSections - 2].PointerToRawData + pSectionHeader[pFileHeader->NumberOfSections - 2].SizeOfRawData;
DWORD size = CopyImageBufferToNewBuffer(pImageBuffer, &pNewBuffer);

bool isSucc = MemeryToFile(pNewBuffer, size, lpszOutFile);
if (isSucc == true) cout << "Success\n";
else cout << "woshishabi\n";
}

void TestAddSecInDosStub(IN LPSTR lpszFile, IN DWORD AddNumber, OUT LPSTR lpszOutFile)
{
LPVOID pFileBuffer = NULL;
LPVOID pImageBuffer = NULL;
LPVOID pNewBuffer = NULL;
//从filebuffer - imagebuffer
DWORD fileLen = ReadPEFile(lpszFile, &pFileBuffer);
DWORD copySizeFromFile = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);


PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
DWORD elfanew = pDosHeader->e_lfanew;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);
//保留原始属性???
DWORD NumOfSec = pFileHeader->NumberOfSections;
DWORD virtualsize = pSectionHeader[NumOfSec - 1].Misc.VirtualSize;
DWORD virtualaddress = pSectionHeader[NumOfSec - 1].VirtualAddress;
DWORD pointertorawdata = pSectionHeader[NumOfSec - 1].PointerToRawData;
DWORD sizeofrawdata = pSectionHeader[NumOfSec - 1].SizeOfRawData;
DWORD sectionalignment = pOptionHeader->SectionAlignment;
//判断空闲区大小是否足够添加节表

//if ((DWORD)pImageBuffer + pOptionHeader->SizeOfHeaders - pDosHeader->e_lfanew - IMAGE_SIZEOF_FILE_HEADER - pFileHeader->SizeOfOptionalHeader - pFileHeader->NumberOfSections * IMAGE_SIZEOF_SECTION_HEADER < 2 * IMAGE_SIZEOF_SECTION_HEADER)
//{
cout << "提升并覆盖dos stub!\n";
LPVOID dest = (LPVOID)((DWORD)pImageBuffer + 0x40); //DOS有用数据结束,垃圾数据开始
LPVOID source = (LPVOID)((DWORD)pImageBuffer + pDosHeader->e_lfanew); //NT begin position
DWORD size = pDosHeader->e_lfanew - 0x40; //向上提升的空间
cout << size << "\n";
DWORD shabi = (4 + IMAGE_SIZEOF_FILE_HEADER + pFileHeader->SizeOfOptionalHeader + pFileHeader->NumberOfSections * IMAGE_SIZEOF_SECTION_HEADER); //signature,nt,file,option,节表提升抬升
memcpy(dest, source, shabi);
//LPVOID newone = (LPVOID)((DWORD)pImageBuffer + pDosHeader->e_lfanew + IMAGE_SIZEOF_FILE_HEADER + pFileHeader->SizeOfOptionalHeader + pFileHeader->NumberOfSections * IMAGE_SIZEOF_SECTION_HEADER + 4 - size);
//memset(newone,0,size);
//}

//更新e->lfanew
pDosHeader->e_lfanew = elfanew - size;
//更新头指针
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
//PE头
pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4); //NT头地址 + 4 为 FileHeader 首址
//可选PE头
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);//SIZEOF_FILE_HEADER为固定值且不存在于PE文件字段中
//首个节表
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);



//开始新增节
//把节表中第一个节复制到节表中最后一个节
//计算开始复制的位置和第一个节的位置
LPVOID SecBegin = (LPVOID)pSectionHeader; //第一个节的位置
LPVOID MyBegin = (LPVOID)((DWORD)SecBegin + pFileHeader->NumberOfSections * 40);//复制到的目标位置

//LPVOID SecBegin = (LPVOID)((DWORD)pImageBuffer + pDosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER + pFileHeader->SizeOfOptionalHeader - size); //第一个节的位置
//LPVOID MyBegin = (LPVOID)((DWORD)pImageBuffer + pDosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER + pFileHeader->SizeOfOptionalHeader + pFileHeader->NumberOfSections * IMAGE_SIZEOF_SECTION_HEADER - size);//复制到的目标位置
//LPVOID MyBegin = (LPVOID)(PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + (pFileHeader->NumberOfSections)*IMAGE_SIZEOF_SECTION_HEADER - size); //复制到的目标位置
//cout << pFileHeader->NumberOfSections << "\n";
memcpy(MyBegin, SecBegin, 40);

//在新增节表后面再加IMAGE_SIZEOF_SECTION_HEADER个0
//memset((PBYTE)MyBegin + NumOfSec * 40 + IMAGE_SIZEOF_SECTION_HEADER, 0, IMAGE_SIZEOF_SECTION_HEADER);
memset((PBYTE)MyBegin + 40, 0, 40);

//修改NumberOfSections
pFileHeader->NumberOfSections += 1;
#if 1
//新增节,NewSize为新增节的大小
//LPVOID codeBegin = (LPVOID)((DWORD)pImageBuffer + pSectionHeader[pFileHeader->NumberOfSections-2].VirtualAddress + pSectionHeader[pFileHeader->NumberOfSections-2].SizeOfRawData);
//memset(codeBegin, 0, AddNumber);

//修改SizeOfImage
pOptionHeader->SizeOfImage += ((AddNumber / sectionalignment) + 1)* sectionalignment;
//修改节表
pSectionHeader[pFileHeader->NumberOfSections - 1].Misc.VirtualSize = AddNumber; //vitualsize = 新增大小
pSectionHeader[pFileHeader->NumberOfSections - 1].VirtualAddress = pSectionHeader[pFileHeader->NumberOfSections - 2].VirtualAddress + pSectionHeader[pFileHeader->NumberOfSections - 2].SizeOfRawData; //上一个节的VirtualAddress+sizeofrawdata
DWORD num = (AddNumber / pOptionHeader->SectionAlignment) + 1;
pSectionHeader[pFileHeader->NumberOfSections - 1].SizeOfRawData = ((AddNumber / pOptionHeader->SectionAlignment) + 1) * pOptionHeader->SectionAlignment; //sizeofrawdata 对齐后
pSectionHeader[pFileHeader->NumberOfSections - 1].Characteristics = pSectionHeader[0].Characteristics;
pSectionHeader[pFileHeader->NumberOfSections - 1].PointerToRawData = pSectionHeader[pFileHeader->NumberOfSections - 2].PointerToRawData + pSectionHeader[pFileHeader->NumberOfSections - 2].SizeOfRawData;
DWORD size1 = CopyImageBufferToNewBuffer(pImageBuffer, &pNewBuffer);

bool isSucc = MemeryToFile(pNewBuffer, size1, lpszOutFile);
if (isSucc == true) cout << "Success\n";
else cout << "woshishabi\n";
#endif
}

//valid program
void TestExpandLastSec(IN LPSTR lpszFile, IN DWORD AddNumber, OUT LPSTR lpszOutFile)
{
LPVOID pFileBuffer = NULL;
LPVOID pImageBuffer = NULL;
LPVOID pNewBuffer = NULL;
//从filebuffer - imagebuffer
DWORD fileLen = ReadPEFile(lpszFile, &pFileBuffer);
DWORD copySizeFromFile = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);


PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);

//开辟大小为SizeOfImage + x的空间
LPVOID newspace = (LPVOID)malloc(pOptionHeader->SizeOfImage + AddNumber);
//修改最后一个节的属性
//virtualsize内存对齐后的值
DWORD newvirtualsize = (pSectionHeader[pFileHeader->NumberOfSections-1].Misc.VirtualSize / pOptionHeader->SectionAlignment +1 ) * pOptionHeader->SectionAlignment;
//max(sizeofrawdat,virtualsize)
DWORD maxsize = newvirtualsize > pSectionHeader[pFileHeader->NumberOfSections-1].SizeOfRawData ? newvirtualsize : pSectionHeader[pFileHeader->NumberOfSections-1].SizeOfRawData;
pSectionHeader[pFileHeader->NumberOfSections-1].SizeOfRawData = (((maxsize + AddNumber)/pOptionHeader->SectionAlignment)+1) * pOptionHeader->SectionAlignment;
pSectionHeader[pFileHeader->NumberOfSections-1].Misc.VirtualSize = maxsize + AddNumber;
cout << pSectionHeader[pFileHeader->NumberOfSections-1].SizeOfRawData << " " << pSectionHeader[pFileHeader->NumberOfSections-1].Misc.VirtualSize << "\n";

//修改sizeofimage
//注意修正SizeofImage,错误的sizeofimage会导致非法的win32程序
//pOptionHeader->SizeOfImage = ((pSectionHeader[pFileHeader->NumberOfSections-1].Misc.VirtualSize + pSectionHeader[pFileHeader->NumberOfSections-1].VirtualAddress + AddNumber)/pOptionHeader->SectionAlignment + 1) * pOptionHeader->SectionAlignment;
pOptionHeader->SizeOfImage = \
(((pSectionHeader[pFileHeader->NumberOfSections - 1].Misc.VirtualSize + pSectionHeader[pFileHeader->NumberOfSections - 1].VirtualAddress) / pOptionHeader->SectionAlignment) + 1) * pOptionHeader->SectionAlignment;
//修改character
pSectionHeader[pFileHeader->NumberOfSections-1].Characteristics = pSectionHeader[0].Characteristics | pSectionHeader[pFileHeader->NumberOfSections-1].Characteristics;


DWORD size1 = CopyImageBufferToNewBuffer(pImageBuffer, &pNewBuffer);
bool isSucc = MemeryToFile(pNewBuffer, size1, lpszOutFile);
if (isSucc == true) cout << "Success\n";
else cout << "woshishabi\n";
free(newspace);
}

//返回对齐后大小,x为对齐前大小,y为pOptionHeader->SectionAlignment/pOptionHeader->FileAlignment
DWORD Align(int x, int y)
{
return ((x/y)+1)*y;
}

//merge all section to one section
void TestMergeSec(IN LPSTR lpszFile, OUT LPSTR lpszOutFile)
{
LPVOID pFileBuffer = NULL;
LPVOID pImageBuffer = NULL;
LPVOID pNewBuffer = NULL;
//从filebuffer - imagebuffer
DWORD fileLen = ReadPEFile(lpszFile, &pFileBuffer);
DWORD copySizeFromFile = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);


PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER pFirstSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);
pFirstSectionHeader = pSectionHeader;
//保留原来节表数量
DWORD origin_sec = pFileHeader->NumberOfSections;
//修改第一个节的属性
pSectionHeader[0].Misc.VirtualSize = pSectionHeader[0].SizeOfRawData = (((copySizeFromFile - pSectionHeader[0].VirtualAddress)/pOptionHeader->FileAlignment)+1)*pOptionHeader->FileAlignment;
for(int i=1;i<pFileHeader->NumberOfSections;i++)
{
pSectionHeader[0].Characteristics = pSectionHeader[0].Characteristics | pSectionHeader[i].Characteristics;
memset(pSectionHeader+i, 0, IMAGE_SIZEOF_SECTION_HEADER);
}
//modify sectionnumber
pFileHeader->NumberOfSections = 1;
//memset(pFirstSectionHeader+40,0,(origin_sec-1)*40);

#if 1
DWORD size1 = CopyImageBufferToNewBuffer(pImageBuffer, &pNewBuffer);

bool isSucc = MemeryToFile(pNewBuffer, size1, lpszOutFile);
if (isSucc == true) cout << "Success\n";
else cout << "woshishabi\n";
#endif
}

void PrintImageDirectory(IN LPSTR lpszFile)
{
LPVOID pFileBuffer = NULL;
LPVOID pImageBuffer = NULL;
LPVOID pNewBuffer = NULL;
//从filebuffer - imagebuffer
DWORD fileLen = ReadPEFile(lpszFile, &pFileBuffer);
DWORD copySizeFromFile = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);


PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
//pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);
char* name[16] = {"导出表","导入表","资源表","异常表","安全证书表","重定位表","调试信息表","版权所有表","全局指针表","TLS表","加载配置表","绑定导入表","IAT表","延迟导入表","COM","Reverse"};

for(int i=0;i<pOptionHeader->NumberOfRvaAndSizes;i++)
{
cout << "------------------------------------\n";
printf("%s\n",name[i]);
printf("Size:%X\n",pOptionHeader->DataDirectory[i].Size);
printf("VirtualAddress:%X\n",pOptionHeader->DataDirectory[i].VirtualAddress);
}

}

参考了大佬们的文章:

增节_ma_lic 的博客 - CSDN 博客

扩大节_滴水逆向扩大节_Revival_S 的博客 - CSDN 博客

# 静态链接库

1
2
3
4
5
6
7
8
1.创建静态lib
2.复制debug下的 .lib 和 ../.h到当前工程
3.使用静态或者动态链接
静态: 在需要使用的项目中 #include "x.h" #pragma comment(lib,"x.lib")
隐式:不写第二个pragama ,使用setting -> link -> 对象/模块库 -> 复制自己的lib名称

使用:和普通函数调用一样
int x= Plus(1,2);

手动连接

image-20230212154955361

#include 中只有函数说明,没有实现,VC 中对象 / 库模块中默认包含了很多,见上图

代码的重复使用

二进制代码直接编译到 exe 中

改 lib 代码需要重新编译 exe

# 动态链接库

1
2
3
4
5
6
7
8
9
10
函数声明:
extern "C" _declspec(dllexport) __stdcall int Add(int ,int);

1、 extern表示这是个全局函数,可以供各个其他的函数调用;
2、"℃”按照c语言的方式进行编译、链接
3、declspec(dllexport)告诉编译器此函数为导出函数;

如果不加 “C”会按照C++导出为了防止重载会在函数名后加上符号

动态链接库代码编译后在自己的dll中

VC6 自带 depend 识别 dll

如果不加 extern “C” 导出:

image-20230308194335559

如果加 extern “C” :

image-20230308194531511

1
2
3
4
5
6
7
8
9
10
11
12
13
使用dll
1.隐式连接
1.将lib,dll放在工程目录下,代码存在于dll中
2.#pragma comment(lib,"x.lib")
3.extern "C" _declspec(dllimport) __stdcall int Add(int ,int); 添加到调用文件中
如果导出时加了__stdcall,那么在导入时也需要加,建议导出方式为stdcall

2,.显示连接
定义函数指针 typedef int (__stdcall *lpPlus)(int ,int);
声明指针类型变量 lpPlus myPlus;
动态加载dll到内存中 #include <Windows.h> HINSTANCE hModule = LoadLibrary("x.dll"); 如果加了__stdcall ,函数名会变为_Plus@8
获取函数地址 mpPlus = (lpPlus)GetProcAddress(hModule, "Plus");
myPlus(1,2);

dll 中代码修改后不需要重新编译 exe,dll 的代码不在 exe 里,在 dll’的空间中

1
2
3
4
5
6
7
Handle是代表系统的内核对象,如文件句柄,线程句柄,进程句柄。
HMODULE是代表应用程序载入的模块
HINSTANCE在win32下与HMODULE是相同的东西Win16遗留
HWND是窗口句柄
本质都是无符号整数 unsigned int
这么设计可以可读性更好,避免进行运算

1
2
3
4
5
6
7
8
9
.def无函数名导出
新建.def文件,使用序号到达隐藏
头文件不能再加export
创建.def
@是导出序号

EXPORTS
Plus @12
Sub @15 NONAME

image-20230308202844475

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma comment(lib,"TestDll.lib")
extern "C" _declspec(dllimport) int Add(int ,int);
extern "C" _declspec(dllimport) int Mul(int ,int);

void testDll()
{
typedef int (*lpAdd)(int ,int);
typedef int (*lpMul)(int ,int);
lpAdd myadd;
lpMul mymul;
HINSTANCE hModule = LoadLibrary("TestDll.dll");
myadd = (lpAdd)GetProcAddress(hModule, "Add");
mymul = (lpMul)GetProcAddress(hModule, "Mul");
cout << myadd(3,4) << "\n";
cout << mymul(3,4) << "\n";
}

# 导出表

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
exe和dll都可以提供函数。一般情况是dll提供函数。
提供函数给别人用必须要用导出表
如果导出表的RVA和size都是00000000证明他没有导出表

IMAGE_DIRECTORY_ENTRY_EXPORT
{
DWORD VitualAddress;
DWORD Size;
};

virtualaddress是内存中的,需要转成FOA
DWORD RvaToFileOffset(IN LPVOID pFileBuffer, DWORD dwRva)
{

//define pe structure
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);

printf("pFileBuffer: %X\n", pFileBuffer); //450068
printf("DWRVA: %X\n", dwRva);
DWORD MemOffset = (dwRva - (DWORD)pFileBuffer); //45C29C - 450068 = C234,内存中的rva
printf("MemOffset: %X\n", MemOffset);

//当前RVA在哪个节中
int NumOfSec = 0;
//RVA所在节的VirtualAddress
DWORD thisVA = 0;
//RVA所在节的PointerToRawData
DWORD thisFile = 0;
//距离当前节开始的便宜
DWORD thisoffset = 0;
//在文件中的FOA
DWORD FilePos = 0;
//判断在哪个节的条件
while (pFileHeader->NumberOfSections > 0)
{
if ((DWORD)MemOffset > (DWORD)pSectionHeader->VirtualAddress && (DWORD)MemOffset < (DWORD)pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize)
{
thisVA = pSectionHeader->VirtualAddress;
thisFile = pSectionHeader->PointerToRawData;
printf("thisVA:%X\n",thisVA); //C000
break;
}
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER);
pFileHeader->NumberOfSections--;
NumOfSec++;
}
printf("NumOfSec:%d\n", NumOfSec+1);
thisoffset = MemOffset - thisVA; //C234-C000 = 234;
printf("thisoffset:%X\n",thisoffset);
FilePos = thisoffset + thisFile; // 234 + AC00 = AE3
printf("FilePos:%X\n",FilePos);
return FilePos;
}


typedef struct _IMAGE_EXPORT_DIRECTORY {
DWORD Characteristics;
DWORD TimeDateStamp;
WORD MajorVersion;
WORD MinorVersion;
DWORD Name;
DWORD Base;
DWORD NumberOfFunctions;
DWORD NumberOfNames;
DWORD AddressOfFunctions; // RVA from base of image
DWORD AddressOfNames; // RVA from base of image
DWORD AddressOfNameOrdinals; // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

AddressofFunctions是函数地址,但是他是RVA
导出函数数量由NumberofFunctions决定
AddressOfName是函数名的地址,还是RVA,noname导出这里就不会有
名字的数量是由NumberOfNames决定,这里只包含以函数名导出的函数的个数
AddressOfFunctions 和 AddressOfnames 长度可以不一样,可以存在noname函数或者两个函数名称指向一个函数地址
NumberofName只代表以函数名导出的个数
AddressOfNameOrdinals,大小是NumberOfNames,导出序号 = base + AddressOfNameOrdinals


通过函数名找函数时,先把AddressOfName转成FOA找到里面的名字,再进行比较字符串
再把查找到的AddressOfNames索引到AddressOfNameOrdinals中找索引对应的值,这个值就是AddressOfFunctions里面的值
比如AddressOfNames中索引为2,到AddressOfNameOrdinals也找索引为2对应的序号,这个序号就是AddressOfFunctions中对应的下标

系统认为的导出函数的个数 = AddressOfNameOrdinals最大序号 - 最小序号 + 1
如果序号不连续这个数量就会有问题

用序号找
序号 - base = AddressOfFunctions的下标

为什么要分成3张表?
1、函数导出的个数与函数名的个数未必一样.所以要将函数地址表和函数名称表分开
2、函数地址表是不是一定大于函数名称表?
未必,一个相同的函数地址,可能有多个不同的名字
3、如何根据函数的名字获取一个函数的地址?

image-20230310152135716

image-20230310154212089

image-20230310160821694

hmodule 是拉伸后的,filebuffer 是文件中的

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
void PrintfExportDir(IN LPSTR lpszFile)
{
LPVOID pFileBuffer = NULL;

//从filebuffer - imagebuffer
DWORD fileLen = ReadPEFile(lpszFile, &pFileBuffer);

PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER pFirstSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
// cout << hex << pOptionHeader->DataDirectory[0].VirtualAddress << "\n";
//把VirtualAddress转为foa
DWORD FOA = RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress);
//打印export ditectory
// cout << hex << FOA << "\n";
PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + FOA);
printf("pExportDirectory->Characteristics:%X\n",pExportDirectory->Characteristics);
printf("pExportDirectory->TimeDateStamp:%X\n",pExportDirectory->TimeDateStamp);
printf("pExportDirectory->MajorVersion:%X\n",pExportDirectory->MajorVersion);
printf("pExportDirectory->MinorVersion:%X\n",pExportDirectory->MinorVersion);
printf("pExportDirectory->Name:%X\n",pExportDirectory->Name);
printf("pExportDirectory->Base:%X\n",pExportDirectory->Base);
printf("pExportDirectory->NumberOfFunctions:%X\n",pExportDirectory->NumberOfFunctions);
printf("pExportDirectory->NumberOfNames:%X\n",pExportDirectory->NumberOfNames);
printf("pExportDirectory->AddressOfFunctions:%X\n",pExportDirectory->AddressOfFunctions);
printf("pExportDirectory->AddressOfNameOrdinals:%X\n",pExportDirectory->AddressOfNameOrdinals);
printf("pExportDirectory->AddressOfNames:%X\n",pExportDirectory->AddressOfNames);
cout << "-------------------" << "\n";

//打印addresofFunction
DWORD NumOfFunFoa = RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfFunctions);
for (int i=0;i<pExportDirectory->NumberOfFunctions;i++)
{
cout << hex << *(PDWORD)((DWORD)pFileBuffer + NumOfFunFoa + i*4) << "\n";
}

cout << "-------------------" << "\n";
//打印addressofnames
DWORD AddressFoa = RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfNames);
DWORD nameFOA = 0;
char* name = NULL;
for (int k=0;k<pExportDirectory->NumberOfNames;k++)
{
nameFOA = RvaToFileOffset(pFileBuffer, *(PDWORD)((DWORD)pFileBuffer + AddressFoa));
name = (char*)(nameFOA + (DWORD)pFileBuffer);
printf("%s\n",name);
AddressFoa += 4;
}


cout << "-------------------" << "\n";

//打印AddressOfNameOrdinals
DWORD OrdinalFoa = RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfNameOrdinals);
for (int m=0;m<pExportDirectory->NumberOfNames;m++)
{
cout << hex << *(PWORD)((DWORD)pFileBuffer + OrdinalFoa + m*2) + pExportDirectory->Base << "\n";
}
}

DWORD GetFunctionAddrByName(IN LPSTR lpszFile, IN LPVOID pFileBuffer, IN char* pFuncName)
{
DWORD fileLen = ReadPEFile(lpszFile, &pFileBuffer);

PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER pFirstSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
//把VirtualAddress转为foa
DWORD FOA = RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress);
DWORD nameFOA = 0;
char* name = NULL;
PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + FOA);
DWORD AddressFoa = RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfNames);
DWORD OrdinalFoa = RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfNameOrdinals);
DWORD NumOfFunFoa = RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfFunctions);

for (int k=0;k<pExportDirectory->NumberOfNames;k++)
{
nameFOA = RvaToFileOffset(pFileBuffer, *(PDWORD)((DWORD)pFileBuffer + AddressFoa));
name = (char*)(nameFOA + (DWORD)pFileBuffer);
if (strcmp(name,(char*)pFuncName) == 0)
{
DWORD num = *(PWORD)((DWORD)pFileBuffer + OrdinalFoa + k*2);
DWORD haha = *(PDWORD)((DWORD)pFileBuffer + NumOfFunFoa + num*4);
//cout << hex << haha << "\n";
return haha;
}
AddressFoa += 4;
}

return 0;
}

DWORD GetFunctionAddrByOrdinals(IN LPSTR lpszFile, IN LPVOID pFileBuffer, IN DWORD OutNum)
{
DWORD fileLen = ReadPEFile(lpszFile, &pFileBuffer);

PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_SECTION_HEADER pFirstSectionHeader = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
//把VirtualAddress转为foa
DWORD FOA = RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress);
DWORD nameFOA = 0;
char* name = NULL;
PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + FOA);
DWORD AddressFoa = RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfNames);
DWORD OrdinalFoa = RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfNameOrdinals);
DWORD NumOfFunFoa = RvaToFileOffset(pFileBuffer, pExportDirectory->AddressOfFunctions);


int xiabiao = OutNum - pExportDirectory->Base;
for (int i=0;i<pExportDirectory->NumberOfFunctions;i++)
{
if (i == xiabiao)
{
DWORD address = *(PDWORD)((DWORD)pFileBuffer + NumOfFunFoa + i*4);
return address;
}
}

return 0;
}

# 重定位表

image-20230311170507585

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
程序加载可以理解为贴图的过程,先开辟4G虚拟内存,先把exe放在ImageBase,分SizeOfimage大小
在加载dll,一般在70000000开始,每个dll都从自己的imagebase开始,分sizeofimage大小
dll贴完,指向oep,程序就可以运行了

一般情况下,EX都是可以按照ImageBase的地址进行加载的.因为Exe拥有自己独立的4GB 的虚拟内存空间但DLL不是DLL是有EXE使用它,才加载到相关EXE的进程空间的.


为了提高搜索的速度,模块间地址也是要对齐的模块地址对齐为10000H也就是64K

编译时生成的地址= ImageBase + RVA
如果加载的时候没有按照预定加载,那么绝对地址会不可用
所以需要一张表保存绝对地址

目录向的第六个结构是重定位表

typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

RVA -> FOA

typedef struct _IMAGE_BASE_RELOCATION {
DWORD VirtualAddress;
DWORD SizeOfBlock;
} IMAGE_BASE_RELOCATION;
typedef IMAGE_BASE_RELOCATION UNALIGNED * PIMAGE_BASE_RELOCATION;

image-20230311173859344

1
2
3
4
5
6
7
8
9
10
11
12
13
14
virtualaddress放修改的起始地址
Virtualaddress + 块中数据 = 要修改的地址的RVA
每一块中是一页中需要修改的地址
每页为4k,每个页只需要12位二进制,因此2字节够标识所有的地址,即块内数据都是2字节
内存中的页大小是1000H也就是说2的12次方就可以表示一个页内所有的偏移地址具体项的宽度是16字节高四位代表类型:值为3代表的是需要修改的数据值为o代表的是用于数据对齐的数据,可以不用修改.也就是说我们只关注高四位为3


但分空间还是要分16个,因为内存对齐
因此计算RVA时只需要计算低12位,低12位 + x就是需要修改的RVA
高四位果 = 3,那么需要改修, =0代表不需要修复

最后一个块是一个全0结构, & 判断

块中多少 = (SizeOfBlock - 8) / 2

image-20230311175130539

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
void PrintRelocationDirectory(IN LPSTR lpszFile)
{
LPVOID pFileBuffer = NULL;
DWORD fileLen = ReadPEFile(lpszFile, &pFileBuffer);

PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL;
PIMAGE_BASE_RELOCATION pRelocationTable = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; // 强转 DOS_HEADER 结构体指针
//可选PE头 简化后的处理
pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileBuffer + pDosHeader->e_lfanew + 4 + IMAGE_SIZEOF_FILE_HEADER);
pRelocationTable = (PIMAGE_BASE_RELOCATION)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress));
//pRelocationTable = (PIMAGE_BASE_RELOCATION)(RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress));
printf("Relocation Table Rva: %#x\n", pRelocationTable->VirtualAddress);

int i = 0;


//pRelocationTable = (PIMAGE_BASE_RELOCATION)RvaToFileOffset(pFileBuffer,pRelocationTable->VirtualAddress);
while ( pRelocationTable->VirtualAddress && pRelocationTable->SizeOfBlock)
{
printf("第%d个块VirtualAddress:%X\n", i, pRelocationTable->VirtualAddress);
printf("第%d个块SizeOfBlock:%X\n", i,pRelocationTable->SizeOfBlock);
printf("第%d个块项数:%X\n", i, (pRelocationTable->SizeOfBlock - 8)/2);
//往后八个字节是重定位开始,每次往后两个字节
WORD* recAddr = (WORD*)((BYTE*)pRelocationTable+0x08);
//preadd += 4;
//for(int k=1;k<(pRelocationTable->SizeOfBlock - 8)/2;k++)
//{
// preadd = (*preadd) + (WORD*)pRelocationTable->VirtualAddress;
// printf("%X-%X-%X",i,k,preadd);
//}
for(int k=0;k<(pRelocationTable->SizeOfBlock - 8)/2;k++)
{
//&0xFFF 把后12位拿出来
DWORD pRepair_RvaOffset = (recAddr[k] & 0x0FFF) + RvaToFileOffset(pFileBuffer,pRelocationTable->VirtualAddress);
//DWORD pRepair_RvaOffset = RvaToFileOffset(pFileBuffer,pRelocationTable[k].VirtualAddress);
WORD type = recAddr[k] >> 12; //判断高四位是否为3
if(type!=0)
{
printf("RVA:%X,type:%X\r\n",pRepair_RvaOffset,type);
}
}
//pRelocationTable = (DWORD)pRelocationTable + pRelocationTable->SizeOfBlock;
pRelocationTable = (PIMAGE_BASE_RELOCATION )((BYTE *)pRelocationTable + pRelocationTable->SizeOfBlock);
i++;
}
}

打印出重定位表(C 语言实现)_打印重定位表_lygl35 的博客 - CSDN 博客

# 移动导出表

1
2
3
4
5
6
7
8
1.在DLL中新增节,返回FOA
2.复制AddressOfFunctions 长度 4*NumberOfFunctions
3.移动AddressOfNameOrdinals 长度 2*NumberOfNames
4.复制AddressOfNames 长度 4*NumberOfNames
5.复制函数名 长度不确定,直接修复AddressOfNames,复制完一个名字,算下一个地址在哪里,算完在修复Names表
6.复制_IMAGE_EXPORT_DIRECTORY结构
7.修复_IMAGE_EXPORT_DIRECTORY中的AddressOfFunctions,AddressOfNames,AddressOfNameOrdinals
8.修复目录项中的值,指向新的_IMAGE_EXPORT_DIRECTORY
1
2
1.新增节
2.目录项改为新增节起始位置

image-20230312153214517

# IAT

1
2
3
4
5
6
7
8
9
10
11
12
13
12:       MessageBox(0,0,0,0);
00401658 8B F4 mov esi,esp
0040165A 6A 00 push 0
0040165C 6A 00 push 0
0040165E 6A 00 push 0
00401660 6A 00 push 0
00401662 FF 15 44 63 48 00 call dword ptr [__imp__MessageBoxA@16 (00486344)]
00401668 3B F4 cmp esi,esp
0040166A E8 91 23 02 00 call __chkesp (00423a00)


我们自己写的程序,call后的地址是写死的,直接编译到exe中,不管中间是否有jmp
调用dll的程序,call后是[x],[x]才是dll中函数真实的地址,x是exe空间内,[x]在dll空间内,x只有在所有dll都贴完只有才改x

image-20230313165403957

重定位表只能保证自己程序中的地址是正确的

IAT 是保证调用 dll 的时候 call 的地址是正确的

E8 call 是偏移

FF 15 call 是绝对地址

image-20230313171609509

只要用 dll 函数,全都是通过 [x] 调用,这样形成的表叫做 IAT 表

image-20230313171956653

IAThook

# 导入表

目录项第二个就是导入表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics; // 0 for terminating null import descriptor
DWORD OriginalFirstThunk; // RVA 指向 original unbound IAT (PIMAGE_THUNK_DATA) RVA指向IMAGE_THUNK_DATA结构数组
} DUMMYUNIONNAME;
DWORD TimeDateStamp; // 0 if not bound,
// -1 if bound, and real date\time stamp
// in IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT (new BIND)
// O.W. date/time stamp of DLL bound to (Old BIND)

DWORD ForwarderChain; // -1 if no forwarders
DWORD Name; // RVA指向dll名字,以0结尾
DWORD FirstThunk; // RVA指向IMAGE_THUNK_DATA结构数组
} IMAGE_IMPORT_DESCRIPTOR;
typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;

PE 文件加载前

image-20230222142303612

1
2
3
4
5
6
7
8
9
10
11
12
13
14
通过导入表的FirstThunk找到IAT
或者通过IMAGE_DATA_DIRECTORY的倒数第三张表找到IAT

typedef struct _IMAGE_THUNK_DATA32 {
union {
DWORD ForwarderString; // PBYTE
DWORD Function; // PDWORD
DWORD Ordinal;
DWORD AddressOfData; // PIMAGE_IMPORT_BY_NAME
} u1;
} IMAGE_THUNK_DATA32;
typedef IMAGE_THUNK_DATA32 * PIMAGE_THUNK_DATA32;

加载前IAT和LNT里面都是RVA,且内容完全一样

加载后

image-20230222143253824

1
INT 表作为加载后函数名的备份,加载后IAT里面是全地址
1
2
3
4
5
6
7
8
9
10
11
12
打印导入表
1、循环IMAGE_IMPORT_DESCP
目录项目的第二个结构就是导入表
RVA->FOA
有多个IMAGE_IMPORT_DESCP个结构,因为会有多个dll
全0的结构代表结束


2.输出dll名字 Name
是RVA


image-20230314161704488

1
2
3
4
5
6
typedef struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;
CHAR Name[1];
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;

把IMAGE_THUNK_DATA当左DWORD处理就行

4. 遍历 fitstThunk

image-20230314164742765

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
void PrintImportDirectory(IN LPSTR lpszFile)
{
LPVOID pFileBuffer = NULL;
DWORD fileLen = ReadPEFile(lpszFile, &pFileBuffer);

PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNTHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL;
PIMAGE_SECTION_HEADER pSectionHeader = NULL;
PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL;
PIMAGE_BASE_RELOCATION pRelocationTable = NULL;
PIMAGE_IMPORT_DESCRIPTOR pImportTable = NULL;
PIMAGE_IMPORT_BY_NAME pImportByName = NULL;
PIMAGE_THUNK_DATA pThunkData = NULL;
PDWORD intname = NULL;
PDWORD iatname = NULL;

//PIMAGE_BASE_RELOCATION pRelocationTable = NULL;

pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer; // 强转 DOS_HEADER 结构体指针
//可选PE
pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)(sizeof(pNTHeader->Signature) + (DWORD)pNTHeader);
pOptionHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER);
pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader);
pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress));
cout << RvaToFileOffset(pFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress) << "\n";

char end[sizeof(IMAGE_IMPORT_DESCRIPTOR)] = {0};
for(int i=1;memcmp(end,pImportTable,sizeof(end));i++)
{
printf("pImportTable->Name:%s\n",(DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pImportTable->Name));
printf("pImportTable->Characteristics:%X\n",pImportTable->Characteristics);
printf("pImportTable->TimeDateStamp:%X\n",pImportTable->TimeDateStamp);
printf("pImportTable->ForwarderChain:%X\n",pImportTable->ForwarderChain);
printf("pImportTable->FirstThunk:%X\n",pImportTable->FirstThunk);
printf("pImportTable->OriginalFirstThunk:%X\n",pImportTable->OriginalFirstThunk);
cout << "------------------------\n";

intname = (PDWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pImportTable->OriginalFirstThunk));
for(int j=1;*intname;j++)
{
if (*intname & 0x80000000)
{
printf("按序号导出:%X\n",*intname & 0x7fffffff);
}
else
{
pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, *intname));
printf("pImportByName->Hint:%x, pImportByName->Name:%s\n",pImportByName->Hint, pImportByName->Name);
}
*intname++;
}
cout << "----------------------\n";
iatname = (PDWORD)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, pImportTable->FirstThunk));
for(int k=1;*iatname;k++)
{
if (*iatname & 0x80000000)
{
printf("按序号导出:%X\n",*iatname & 0x7fffffff);
}
else
{
pImportByName = (PIMAGE_IMPORT_BY_NAME)((DWORD)pFileBuffer + RvaToFileOffset(pFileBuffer, *iatname));
printf("pImportByName->Hint:%x, pImportByName->Name:%s\n",pImportByName->Hint, pImportByName->Name);
}
*iatname++;
}
cout << "----------------------\n";
pImportTable++;
system("pause");
}
}

# 绑定导入表

启动速度更快,在加载前 FirstThunk 已经是 RVA 了。如果 dll 需要重定位或者 dll 被修改了,那么这样就会有问题

timedatestamp = -1 代表已经绑定了地址,真正的绑定时间为 IMAGE_BOUND_IMPORT_DESCRIPTOR 的 TimeDateStamp

timedatestamp = 0 代表 dll 函数地址还没绑定

绑定导入表位于数据目录的第 12 项

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct _IMAGE_BOUND_IMPORT_DESCRIPTOR {
DWORD TimeDateStamp; //判断和fileheader里面的时间戳对比是否是同一版本
WORD OffsetModuleName;
WORD NumberOfModuleForwarderRefs; // Array of zero or more IMAGE_BOUND_FORWARDER_REF follows boundim后面跟了多少个ref结构
} IMAGE_BOUND_IMPORT_DESCRIPTOR, *PIMAGE_BOUND_IMPORT_DESCRIPTOR;

typedef struct _IMAGE_BOUND_FORWARDER_REF {
DWORD TimeDateStamp; //和前一个一样
WORD OffsetModuleName; //名字
WORD Reserved;
} IMAGE_BOUND_FORWARDER_REF, *PIMAGE_BOUND_FORWARDER_REF;

当 IMAGE_BOUND_IMPORT_DESCRIPTOR 结构中的 TimeDateStamp 与 DLL 文件标准 PE 头中的 TimeDateStamp 值不相符时,或者 DLL 需要重新定位的时候,就会重新计算 IAT 中的值.

第一个 PIMAGE_BOUND_IMPORT_DESCRIPTOR 的值 + OffsetModuleName = 名字的地址(OffsetModuleName)

打印绑定导入表时如果 RVA < header 的长度,那么直接输出,不在转 FOA

移动导入表的时候可以移动 INT 表,但不能移动 IAT 表

# 导入表注入

1、注册表注入

5、无 DLL 注入

2、导入表注入

6、Apc 注入

3、特洛伊注入

7、Windows 挂钩注入 DLL

4、远程线程注入

8、输入法注入

当 Exe 被加载时,系统会根据 Exe 导入表信息来加载需要用到的 DLL, 导入表注入的原理就是修改 exe 导入表,将自己的 DLL 添加到 exe 的导入表中,这样 exe 运行时可以将自己的 DLL 加载到 exe 的进程空间.

先加一个 PIMAGE_IMPORT_SEC,在创建 IAT 和 INT

# C++

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
struct haha
{
int x;
int y;
int z;
};

void fun(haha h)
{

}

int main(int argc, char* argv[])
{
haha sb;
fun(sb);
}

00401068 83 EC 0C sub esp,0Ch
0040106B 8B C4 mov eax,esp
0040106D 8B 4D F4 mov ecx,dword ptr [ebp-0Ch]
00401070 89 08 mov dword ptr [eax],ecx
00401072 8B 55 F8 mov edx,dword ptr [ebp-8]
00401075 89 50 04 mov dword ptr [eax+4],edx
00401078 8B 4D FC mov ecx,dword ptr [ebp-4]
0040107B 89 48 08 mov dword ptr [eax+8],ecx
0040107E E8 82 FF FF FF call @ILT+0(fun) (00401005)
00401083 83 C4 0C add esp,0Ch

结构体作为参数时传的是结构体的副本,应该使用结构体指针

int Max(haha* sb)
{
if(sb->x > sb->y);
}

//结构体指针用->取结构体的成员
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
struct haha
{
int x;
int y;
int z;
int Max(haha* sb)
{
if(sb->x > sb->y);
}
};

//结构体中不计算函数的大小
//函数在结构体里时会默认传递一个参数,即结构体的首地址,放在ECX中,用于标识成员在那个对象中

//当参数和变量同名时使用this
void init(int x, int y)
{
this->x = x;
this->y = y;
}

//返回当前对象首地址
return *(int*)this;

//this不允许重新赋值

image-20230225145426715

3. 孔结构体大小是多少

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72


class Cal
{
public:
int x;
int y;

int Add()
{
return (this->x + this->y);
}
int Sub()
{
return (this->x - this->y);
}
int Mul()
{
return (this->x * this->y);
}
int Div()
{
return (this->x / this->y);
}
};



int main(int argc, char* argv[])
{
Cal haha;
haha.x = 5;
haha.y = 2;
int a = haha.Add();

printf("%d \n", a);
return 0;
}

0040D768 C7 45 F8 05 00 00 00 mov dword ptr [ebp-8],5
0040D76F C7 45 FC 02 00 00 00 mov dword ptr [ebp-4],2
0040D776 8D 4D F8 lea ecx,[ebp-8]
0040D779 E8 B4 38 FF FF call @ILT+45(Cal::Add) (00401032)
0040D77E 89 45 F4 mov dword ptr [ebp-0Ch],eax
0040D781 8B 45 F4 mov eax,dword ptr [ebp-0Ch]
0040D784 50 push eax
0040D785 68 1C 20 42 00 push offset string "%d %d %d %d\n" (0042201c)
0040D78A E8 31 39 FF FF call printf (004010c0)
0040D78F 83 C4 08 add esp,8
0040D792 33 C0 xor eax,eax
0040D794 5F pop edi
0040D795 5E pop esi
0040D796 5B pop ebx
0040D797 83 C4 4C add esp,4Ch
0040D79A 3B EC cmp ebp,esp
0040D79C E8 9F 39 FF FF call __chkesp (00401140)
0040D7A1 8B E5 mov esp,ebp
0040D7A3 5D pop ebp
0040D7A4 C3 ret

0040D80A 89 4D FC mov dword ptr [ebp-4],ecx
0040D80D 8B 45 FC mov eax,dword ptr [ebp-4]
0040D810 8B 00 mov eax,dword ptr [eax]
0040D812 8B 4D FC mov ecx,dword ptr [ebp-4]
0040D815 03 41 04 add eax,dword ptr [ecx+4]
0040D818 5F pop edi
0040D819 5E pop esi
0040D81A 5B pop ebx
0040D81B 8B E5 mov esp,ebp
0040D81D 5D pop ebp
0040D81E C3 ret

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
55
56
57
58
59
60
//空结构体大小为1,如果没有成员变量只有成员函数,那么大小也是1
struct Cal
{
int Add()
{
return 1;
}
int Sub()
{
return 2;
}
int Mul()
{
return 3;
}
int Div()
{
return 4;
}
};



int main(int argc, char* argv[])
{


printf("%d \n", sizeof(Cal)); //1
return 0;
}

struct Cal
{
int Add()
{
return 1;
}
int Sub()
{
return 2;
}
int Mul()
{
return 3;
}
int Div()
{
return 4;
}
};



int main(int argc, char* argv[])
{


printf("%d \n", sizeof(Cal)); //1
return 0;
}
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
//入栈顺序从右到左
//最后在把this使用ecx传参
//采用内平栈

class Cal
{
public:
int x;
int y;
int z;

void init(int x,int y, int z)
{
this->x = x;
this->y = y;
this->z = z;

}
};



int main(int argc, char* argv[])
{
Cal haha;
haha.init(3,4,5);

//printf("%d \n", a);
return 0;
}


0040D768 6A 05 push 5
0040D76A 6A 04 push 4
0040D76C 6A 03 push 3
0040D76E 8D 4D F4 lea ecx,[ebp-0Ch]
0040D771 E8 C1 38 FF FF call @ILT+50(Cal::init) (00401037)


0040D7C9 5B pop ebx
0040D7CA 8B E5 mov esp,ebp
0040D7CC 5D pop ebp
0040D7CD C2 0C 00 ret 0Ch

# 构造函数

1. 没有返回值

2. 根类同名

3. 在创建对象的时候调用

可以重载构造函数,但参数不能完全一样

成员函数也可以重载,名字一样,参数类型或者个数不一样

# 析构函数

堆是动态分配,自己申请自己释放

析构函数不能重载

不能有任何参数

不能有返回值

用于清理工作

不一定一定要有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
struct Person
{
int x;
int y;
char* arr;
Person(int x, int y)
{
this->x = x;
this->y = y;
arr = (char*)malloc(100);
}
~Person()
{
free(arr);
}
};

# 继承

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
struct Man
{
int a;
int b;
};

struct Mankind:Man
{
int c;
int d;
};

int main()
{
Mankind m;
m.a = 1;
m.d = 2;
std::cout << "Hello World!\n";
}

上面的代码反汇编时和: 一样
struct Mankind
{
int a;
int b;
int c;
int d;
};

继承就是数据复制

减少重复代码

父类 基类

子类 派生类

可以用父类的指针访问子类的对象,但该指针不能访问子类特有的成员

子类的指针访问父类的对象需要强转,但可能会访问到父类不存在的对象

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
struct Man
{
int a;
int b;
};

struct Mankind:Man
{
int c;
int d;
};

void fun()
{
Man n;
Mankind m;

Man* haha = &m;
Mankind* xixi = (Mankind*)&n;
}

int main()
{
fun();
}

多层继承时会计算所有成员变量的大小

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
struct A
{
int a;
int b;
};

struct B:A
{
int c;
int d;
};

struct C:B
{
int e;
int f;
};

void fun()
{
C c;
A* pa = &c;
B* pb = &c;

sizeof(c); //24
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct A
{
int a;
int b;
};

struct B:A
{
int a;
int d;
};

struct C:B
{
int e;
int f;
};
void fun()
{
C c;
c.A::a = 1;
c.B::a = 2;
//不指定空间全部按照子类处理
}

多重继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct A
{
int a;
int b;
};

struct B
{
int c;
int d;
};

struct C:A,B
{
int e;
int f;
};

sizeof() //24

多重继承增加了程序的复杂度,容易出错

微软建议使用单继承,如果需要多重继承可以改为多层继承

image-20230226151844722

image-20230226152028331

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
struct DateInfo
{
int year;
int month;
int day;
DateInfo(int x, int y, int z)
{
this->year = x;
this->month = y;
this->day = z;
}
DateInfo()
{
this->year = 2015;
this->month = 4;
this->day = 2;
}
void SetDay(int day)
{
this->day = day;
}
int GetDay()
{
printf("%d\n", this->day);
return this->day;
}
};

struct TimeInfo:DateInfo
{
int hours;
int minutes;
int seconds;
TimeInfo(int x, int y, int z)
{
this->hours = x;
this->minutes = y;
this->seconds = z;
}
int GetHour()
{
std::cout << this->hours << " \n";
return this->hours;
}
};

void func1()
{
DateInfo d;
TimeInfo t(1,2,3);

DateInfo* date = &t;
TimeInfo* time = &t;

std::cout << date->day << " " << time->hours << "\n";
}

struct MyString
{
char* arr;
MyString()
{
arr = (char*)malloc(1024);
}
MyString(int length)
{
arr = (char*)malloc(length);
}
~MyString()
{
free(arr);
}
void SetString(char* str)
{
int i = 0;
int length = strlen(str);
while(str[i] != 0 && length>0)
{
arr[i] = str[i];
i++;
length--;
}
arr[i] = '\0';
}
void PrintString()
{
int i = 0;

while(arr[i] != 0)
{
printf("%c", arr[i]);
i++;
}

printf("\n",i);
}
void AppendString(char* str2)
{
strcat(arr, str2);
}
void Size()
{
std::cout << strlen(arr) << "\n";
}
};



int main(int argc, char* argv[])
{
MyString string1;

string1.SetString("woshishabi");

string1.PrintString();

string1.AppendString("haha");

string1.PrintString();

string1.Size();

printf("Hello World!\n");

return 0;
}


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
//formbase.h
class formbase
{
public:
int x;
int y;

formbase();
~formbase();

void fun1();
void fun2();
};

//formbase.cpp
#include "formbase.h"

formbase::formbase()
{

}

formbase::~formbase()
{

}

void formbase::fun1()
{

}

void formbase::fun2()
{

}

1、xxx.h 只是一个文件,可以是任何的后缀名,如果你愿意,可以叫 xxx.exe

2、#include 的作用只是把里面的内容复制过来仅此而已。如: #include “abc.exe”

3、xxx.h 与 xxx.cpp 并不要求一定同名

# 权限控制

public 的意思是,这个成员哪里都可以用,不用担心被修改,所以,一旦发布成 public 的成员,是不能够改名字的.

private 的意思是,这个成员只用于内部使用,不要在其他的地方使用.

1
2
3
4
5
6
7
8
9
10
struct Person
{
public:
int x;
private:
int y;
private:
void fun1();
void fun2();
};

1、对外提供的函数或者变量,发布成 public 的但不能随意改动.

2、可能会变动的函数或者变量,定义成 private 的这样编译器会在使用的时候做检测.

3、只有结构体内部的函数才可以访问 private 的成员.

4、public/private 可以修饰函数也可以修饰变量.

1
2
3
4
5
6
7
8
9
10
//通过指针强制访问private
void haha()
{
Person p;
p.init(45, 67);
int* x = (int*)&p;
int m = *(x + 0);
int n = *(x + 1);
std::cout << m << " " << n << "\n";
}

struct 中默认全是 public

class 中默认全是 private

class 继承时会将原来的 public 变为 private,如果想要保持 pubic,需要 class Sub:public Person

构造时先构造父类,在构造子类,因为需要继承构造之后的子类

没有构造函数的时候系统会给一个默认的构造函数,

public 继承会复制父类私有的成员,但无法直接使用,需要通过指针访问

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
class shabi
{
public:
shabi()
{
a= 99;
b= 88;
}
private:
int a;
int b;
};

class zhizhang:public shabi
{
public:
int x;
int y;
};

void xixi()
{
zhizhang zz;
zz.x = 77;
zz.y = 66;
int *q = (int*)&zz;
std::cout << *(q+0) << " " << *(q+1) << " " << *(q+2) << " " << *(q+3) << " \n";
}
//99 88 77 66

将上一节课的所有练习改为 class 实现

1、添加 private/public 进行权限控制.

2、将类的定义与实现分开来写:定义写到 xxx.h 中函数实现写在 xx.x…cpp 中

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//xixixi.h
class MyString
{
public:
MyString();
MyString(int length);
~MyString();
private:
char* arr;
public:
void SetString(char* str);
void PrintString();
void AppendString(char* str2);
void Size();
};

//xixixi.cpp
MyString::MyString()
{
arr = (char*)malloc(1024);
}
MyString::MyString(int length)
{
arr = (char*)malloc(length);
}
MyString::~MyString()
{
free(arr);
}
void MyString::SetString(char* str)
{
int i = 0;
int length = strlen(str);
while(str[i] != 0 && length>0)
{
arr[i] = str[i];
i++;
length--;
}
arr[i] = '\0';
}
void MyString::PrintString()
{
int i = 0;

while(arr[i] != 0)
{
printf("%c", arr[i]);
i++;
}

printf("\n",i);
}
void MyString::AppendString(char* str2)
{
strcat(arr, str2);
}
void MyString::Size()
{
std::cout << strlen(arr) << "\n";
}

int main(int argc, char* argv[])
{

MyString string1;

string1.SetString("woshishabi");

string1.PrintString();

string1.AppendString("haha");

string1.PrintString();

string1.Size();

printf("Hello World!\n");

return 0;
}

# 虚函数表

直接调用和间接调用

直接调用 call 地址 E8 …

间接调用 call […] FF …

虚函数通过对象调用时和普通函数没有区别

虚函数通过指针调用时是间接调用的

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
class Test
{
int x;
int y;
public:
void func1()
{
std::cout << "Hello World 111 !\n";
}
virtual void func2()
{
std::cout << "Hello World! 222 \n";
}
};

int main(int argc, char* argv[])
{
Test t;
Test* pt = &t;
pt->func1();
pt->func2();


std::cout << "Hello World!\n";
return 0;
}

00401398 8D 4D FC lea ecx,[ebp-4]
0040139B E8 F1 FC FF FF call @ILT+140(Test::Test) (00401091)
24: Test* pt = &t;
004013A0 8D 45 FC lea eax,[ebp-4]
004013A3 89 45 F8 mov dword ptr [ebp-8],eax
25: pt->func1();
004013A6 8B 4D F8 mov ecx,dword ptr [ebp-8]
004013A9 E8 61 FC FF FF call @ILT+10(Test::func1) (0040100f)
26: pt->func2();
004013AE 8B 4D F8 mov ecx,dword ptr [ebp-8]
004013B1 8B 11 mov edx,dword ptr [ecx]
004013B3 8B F4 mov esi,esp
004013B5 8B 4D F8 mov ecx,dword ptr [ebp-8]
004013B8 FF 12 call dword ptr [edx]


sizeof(Test) = 12;
虚函数 + 4字节,不管有多少虚函数,他指向一个数组,数组中存储对象中所有虚函数地址
虚函数表在对象的首地址,是一个四字节的地址

image-20230228161556184

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
class Base
{
public:
virtual void func1()
{
std::cout << "111\n";
}
virtual void func2()
{
std::cout << "222\n";
}
virtual void func3()
{
std::cout << "333\n";
}
};

class Sub :Base
{
virtual void func4()
{
std::cout << "444\n";
}
virtual void func5()
{
std::cout << "555\n";
}
virtual void func6()
{
std::cout << "666\n";
}
};
//打印Sub虚函数表,画图


Base* bp1 = &base;
Base* bp2 = (Base*)&sub;
printf("%X\n",bp1);
printf("%X\n",bp2);

printf("%X\n", *(((int*)bp1) + 0));
printf("%X\n", *(((int*)bp1) + 1));

printf("%X\n", *(((int*)bp2) + 0));
printf("%X\n", *(((int*)bp2) + 1));


18FF44
18FF40
432020
18FF88
432048
432020


0018FF38 40 FF 18 00 @... //sub
0018FF3C 44 FF 18 00 D... //Base
0018FF40 48 20 43 00 H C. //sub虚表
0018FF44 20 20 43 00 C.
0018FF48 88 FF 18 00 .... //ebp

00432020 05 10 40 00 ..@.
00432024 78 10 40 00 x.@.
00432028 96 10 40 00 ..@.
0043202C 00 00 00 00 ....
00432030 31 31 31 0A 111.
00432034 00 00 00 00 ....
00432038 32 32 32 0A 222.
0043203C 00 00 00 00 ....
00432040 33 33 33 0A 333.
00432044 00 00 00 00 ....
00432048 05 10 40 00 ..@.
0043204C 78 10 40 00 x.@.
00432050 96 10 40 00 ..@.
00432054 69 10 40 00 i.@.
00432058 C3 10 40 00 ..@.
0043205C 14 10 40 00 ..@.
00432060 00 00 00 00 ....
00432064 34 34 34 0A 444.
00432068 00 00 00 00 ....
0043206C 35 35 35 0A 555.
00432070 00 00 00 00 ....
00432074 36 36 36 0A 666.
00432078 00 00 00 00 ....

int main(int argc, char* argv[])
{
Base base;
Sub sub;

Base* bp1 = &base;
Base* bp2 = (Base*)&sub;

typedef void(*pFunc)(void);

pFunc ppp;

ppp = (pFunc)*((int*)(*(int*)(bp1)+0)+0); //取值范围bp1+0+0 - bp1+0+2 111-333
ppp = (pFunc)*((int*)(*(int*)(bp2)+0)+3); //取值范围bp2+0+0 - bp2+0+5 111-666

ppp();

return 0;
}

image-20230228181939235

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class Base
{
public:
virtual void func1()
{
std::cout << "111\n";
}
virtual void func2()
{
std::cout << "222\n";
}
virtual void func3()
{
std::cout << "333\n";
}
};

class Sub :Base
{
virtual void func1()
{
std::cout << "sub:111\n";
}
virtual void func2()
{
std::cout << "Sub:222\n";
}
virtual void func6()
{
std::cout << "666\n";
}
};
//打印Sub虚函数表,画图


0018FF38 40 FF 18 00 @... //sub
0018FF3C 44 FF 18 00 D... //Base
0018FF40 48 20 43 00 H C. //sub虚表
0018FF44 20 20 43 00 C.
0018FF48 88 FF 18 00 .... //ebp

0432020 0A 10 40 00 ..@.
00432024 7D 10 40 00 }.@.
00432028 9B 10 40 00 ..@.
0043202C 00 00 00 00 ....
00432030 31 31 31 0A 111.
00432034 00 00 00 00 ....
00432038 32 32 32 0A 222.
0043203C 00 00 00 00 ....
00432040 33 33 33 0A 333.
00432044 00 00 00 00 ....
00432048 1E 10 40 00 ..@.
0043204C 3C 10 40 00 <.@.
00432050 9B 10 40 00 ..@.
00432054 05 10 40 00 ..@.

int main(int argc, char* argv[])
{
Base base;
Sub sub;

Base* bp1 = &base;
Base* bp2 = (Base*)&sub;

typedef void(*pFunc)(void);

pFunc ppp;

ppp = (pFunc)*((int*)(*(int*)(bp1)+0)+0); //取值范围bp1+0+0 - bp1+0+2 111-333
ppp = (pFunc)*((int*)(*(int*)(bp2)+0)+3); //取值范围bp2+0+0 - bp2+0+3 sub111-sub222-333-666

ppp();

return 0;
}

多继承

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
class A
{
virtual void func1()
{
std::cout << "111\n";
}
virtual void func2()
{
std::cout << "222\n";
}
}
class B
{
virtual void func3()
{
std::cout << "333\n";
}
virtual void func4()
{
std::cout << "444\n";
}
}
class C:A,B
{
virtual void func5()
{
std::cout << "555\n";
}
virtual void func6()
{
std::cout << "666\n";
}
}

sizeof(C) = 8;
有几个直接分类就有几个虚函数表

image-20230301140806277

多重继承有覆盖

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
class A
{
virtual void func1()
{
std::cout << "111\n";
}
virtual void func2()
{
std::cout << "222\n";
}
}
class B
{
virtual void func3()
{
std::cout << "333\n";
}
virtual void func4()
{
std::cout << "444\n";
}
}
class C:A,B
{
virtual void func1()
{
std::cout << "sub:555\n";
}
virtual void func3()
{
std::cout << "sub:666\n";
}
virtual void func7()
{
std::cout << "77\n";
}
}

sizeof(C) = 8;
有几个直接分类就有几个虚函数表
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
class A
{
virtual void func1()
{
std::cout << "111\n";
}
virtual void func2()
{
std::cout << "222\n";
}
}
class B:A
{
virtual void func3()
{
std::cout << "333\n";
}
virtual void func4()
{
std::cout << "444\n";
}
}
class C: B
{
virtual void func5()
{
std::cout << "555\n";
}
virtual void func6()
{
std::cout << "666\n";
}
}

# 动态绑定

编译器确定的成为前期或者编译器绑定

调用时再知道地址的成为运行期绑定 / 动态绑定 / 晚绑定

成员变量和普通的成员函数时编译时绑定

virtual 函数时动态绑定,call 是 FFcall,通过虚表实现

动态绑定就是多态,相同函数体现出了不同的行为

多态实现了父类的指针访问子类的方法

虚析构在继承的时候有用,把父类的析构定义成虚函数,这样子类在析构时不会调用父类的析构

image-20230301150809049

image-20230301150833000

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
class Base
{
public:
int x;
int y;
Base()
{
this->x = 1;
this->y = 2;
}
virtual void print()
{
printf("%d %d\n",this->x,this->y);
}
};

class Sub1:Base
{
public:
int A;

Sub1()
{
this->x = 3;
this->y = 4;
this->A = 5;
}
void print()
{
printf("%d %d %d\n",this->x,this->y,this->A);
}
};

class Sub2:Base
{
public:
int B;

Sub2()
{
this->x = 6;
this->y = 7;
this->B = 8;
}
void print()
{
printf("%d %d %d\n",this->x,this->y,this->B);
}
};

int main(int argc, char* argv[])
{
Base b;
Sub1 s1;
Sub2 s2;
Base* arr[] = {&b,(Base*)&s1,(Base*)&s2};

for(int i=0;i<3;i++)
{
arr[i]->print();
}

printf("Hello World!\n");
return 0;
}

//如果Base的print存在virtual,那么结果为1 2 3 4 5 6 7 8,可以使用父类的指针访问子类的print
//如果没有virtual,那么结果为1 2 3 4 6 7,调用的都是父类的方法
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
class Animal
{
public:
virtual void sing()
{
std::cout<< "nishishabi" << "\n";
}
};

class Dog:public Animal
{
public:
void sing()
{
std::cout<< "wangwang" << "\n";
}
};

class Cat:public Animal
{
public:
void sing()
{
std::cout<< "miaomiao!!!" << "\n";
}
};


int main(int argc, char* argv[])
{
Dog d;

Animal* a = (Animal*)&d;
a->sing(); //if virtual exists,print wangwang,else print nishishabi

return 0;
}

# 模板

函数模板

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
template <class T>
void sort(T arr, int nLength)
{
for(int k=0; k<nLength-1; k++)
{
for(int i=0;i<nLength-k-1; i++)
{
if(arr[i]>arr[i+1])
{
T temp = arr[i];
arr[i] = arr[i+1];
arr[i+1] = temp;
}
}
}
}

template<class T, class E>
//T和E是两个不同的类型,T是int* E是int
int Find(T arr, int nLength, E nElement)
{
int nBegin = 0, nEnd = nLength - 1, nIndex;
while (nBegin <= nEnd)
{
nIndex = (nBegin + nEnd) / 2;
if (nElement > arr[nIndex])
{
nBegin = nIndex + 1;
; }
else if (nElement < arr[nIndex])
{
nEnd = nIndex - 1;
}
else
{
return nIndex;
}
}
return -1;
}

类模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template<class T, class M>
class MyClass
{
public:
T Max()
{

}
M Min()
{

}

private:
T x;
T y;
M a;
M b;
};

image-20230302153313237

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template <class T, class E>
void swap(T x, T y, E sb)
{
E temp = *x;
*x = *y;
*y = temp;
}

int main(int argc, char* argv[])
{
double a = 4.45, b = 8.99, c;

std::cout << a << " " << b << '\n';

swap(&a,&b,c);

std::cout << a << " " << b << '\n';


std::cout << "Hello World!\n";
return 0;
}
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
class Base
{
public:
int x;
int y;
};

template <class T, class E>
void sort(T arr, int nLength, E sb)
{
for (int k = 0; k < nLength - 1; k++)
{
for (int i = 0; i < nLength - k - 1; i++)
{
//class Base' does not define this operator or a conversion to a type acceptable to the predefined operator
if (*arr[i] > *arr[i + 1])
{
E temp = *arr[i];
*arr[i] = *arr[i + 1];
*arr[i + 1] = *temp;
}
}
}
}

int main(int argc, char* argv[])
{

Base b1, b2, b3, b4, b5;
Base* arr1[5] = { &b1, &b2, &b3, &b4, &b5 };
sort(arr1, 5, &b1);

for(int i=0;i<5;i++)
{
printf("%d\n",arr1[i]);
}


std::cout << "Hello World!\n";
return 0;


# 引用类型

指针传递的时候指针可以修改

能使用指针的就能使用引用

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
void Test(int& x)
{
x = 1;
}

void Test2(int* x)
{
*x = 10086;
}


int main()
{
int a = 100;
Test(a);
std::cout<< a << "\n"; //1
Test2(&a);
std::cout<< a << "\n"; //10086
}

174: Test(a);
0040181F 8D 45 FC lea eax,[ebp-4]
00401822 50 push eax
00401823 E8 04 F9 FF FF call @ILT+295(Test) (0040112c)
00401828 83 C4 04 add esp,4
176: Test2(&a);
00401847 8D 55 FC lea edx,[ebp-4]
0040184A 52 push edx
0040184B E8 E0 F9 FF FF call @ILT+555(Test2) (00401230)
00401850 83 C4 04 add esp,4

引用和指针在汇编是一样的,都是 lea,但编译器不允许引用再指向别的地方

1
2
3
4
5
6
1、引用类型是C++里面的类型
2、引用类型只能赋值一次,不能重新赋值
3、引用只是变量的一个别名.
4、引用可以理解成是编译器维护的一个指针,但并不占用空间(如何去理解这句话?).
5、使用引用可以像指针那样去访问、修改对象的内容,但更加安全.

2、从反汇编的角度说说引用与指针的区 参数传递,读写变量

1
2
3
4
5
6
7
8
9
int sb = 100;
const int& x = sb;
std::cout<< x << "\n";

sb = 15;
std::cout<< x << "\n";

x = 13; //error
std::cout<< x << "\n";

C 中引用(&)的用法和拷贝 / 赋值函数的区别_c 拷贝和引用的区别_Zenhobby 的博客 - CSDN 博客

可以参考一下上面的连接,对但不完全对

# 友元函数

让类外面的方法也可以访问类中的私有成员

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person
{
private:
int x;
int y;
public:
Person(int x, int y)
{
this->x = x;
this->y = y;
}
//声明友元函数
friend void Print(const Person& refPer);
};

void Print(const Person& refPer)
{
std::cout << refPer.x;
}

友元类让另一个类访问一个类的私有成员

1
2
3
4
5
6
7
什么情况下需要友元函数:
1)运算符重载的某些场合需要使用友元.
2两个类要共享数据的时候.中
友元函数和类的成员函数的区别
(1)成员函数有this指针,而友元函数没有this指针
(2)友元函数是不能被继承的,就像父亲的朋友未必是儿子的朋友

# 运算符重载

函数必须返回自己的类型

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
class Number
{
private:
int x;
int y;
public:
Number();
Number operator++();
Number operator+(const Number& p);
//bool operator>(const Number& p);
void print();
};

Number::Number()
{
this->x = 1;
this->y = 2;
}

Number Number::operator++()
{
this->x++;
this->y++;
return *this;
}

Number Number::operator+(const Number& p)
{
this->x += p.x;
this->y += p.y;
//return Number();
return *this;
}

void Number::print()
{
std::cout << this->x << " " << this->y << "\n";
}

int main(int argc, char* argv[])
{
Number n;
Number n1;
n.print();
n = n + n1;
n.print();
printf("Hello World!\n");
return 0;
}

1
2
3
. :: ?: sizeof # 不能重载
运算符重载就是函数替换
常用的运算符重载 < > == =

定义一个类使用友元函数实现重载 operator

image-20230303155110456

image-20230303155158528

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
可实现对不同类的操作,不使用友元一定会对this操作

class Number
{
private:
int x;
int y;
public:
Number();
Number(int x, int y);
#if 1
friend Number operator++(Number& p1);
friend Number operator--(Number& p1);
friend Number operator+(Number& p1, Number& p2);
friend Number operator-(Number& p1, Number& p2);
friend Number operator*(Number& p1, Number& p2);
friend Number operator/(Number& p1, Number& p2);
friend bool operator>(Number& p1, Number& p2);
friend bool operator<(Number& p1, Number& p2);
friend bool operator>=(Number& p1, Number& p2);
friend bool operator<=(Number& p1, Number& p2);
friend void print(Number& p1);
#endif
};

void print(Number& p1)
{
std::cout << p1.x << " " << p1.y << "\n";
}

Number::Number()
{
this->x = 1;
this->y = 2;
}

Number::Number(int x, int y)
{
this->x = x;
this->y = y;
}

Number operator++(Number& p1)
{
p1.x++;
p1.y++;
return p1;
}

Number operator--(Number& p1)
{
p1.x--;
p1.y--;
return p1;
}

Number operator+(Number& p1, Number& p2)
{
p1.x += p2.x;
p1.y += p2.y;
return p1;
}

Number operator-(Number& p1, Number& p2)
{
p1.x -= p2.x;
p1.y -= p2.y;
return p1;
}

Number operator*(Number& p1, Number& p2)
{
p1.x *= p2.x;
p1.y *= p2.y;
return p1;
}

Number operator/(Number& p1, Number& p2)
{
p1.x /= p2.x;
p1.y /= p2.y;
return p1;
}

bool operator>(Number& p1, Number& p2)
{
if (p1.x > p2.x && p1.y > p2.y)
{
return true;
}
return false;
}

bool operator<(Number& p1, Number& p2)
{
if (p1.x < p2.x && p1.y < p2.y)
{
return true;
}
return false;
}

bool operator>=(Number& p1, Number& p2)
{
if (p1.x >= p2.x && p1.y >= p2.y)
{
return true;
}
return false;
}


bool operator<=(Number& p1, Number& p2)
{
if (p1.x <= p2.x && p1.y <= p2.y)
{
return true;
}
return false;
}

int main(int argc, char* argv[])
{
Number n1;
print(n1);
n1++;
print(n1);
n1--;
print(n1);
Number n2(10,11);
Number n3(100, 200);
n2 = n2 + n3;
print(n2);
n2 = n2 - n3;
print(n2);
Number n4(100, 200);
Number n5(10, 50);
n4 = n4 / n5;
print(n4);
n4 = n4 * n5;
print(n4);

if (n4 > n5)
{
std::cout << "both n4 x&y > n5 \n";
}
if (n4 < n5)
{
std::cout << "both n4 x&y < n5 \n";
}

printf("Hello World!\n");
return 0;
}

# new-delete

静态库函数直接编译到 exe 里,dll 在自己模块里

找 call 时先找函数修改的内存空间,在看哪个 call 改写了这个空间

1
2
3
4
5
6
malloc -> _nh_malloc_dbg -> _heap_alloc_dbg -> _heap_alloc_base -> HeapAlloc
free -> free_dbg -> _free_base -> HeapFree
HeapAlloc 和 HeapFree 是Kernel32的 DLL

new -> _free_dbg -> _nh_malloc_dbg -> _heap_alloc_dbg -> _heap_alloc_base -> HeapAlloc
delete -> free_dbg -> _free_base -> HeapFree
1
2
3
4
5
6
7
8
9
10
int* pi = new int;
delete pi;


int* pi = new int(5); //初始值为5的int

Person* pa = new Person();

int* i = new int[5];
delete[] i;

# Vector

1、本质就是一个数组

2、可以动态扩充容量

3、支持下标方法,查询性能好

4、新增数据和删除数据较差

1、实现一个 Vector 类

2、读懂每一个方法的反汇编实现

image-20230304154132126

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
#include "stdafx.h"
#include <iostream>
#include <Windows.h>
#include <string.h>
#include <stdio.h>
#define SUCCESS 1
#define MALLOC_ERROR -2 //申请内存失败
#define INDEX_ERROR -3 //失败的索引号
using namespace std;

template<class T>
class Vector
{
public:
Vector();
Vector(DWORD dwSize);
~Vector();
public:
DWORD at(DWORD dwIndex, OUT T * pEle); //根据给定的元素得到索引
DWORD push_back(T Element); //将元素存储到容器最后一个位置
void pop_back(); //删除最后一个元素
DWORD insert(DWORD dwIndex, T Element); //向指定位置新增一个元素
DWORD capacity(); //返回在不增容的情况下还能存储多少元素
void clear(); //清空所有元素
bool empty(); //判断vector是否为空,返回true时为空
DWORD erase(DWORD dwIndex); //删除指定元素
DWORD size(); //返回vector元素数量大小
void print(); //打印
private:
bool expand();
private:
DWORD m_dwIndex; //下一个可用索引
DWORD m_dwIncrement; //每次新增容大小
DWORD m_dwLen; //当前容器长度
DWORD m_dwInitSize; //默认初始化大小
T *m_pVector; //容器指针
};

template<class T>
Vector<T>::Vector():m_dwInitSize(100),m_dwIncrement(5)
{
//int* i = new int[5];
//创建长度为m_dwInitSize个T对象
m_pVector = new T[m_dwInitSize];
//初始化
memset(m_pVector, 0, m_dwInitSize * sizeof(T));
//设置其他值
m_dwIndex = 0;
m_dwLen = m_dwInitSize;

}

template<class T>
Vector<T>::Vector(DWORD dwSize)
{
//创建长度为dwSize个T对象
m_pVector = new T[dwSize];
//初始化
memset(m_pVector, 0, dwSize * sizeof(T));
//设置其他值
m_dwIndex = 0;
m_dwLen = dwSize;

}

template<class T>
Vector<T>::~Vector()
{
delete[] m_pVector;
m_pVector = NULL;
}

template<class T>
DWORD Vector<T>::at(DWORD dwIndex, OUT T* pEle)
{
//判断索引是否越界
if (dwIndex < 0 || dwIndex > m_dwIndex)
{
return INDEX_ERROR;
}
//复制到pEle中
memcpy(pEle, &m_pVector[dwIndex], sizeof(T));
return SUCCESS;
}

template<class T>
DWORD Vector<T>::push_back(T Element)
{
//判断是否需要增容
if (m_dwIndex >= m_dwLen)
{
expand();
}
//复制到最后一个位置
memcpy(&m_pVector[m_dwIndex], &Element, sizeof(T));
m_dwIndex++;

return SUCCESS;
}

template<class T>
void Vector<T>::pop_back()
{
memset(&m_pVector[m_dwLen - 1], 0, sizeof(T));
m_dwIndex--;
}

template<class T>
DWORD Vector<T>::insert(DWORD dwIndex, T Element)
{
//判断合理区间
if (dwIndex<0 || dwIndex>m_dwIndex)
{
return INDEX_ERROR;
}
//判断是否需要增容,如果需要就调用增容的函数
if (m_dwIndex >= m_dwLen)
{
expand();
}

//后移
for (size_t i = m_dwIndex; i > dwIndex; i--)
{
memcpy(&m_pVector[i], &m_pVector[i-1], sizeof(T));
}

//复制
memcpy(&m_pVector[dwIndex], &Element, sizeof(T));

//修改其他值
m_dwIndex++;

return SUCCESS;
}

template<class T>
DWORD Vector<T>::capacity()
{

return m_dwLen - m_dwIndex;
}

template<class T>
void Vector<T>::clear()
{
memset(m_pVector, 0, sizeof(T) * m_dwLen);
m_dwIndex = 0;
m_dwLen = 0;
}

template<class T>
bool Vector<T>::empty()
{
if (m_dwLen == 0 & m_dwIndex == 0)
{
return true;
}
return false;
}

template<class T>
DWORD Vector<T>::erase(DWORD dwIndex)
{
if (dwIndex > m_dwIndex && dwIndex < 0)
{
return INDEX_ERROR;
}
for (size_t i = dwIndex; i < m_dwLen; i++)
{
memcpy(&m_pVector[i], &m_pVector[i + 1], sizeof(T));
}
m_dwIndex--;
return SUCCESS;
}

template<class T>
DWORD Vector<T>::size()
{
return m_dwLen;
}

template<class T>
void Vector<T>::print()
{
for (int i = 0; i < m_dwLen; i++)
{
cout << m_pVector[i] << "\n";
}
}

template<class T>
bool Vector<T>::expand()
{

T* newPointer = NULL;
//计算增加后的长度
DWORD newLen = m_dwIncrement + m_dwLen;

//申请空间
newPointer = new T[newLen];
memset(newPointer, 0, sizeof(T)*newLen);

//数据复制到新空间
memcpy(newPointer, m_pVector, sizeof(T)*m_dwLen);


//释放原来空间
delete[] m_pVector;


//属性赋值
m_pVector = newPointer;
newPointer = NULL;
m_dwLen = newLen;
return SUCCESS;
#if 0
if (m_dwIndex >= m_dwLen)
{
//创建一个新容器
T* p = new T[m_dwLen + m_dwIncrement];
//复制原来容器元素到新容器
memcpy(p, m_pVector, sizeof(T)*m_dwLen);
//删除原数据
delete[] m_pVector;
m_pVector = p;
m_dwLen = m_dwLen + m_dwIncrement;
return true;
}
else
{
printf("当前容量充足");

}
return false;
#endif
}

int main(int argc, char* argv[])
{
Vector<int> haha(5);
//for (size_t i = 0; i < haha.size(); i++)
//{
// haha.insert(i, i);
//}
//haha.print();

//int p = 0;
//haha.at(50, &p);
//cout << p << "\n";

//cout <<haha.capacity() << "\n";

//haha.erase(90);
//haha.erase(91);
//cout << haha.capacity() << "\n";
//haha.print();

//if (haha.empty() == 1)
//{
// cout << "empty!\n";

//}
//else
//{
// cout << "not empty\n";
//}

//haha.clear();

//if (haha.empty() == 1)
//{
// cout << "empty!\n";

//}
//else
//{
// cout << "not empty\n";
//}


//haha.clear();
haha.push_back(5);
haha.push_back(4);
haha.push_back(3);
haha.push_back(2);
haha.push_back(1);
//haha.print();

//haha.print();
//haha.pop_back();
//haha.print();

cout << haha.size() << "\n\n";

haha.insert(1, 100);
cout << haha.size() << "\n\n";
haha.print();

std::cout << "Hello World!\n";
return 0;
}

# 链表

1、数据分散存储

2、查询性能没有 Vector 好

3、新增与删除的性能好于 Vector

单链表,循环链表,双向链表

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
#include <iostream>
#include <Windows.h>
#include <string.h>
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#define SUCCESS 1
#define INDEX_ERROR -2 //失败的索引号
#define BUFFER_ERROR -3 //申请内存失败
using namespace std;

template<class T>
class LinkList
{
public:
LinkList();
~LinkList();
public:
bool IsEmpty(); //是否为空,空1 非空0
void clear(); //清空链表
DWORD GetElement(IN DWORD dwIndex, OUT T& Element); //根据索引获取元素
DWORD GetElementIndex(IN T& Element); //根据元素获取索引
DWORD Insert(IN T Element); //新增元素到最后
DWORD Insert(IN DWORD dwIndex, IN T Element); //根据索引新增元素
DWORD Delete(IN DWORD dwIndex); //根据索引删除元素
DWORD GetSize(); //获取链表元素数量
void Print();
private:
typedef struct _NODE
{
T Data;
_NODE *pNext;
}NODE, *PNODE;
//PNODE GetIndexCurrentNode(DWORD dwIndex); //获取索引为dwIndex的指针
//PNODE GetIndexPreviousNode(DWORD dwIndex); //获取索引为dwIndex的前一个节点指针
//PNODE GetIndexNextNode(DWORD dwIndex); //获取索引为dwIndex的后一个节点指针
private:
PNODE m_pList; //链表头指针,指向第一个节点
DWORD m_dwLength; //元素的数量
};

template<class T>
LinkList<T>::LinkList():m_pList(NULL),m_dwLength(0)
{

}


template<class T>
LinkList<T>::~LinkList()
{
clear();
}

template<class T>
bool LinkList<T>::IsEmpty()
{
//判断头结点
if (m_pList == NULL || m_dwLength == 0)
{
cout << "invalid index\n";
return true;
}
return false;
}

template<class T>
void LinkList<T>::clear()
{
//判断链表是否非空
IsEmpty();

//循环删除节点
PNODE ptempNode = m_pList;
PNODE pdeleteNode = NULL;
for (size_t i = 0; i < m_dwLength; i++)
{
pdeleteNode = ptempNode;
ptempNode = ptempNode->pNext;
delete pdeleteNode;
}

//删除最后一个二节点,长度置0
m_dwLength = 0;
}

template<class T>
DWORD LinkList<T>::GetElement(IN DWORD dwIndex, OUT T & Element) //根据索引获取元素
{
if (dwIndex <0 || dwIndex > m_dwLength)
{
cout << "index out of range\n";
}
PNODE ptempNode = m_pList;
DWORD data = 0;
for (size_t i = 0; i < dwIndex; i++)
{
ptempNode = ptempNode->pNext;
}
data = ptempNode->Data;
Element = data;
return SUCCESS;
}

template<class T>
DWORD LinkList<T>::GetElementIndex(IN T & Element)
{
PNODE ptempNode = m_pList;

if (m_pList == NULL || m_dwLength == 0)
{
cout << "invalid index\n";
return INDEX_ERROR;
}

for (size_t i = 0; i < m_dwLength; i++)
{
//memcmp(ptempNode,Element,sizeof(T))
if (ptempNode->Data == Element)
{
Element == ptempNode->Data;
return i;
}
ptempNode = ptempNode->pNext;

}
return ERROR;
}

template<class T>
DWORD LinkList<T>::Insert(IN T Element)
{
//有元素,遍历到最后一个
PNODE pNewnode = new NODE;
memset(pNewnode, 0, sizeof(NODE));
memcpy(&pNewnode->Data, &Element, sizeof(T));

//是否为空,空就往头结点后面插
if (m_pList == NULL || m_dwLength == 0)
{
m_pList = pNewnode;
m_dwLength++;
return SUCCESS;
}
//不为空
PNODE ptempNode = m_pList; //保存头指针
//while(ptempNode->next != NULL)
for (size_t i = 0; i < m_dwLength-1; i++)
{
ptempNode = ptempNode->pNext;

}
ptempNode->pNext = pNewnode;
pNewnode->pNext = NULL;
m_dwLength++;

return SUCCESS;
}

template<class T>
DWORD LinkList<T>::Insert(IN DWORD dwIndex, IN T Element)
{

PNODE pNewnode = new NODE;
memset(pNewnode, 0, sizeof(PNODE));
memcpy(&pNewnode->Data, &Element, sizeof(T));
//判断是否为空,即此时链表为空,要往第1个位置插
if (m_pList == NULL || m_dwLength == 0)
{
if (dwIndex == 0)
{
m_pList = pNewnode;
m_dwLength++;
return SUCCESS;
}
return INDEX_ERROR;
}

//是否索引有效
if (dwIndex <0 || dwIndex > m_dwLength)
{
cout << "index out of range\n";
return INDEX_ERROR;
}

PNODE ptempNode = m_pList;


//如果索引为0,即链表不为空,但想往第一个位置插
if (dwIndex == 0)
{
pNewnode->pNext = m_pList;
m_pList = pNewnode;

m_dwLength++;
return SUCCESS;
}
if (dwIndex == m_dwLength)
{
//如果索引为链表尾,即链表不为空,但要往最后一个插
for (size_t i = 0; i < m_dwLength - 1; i++)
{
ptempNode = ptempNode->pNext;
}
ptempNode->pNext = pNewnode;
pNewnode->pNext = NULL;
m_dwLength++;
return SUCCESS;
}
//如果索引为链表中
for (size_t i = 0; i < dwIndex-1; i++)
{
ptempNode = ptempNode->pNext; //循环到前一个节点
}
ptempNode->pNext = pNewnode; //把前一个节点和新节点相连
pNewnode->pNext = ptempNode->pNext->pNext; //新节点和原来的next相连
m_dwLength++;
// 1 2 3 5 6
// 4
return SUCCESS;
}

template<class T>
DWORD LinkList<T>::Delete(IN DWORD dwIndex)
{
// 1. 判断链表是否为空
if (m_pList == NULL || m_dwLength == 0)
{
return ERROR;
}

// 2. 判断索引值是否有效
if (dwIndex <0 || dwIndex > m_dwLength)
{
cout << "index out of range\n";
return INDEX_ERROR;
}
PNODE ptempNode = m_pList;
// 3. 如果链表中只有头节点,且要删除头节点
if (dwIndex == 0 && m_dwLength == 1)
{
delete m_pList;
m_pList = NULL;
m_dwLength--;
return SUCCESS;
}
// 4. 如果要删除头节点
if (dwIndex == 0)
{
//H 1 2 3 4 5
delete m_pList;
m_pList = ptempNode->pNext;
m_dwLength--;
return SUCCESS;
}

// 5. 如果是其他情况
ptempNode = m_pList;
for (size_t i = 0; i < dwIndex-1; i++)
{
// H 1 2 3 4 5
ptempNode = ptempNode->pNext;
}
ptempNode->pNext = ptempNode->pNext->pNext;
delete ptempNode->pNext;

m_dwLength--;
return SUCCESS;

return 0;
}

template<class T>
DWORD LinkList<T>::GetSize()
{
DWORD size = 0;
PNODE ptempNode = m_pList;
while (ptempNode != NULL)
{
ptempNode = ptempNode->pNext;
size++;
}
return size;
}

template<class T>
void LinkList<T>::Print()
{
PNODE pTempNode = m_pList;
for (DWORD i = 0; i < m_dwLength; i++)
{
printf("%d\n", pTempNode->Data);
pTempNode = pTempNode->pNext;
}
}


int main(int argc, char* argv[])
{
//LinkList<int> Link;
//Link.Insert(0,9);
//Link.Insert(1,8);
//Link.Insert(7);
//Link.Insert(6);
//9 8 7 6
//int x = 6;
//cout << Link.GetElementIndex(x) << "\n";
//cout << Link.GetSize() << "\n";

//int w;
//Link.GetElement(3,w);
//cout << w << "\n";

//Link.Delete(2);

//Link.Print();

//Link.Delete(0);
//Link.Print();

//bool haha = IsEmpty();
//if(haha == true) cout << "empty\n";
//else cout << "not empty\n";

//Link.clear();
//Link.Print();

LinkList<int>* shabi = new LinkList<int>;
shabi->Insert(0,9);
shabi->Insert(1,8);
shabi->Insert(7);
shabi->Insert(6);

//shabi->Print();
cout << "\n";
shabi->Delete(2);
//shabi->Print();
cout << "\n";

shabi->clear();
//shabi->Print();
cout << "\n";

printf("Hello World!\n");
return 0;
}


滴水逆向 ——C++_链表 - 灰信网(软件开发博客聚合) (freesion.com)

# 二叉树

image-20230307171158951

前序遍历

​ 根左右

1
G D A F E M H Z

中序遍历

​ 左根右

1

后序遍历

​ 左右根

1

image-20230307171937417

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#include "windows.h"
#include <stdio.h>
#include "stdlib.h"
#include "malloc.h"
#include <string.h>
#include <iostream>
using namespace std;

#define SUCCESS 1 // 执行成功
#define ERRORR -1 // 执行失败


//基础怪物模板
class Monster {

public:
int Id; //怪物编号
char Name[20];//名字
int Level;//等级
public:
Monster() {};//构造 ,默认怪物
Monster(int Id, char* Name, int Level); //怪物参数
};


Monster::Monster(int Id, char* Name, int Level)
{
this->Id = Id;
this->Level = Level;
memcpy(&this->Name, Name, strlen(Name) + 1);
}


template<class T>
class TreeNode {
public:
T element; //节点数据
TreeNode<T>* pLeft; //左树指针
TreeNode<T>* pRight;//右树指针

TreeNode(T& ele) {
//初始化node节点
memset(&element, 0, sizeof(TreeNode));
//为元素复制
memcpy(&element, &ele, sizeof(T));
pLeft = pRight = NULL;

}
};

template<class T>
class BsortTree {
public:
BsortTree();
~BsortTree();
public:
void InOrderTraverse(TreeNode<T>* pNode); //中序遍历
void PreOrderTraverse(TreeNode<T>* pNode);//前序遍历
void PostOrderTraverse(TreeNode<T>* pNode);//后序遍历
TreeNode<T>* GetRoot();//返回根节点
int GetDepth(TreeNode<T>* Node);// 返回当前节点高度

private:
void Init();//初始化一些数值
void Destroy(TreeNode<T>* Node);
private:
TreeNode<T>* pRootNode; //根节点指针
int size;//树中元素总数

};

template<class T>
void BsortTree<T>::Init()
{
Monster m1(1, (char*)"AAA", 50);
Monster m2(2, (char*)"BBB", 100);
Monster m3(3, (char*)"CCC", 74);
Monster m4(4, (char*)"DDD", 56);
Monster m5(5, (char*)"EEE", 88);
Monster m6(6, (char*)"FFF", 999);
Monster m7(7, (char*)"GGG", 7);
TreeNode<Monster>* n1 = new TreeNode<Monster>(m1);
TreeNode<Monster>* n2 = new TreeNode<Monster>(m2);
TreeNode<Monster>* n3 = new TreeNode<Monster>(m3);
TreeNode<Monster>* n4 = new TreeNode<Monster>(m4);
TreeNode<Monster>* n5 = new TreeNode<Monster>(m5);
TreeNode<Monster>* n6 = new TreeNode<Monster>(m6);
TreeNode<Monster>* n7 = new TreeNode<Monster>(m7);

pRootNode = n5;
n5->pLeft = n4;
n5->pRight = n6;
n4->pLeft = n1;
n1->pRight = n2;
n6->pLeft = n3;
n3->pRight = n7;
size = 7;
// 5
// 4 6
//1 3
// 2 7

}

template<class T>
void BsortTree<T>::Destroy(TreeNode<T>* pNode)
{
if (pNode == NULL)
{
return;
}
else
{
Destroy(pNode->pLeft);
Destroy(pNode->pRight);
delete pNode;
pNode = NULL;
}
}


template<class T>
BsortTree<T>::BsortTree() {
Init();
}

template<class T>
BsortTree<T>::~BsortTree()
{
//释放所有节点空间
Destroy(pRootNode);
}

// 返回当前节点高度Node
template<class T>
int BsortTree<T>::GetDepth(TreeNode<T>* Node) {
if (Node == NULL) {
return 0;
}
else
{
int Left = GetDepth(Node->pLeft);
int Right = GetDepth(Node->pRight);
return (Left > Right) ? (Left + 1) : (Right + 1);
}

}


//获取根节点
template<class T>
TreeNode<T>* BsortTree<T>::GetRoot()
{
return pRootNode;
}





//中序遍历
template<class T>
void BsortTree<T>::InOrderTraverse(TreeNode<T>* pNode)
{
//列出怪的名字
if (pNode == NULL)
{
return;
}
InOrderTraverse(pNode->pLeft);
cout << pNode->element.Name << "\n";
InOrderTraverse(pNode->pRight);



}

//前序遍历
template<class T>//前序遍历
void BsortTree<T>::PreOrderTraverse(TreeNode<T>* pNode)
{
if (pNode == NULL)
{
return;
}
cout << pNode->element.Name << "\n";
PreOrderTraverse(pNode->pLeft);
PreOrderTraverse(pNode->pRight);
}


//后序遍历
template<class T>
void BsortTree<T>::PostOrderTraverse(TreeNode<T>* pNode)
{
if (pNode == NULL)
{
return;
}
PostOrderTraverse(pNode->pLeft);
PostOrderTraverse(pNode->pRight);
cout << pNode->element.Name << "\n";
}

void test()
{
BsortTree<Monster>* p = new BsortTree<Monster>();
p->InOrderTraverse(p->GetRoot());
p->PostOrderTraverse(p->GetRoot());
}

int main()
{
test();
std::cout << "Hello World!\n";
}

(58 条消息) 滴水二叉树实现详细注释_wow. 的博客 - CSDN 博客

搜索二叉树

1. 有很好的查询性能

2. 有很好的新增和删除性能

3. 若左子树不空,则左子树上所有结点的值均小于它的根结点的值

即 左小右大

搜索二叉树中序遍历后就是排序后的结果

image-20230308170011973

删除叶子节点,直接删

删除节点只有一个子树,直接把下面节点提上去

既有左也有右,用右子树最小节点取代原节点,递归删除最小节点 、 或者左边最大

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
#include "stdafx.h"
#include "windows.h"

#define SUCCESS 1 // 执行成功
#define ERRORR -1 // 执行失败


//基础怪物模板
class Monster {

public:
int Id; //怪物编号
char Name[20];//名字
int Level;//等级

public:
Monster() {};//构造 ,默认怪物
Monster(int Id, char* Name, int Level); //怪物参数

bool Monster::operator<(const Monster& p) {
return this->Id < p.Id ? true : false;
}
bool Monster::operator>(const Monster& p) {
return this->Id > p.Id ? true : false;
}
bool Monster::operator==(const Monster& p) {
return this->Id == p.Id ? true : false;
}



};


Monster::Monster(int Id, char* Name, int Level) {
this->Id = Id;
memcpy(&this->Name, Name, strlen(Name)+1);
this->Level=Level;
}


template<class T>
class TreeNode {
public:
T element; //节点数据
TreeNode<T>* pLeft; //左树指针
TreeNode<T>* pRight;//右树指针
TreeNode<T>* pParent;//父节点指针

TreeNode(T& ele) {
//初始化
memset(&element, 0, sizeof(TreeNode));
memcpy(&element, &ele, sizeof(T));
pLeft = pRight = NULL;

}
/*
bool operator<(const TreeNode<T>& p) {
return this->element<p->element ? true : false;
}
bool operator>(const TreeNode<T>& p) {
return this->element>p->element ? true : false;
}
bool operator==(const TreeNode<T>& p) {
return this->element == p->element ? true : false;
}*/
};

template<class T>
class BsortTree {
public:
BsortTree();
~BsortTree();
public:
void InOrderTraverse(TreeNode<T>* pNode); //中序遍历
void PreOrderTraverse(TreeNode<T>* pNode);//前序遍历
void PostOrderTraverse(TreeNode<T>* pNode);//后序遍历
TreeNode<T>* GetRoot();//返回根节点
int GetDepth(TreeNode<T>* Node);// 返回当前节点高度
bool IsEmpty();//判断树是否为空
DWORD Insert(T element);//新增节点
void Delete(T element);//删除节点

private:
void Init();//初始化一些数值
void clear(TreeNode<T>* pNode);//删除所有节点
DWORD InsertNode(TreeNode<T>* pNode, T element);
TreeNode<T>* DeleteNode(T element, TreeNode<T>* pNode);
TreeNode<T>* GetMinNode(TreeNode<T>* pNode);//获取以pNode为根的最小节点,注:删除NODE下,寻找PNODE右树的最小节点顶到NODE节点上。再删除原最小节点
TreeNode<T>* GetMaxNode(TreeNode<T>* pNode);//获取以pNode为根的最大节点
private:
TreeNode<T>* pRootNode; //根节点指针
int size;//树中元素总数

};

template<class T>
TreeNode<T>* BsortTree<T>::GetMinNode(TreeNode<T>* pNode) {
TreeNode<T>* pMin = NULL;
if (pNode!=NULL) {
int data = pNode->element;//说一下,这里为什么不用T类型,因为T代表整个类型大小,但是我们只需要NODE头4个字节的数据,这里有一点不通用,节点的头四个字节数据类型被限制了。
if (data>=pNode->element) {
pMin = (TreeNode<T>*)pNode->element;//把下一个比较小的节点存起来
}
GetMinNode(pNode->pLeft); //这里为什么用pNode->pLeft呢,因为根据左树节点比根树节点小。
return pMin;
}
}

template<class T>
TreeNode<T>* BsortTree<T>::DeleteNode(T element, TreeNode<T>* pNode) {
if (pRootNode == NULL) {
return NULL;
}
else if (element == pNode->element)
{
if (pNode->pLeft != NULL && pNode->pRight == NULL)
{//左子树
TreeNode<T>* pPt = pNode->pParent;//拿到当前节点的父节点
pPt->pLeft = pNode->pLeft;
delete pNode;
}
else if (pNode->pLeft = NULL && pNode->pRight != NULL)
{//右子树
TreeNode<T>* pPt = pNode->pParent;//拿到当前节点的父节点
pPt->pRight = pNode->pRight;
delete pNode;
}
else if (pNode->pLeft == NULL&&pNode->pRight == NULL)
{//左右树均为空
TreeNode<T>* p;
p = pNode->pParent;
if (p->pLeft != NULL&&p->pRight == NULL) {//这里实际意义就是看当前节点在父节点的左边还是右边
delete p->pLeft;
p->pLeft = NULL;
}
if (p->pLeft == NULL&&p->pRight != NULL) {
delete p->pRight;
p->pRight = NULL;
}
//
delete p->pRight;
p->pRight = NULL; //这两行就是代表子节点一样,且不为空,删除哪个都一样。
}
else if (pNode->pLeft != NULL&&pNode->pRight != NULL) {//左右子树均不为空
TreeNode<T>* pCurrent = pNode->pRight;
TreeNode<T>* pMin = GetMinNode(pNode->pRight);//GetMinNode是一个递归函数,返回NODE子树里的最小节点
pNode->element = (int)pMin;//把最小的节点元素抬到Pnode节点这里来
DeleteNode(pNode->element, pCurrent);//此时此刻,Pnode已经替换为最小节点的元素了,
//那么只需要删除pCurrent(pNode->pRight)下的子树最小节点就可以了,这里用的递归。

}
}
else if (element > pNode->element)
{//Element 大于Pnode的E。那么就往Pnode右树开始找
DeleteNode(element, pNode->pRight);

}
else
{
DeleteNode(element, pNode->pLeft);
}
return NULL;


}


template<class T>
void BsortTree<T>::Delete(T element) {
DeleteNode(T element, TreeNode<T>* pNode);
}






template<class T>
DWORD BsortTree<T>::InsertNode(TreeNode<T>* pNode, T element) {
//建立一个节点,把元素存进去
TreeNode<T>* pElement = new TreeNode<T>(element);
//如果元素等于当前节点就直接返回
if (pNode->element==element ) {
return SUCCESS;
}
//如果pnode节点的左子节点为null且element<pnode节点
if (pNode->pLeft == NULL && pNode->element>element ) {
pNode->pLeft = pElement;
size++;
return SUCCESS;
}
//如果pnode节点的右子节点为null且element>pnode节点
if (pNode->pRight == NULL&&pNode->element<element ) {
pNode->pRight = pElement;
size++;
return SUCCESS;
}
//pnode左节点不为空且element<pnode节点----------此处为递归
if (pNode->element>element) {

InsertNode(pNode->pLeft, element);
}
else {
InsertNode(pNode->pRight, element);
}
}


template<class T>
DWORD BsortTree<T>::Insert(T element) {
if (!pRootNode) {//如果根节点为空
pRootNode = new TreeNode<T>(element);
size++;
return SUCCESS;
}
return InsertNode(pRootNode, element);


}
template<class T>
bool BsortTree<T>::IsEmpty() {
return size == 0 ? true : false;
}

template<class T>
BsortTree<T>::~BsortTree() {
//循环删除节点元素
clear(pRootNode);


}

template<class T>
void BsortTree<T>::clear(TreeNode<T>* pNode) {
if (pNode != NULL) {
clear(pNode->pLeft);
clear(pNode->pRight);
delete pNode;
pNode = NULL;
}

}

template<class T>
BsortTree<T>::BsortTree() {
Init();
}


template<class T>// 返回当前节点高度,Node
int BsortTree<T>::GetDepth(TreeNode<T>* Node) {
if (Node == NUll) {
return 0;
}
else
{
int Left = GetDepth(Node->pLeft);
int Right = GetDepth(Node->pRight);
return (Left > Right) ? (Left + 1) : (Right + 1);
}

}


//获取根节点
template<class T>
TreeNode<T>* BsortTree<T>::GetRoot() {
return pRootNode;
}





//中序遍历
template<class T>
void BsortTree<T>::InOrderTraverse(TreeNode<T>* pNode) {



if (pNode == NULL)
{
return;
}
else
{
InOrderTraverse(pNode->pLeft);
printf("%d\n", pNode->element);
InOrderTraverse(pNode->pRight);
}

}

//前序遍历
template<class T>//前序遍历
void BsortTree<T>::PreOrderTraverse(TreeNode<T>* pNode) {
if (pNode != NULL) {
printf("%d\n", pNode->element);
PreOrderTraverse(pNode->pLeft);
PreOrderTraverse(pNode->pRight);
}
}

//后序遍历
template<class T>
void BsortTree<T>::PostOrderTraverse(TreeNode<T>* pNode) {
if (pNode != NULL) {
PostOrderTraverse(pNode->pLeft);
PostOrderTraverse(pNode->pRight);
printf("%d\n", pNode->element);
}

}





template<class T>
void BsortTree<T>::Init() {


Monster m1(1, "混世魔王",50);
Monster m2(2, "金斯尔", 100);
Monster m3(3, "游离", 74);
Monster m4(4, "尔康", 56);
Monster m5(5, "二讹", 88);
Monster m6(6, "传奇", 999);
TreeNode<Monster>* n1 = new TreeNode<Monster>(m1);
TreeNode<Monster>* n2 = new TreeNode<Monster>(m2);
TreeNode<Monster>* n3 = new TreeNode<Monster>(m3);
TreeNode<Monster>* n4 = new TreeNode<Monster>(m4);
TreeNode<Monster>* n5 = new TreeNode<Monster>(m5);
TreeNode<Monster>* n6 = new TreeNode<Monster>(m6);

pRootNode = n3;
n3->pLeft = n2;
n3->pRight = n1;
n1->pLeft = n4;
n1->pRight = n5;
//n5->pRight = n6;
//size = 6;

/*
3
2 1
4 5
6


*/

}

int main()
{

}

上面的代码参参考了滴水二叉树实现详细注释_wow. 的博客 - CSDN 博客的代码,实在写不出

# Vector,LinkList,Btree 反汇编

# win32

# 宽字符

ASCII:0xxxxxxx

extend ASCII:0xxxxxxx-1xxxxxxx

GB2312:用两个字节存储一个汉字,最高位都是 1,可能乱码,因为可能不同国家都对他做了扩展,其次存储不方便

Unicode:解决乱码

乱码就是一个多个符号对应同一个编码

utf8/utf16: 变长的 Unicode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//中ASCII d6 d0  unicode 4e 2d
char x = '中'; //产生截断D0
wchar_t x1 = '中'; //D0 D6,按照ASCII解析
wchar_t x2 = L'中'; //2d 4e按照Unicode解析

char x[] = "中国"; //使用扩展ASCII,以00(\n)结尾
wchar_t x1[] = L"中国"; //使用Unicode,结尾是00 00 (\0\0)

printf("%s\n",x);
wprintf(L"%s\n",x1);

#include <locale.h>
int main {
setlocale(LC_ALL,"");
wprintf(L"%s\n",x1);
}//设置地域,这样才能正常打印

#include <string.h>
strlen(x) //取得多字节中字符长度,4,不包含00
wcslen(x1); //取得多字节字符串中字符长度,2,不包含00 00

中A国 5 3

wcscpy(x1,x1);

image-20230309190055917

Win32 API (应用程序接口)

主要存放在 windows/system32 下面所有的 DLL,是 Windows 提供的函数,供使用

Kernel32.dll: 最核心的功能模块,比如管理内存、进程和线程相关的函数等

User32.dll: 是 Windows 用户界面相关应用程序接口,如创建窗口和发送消息等.

GDI32.dll: 全称是 Graphical Device Interface (图形设备接口), 包含用于画图和显示文本的函数比如要显示一个程序窗口,就调用了其中的函数来画这个窗口

这些 dll 是一个 ring3 接口,通过这些 dll 调用内核函数,可以理解为 ring3 和 ring0 的接口

Windows 类型

char CHAR

wchar_t WCHAR

TCHAR 是个可变类型,取决于当前工程是宽字节还是窄字节,是个宏

1
2
3
CHAR cha[] = "中国";
WCHAR chw[] = L"中国";
TCHAR cht[] = TEXT("中国");

LPSTR(PSTR)

LPTSTR(PTSTR)

1
2
3
PSTR pszChar[] = "中国";
PWSTR pszWChar[] = L"中国";
PTSTR pszTChar[] = TEXT("中国");

Windows 内核函数都是 w 的,如果是 a 调用,系统会先转成 w 在调用。并且都是用 c 写的

1
MessageBox() 也是一个宏,根据当前项目决定使用a还是w

image-20230309192926826

创建 win32application

image-20230309193255058

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
#include "stdafx.h"

//APIENTRY = winapi = __stdcall
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.

return 0;
}
//hinstance 就是imagebase,在setting->Link->output->base address,里面是十进制
//win32没有控制台,没法输出,要是用OutputDebugString()需要按照以下方法,新建一个类,在源文件中包含

#include <stdio.h>
.h
void __cdecl OutputDebugStringF(const char* format, ...);
#ifdef _DEBUG
#define DbgPrintf OutputDebugStringF
#else
#define DbgPrintf
#endif // DEBUG

.cpp
void __cdecl OutputDebugStringF(const char* format, ...)
{
va_list vlArgs;
char *strBuffer = (char*)GlobalAlloc(GPTR, 4096);
va_start(vlArgs, format);
_vsnprintf(strBuffer, 4096 - 1, format, vlArgs);
va_end(vlArgs);
strcat(strBuffer, "\n");
OutputDebugStringA(strBuffer);
GlobalFree(strBuffer);
return;
}

OutputDebugString("hello world\n");
DWORD a = 11122;
DbgPrintf("%d = 111", a);

DWORD errors = GetLastError(); 放到出问题那行下面,得到错误编号,去Error Lookup查对应编号
1
2
3
4
5
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow);
hInstance 称为“实例句柄”或“模块句柄”。操作系统使用此值在内存中加载可执行文件时标识可执行文件 (EXE) 。 某些Windows函数需要实例句柄,例如加载图标或位图。
hPrevInstance 没有意义。 它在 16 位Windows中使用,但现在始终为零。
pCmdLine 包含命令行参数作为 Unicode 字符串。 111.exe hahahaha hahahaha 就是命令行参数
nCmdShow 是一个标志,指示主应用程序窗口是最小化、最大化还是正常显示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <locale.h>
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
//TestVector();
setlocale(LC_ALL,"");
wchar_t x1[] = L"傻逼";
wprintf(L"%s\n",x1);
cout << wcslen(x1) << "\n";
wchar_t x2[] = L"你是智障";
//wcscpy(x2,x1);
//wprintf(L"%s\n",x2);
//cout << wcscmp(x1,x1) << "\n";
wchar_t x3[] = L"你是傻逼";
//x1第一次在x3中出现的位置,否则返回null
cout << wcsstr(x3,x1) << "\n";


printf("Hello World!\n");
return 0;
}

# 事件、消息

Windows 中的事件是一个 “动作”,这个动作可能是用户操作应用程序产生的,也可能是 Windows 自己产生的

而消息,就是用来描述这些 “动作” 的,比如:

Windows 为了能够准确的描述这些信息,提供了一个结构体:MSG,该结构体里面记录的事件的详细信息

1
2
3
4
5
6
7
8
9
typedef struct tagMsg
{
HWND hwnd; //窗口句柄,由那个窗口处理
UINT message; //动作类型/消息类型
WPARAM wParam; //消息的详细说明
LPARAM lParam; //消息的详细说明
DWORD time; //时间
POINT pt; //坐标,x,y
}MSG,*PMSG;

系统消息队列与应用程序消息队列

image-20230310192516428

根据窗口句柄将消息分发到不同应用程序队列

判断应用程序队列中消息 类型是不是我关心的是就处理不是让 windows 去处理

image-20230310193925308

image-20230310194619857

1
2
3
4
5
6
7
8
9
10
11
12
13
14
typedef WNDCLASSA WNDCLASS;

typedef struct tagWNDCLASSA {
UINT style;
WNDPROC lpfnWndProc; //处理消息的函数,传入函数名
int cbClsExtra;
int cbWndExtra;
HINSTANCE hInstance; //窗口属于哪个应用程序
HICON hIcon;
HCURSOR hCursor;
HBRUSH hbrBackground;
LPCSTR lpszMenuName;
LPCSTR lpszClassName;
} WNDCLASSA, *PWNDCLASSA, NEAR *NPWNDCLASSA, FAR *LPWNDCLASSA;

image-20230310203005194

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// 11111.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "stdio.h"
#include "Tools.h"

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
///OutputDebugString("hahahaha");
//OutputDebugString("hello world\n");
//DWORD a = 11122;
//DbgPrintf("%d = 111", a);


TCHAR className[] = "shabi";

//创建一个自己的窗口
WNDCLASS wndclass = {0};
wndclass.hbrBackground = (HBRUSH)COLOR_HIGHLIGHTTEXT;
wndclass.lpfnWndProc = WindowProc;
wndclass.lpszClassName = className;
wndclass.hInstance = hInstance;

//注册 必须给所有的成员赋值调用时
RegisterClass(&wndclass);




HWND hwnd = CreateWindowEx(
0, // Optional window styles.
className, // Window class
TEXT("Learn to Program Windows"), // Window text
WS_OVERLAPPEDWINDOW, // Window style

// Size and position
//相对于父窗口的x y坐标
//窗口的宽度和高度
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);

if (hwnd == NULL)
{
return 0;
}
ShowWindow(hwnd, SW_SHOW);

MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}




LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;

case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);

// All painting occurs here, between BeginPaint and EndPaint.

FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));

EndPaint(hwnd, &ps);
}
return 0;

}
//处理当前程序不关心的消息类型
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}




创建窗口 - Win32 apps | Microsoft Learn

消息类型

Windows 消息类型及说明(全面)_战胜。的博客 - CSDN 博客

关于消息和消息队列 - Win32 apps | Microsoft Learn

如果直接定义而不初始化,那么没初始化的参数的地方还是 CC,系统执行到这里就会产生中断,

image-20230311112852539

WNDCLASS wndclass = {0};

1
2
3
4
5
00401061   mov         dword ptr [ebp-30h],0
00401068 mov ecx,9
0040106D xor eax,eax
0040106F lea edi,[ebp-2Ch]
00401072 rep stos dword ptr [edi]

系统会循环填充所有参数为 0

image-20230311113120902

# 入口程序分析

1
2
3
4
5
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)

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
0040124C  |> \50            push    eax
0040124D |. FF75 9C push [local.25]
00401250 |. 56 push esi
00401251 |. 56 push esi ; /pModule
00401252 |. FF15 2C504000 call dword ptr ds:[<&KERNEL32.GetModuleHandleA>] ; \GetModuleHandleA,获取imagebase
00401258 |. 50 push eax
00401259 |. E8 A2FDFFFF call 00401000


00401000 /$ 83EC 4C sub esp,0x4C ; esp+4c返回地址,第一个参数esp+50,esp寻址
00401003 |. A1 4C604000 mov eax,dword ptr ds:[0x40604C]
00401008 |. 66:8B0D 50604>mov cx,word ptr ds:[0x406050]
0040100F |. 56 push esi
00401010 |. 8B7424 54 mov esi,dword ptr ss:[esp+0x54]
00401014 |. 57 push edi
00401015 |. 894424 08 mov dword ptr ss:[esp+0x8],eax
00401019 |. 66:894C24 0C mov word ptr ss:[esp+0xC],cx
0040101E |. B9 09000000 mov ecx,0x9
00401023 |. 33C0 xor eax,eax
00401025 |. 8D7C24 30 lea edi,dword ptr ss:[esp+0x30]
00401029 |. C74424 2C 000>mov dword ptr ss:[esp+0x2C],0x0
00401031 |. 8D5424 08 lea edx,dword ptr ss:[esp+0x8]
00401035 |. F3:AB rep stos dword ptr es:[edi]
00401037 |. 8D4424 2C lea eax,dword ptr ss:[esp+0x2C]
0040103B |. C74424 48 0E0>mov dword ptr ss:[esp+0x48],0xE
00401043 |. 50 push eax ; /pWndClass
00401044 |. C74424 34 F01>mov dword ptr ss:[esp+0x34],004010F0 ; |windproc函数地址
0040104C |. 895424 54 mov dword ptr ss:[esp+0x54],edx ; |
00401050 |. 897424 40 mov dword ptr ss:[esp+0x40],esi ; |
00401054 |. FF15 C4504000 call dword ptr ds:[<&USER32.RegisterClassA>] ; \RegisterClassA
0040105A |. 6A 00 push 0x0 ; /lParam = NULL
0040105C |. 56 push esi ; |hInst
0040105D |. 6A 00 push 0x0 ; |hMenu = NULL
0040105F |. 6A 00 push 0x0 ; |hParent = NULL
00401061 |. 68 00000080 push 0x80000000 ; |Height = 80000000 (-2147483648.)
00401066 |. 68 00000080 push 0x80000000 ; |Width = 80000000 (-2147483648.)
0040106B |. 68 00000080 push 0x80000000 ; |Y = 80000000 (-2147483648.)
00401070 |. 68 00000080 push 0x80000000 ; |X = 80000000 (-2147483648.)
00401075 |. 68 0000CF00 push 0xCF0000 ; |Style = WS_OVERLAPPED|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_SYSMENU|WS_THICKFRAME|WS_CAPTION
0040107A |. 8D4C24 2C lea ecx,dword ptr ss:[esp+0x2C] ; |
0040107E |. 68 30604000 push 00406030 ; |WindowName = "Learn to Program Windows"
00401083 |. 51 push ecx ; |Class
00401084 |. 6A 00 push 0x0 ; |ExtStyle = 0
00401086 |. FF15 C8504000 call dword ptr ds:[<&USER32.CreateWindowExA>] ; \CreateWindowExA


image-20230329194051583

image-20230329194443915

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 找回调函数

TCHAR className[] = "shabi";

//创建一个自己的窗口
WNDCLASS wndclass = {0};
wndclass.hbrBackground = (HBRUSH)COLOR_HIGHLIGHTTEXT;
wndclass.lpfnWndProc = WindowProc; // 回调函数
wndclass.lpszClassName = className;
wndclass.hInstance = hInstance;
//注册 必须给所有的成员赋值调用时
RegisterClass(&wndclass);

//register 上面会有回调函数
//第二个参数就是回调函数
00401191 |. 50 push eax ; /pWndClass
00401192 |. C74424 4C 0E0>mov dword ptr ss:[esp+0x4C],0xE ; |
0040119A |. C74424 34 501>mov dword ptr ss:[esp+0x34],00401250 ; |
004011A2 |. 895424 54 mov dword ptr ss:[esp+0x54],edx ; |
004011A6 |. 897424 40 mov dword ptr ss:[esp+0x40],esi ; |
004011AA |. FF15 C8604000 call dword ptr ds:[<&USER32.RegisterClassA>] ; \RegisterClassA


# 子窗口

按钮本质也是子窗口

image-20230330174237192

image-20230330181247150

[esp+8] == WM_COMMAND

[esp+8] == WM_COMMAND && [esp + 0xc] == 0x3eb

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// 11111.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "stdio.h"
#include "Tools.h"

HINSTANCE hAppInstance;

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

void CreateButton(HWND hwnd)
{
HWND hwndPushButton;
HWND hwndCheckBox;
HWND hwndRadio;

hwndPushButton = CreateWindow(
TEXT("button"), // 系统预定义
TEXT("普通按钮"),
//WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_DEFPUSHBUTTON,
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_DEFPUSHBUTTON,
10, 10,
80, 20,
hwnd,
(HMENU)1001, //子窗口ID
hAppInstance,
NULL);

// 获取系统自定义button的WNDCLASS结构
TCHAR szBuffer[0x20];
GetClassName(hwndPushButton, szBuffer, 0x20); //in,out,in,获取classname

WNDCLASS wc;
GetClassInfo(hAppInstance, szBuffer, &wc); //获取classname中其他信息
OutputDebugStringF("-->%s\n", wc.lpszClassName);
OutputDebugStringF("-->%x\n", wc.lpfnWndProc);
//-->Button
//-->7710abd3


hwndCheckBox = CreateWindow(
TEXT("button"), //系统定义样式
TEXT("复选框"),
//WS_CHILD | WS_VISIBLE | BS_CHECKBOX | BS_AUTOCHECKBOX,
WS_CHILD | WS_VISIBLE | BS_CHECKBOX | BS_AUTOCHECKBOX,
10, 40,
80, 20,
hwnd,
(HMENU)1002, //子窗口ID
hAppInstance,
NULL);

hwndRadio = CreateWindow(
TEXT("button"),
TEXT("单选按钮"),
//WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON | BS_AUTORADIOBUTTON,
WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON,
10, 70,
80, 20,
hwnd,
(HMENU)1003, //子窗口ID
hAppInstance,
NULL);

}

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
///OutputDebugString("hahahaha");
//OutputDebugString("hello world\n");
//DWORD a = 11122;
//DbgPrintf("%d = 111", a);

hAppInstance = hInstance;


TCHAR className[] = "shabi";

//创建一个自己的窗口
WNDCLASS wndclass = {0};
wndclass.hbrBackground = (HBRUSH)COLOR_HIGHLIGHTTEXT;
wndclass.lpfnWndProc = WindowProc;
wndclass.lpszClassName = className;
wndclass.hInstance = hInstance;

//注册 必须给所有的成员赋值调用时
RegisterClass(&wndclass);




HWND hwnd = CreateWindowEx(
0, // Optional window styles.
className, // Window class
TEXT("Learn to Program Windows"), // Window text
WS_OVERLAPPEDWINDOW, // Window style

// Size and position
//相对于父窗口的x y坐标
//窗口的宽度和高度
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,

NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);

if (hwnd == NULL)
{
return 0;
}

CreateButton(hwnd);
ShowWindow(hwnd, SW_SHOW);

MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}




LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//OutputDebugStringF("%X\n",uMsg);
CREATESTRUCT* createst = (CREATESTRUCT*)lParam;
DWORD xPos;
DWORD yPos;
switch (uMsg)
{
case WM_CREATE:
//DbgPrintf("%s\n",createst->lpszClass);
return 0;
case WM_MOVE:
xPos = (int)(short)LOWORD(lParam);
yPos = (int)(short)HIWORD(lParam);
//DbgPrintf("%d %d\n",xPos,yPos);
return 0;
case WM_SIZE:
//DbgPrintf("%d %d\n",wParam,lParam);
xPos = (int)(short)LOWORD(lParam);
yPos = (int)(short)HIWORD(lParam);
//DbgPrintf("%d %d\n",xPos,yPos);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_KEYDOWN:
return 0;
case WM_LBUTTONDOWN:
return 0;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case 1001:
MessageBox(hwnd, "Hello Button 1", "Demo", MB_OK);
return 0;
case 1002:
MessageBox(hwnd, "Hello Button 2", "Demo", MB_OK);
return 0;
case 1003:
MessageBox(hwnd, "Hello Button 3", "Demo", MB_OK);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
//处理当前程序不关心的消息类型
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

按钮是一种特殊的窗体,并不需要提供单独的窗口回调函数.

当按钮有事件产生时,会给父窗口消息处理程序发送一个 WM_COMMAND 消息

3E9

3EA

# 资源文件

image-20230331190313883

image-20230331190337635

image-20230331190351737

image-20230331190406647

image-20230331190729027

使用 dialog 之后只需要写创建窗口和消息处理函数 DialogBox ()

对话框处理返回 true,没有处理或者不关心返回 false

1
2
3
4
5
6
7
8
9
10
11
12
13
0040112F      90            nop
00401130 /$ 8B4424 04 mov eax,dword ptr ss:[esp+0x4]
00401134 |. 6A 00 push 0x0 ; /lParam = NULL
00401136 |. 68 00104000 push 00401000 ; |DlgProc = win32_di.00401000
0040113B |. 6A 00 push 0x0 ; |hOwner = NULL
0040113D |. 6A 66 push 0x66 ; |pTemplate = 0x66
0040113F |. 50 push eax ; |hInst
00401140 |. A3 F0844000 mov dword ptr ds:[0x4084F0],eax ; |
00401145 |. FF15 9C504000 call dword ptr ds:[<&USER32.DialogBoxParamA>] ; \DialogBoxParamA

DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,DialogProc);
// 回调函数是401000

系统的消息处理函数一定会调用我们写的消息处理函数

消息断点

LBUTTONUP

本质上就是条件断点 [esp+8] == LBUTTONUP

image-20230331195751169

image-20230412203526086

在代码段下内存访问断点

image-20230412203651950

继续执行程序

image-20230412203733682

判断是否是对应动作触发的断点

1
2
3
0018F780   76E662FA  返回到 user32.76E662FA
0018F784 0017031C
0018F788 00000135 # 111 WM_COMMAND 才是对应的消息

一直往下跟,又回到 7xx

image-20230412204541268

继续执行 F9

查看此次 MSG 类型

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
BOOL CALLBACK DialogProc(
HWND hwndDlg, // handle to dialog box
UINT uMsg, // message
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
HWND hEditUser = NULL;
HWND hEditPass = NULL;
TCHAR szUserBuff[0x50];
TCHAR szUserPass[0x50];
switch (uMsg)
{
case WM_INITDIALOG:
//MessageBox(NULL, TEXT("WM_INITDIALOG"), TEXT("INIT"), MB_OK);
return TRUE; //处理返回true
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_BUTTON_OK:
//先获取文本框句柄
hEditUser = GetDlgItem(hwndDlg, IDC_EDIT_USERNAME);
hEditPass = GetDlgItem(hwndDlg, IDC_EDIT_PASSWORD);
//通过句柄得到里面内容
GetWindowText(hEditUser, szUserBuff, 0x50);
GetWindowText(hEditPass, szUserPass, 0x50);

MessageBox(NULL, TEXT("IDC_BUTTON_OK"), TEXT("OK"), MB_OK);
return TRUE;
case IDC_BUTTON_ERROR:
MessageBox(NULL, TEXT("IDC_BUTTON_ERROR"), TEXT("ERROR"), MB_OK);
EndDialog(hwndDlg, 0);
return TRUE;
}
break;
}
return FALSE; //没有处理返回false
}

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.

DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,DialogProc);

return 0;
}

# 提取图标

设置 icon

image-20230413183550422

ALT+TAB 大图标

其他地方的是小图标

所有资源文件都是 Unicode 的编码

Windows 程序在处理图标时需要按照分辨率显示图标,所以图标中会有很多相同的大小不一样的

图标组是描述图标信息的,比如 32*32 16.8 色

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
55
56
57
58
59
60
61
62
63
64
65
#include "stdafx.h"
#include "resource.h"

HINSTANCE hAPPInstance;
//窗口处理函数
BOOL CALLBACK DialogProc(
HWND hwndDlg, // handle to dialog box
UINT uMsg, // message
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
)
{
HWND hEditUser = NULL;
HWND hEditPass = NULL;
HICON hBigIcon;
HICON hSmallIcon;
TCHAR szUserBuff[0x50];
TCHAR szUserPass[0x50];

switch (uMsg)
{
case WM_INITDIALOG:
hBigIcon = LoadIcon (hAPPInstance, MAKEINTRESOURCE(IDI_ICON_BIG));
hSmallIcon = LoadIcon (hAPPInstance, MAKEINTRESOURCE(IDI_ICON_SMALL));
//设置图标
SendMessage(hwndDlg,WM_SETICON,ICON_BIG,(DWORD)hBigIcon);
SendMessage(hwndDlg,WM_SETICON,ICON_SMALL,(DWORD)hSmallIcon);
return TRUE;

case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_BUTTON_OK:
//先获取文本框句柄
hEditUser = GetDlgItem(hwndDlg, IDC_EDIT_USERNAME);
hEditPass = GetDlgItem(hwndDlg, IDC_EDIT_PASSWORD);
//通过句柄得到里面内容
GetWindowText(hEditUser, szUserBuff, 0x50);
GetWindowText(hEditPass, szUserPass, 0x50);

MessageBox(NULL, TEXT("IDC_BUTTON_OK"), TEXT("OK"), MB_OK);
return TRUE;
case IDC_BUTTON_ERROR:
MessageBox(NULL, TEXT("IDC_BUTTON_ERROR"), TEXT("ERROR"), MB_OK);
EndDialog(hwndDlg, 0);
return TRUE;
}
break;
}
return FALSE; //没有处理返回false
}

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.

hAPPInstance = hInstance;

DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,DialogProc);

return 0;
}

LoadPE 解析资源

image-20230405134430103

resource hack 展示资源

image-20230405134510371

可以产生 dmp 文件,直接改 dmp 的后缀不能直接显示

需要添加对应的头信息,拼成对应的格式

PE - 资源表_pe 文件自定义资源_megaparsec 的博客 - CSDN 博客

image-20230413192958983

绿的是_IMAGE_RESOURCE_DIRECTORY 资源目录

1
2
3
NumberOfNamedEntries;						//以名称命名的资源数量		
NumberOfIdEntries; //以ID命名的资源数量
NumberOfNamedEntries + NumberOfIdEntries = 黄色结构体数量

黄的是 _IMAGE_RESOURCE_DIRECTORY_ENTRY 资源目录项

资源目录就是第三个 datadirectory

所有节点都是绿的 + n 个黄的,绿的来确定下面有多少黄的

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
//资源目录
typedef struct _IMAGE_RESOURCE_DIRECTORY {
DWORD Characteristics; //资源属性 保留 0
DWORD TimeDateStamp; //资源创建的时间
WORD MajorVersion; //资源版本号 未使用 0
WORD MinorVersion; //资源版本号 未使用 0
WORD NumberOfNamedEntries; //以名称命名的资源数量
WORD NumberOfIdEntries; //以ID命名的资源数量
// IMAGE_RESOURCE_DIRECTORY_ENTRY DirectoryEntries[];
} IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY;


//资源目录项:

typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY {
union { //目录项的名称、或者ID
struct {
DWORD NameOffset:31; // :称为位域,低位31位 位的精确控制
DWORD NameIsString:1; // 第32位
};
DWORD Name;
WORD Id;
};
union {
DWORD OffsetToData; //目录项指针
struct {
DWORD OffsetToDirectory:31;
DWORD DataIsDirectory:1;
};
};
} IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY;
// 第一层 用于判断资源类型,光标,位图...一共有16种类型
// :位域/位段
// 第二层name代表资源编号
// 第三层代表代码页



//数据项:

typedef struct _IMAGE_DATA_DIRECTORY {
DWORD VirtualAddress;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

位段或者位域的作用:

对位的精确控制

不使用位域需要 name & 0x80000000 == 0x80000000 判断最高位是 1

存在位域时可以直接判断 NameIsString

NameIsString = 0,name 低 31 位是编号,int 类型的编号

NameIsString = 1,name 低 31 位是指针,指向 Unicode 字符串

1
2
3
4
5
6
7
NameIsString==1,低31位指向结构:
typedef struct _IMAGE_RESOURCE_DIR_STRING_U {
WORD Length;
WCHAR NameString[ 1 ];
} IMAGE_RESOURCE_DIR_STRING_U, *PIMAGE_RESOURCE_DIR_STRING_U;

NameIsString==1,低31位是ID
1
2
printf("%x\n",(pResourceEntry[i].Name & 0x80000000) == 0x80000000);								
printf("%x\n",pResourceEntry[i].NameIsString == 1);
1
2
3
4
OffsetToData的含义:							

最高位如果为1,低31位 + 资源地址 == 下一层目录节点的起始位置

# 通用控件

Windows 标准控件,标准控件总是可用的:
Static
Group Box
Button
Check Box
Radio Button
Edit
ComboBox
ListBox

Windows 通用控件,代码包含在 Comctrl32.dll

1
2
3
4
5
6
7
8
#include <commctrl.h>
#pragma comment(lib,"comctl32.lib")

INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&icex);

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#include "stdafx.h"
#include "resource.h"
#include <commctrl.h>
#pragma comment(lib,"comctl32.lib")

int PrintModules( DWORD processID )
{
HMODULE hMods[1024];
HANDLE hProcess;
DWORD cbNeeded;
unsigned int i;

// Print the process identifier.

printf( "\nProcess ID: %u\n", processID );

// Get a handle to the process.

hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, processID );
if (NULL == hProcess)
return 1;

// Get a list of all the modules in this process.

if( EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded))
{
for ( i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ )
{
TCHAR szModName[MAX_PATH];

// Get the full path to the module's file.

if ( GetModuleFileNameEx( hProcess, hMods[i], szModName,
sizeof(szModName) / sizeof(TCHAR)))
{
// Print the module name and handle value.

_tprintf( TEXT("\t%s (0x%08X)\n"), szModName, hMods[i] );
}
}
}

// Release the handle to the process.

CloseHandle( hProcess );

return 0;
}

DWORD cProcesses;
DWORD* GetProcess()
{
DWORD aProcesses[1024];
DWORD cbNeeded;
//unsigned int i;



// Get the list of process identifiers.

if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
return NULL;

// Calculate how many process identifiers were returned.

cProcesses = cbNeeded / sizeof(DWORD);

// Print the names of the modules for each process.

//for ( i = 0; i < cProcesses; i++ )
//{
// PrintModules( aProcesses[i] );
//}

return aProcesses;
}


void EnumProccess(HWND hListProcess)
{
LV_ITEM vitem;
DWORD* aProcesses = GetProcess();

//初始化
memset(&vitem,0,sizeof(LV_ITEM));
vitem.mask = LVIF_TEXT;

#if 1
vitem.pszText = "csrss.exe";
vitem.iItem = 0;
vitem.iSubItem = 0;
//ListView_InsertItem(hListProcess, &vitem);
SendMessage(hListProcess, LVM_INSERTITEM,0,(DWORD)&vitem);

vitem.pszText = TEXT("448");
vitem.iItem = 0;
vitem.iSubItem = 1;
ListView_SetItem(hListProcess, &vitem);

vitem.pszText = TEXT("56590000");
vitem.iItem = 0;
vitem.iSubItem = 2;
ListView_SetItem(hListProcess, &vitem);

vitem.pszText = TEXT("000F0000");
vitem.iItem = 0;
vitem.iSubItem = 3;
ListView_SetItem(hListProcess, &vitem);


vitem.pszText = TEXT("winlogon.exe");
vitem.iItem = 1;
vitem.iSubItem = 0;
//ListView_InsertItem(hListProcess, &vitem);
SendMessage(hListProcess, LVM_INSERTITEM,0,(DWORD)&vitem);

vitem.pszText = TEXT("456");
vitem.iSubItem = 1;
ListView_SetItem(hListProcess, &vitem);

vitem.pszText = TEXT("10000000");
vitem.iSubItem = 2;
ListView_SetItem(hListProcess, &vitem);

vitem.pszText = TEXT("000045800");
vitem.iSubItem = 3;
ListView_SetItem(hListProcess, &vitem);
#endif

}

void EnumModules(HWND hListProcess,WPARAM wParam,LPARAM lParam)
{

//MessageBox(0,0,0,0);
DWORD dwRowId;
TCHAR szPid[0x20];
LV_ITEM lv;

//初始化
memset(&lv,0,sizeof(LV_ITEM));
memset(szPid,0,0x20);

//获取选择行
dwRowId = SendMessage(hListProcess,LVM_GETNEXTITEM,-1,LVNI_SELECTED);
if(dwRowId == -1)
{
MessageBox(NULL,TEXT("选择进程"),TEXT("出错啦"),MB_OK);
return;
}
//获取PID
lv.iSubItem = 1;
lv.pszText = szPid;
lv.cchTextMax = 0x20;
SendMessage(hListProcess,LVM_GETITEMTEXT,dwRowId,(DWORD)&lv);
MessageBox(NULL,szPid,TEXT("PID"),MB_OK);

}

void InitProcessListView(HWND hDlg)
{
LV_COLUMN lv;
HWND hListProcess;

//初始化
memset(&lv,0,sizeof(LV_COLUMN));

//获取IDC_LIST_PROCESS句柄
hListProcess = GetDlgItem(hDlg,IDC_LIST_PROCESS);

//设置整行选中
SendMessage(hListProcess,LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_FULLROWSELECT,LVS_EX_FULLROWSELECT);

//第一列
lv.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
lv.pszText = TEXT("进程"); //列标题
lv.cx = 130;
lv.iSubItem = 0;
//ListView_InsertColumn(hListProcess, 0, &lv);
SendMessage(hListProcess,LVM_INSERTCOLUMN,0,(DWORD)&lv);
//第二列
lv.pszText = TEXT("PID");
lv.cx = 60;
lv.iSubItem = 1;
//ListView_InsertColumn(hListProcess, 1, &lv);
SendMessage(hListProcess,LVM_INSERTCOLUMN,1,(DWORD)&lv);
//第三列
lv.pszText = TEXT("镜像基址");
lv.cx = 100;
lv.iSubItem = 2;
ListView_InsertColumn(hListProcess, 2, &lv);
//第四列
lv.pszText = TEXT("镜像大小");
lv.cx = 120;
lv.iSubItem = 3;
ListView_InsertColumn(hListProcess, 3, &lv);


EnumProccess(hListProcess);

}

void InitModuleListView(HWND hDlg)
{
LV_COLUMN lv1;
HWND hListProcess1;

memset(&lv1,0,sizeof(LV_COLUMN));
hListProcess1 = GetDlgItem(hDlg,IDC_LIST_MOUDLE);
SendMessage(hListProcess1,LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_FULLROWSELECT,LVS_EX_FULLROWSELECT);

//第一列
lv1.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM;
lv1.pszText = TEXT("模块名称"); //列标题
lv1.cx = 200;
lv1.iSubItem = 0;
//ListView_InsertColumn(hListProcess, 0, &lv);
SendMessage(hListProcess1,LVM_INSERTCOLUMN,0,(DWORD)&lv1);
//第二列
lv1.pszText = TEXT("模块位置");
lv1.cx = 210;
lv1.iSubItem = 1;
//ListView_InsertColumn(hListProcess, 1, &lv);
SendMessage(hListProcess1,LVM_INSERTCOLUMN,1,(DWORD)&lv1);

EnumProccess(hListProcess1);

}

BOOL CALLBACK DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CLOSE:
{
EndDialog(hDlg,0);
break;
}
case WM_INITDIALOG:
{
InitProcessListView(hDlg);
InitModuleListView(hDlg);

}
case WM_NOTIFY:
{
NMHDR* pNMHDR = (NMHDR*)lParam;
if(wParam == IDC_LIST_PROCESS && pNMHDR->code == NM_CLICK)
{
EnumModules(GetDlgItem(hDlg,IDC_LIST_PROCESS),wParam,lParam);
}
break;
}
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
case IDC_BUTTON_PE:
{
return TRUE;
}

case IDC_BUTTON_ABOUT:
{
return TRUE;
}


case IDC_BUTTON_LOGOUT:
{
return TRUE;
}

}
break;
}
return 0;

}
//处理当前程序不关心的消息类型
return FALSE;
}


int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_WIN95_CLASSES;
InitCommonControlsEx(&icex);

DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,DialogProc);


return 0;
}

WM_NOTIFY

该消息类型与 WM_COMMAND 类型相似,都是由子窗口向父窗口发送的消息。

WM_NOTIFY 可以包含比 WM_COMMAND 更丰富的信息

Windows 通用组件中有很多消息,都是通过 WM_NOTIFY 来描述的.

1
2
3
4
5
6
7
8
WM_NOTIFY消息中的参数如下:				
wParam:控件ID
lParam:指向一个结构tagNMHDR
typedef struct tagNMHDR {
HWND hwndFrom; //发送通知消息的控制窗口句柄
UINT idFrom; //发送通知消息的控制ID值
UINT code; //通知码,如LVM_SELCHANGED
} NMHDR;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
这个结构体能满足一般的要求,但能描述的信息还是有限的													
解决方案:对每种不同用途的通知消息都定义另一种结构来表示
typedef struct tagNMLVCACHEHINT {
NMHDR hdr;
int iFrom;
int iTo;
} NMLVCACHEHINT, *PNMLVCACHEHINT;

typedef struct tagLVDISPINFO {
NMHDR hdr;
LVITEM item;
} NMLVDISPINFO, FAR *LPNMLVDISPINFO;

typedef struct _NMLVFINDITEM {
NMHDR hdr;
int iStart;
LVFINDINFO lvfi;
} NMLVFINDITEM, *PNMLVFINDITEM;

以上三个结构体都继承了tagNMHDR

image-20230701220743398

# 创建线程

一个进程中至少有一个线程。

多线程会产生多个堆栈,每个线程都有自己的栈的空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);		

HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全属性 通常为NULL
SIZE_T dwStackSize, // 参数用于设定线程可以将多少地址空间用于它自己的堆栈
// 每个线程拥有它自己的堆栈
LPTHREAD_START_ROUTINE lpStartAddress, // 参数用于指明想要新线程执行的线程函数的地址,并不是立即执行,取决于cpu
LPVOID lpParameter, // 线程函数的参数
// 在线程启动执行时将该参数传递给线程函数
// 既可以是数字,也可以是指向包含其他信息的一个数据结构的指针
DWORD dwCreationFlags, // 0 创建完毕立即调度 CREATE_SUSPENDED创建后挂起
OUT LPDWORD lpThreadId // 线程ID
);
// 返回值:线程句柄

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
#include "stdafx.h"
#include <windows.h>

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
for (int i = 0; i < 1000; i++)
{
//Sleep(1000);
printf("----------\n");
}
return 0;
}

void MyTest()
{
HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);

::CloseHandle(hThread);
}

int main(int argc, char* argv[])
{
MyTest();
for(int i=0;i<1000;i++)
{
Sleep(1000);
printf("++++++++++\n");
}
return 0;
}

image-20230815185107177

image-20230815185142573

1
2
3
4
5
线程句柄与线程ID:				

线程是由Windows内核负责创建与管理的,句柄相当于一个令牌,有了这个令牌就可以使用线程对象.

线程ID是身份证,唯一的,系统进行线程调度的时候要使用的.

::CreateThread 代表是一个全局的函数

::CloseHandle 当前句柄释放,线程还在运行

# 线程传参

全局变量

线程参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
int x = (int)lpParameter
for (int i = 0; i < 1000; i++)
{
//Sleep(1000);
printf("-----%d-----\n",x);
}
return 0;
}

void MyTest()
{
int x = 2;
HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, (void*)x, 0, NULL);

::CloseHandle(hThread);
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
int* x = (int *)lpParameter
for (int i = 0; i < 1000; i++)
{
//Sleep(1000);
printf("-----%d-----\n",*x);
}
return 0;
}
int x = 2;
void MyTest()
{

HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, (void*)&x, 0, NULL);

::CloseHandle(hThread);
}
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include "stdafx.h"
#include "resource.h"

HWND text1;
HWND text2;

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
TCHAR szBuffer1[10];
TCHAR szBuffer2[10];
memset(szBuffer1,0,10);
memset(szBuffer2,0,10);
DWORD dwTimer1;
DWORD dwTimer2;
DWORD a = 1000;

while(a-- > 0)
{
GetWindowText(text1,szBuffer1,10);
GetWindowText(text2,szBuffer2,10);

sscanf( szBuffer1, "%d", &dwTimer1 );
sscanf( szBuffer2, "%d", &dwTimer2 );
memset(szBuffer1,0,10);
memset(szBuffer2,0,10);
sprintf(szBuffer1,"%d",--dwTimer1);
sprintf(szBuffer2,"%d",++dwTimer2);

SetWindowText(text1,szBuffer1);
SetWindowText(text2,szBuffer2);
Sleep(1000);
}
return 0;
}

void MyTest()
{
HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);

::CloseHandle(hThread);
}

BOOL CALLBACK DialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
text1 = GetDlgItem(hDlg,IDC_EDIT);
text2 = GetDlgItem(hDlg,IDC_EDIT2);



switch (uMsg)
{
case WM_CLOSE:
{
EndDialog(hDlg,0);
break;
}
case WM_INITDIALOG:
{
SetWindowText(text1,"999");
SetWindowText(text2,"0");
break;

}
case WM_COMMAND:
{
switch(LOWORD(wParam))
{

case IDC_BUTTON:
{
MyTest();

return TRUE;
}

}
break;
}
return 0;

}
//处理当前程序不关心的消息类型
return FALSE;
}



int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.
DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG),NULL,DialogProc);
return 0;
}

# 线程控制

线程挂起之后是不分 cpu 的

1
2
3
4
5
6
7
8
9
挂起线程:			

::SuspendThread(hThread);

恢复线程:

::ResumeThread(hThread);


恢复线程需要看调度程序是否空闲

线程退出之后这个线程的堆栈会被释放

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
方式一:				

::ExitThread(DWORD dwExitCode);
exitcode决定了程序返回什么代码
这个函数不会释放资源在退出之前
同步执行

方式二:

线程函数返回

可以在结束前干一些事情

方式三:

::TerminateThread(hThread,2);
异步调用,可能会导致程序执行顺序问题,函数返回的时候线程可能还没退出。退出后堆栈还在

::WaitForSingleObject(hThread,INFINITE);
线程阻塞,直到线程退出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
判断线程是否结束								

BOOL GetExitCodeThread(
HANDLE hThread,
LPDWORD lpExitCode
);

STILL_ACTIVE 正在运行


参数:

hThread: 要结束的线程句柄
dwExitCode: 指定线程的退出代码。可以通过GetExitCodeThread来查看一个线程的退出代码

# context

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
每个线程在执行的时候,都会独自占用一个CPU,当系统中的线程数量 > CPU的数量时,就会存在多个线程共用一个CPU							
的情况。但CPU每次只能运行一个线程,Windows每隔20毫秒会进行线程的切换,那比如线程A执行到地址:0x2345678
eax:1 ecx:2 edx:3 ebx:4...还有eflag标志寄存器中的值等等。。。
此时,线程执行时间到了,被切换到了线程B。。。。当线程B的时间片也到了,再切换会线程A时,系统是如何知道该
从哪个地址开始执行呢?被切换前用到的各种寄存器的值该如何恢复呢?


CONTEXT:

该结构包含了特定处理器的寄存器数据。


typedef struct _CONTEXT {

//
// The flags values within this flag control the contents of
// a CONTEXT record.
//
// If the context record is used as an input parameter, then
// for each portion of the context record controlled by a flag
// whose value is set, it is assumed that that portion of the
// context record contains valid context. If the context record
// is being used to modify a threads context, then only that
// portion of the threads context will be modified.
//
// If the context record is used as an IN OUT parameter to capture
// the context of a thread, then only those portions of the thread's
// context corresponding to set flags will be returned.
//
// The context record is never used as an OUT only parameter.
//

DWORD ContextFlags; // 可以决定获取到哪些寄存器的值 context_full + debug = 所有寄存器

//
// This section is specified/returned if CONTEXT_DEBUG_REGISTERS is
// set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT
// included in CONTEXT_FULL.
// debug 寄存器

DWORD Dr0;
DWORD Dr1;
DWORD Dr2;
DWORD Dr3;
DWORD Dr6;
DWORD Dr7;

//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_FLOATING_POINT.
//

FLOATING_SAVE_AREA FloatSave;

//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_SEGMENTS.
//

DWORD SegGs;
DWORD SegFs;
DWORD SegEs;
DWORD SegDs;

//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_INTEGER.
//

DWORD Edi;
DWORD Esi;
DWORD Ebx;
DWORD Edx;
DWORD Ecx;
DWORD Eax;

//
// This section is specified/returned if the
// ContextFlags word contians the flag CONTEXT_CONTROL.
//

DWORD Ebp;
DWORD Eip;
DWORD SegCs; // MUST BE SANITIZED
DWORD EFlags; // MUST BE SANITIZED
DWORD Esp;
DWORD SegSs;

//
// This section is specified/returned if the ContextFlags word
// contains the flag CONTEXT_EXTENDED_REGISTERS.
// The format and contexts are processor specific
//

BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION];

} CONTEXT;


获取线程CONTEXT结构:

//挂起线程
SuspendThread(线程句柄);

CONTEXT context

//设置要获取寄存器的类型

context.ContextFlags = CONTEXT_CONTROL;

//获取

BOOL ok = ::GetThreadContext(hThread,&context);

//设置

context.Eip = 0x401000;

SetThreadContext(hThread,&context);

// 切换线程

::ResumeThread(hThread);

// 但是没保存寄存器,所以程序并不能完成运行

# 线程安全问题

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
HWND hEdit ;					
DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
TCHAR szBuffer[10];
DWORD dwIndex = 0;
DWORD dwCount;

while(dwIndex<10)
{
GetWindowText(hEdit,szBuffer,10);
sscanf( szBuffer, "%d", &dwCount );
dwCount++;
memset(szBuffer,0,10);
sprintf(szBuffer,"%d",dwCount);
SetWindowText(hEdit,szBuffer);
dwIndex++;
}

return 0;
}
DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
TCHAR szBuffer[10];
DWORD dwIndex = 0;
DWORD dwCount;


while(dwIndex<10)
{
GetWindowText(hEdit,szBuffer,10);
sscanf( szBuffer, "%d", &dwCount );
dwCount++;
memset(szBuffer,0,10);
sprintf(szBuffer,"%d",dwCount);
SetWindowText(hEdit,szBuffer);
dwIndex++;
}

return 0;
}

BOOL CALLBACK MainDlgProc(HWND hDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
BOOL bRet = FALSE;

switch(uMsg)
{
case WM_CLOSE:
{
EndDialog(hDlg,0);
break;
}
case WM_INITDIALOG:
{
hEdit = GetDlgItem(hDlg,IDC_EDIT1);
SetWindowText(hEdit,"0");

break;
}
case WM_COMMAND:

switch (LOWORD (wParam))
{
case IDC_BUTTON_T1:
{
HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1,
NULL, 0, NULL);

::CloseHandle(hThread1);
return TRUE;
}
case IDC_BUTTON_T2:
{
HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2,
NULL, 0, NULL);

::CloseHandle(hThread2);
return TRUE;
}
}
break ;
}

return bRet;
}

int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// TODO: Place code here.

DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL,MainDlgProc);

return 0;
}

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
多线程访问局部变量时存在安全问题

1、创建CRITICAL_SECTION:

CRITICAL_SECTION cs;

2、在使用前进行初始化

InitializeCriticalSection(&cs);


3、在函数中使用:

DWORD WINAPI 线程A(PVOID pvParam)
{
EnterCriticalSection(&cs);

//对全局遍历X的操作

LeaveCriticalSection(&cs);
return(0);
}


DWORD WINAPI 线程B(PVOID pvParam)
{
EnterCriticalSection(&g_cs);

//对全局遍历X的操作

LeaveCriticalSection(&g_cs);
return(0);
}

4、删除CRITICAL_SECTION

VOID DeleteCriticalSection(PCRITICAL_SECTION pcs);

当线程不再试图访问共享资源时

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
typedef struct _RTL_CRITICAL_SECTION {		
PRTL_CRITICAL_SECTION_DEBUG DebugInfo;
LONG LockCount;
LONG RecursionCount;
HANDLE OwningThread;
HANDLE LockSemaphore;
DWORD SpinCount;
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;

LockCount:
它被初始化为数值 -1
此数值等于或大于 0 时,表示此临界区被占用

等待获得临界区的线程数:LockCount - (RecursionCount -1)

RecursionCount:
此字段包含所有者线程已经获得该临界区的次数

OwningThread:
此字段包含当前占用此临界区的线程的线程标识符
此线程 ID 与GetCurrentThreadId 所返回的 ID 相同


测试代码:

#include "stdafx.h"
#include <windows.h>
CRITICAL_SECTION cs;

DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
for(int x=0;x<1000;x++)
{
EnterCriticalSection(&cs);

Sleep(1000);

printf("11111:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);

LeaveCriticalSection(&cs);

}
return 0;
}

DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
for(int x=0;x<1000;x++)
{
EnterCriticalSection(&cs);

Sleep(1000);

printf("22222:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);

LeaveCriticalSection(&cs);

}

return 0;
}

DWORD WINAPI ThreadProc3(LPVOID lpParameter)
{
for(int x=0;x<1000;x++)
{
EnterCriticalSection(&cs);

Sleep(1000);

printf("33333:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);

LeaveCriticalSection(&cs);

}

return 0;
}

DWORD WINAPI ThreadProc4(LPVOID lpParameter)
{
for(int x=0;x<1000;x++)
{
EnterCriticalSection(&cs);

Sleep(1000);

printf("44444:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);

LeaveCriticalSection(&cs);

}

return 0;
}


int main(int argc, char* argv[])
{
InitializeCriticalSection(&cs);

//printf("主线程:%x %x %x\n",cs.LockCount,cs.RecursionCount,cs.OwningThread);

//创建一个新的线程
HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1,
NULL, 0, NULL);

//创建一个新的线程
HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2,
NULL, 0, NULL);

//创建一个新的线程
HANDLE hThread3 = ::CreateThread(NULL, 0, ThreadProc3,
NULL, 0, NULL);

//创建一个新的线程
HANDLE hThread4 = ::CreateThread(NULL, 0, ThreadProc4,
NULL, 0, NULL);

//如果不在其他的地方引用它 关闭句柄
::CloseHandle(hThread1);
::CloseHandle(hThread2);
::CloseHandle(hThread3);
::CloseHandle(hThread4);

Sleep(1000*60*60);

return 0;
}

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
CRITICAL_SECTION g_csX; 
CRITICAL_SECTION g_csY;
CRITICAL_SECTION g_csZ;

线程1
DWORD WINAPI ThreadFunc(PVOID pvParam)
{
EnterCriticalSection(&g_csX);
使用X
LeaveCriticalSection(&g_csX);
EnterCriticalSection(&g_csY);
使用Y
LeaveCriticalSection(&g_csY);

return(0);
}

线程2
DWORD WINAPI ThreadFunc(PVOID pvParam)
{
EnterCriticalSection(&g_csX);
使用X
LeaveCriticalSection(&g_csX);
EnterCriticalSection(&g_csZ);
使用Z
LeaveCriticalSection(&g_csZ);

return(0);
}

线程3
DWORD WINAPI ThreadFunc(PVOID pvParam)
{
EnterCriticalSection(&g_csX);
使用X
LeaveCriticalSection(&g_csX);
return(0);
}

避免死锁:

拿令牌顺序一样,不要嵌套拿令牌

# 互斥体

# WaitForSingleObject

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
55
56
57
58
59
60
61
62
63
64
65
DWORD WaitForSingleObject(					
HANDLE hHandle, // handle to object
DWORD dwMilliseconds // time-out interval
);

功能说明:

等待函数可使线程自愿进入等待状态,直到一个特定的内核对象变为已通知状态为止.

hHandle:

内核对象句柄,可以是进程也可以是线程.

dwMilliseconds:

等待时间,单位是毫秒 INFINITE(-1)一直等待

返回值:

WAIT_OBJECT_0(0) 等待对象变为已通知

WAIT_TIMEOUT(0x102) 超时


特别说明:

1、内核对象中的每种对象都可以说是处于已通知或未通知的状态之中

2、这种状态的切换是由Microsoft为每个对象建立的一套规则来决定的

3、当线程正在运行的时候,线程内核对象处于未通知状态

4、当线程终止运行的时候,它就变为已通知状态

5、在内核中就是个BOOL值,运行时FALSE 结束TRUE

代码演示:

DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
for(int i=0;i<5;i++)
{
printf("+++++++++\n");
Sleep(1000);
}
return 0;
}

int main(int argc, char* argv[])
{

//创建一个新的线程
HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1,
NULL, 0, NULL);

DWORD dwCode = ::WaitForSingleObject(hThread1, INFINITE);
//DWORD dwCode = ::WaitForSingleObject(hThread1, INFINITE);
//如果这样写可能导致程序无法继续向下执行,因为有些内核对象执行完之后会将变为未通知状态,但是线程和进程不会
//DWORD dwCode = ::WaitForSingleObject(hThread1, INFINITE);

MessageBox(0,0,0,0);

return 0;
}

# WaitForMultipleObjects

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
DWORD WaitForMultipleObjects(							
DWORD nCount, // number of handles in array
CONST HANDLE *lpHandles, // object-handle array
BOOL bWaitAll, // wait option
DWORD dwMilliseconds // time-out interval
);

功能说明:

同时查看若干个内核对象的已通知状态

nCount:

要查看内核对象的数量

lpHandles:

内核对象数组

bWaitAll:

等到类型 TRUE 等到所有变为已通知 FALSE 只要有一个变为已通知

dwMilliseconds:

超时时间

INFINITE一直等待

返回值:
bWaitAll为TRUE时,返回WAIT_OBJECT_0(0) 代码所以内核对象都变成已通知
bWaitAll为FALSE时,返回最先变成已通知的内核对象在数组中的索引
WAIT_TIMEOUT(0x102) 超时

bWaitAll
true 所有内核对象都结束之后才
false 有一个内核对象结束就行


DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
for(int i=0;i<5;i++)
{
printf("+++++++++\n");
Sleep(1000);
}
return 0;
}

DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
for(int i=0;i<3;i++)
{
printf("---------\n");
Sleep(1000);
}

return 0;
}


int main(int argc, char* argv[])
{

HANDLE hArray[2];

//创建一个新的线程
HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1,
NULL, 0, NULL);

//创建一个新的线程
HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2,
NULL, 0, NULL);

hArray[0] = hThread1;
hArray[1] = hThread2;

DWORD dwCode = ::WaitForMultipleObjects(2, hArray,FALSE,INFINITE);

MessageBox(0,0,0,0);

return 0;
}

互斥体:跨进程线程控制

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
进程一:

HANDLE g_hMutex = CreateMutex(NULL,FALSE, "XYZ"); //第一个参数为空代表不是内核对象


进程二:

HANDLE g_hMutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE, "XYZ");

WaitForSingleObject(g_hMutex,INFINITE);

//逻辑代码

ReleaseMutex(g_hMutex);


进程三:

HANDLE g_hMutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE, "XYZ");

WaitForSingleObject(g_hMutex,INFINITE);

//逻辑代码

ReleaseMutex(g_hMutex);





互斥体与临界区的区别:

1、临界区只能用于单个进程间的线程控制.

2、互斥体可以设定等待超时,但临界区不能.

3、线程意外终结时,Mutex可以避免无限等待.

4、Mutex效率没有临界区高.

# 内核对象

image-20230819183130009

进程、线程、文件、文件映射、事件、互斥体等等都是内核对象

内核对象会保留一个计数器,只有当计数器为 0 时内核对象才被销毁。互斥体就是一个内核对象

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
2、事件内核对象的创建							

HANDLE g_hEvent = CreateEvent(NULL, TRUE, FALSE, "XYZ");

HANDLE g_hMutex = CreateMutex(NULL,FALSE, "XYZ"); // 返回的是指针的指针,为了防止错误修改内核对象

3、事件内核对象的获取

HANDLE OpenEvent(
DWORD dwDesiredAccess, // access
BOOL bInheritHandle, // inheritance option
LPCTSTR lpName // object name
);

HANDLE g_hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "XYZ");

HANDLE g_hMutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE, "XYZ");

4、内核对象的销毁

BOOL CloseHandle(HANDLE hobj);

(1)、当没有其他程序引用时,系统会销毁内核对象(使用数量).

(2)、内核对象的生命周期,可能比创建它的对象要长.

内核对象会维护一个计数器,当计数器为 0 的时候系统会销毁这个内核对象

CreateMutex +1

closehandle -1

oepnmutex +1

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
进程一:						

HANDLE g_hEvent = CreateEvent(NULL, TRUE, FALSE, "XYZ");

SetEvent(g_hEvent);


进程二:

HANDLE g_hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "XYZ");

WaitForSingleObject(g_hEvent, INFINITE);


实验一:内核对象的生命周期

进程一:

HANDLE g_hEvent = CreateEvent(NULL, TRUE, FALSE, "XYZ");

SetEvent(g_hEvent);


进程二:

HANDLE g_hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "XYZ");

WaitForSingleObject(g_hEvent, INFINITE);

内核对象是否销毁会取决于上面三个进程的执行顺序

# 事件对象

时间可以做线程同步。在于 CreateEvent 的第二个和第三个参数

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
1、事件对象的创建				

HANDLE CreateEvent(
LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性 NULL时为系统默认
BOOL bManualReset, // TRUE 通过调用ResetEvent将事件对象标记为未通知。
// 执行完wait之后如果这个是true,那么还是保持已通知。如果是false wait结束之后会自动变为未通知,如果不手动设置,别的线程会一直阻塞
// true 适用于读操作,实现互斥需要设置为false,并且在最后调用setevent
BOOL bInitialState, // TRUE 已通知状态 FALSE未通知状态
LPCTSTR lpName // 对象名称 以NULL结尾的字符串
);


2、事件对象的控制

BOOL SetEvent(HANDLE hEvent); //将对象设置为已通知

3、关闭时间对象句柄
//关闭句柄
CloseHandle();


4、线程控制实验:只读形式的线程控制

HANDLE g_hEvent;

HWND hEdit1;
HWND hEdit2;
HWND hEdit3;
HWND hEdit4;
HANDLE hThread1;
HANDLE hThread2;
HANDLE hThread3;
HANDLE hThread4;

DWORD WINAPI ThreadProc1(LPVOID lpParameter)
{
//创建事件
//默认安全属性 手动设置未通知状态(TRUE) 初始状态未通知 没有名字
g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
HANDLE hThread[3];
//创建3个线程
hThread[0] = ::CreateThread(NULL, 0, ThreadProc2, NULL, 0, NULL);
hThread[1] = ::CreateThread(NULL, 0, ThreadProc3, NULL, 0, NULL);
hThread[2] = ::CreateThread(NULL, 0, ThreadProc4, NULL, 0, NULL);

//设置文本框的值
SetWindowText(hEdit1,"1000");

//设置事件为已通知
SetEvent(g_hEvent);

//等待线程结束 销毁内核对象
WaitForMultipleObjects(3, hThread, TRUE, INFINITE);
CloseHandle(hThread[0]);
CloseHandle(hThread[1]);
CloseHandle(hThread[2]);
CloseHandle(g_hEvent);

return 0;
}

DWORD WINAPI ThreadProc2(LPVOID lpParameter)
{
TCHAR szBuffer[10] = {0};

//当事件变成已通知时
WaitForSingleObject(g_hEvent, INFINITE);

//读取内容
GetWindowText(hEdit1,szBuffer,10);

SetWindowText(hEdit2,szBuffer);

return 0;
}
DWORD WINAPI ThreadProc3(LPVOID lpParameter)
{
TCHAR szBuffer[10] = {0};

//当事件变成已通知时
WaitForSingleObject(g_hEvent, INFINITE);

//读取内容
GetWindowText(hEdit1,szBuffer,10);

SetWindowText(hEdit3,szBuffer);

return 0;
}
DWORD WINAPI ThreadProc4(LPVOID lpParameter)
{
TCHAR szBuffer[10] = {0};

//当事件变成已通知时
WaitForSingleObject(g_hEvent, INFINITE);

//读取内容
GetWindowText(hEdit1,szBuffer,10);

SetWindowText(hEdit4,szBuffer);

return 0;
}

# 信号量

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
55
56
57
58
59
60
61
62
63
64
65
66
67
HANDLE CreateSemaphore(		
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount,
LONG lMaximumCount,
LPCTSTR lpName
);

函数说明:

第一个参数表示安全控制,一般直接传入NULL

第二个参数表示初始资源数量。0时不发送信号

第三个参数表示最大并发数量。lInitialCount<=lMaximumCount

第四个参数表示信号量的名称,传入NULL表示匿名信号量。


打开信号量

HANDLE OpenSemaphore(

DWORD dwDesiredAccess,

BOOL bInheritHandle,

LPCTSTR lpName

);

函数说明:

第一个参数表示访问权限,对一般传入SEMAPHORE_ALL_ACCESS。详细解释可以查看MSDN文档。

第二个参数表示信号量句柄继承性,一般传入FALSE即可。

第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个信号量。


递增信号量的当前资源计数


BOOL ReleaseSemaphore(

HANDLE hSemaphore,

LONG lReleaseCount,

LPLONG lpPreviousCount

);

函数说明:

第一个参数是信号量的句柄。

第二个参数表示增加个数,必须大于0且不超过最大资源数量。

第三个参数返回当前资源数量的原始值,设为NULL表示不需要传出。

注:没有一个函数可以用来查询信标的当前资源数量的值


信号量的清理与销毁

CloseHandle()

# 进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
一、进程的创建过程:										


打开系统 双击要运行的程序 EXE开始执行



步骤一:

当系统启动后,创建一个进程:Explorer.exe 也就是桌面进程.

步骤二:

当用户双击某一个EXE时,Explorer 进程使用CreateProcess函数创建被双击的EXE,也就是说,我们在桌面上双

击创建的进程都是Explorer进程的子进程.

通过XueTr.exe,查看哪些进程是由Explorer创建的.

explorer 进程创建完父进程就被终结了。进程必须由别的进程创建出来
1
2
3
4
5
6
7
8
9
10
11
12
13
BOOL CreateProcess(									
LPCTSTR lpApplicationName, // name of executable module
LPTSTR lpCommandLine, // command line string
LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
BOOL bInheritHandles, // handle inheritance option
DWORD dwCreationFlags, // creation flags
LPVOID lpEnvironment, // new environment block
LPCTSTR lpCurrentDirectory, // current directory name
LPSTARTUPINFO lpStartupInfo, // startup information
LPPROCESS_INFORMATION lpProcessInformation // process information
);

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
1、创建内核对象										

用户层 内核层

CreateProcess NtCreateProcess

高2G中的一块内存


0x10 0x812356 0 句柄表,刚创建时是空的




如果这个刚刚被创建的进程,里面的线程用创建了其他的内核对象

注意:进程是一个空间的概念,而线程才是真正执行的代码

比如:

CreateProcess CreateMutex

CreateThread CreateFile

CreateEvent CreateFileMapping

...

高2G中的一块内存
内核对象的句柄表

0x0 0x812345 0 为了安全,不会给客户端返回
0x1 0x834567 1 真正的地址,而是一个编号
0x2 0x8XXXXX 0
0x3 0x8..... 1
0x4 0x8..... 1

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
2、分配4GB的虚拟空间(Windows 32位)										


0x00000000 - 0x0000FFFF NULL指针使用

int* pnSomeInteger = (int*)malloc(sizeof(int));
*pnSomeInteger = 5;

0x00010000 - 0x7FFFFFFF 用户区

1、将EXE拉伸,存储到指定的位置

1000000 2、遍历EXE导入表,将需要用到的DLL拉伸存储到
指定位置,如果位置被占用,换的地方,并通过
DLL的重定位表,修复全局遍历.

2000000 3、DLL如果引用了其他的DLL 递归第二步

4、修复EXE/DLL中的IAT表

5、创建线程、设置线程CONTEXT 开始执行




内核:

0x80000000 - 0xFFFFFFFF
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
3、创建进程的主线程								

当进程的空间创建完毕,EXE与导入表中的DLL都正确加载完毕后,会创建一个线程

当线程得到CPU的时候,程序就正开始指向了,EIP的初始值设定为:ImageBase+OEP

HANDLE CreateThread(
PSECURITY_ATTRIBUTES psa,
DWORD cbStack,
PTHREAD_START_ROUTINE pfnStartAddr,
PVOID pvParam,
DWORD fdwCreate,
PDWORD pdwThreadID);


当进程创建成功后,会将进程句柄、主线程句柄、进程ID以及主线程ID存储在

typedef struct _PROCESS_INFORMATION
{
HANDLE hProcess; //进程句柄
HANDLE hThread; //主线程句柄
DWORD dwProcessId; //进程ID
DWORD dwThreadId; //线程ID
} PROCESS_INFORMATION;

也就是,CreateProcess的最后一个 OUT 参数

到此,整个进程创建结束了.

# CreateProcess

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
BOOL CreateProcess(					
PCTSTR pszApplicationName, // 程序名字
PTSTR pszCommandLine, // 命令行参数, char* argv[]
PSECURITY_ATTRIBUTES psaProcess,
PSECURITY_ATTRIBUTES psaThread,
BOOL bInheritHandles,
DWORD fdwCreate,
PVOID pvEnvironment,
PCTSTR pszCurDir,
PSTARTUPINFO psiStartInfo,
PPROCESS_INFORMATION ppiProcInfo
);

VOID TestCreateProcessByCmdline()
{
STARTUPINFO si = {0};
PROCESS_INFORMATION pi;

si.cb = sizeof(si);

TCHAR szCmdline[] =TEXT("c://program files//internet explorer//iexplore.exe http://www.ifeng.com");

BOOL res = CreateProcess(
NULL,
szCmdline,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL, &si, &pi);
}
VOID TestCreateProcess()
{
STARTUPINFO si = {0};
PROCESS_INFORMATION pi;

si.cb = sizeof(si);

TCHAR szCmdline[] =TEXT(" http://www.ifeng.com");

BOOL res = CreateProcess(
TEXT("c://program files//internet explorer//iexplore.exe"),
szCmdline,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL, &si, &pi);
}

typedef struct _STARTUPINFO
{
DWORD cb;
PSTR lpReserved;
PSTR lpDesktop;
PSTR lpTitle;
DWORD dwX;
DWORD dwY;
DWORD dwXSize;
DWORD dwYSize;
DWORD dwXCountChars;
DWORD dwYCountChars;
DWORD dwFillAttribute;
DWORD dwFlags;
WORD wShowWindow;
WORD cbReserved2;
PBYTE lpReserved2;
HANDLE hStdInput;
HANDLE hStdOutput;
HANDLE hStdError;
} STARTUPINFO, *LPSTARTUPINFO;
用来设定要创建的应用程序的属性,比如可以指定新创建的控制台程序的标题等待。

一般情况,只要为第一个成员赋值就可以了.

si.cb = sizeof(si);

1
2
3
4
5
6
7
8
typedef struct _PROCESS_INFORMATION					
{
HANDLE hProcess; //进程句柄
HANDLE hThread; //主线程句柄
DWORD dwProcessId; //进程ID
DWORD dwThreadId; //线程ID
} PROCESS_INFORMATION;

1
2
3
4
5
6
7
8
9
10
11
关于句柄和ID					

1、都是系统分配的一个编号,句柄是客户程序使用 ID主要是系统调度时使用.

2、调用CloseHandle关闭进程或者线程句柄的时候,只是让内核计数器减少一个,并不是终止进程或者线程.
进程或线程将继续运行,直到它自己终止运行。

3、进程ID与线程ID 是不可能相同。但不要通过进程或者线程的ID来操作进程或者线程,因为,这个编号是会
重复使用的,也就是说,当你通过ID=100这个编号去访问一个进程的时候,它已经结束了,而且系统将这个编号
赋给了另外一个进程或者线程.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
终止进程的三种方式:										


1、VOID ExitProcess(UINT fuExitCode) //进程自己调用

2、BOOL TerminateProcess(HANDLE hProcess, UINT fuExitCode); //终止其他进程

3、ExitThread //终止进程中的所有线程,进程也会终止


获取进程的退出码:

BOOL GetExitCodeProcess(HANDLE hProcess,PDWORD pdwExitCode);

进程终止时相关操作:

1、进程中剩余的所有线程全部终止运行

2、进程指定的所有用户对象均被释放,所有内核对象均被关闭

3、进程内核对象的状态变成收到通知的状态

4、进程内核对象的使用计数递减1

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
2、句柄的继承	
只有创建进程的时候是否可以被继承为true,句柄表里也为true才可以被子进程继承

进程A中的代码:

char szBuffer[256] = {0};
char szHandle[8] = {0};
//若要创建能继承的句柄,父进程必须指定一个SECURITY_ATTRIBUTES结构并对它进行初始化
//三个成员的意义:大小、默认安全属性、是否可以继承
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
//创建一个可以被继承的内核对象
HANDLE g_hEvent = CreateEvent(&sa, TRUE, FALSE, NULL);

//组织命令行参数
sprintf(szHandle,"%x",g_hEvent);
sprintf(szBuffer,"C:/z2.exe %s",szHandle);

//定义创建进程需要用的结构体
STARTUPINFO si = {0};
PROCESS_INFORMATION pi;
si.cb = sizeof(si);

//创建子进程
BOOL res = CreateProcess(
NULL,
szBuffer,
NULL,
NULL,
TRUE, TRUE的时候,说明子进程可以继承父进程的句柄表
CREATE_NEW_CONSOLE,
NULL,
NULL, &si, &pi);


//设置事件为已通知
SetEvent(g_hEvent);

//关闭句柄 内核对象是否会被销毁?
CloseHandle(g_hEvent);


进程B中的代码:


char szBuffer[256] = {0};

memcpy(szBuffer,argv[1],8);

DWORD dwHandle = 0;

sscanf(szBuffer,"%x",&dwHandle);

printf("%s\n",argv[0]);

printf("%x\n",dwHandle);

HANDLE g_hEvent = (HANDLE)dwHandle;


printf("开始等待.....\n");
//当事件变成已通知时
WaitForSingleObject(g_hEvent, INFINITE);

DWORD dwCode = GetLastError();

printf("等到消息.....%x\n",dwCode);

getchar();

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
BOOL CreateProcess(								
LPCTSTR lpApplicationName, // name of executable module
LPTSTR lpCommandLine, // command line string
LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
BOOL bInheritHandles, // handle inheritance option
DWORD dwCreationFlags, // creation flags
LPVOID lpEnvironment, // new environment block
LPCTSTR lpCurrentDirectory, // current directory name
LPSTARTUPINFO lpStartupInfo, // startup information
LPPROCESS_INFORMATION lpProcessInformation // process information
);



实现功能:

在A进程中创建一个进程(比如浏览器进程IE),并设定该子进程的进程内核句柄与主线程内核句柄为可继承

在A进程中再创建一个进程B,在B中对IE进程控制

进程A代码:

char szBuffer[256] = {0};
char szHandle[8] = {0};

SECURITY_ATTRIBUTES ie_sa_p;
ie_sa_p.nLength = sizeof(ie_sa_p);
ie_sa_p.lpSecurityDescriptor = NULL;
ie_sa_p.bInheritHandle = TRUE;

SECURITY_ATTRIBUTES ie_sa_t;
ie_sa_t.nLength = sizeof(ie_sa_t);
ie_sa_t.lpSecurityDescriptor = NULL;
ie_sa_t.bInheritHandle = TRUE;
//创建一个可以被继承的内核对象,此处是个进程
STARTUPINFO ie_si = {0};
PROCESS_INFORMATION ie_pi;
ie_si.cb = sizeof(ie_si);

TCHAR szCmdline[] =TEXT("c://program files//internet explorer//iexplore.exe");
CreateProcess(
NULL,
szCmdline,
&ie_sa_p,
&ie_sa_t,
TRUE,
CREATE_NEW_CONSOLE,
NULL,
NULL, &ie_si, &ie_pi);

//组织命令行参数
sprintf(szHandle,"%x %x",ie_pi.hProcess,ie_pi.hThread);
sprintf(szBuffer,"C:/z2.exe %s",szHandle);

//定义创建进程需要用的结构体
STARTUPINFO si = {0};
PROCESS_INFORMATION pi;
si.cb = sizeof(si);

//创建子进程
BOOL res = CreateProcess(
NULL,
szBuffer,
NULL,
NULL,
TRUE,
CREATE_NEW_CONSOLE,
NULL,
NULL, &si, &pi);


进程B代码:

DWORD dwProcessHandle = -1;
DWORD dwThreadHandle = -1;
char szBuffer[256] = {0};


memcpy(szBuffer,argv[1],8);
sscanf(szBuffer,"%x",&dwProcessHandle);

memset(szBuffer,0,256);
memcpy(szBuffer,argv[2],8);
sscanf(szBuffer,"%x",&dwThreadHandle);

printf("获取IE进程、主线程句柄\n");
Sleep(2000);
//挂起主线程
printf("挂起主线程\n");
::SuspendThread((HANDLE)dwThreadHandle);

Sleep(5000);

//恢复主线程
::ResumeThread((HANDLE)dwThreadHandle);
printf("恢复主线程\n");

Sleep(5000);

//关闭ID进程
::TerminateProcess((HANDLE)dwProcessHandle,1);
::WaitForSingleObject((HANDLE)dwProcessHandle, INFINITE);

printf("ID进程已经关闭.....\n");


图解

A:
TCHAR szCmdline[] =TEXT("c://program files//internet explorer//iexplore.exe");
X 进程内核 1 CreateProcess(
Y 线程内核 1

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
BOOL CreateProcess(						
LPCTSTR lpApplicationName, // name of executable module
LPTSTR lpCommandLine, // command line string
LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
BOOL bInheritHandles, // handle inheritance option
DWORD dwCreationFlags, // creation flags
LPVOID lpEnvironment, // new environment block
LPCTSTR lpCurrentDirectory, // current directory name
LPSTARTUPINFO lpStartupInfo, // startup information
LPPROCESS_INFORMATION lpProcessInformation // process information
);


1、获取当前进程所使用的目录

char szBuffer[256] = {0};

GetCurrentDirectory(256,szBuffer);

printf("%s\n",szBuffer);


2、观察下面的代码

STARTUPINFO ie_si = {0};
PROCESS_INFORMATION ie_pi;
ie_si.cb = sizeof(ie_si);


TCHAR szBuffer[256] = "E:\\Project\\z2\\Debug\\z2.exe";
CreateProcess(
NULL, // name of executable module
szBuffer, // command line string
NULL, // SD
NULL, // SD
FALSE, // handle inheritance option
CREATE_NEW_CONSOLE, // creation flags
NULL, // new environment block
NULL, // current directory name
&ie_si, // startup information
&ie_pi // process information
);

# 鼠标按键事件

可以通过 spy++ 获取窗口句柄和类,但是这个会变。

image-20230829201046144

FindWindow('类名','窗口标题名');

SetWindowText('句柄','标题')

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
查找指定窗口								

TCHAR szTitle[MAX_PATH] = {0};
HWND hwnd = ::FindWindow(TEXT("#32770"),TEXT("飞鸽传书 IP Messenger"));
if(hwnd != NULL)
{
//修改窗口标题
::SetWindowText(hwnd,"新的窗口标题");
}
else
{
::MessageBox(NULL,TEXT("窗口没有找到"),TEXT("[ERROR]"),MB_OK);
}


窗口控制

TCHAR szTitle[MAX_PATH] = {0};
HWND hwnd = ::FindWindow(TEXT("#32770"),TEXT("飞鸽传书 IP Messenger"));
if(hwnd != NULL)
{
// 从user32.dll 中加载函数
typedef void (WINAPI *PSWITCHTOTHISWINDOW) (HWND,BOOL);
PSWITCHTOTHISWINDOW SwitchToThisWindow;
HMODULE hUser32=LoadLibrary("user32.dll");
SwitchToThisWindow=(PSWITCHTOTHISWINDOW)GetProcAddress(hUser32,"SwitchToThisWindow");

//切换窗口
SwitchToThisWindow(hwnd,false);

Sleep(3000);
//关闭窗口
::SendMessage(hwnd,WM_CLOSE,0,0);
}
else
{
::MessageBox(NULL,TEXT("窗口没有找到"),TEXT("[ERROR]"),MB_OK);
}


查找子窗口

TCHAR szTitle[MAX_PATH] = {0};
HWND hwnd = ::FindWindow(TEXT("#32770"),TEXT("飞鸽传书 IP Messenger"));
if(hwnd != NULL)
{
//查找子窗口
HWND hEdit = FindWindowEx(hwnd,NULL,"Edit","");
//设置窗口标题
::SetWindowText(hEdit,"文本框新的标题");
//修改内容
::SendMessage(hEdit,WM_SETTEXT,0,(LPARAM)"新的内容");
}
else
{
::MessageBox(NULL,TEXT("窗口没有找到"),TEXT("[ERROR]"),MB_OK);
}


TCHAR szTitle[MAX_PATH] = {0};
HWND hwnd = ::FindWindow(TEXT("#32770"),TEXT("飞鸽传书 IP Messenger"));
if(hwnd != NULL)
{
//查找子窗口
HWND hEdit =::GetDlgItem(hwnd,0x3E9);
//获取内容
::SendMessage(hEdit,WM_GETTEXT,MAX_PATH,(LPARAM)szTitle);
//修改内容
::SendMessage(hEdit,WM_SETTEXT,0,(LPARAM)"新的内容");
}
else
{
::MessageBox(NULL,TEXT("窗口没有找到"),TEXT("[ERROR]"),MB_OK);
}

查找子窗口:找到对应的窗口,右键属性,找到 controll id

image-20230829203807122

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
枚举子窗口									

BOOL CALLBACK EnumChildProc(HWND hWnd,LPARAM lParam)
{
TCHAR szTitle[MAX_PATH] = {0};
::GetWindowText(hWnd,szTitle,MAX_PATH);
MessageBox(NULL,szTitle,"[子窗口]",MB_OK);
return true;
}

VOID EnumChildWindow()
{
TCHAR szTitle[MAX_PATH] = {0};
HWND hWnd = ::FindWindow(TEXT("#32770"),TEXT("飞鸽传书 IP Messenger"));
if(hWnd != NULL)
{
::EnumChildWindows(hWnd,EnumChildProc,0);
}
else
{
::MessageBox(NULL,TEXT("窗口没有找到"),TEXT("[ERROR]"),MB_OK);
}
}

枚举所有打开窗口

BOOL CALLBACK EnumOpenWindowProc(HWND hWnd,LPARAM lParam)
{
TCHAR szTitle[MAX_PATH] = {0};
::GetWindowText(hWnd,szTitle,MAX_PATH);
MessageBox(NULL,szTitle,"[窗口]",MB_OK);
if(strcmp(szTitle,"飞鸽传书 IP Messenger") == 0)
{
MessageBox(NULL,szTitle,"[窗口]",MB_OK);
return FALSE;
}
return TRUE; // true 会一直遍历窗口
}
VOID EnumOpenWindows()
{
EnumWindows(EnumOpenWindowProc,NULL);
}


隐藏控制台

#pragma comment(linker,"/subsystem:\"windows\" /entry:\"mainCRTStartup\"" )

模拟鼠标单击


TCHAR szTitle[MAX_PATH] = {0};
RECT r;
HWND hwnd = ::FindWindow(TEXT("#32770"),TEXT("飞鸽传书 IP Messenger"));
if(hwnd != NULL)
{
HWND hButton = FindWindowEx(hwnd,NULL,"Button","刷新(&R)");

//获取窗口坐标
::GetWindowRect(hButton,&r);

printf("%d %d",r.left,r.top);

//设置鼠标的位置
::SetCursorPos(r.left+10,r.top+10);
Sleep(2000);
//鼠标左键单击
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);//点下左键
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);//松开左键
}
else
{
::MessageBox(NULL,TEXT("窗口没有找到"),TEXT("[ERROR]"),MB_OK);
}


模拟键盘点击(搜索:键盘键与虚拟键码对照表)


TCHAR szTitle[MAX_PATH] = {0};
RECT r;
HWND hwnd = ::FindWindow(TEXT("#32770"),TEXT("飞鸽传书 IP Messenger"));
if(hwnd != NULL)
{
//HWND hButton = FindWindowEx(hwnd,NULL,"Button","刷新(&R)");
HWND hEdit =::GetDlgItem(hwnd,0x3E9);

//获取窗口坐标
::GetWindowRect(hEdit,&r);

//设置鼠标的位置
::SetCursorPos(r.left+1,r.top+1);
Sleep(1000);

//鼠标左键单击
mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);//点下左键
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);//松开左键

//模拟键盘
keybd_event(97,0,0,0);
keybd_event(97,0,KEYEVENTF_KEYUP,0);
Sleep(1000);
keybd_event(66,0,0,0);
keybd_event(66,0,KEYEVENTF_KEYUP,0);
Sleep(1000);
keybd_event(16,0,0,0);
keybd_event(67,0,0,0);
keybd_event(67,0,KEYEVENTF_KEYUP,0);
keybd_event(16,0,KEYEVENTF_KEYUP,0);

}
else
{
::MessageBox(NULL,TEXT("窗口没有找到"),TEXT("[ERROR]"),MB_OK);
}

# shellcode 远程注入

1.hook

2.createremotethread

代码注入

硬编码转 shellcode 不要使用 iat 不要使用全局变量 难被检测

模块注入

不需要改代码,特征明显

远程注入

1
2
3
4
5
6
7
//1.加载DLL					
HINSTANCE hModule = LoadLibrary("InjectDll.dll");


//2.释放DLL
FreeLibrary(hModule);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
HANDLE WINAPI CreateThread(						
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
PDWORD lpThreadId
);

HANDLE WINAPI CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);

# 内存注入

把 exe 扔到自己的数据区里,实现注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
步骤:							

1、将自己进程的ImageBase设置一个较大的值,让自己的程序在高空运行.


2、将要执行的进程读取进来,按照进程的ImageBase和SizeOfImage分配空间


3、拉伸进程


4、修复IAT表


5、跳转到入口执行

image-20230913210303051

# 内存写入

进程 A 只知道有进程写了自己的进程。但不知道是谁写的,写了什么。

相比注入,这个在进程的模块中是找不到的。

exe 扔到 exe 里~

image-20230915201756113

虽然已经修复了重定位表,IAT 会修复一部分。比如 [412345] 会被重定位修复后变为 [512345]

但是 [ ] 得出的值会变,即 api 地址发生变化。而且两个进程加载的 dll 不一定一样。

还需要将 [512345] 算出的值变为进程 A 中的 dll 的地址

# hook

hook 修改

1. 监控:可以获取原来的函数参数和返回值

2. 改行为

# IAT hook

更改 [x] == y 中 y 改成自己的函数的地址

注意函数改之后不能破环堆栈平衡

image-20230915213752344

IAT hook 完之后需要改回来

自己使用 getprocaddress (loadlibrary ()) 加载的不在 iat 里面。或者自己写的函数

# inline hook

jmp 走 再跳回来。跳回来之前需要把我们改的代码贴到我们的代码中

我们的代码执行前需要保存原来寄存器的值 pushad popad

还需要保证堆栈平衡,jmp 之前堆栈一定要和原来一样

同时不能修改原来堆栈的值,或者改完能然程序不崩溃

还需要注意线程安全

1
2
3
4
5
6
7
8
9
10
pushad
pushfd
push xxx
...
...
pop xxx
popfd
popad
原来被我们改的代码
jmp

可以不使用 jmp,使用 call,ret,堆栈会自己平衡

不一定非要五个字节,可以 >=5 ,多出来的 nop

hook 后可以读取当前执行环境寄存器的值或者堆栈中的数据

所有 hook 都需要卸载

image-20230916170113026

# 进程通信

# 自定义消息

B 进程控制 A 进程窗口,send /post message

SendMessage 不进入对方消息队列

PostMessage 进入对方消息队列

WM_USER 0x400 // 系统消息 <0x400 ,自定义消息需要> 0x400

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
发送端代码:							

HWND hwnd = ::FindWindow(NULL,TEXT("接收端窗口名"));
if(hwnd == NULL)
{
MessageBox(0,TEXT("没找到窗口"),TEXT("ERROR"),MB_OK);
}
else
{
// 发送消息
//SendMessage(hwnd,WM_USER+0x1,NULL, (LPARAM)100);
PostMessage(hwnd,WM_USER+0x1, NULL, (LPARAM)100);
}


接收端代码:

switch(uMsg)
{
case WM_CLOSE:
{
EndDialog(hDlg,0);
break;
}
case WM_USER+0x1:
{
DWORD x = wParam;
DWORD y = lParam;

MessageBox(0,0,0,0);
break;
}
case WM_COMMAND:

switch (LOWORD (wParam))
{
case IDC_BUTTON_RECV:
{


return TRUE;
}
}
break ;
}

# 共享内存

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
// 发送端代码:					

HANDLE hMapObject;
HANDLE hMapView;

//创建FileMapping对象
hMapObject = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,0x1000,TEXT("shared"));
if(!hMapObject)
{
MessageBox(NULL,TEXT("共享内存失败"),TEXT("Error"),MB_OK);
return FALSE;
}
//将FileMapping对象映射到自己的进程
hMapView = MapViewOfFile(hMapObject,FILE_MAP_WRITE,0,0,0);
if(!hMapView)
{
MessageBox(NULL,TEXT("内存映射失败"),TEXT("Error"),MB_OK);
return FALSE;
}
//向共享内存写入数据
strcpy((char*)hMapView,"Test Shared Memery");


// 接收端代码:

HANDLE hMapObject;
HANDLE hMapView;

//创建FileMapping对象
hMapObject = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,0x1000,TEXT("shared"));
if(!hMapObject)
{
MessageBox(NULL,TEXT("共享内存失败"),TEXT("Error"),MB_OK);
return FALSE;
}
//将FileMapping对象映射到自己的进程
hMapView = MapViewOfFile(hMapObject,FILE_MAP_WRITE,0,0,0);
if(!hMapView)
{
MessageBox(NULL,TEXT("内存映射失败"),TEXT("Error"),MB_OK);
return FALSE;
}
//从共享内存读取数据
TCHAR szBuffer[0x1000] = {0};
memcpy(szBuffer,hMapView,10);

# 匿名管道

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
父进程:										

HANDLE hRead;
HANDLE hWrite;

SECURITY_ATTRIBUTES sa;

sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);

if(!CreatePipe(&hRead,&hWrite,&sa,0))
{
MessageBox(0,TEXT("创建匿名管道失败!"),TEXT("Error"),MB_OK);
}

STARTUPINFO si;
PROCESS_INFORMATION pi;

ZeroMemory(&si,sizeof(STARTUPINFO));

si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = hRead;
si.hStdOutput = hWrite;
si.hStdError = GetStdHandle(STD_ERROR_HANDLE);

if(!CreateProcess("E:\\Project\\zzzzzzz\\Debug\\zzzzzzz.exe",NULL,NULL,NULL,TRUE,0,NULL,NULL,&si,&pi))
{
CloseHandle(hRead);
CloseHandle(hWrite);
hRead = NULL;
hWrite = NULL;
MessageBox(0,TEXT("创建子进程失败!"),TEXT("Error"),MB_OK);
}
else
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}



//写数据
TCHAR szBuffer[] = "http:\\www.dtdebug.com";
DWORD dwWrite;
if(!WriteFile(hWrite,szBuffer,strlen(szBuffer)+1,&dwWrite,NULL))
{
MessageBox(0,TEXT("写数据失败!"),TEXT("Error"),MB_OK);
}

//读数据
TCHAR szBuffer[100];
DWORD dwRead;
if(!ReadFile(hRead,szBuffer,100,&dwRead,NULL))
{
MessageBox(NULL,TEXT("读取数据失败!"),TEXT("Error"),MB_OK);
}
else
{
MessageBox(NULL,szBuffer,TEXT("[读取数据]"),MB_OK);
}


子进程:

//初始化
HANDLE hRead = GetStdHandle(STD_INPUT_HANDLE);
HANDLE hWrite = GetStdHandle(STD_OUTPUT_HANDLE);

//读数据
TCHAR szBuffer[100];
DWORD dwRead;
if(!ReadFile(hRead,szBuffer,100,&dwRead,NULL))
{
MessageBox(NULL,TEXT("读取数据失败!"),TEXT("Error"),MB_OK);
}
else
{
MessageBox(NULL,szBuffer,TEXT("[读取数据]"),MB_OK);
}

//写数据
TCHAR szBuffer[100] = "匿名管道";
DWORD dwWrite;
if(!WriteFile(hWrite,szBuffer,strlen(szBuffer)+1,&dwWrite,NULL))
{
MessageBox(NULL,TEXT("写入数据失败!"),TEXT("Error"),MB_OK);
}



还可以使用命名管道,共享文件,dll 共享节实现进程通信

# 硬编码

指令编码结构

image-20230920123148804

每一条指令,最短 1 字节,最长 15 字节

# 经典定长指定

定长指令由 opcode 就可以确定长度

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
00489001 >  50              push eax
00489002 51 push ecx
00489003 52 push edx
00489004 53 push ebx
00489005 54 push esp
00489006 55 push ebp
00489007 56 push esi
00489008 57 push edi

00489009 58 pop eax
0048900A 59 pop ecx
0048900B 5A pop edx
0048900C 5B pop ebx
0048900D 5C pop esp
0048900E 5D pop ebp
0048900F 5E pop esi
00489010 5F pop edi

00489011 60 pushad
00489012 61 popad

00489013 40 inc eax
00489014 41 inc ecx
00489015 42 inc edx
00489016 43 inc ebx
00489017 44 inc esp
00489018 45 inc ebp
00489019 46 inc esi
0048901A 47 inc edi

0048901B 48 dec eax
0048901C 49 dec ecx
0048901D 4A dec edx
0048901E 4B dec ebx
0048901F 4C dec esp
00489020 4D dec ebp
00489021 4E dec esi
00489022 4F dec edi

00489023 B0 00 mov al,0x0

00489023 B0 00 mov al,0x0
00489025 B1 00 mov cl,0x0
00489027 B2 00 mov dl,0x0
00489029 B3 00 mov bl,0x0

0048902B B4 00 mov ah,0x0
0048902D B5 00 mov ch,0x0
0048902F B6 00 mov dh,0x0
00489031 B7 00 mov bh,0x0

00489033 B8 008D8594 mov eax,0x94858D00
00489038 B9 BA0050FF mov ecx,0xFF5000BA
0048903D BA A90F0000 mov edx,0xFA9
00489042 BB 858C0400 mov ebx,0x48C85
00489047 BC 8BF08D7D mov esp,0x7D8DF08B
0048904C BD 5756FF95 mov ebp,0x95FF5657
00489051 BE 0F0000AB mov esi,0xAB00000F
00489056 BF 00AE75FD mov edi,0xFD75AE00

0048905B 91 xchg eax,ecx
0048905C 92 xchg eax,edx
0048905D 93 xchg eax,ebx
0048905E 94 xchg eax,esp
0048905F 95 xchg eax,ebp
00489060 96 xchg eax,esi
00489061 97 xchg eax,edi
00489062 98 cwde
00489063 99 cdq


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
1、0x70 - 0x7F 							

条件跳转,后跟一个字节立即数的偏移(有符号),共两个字节。
如果条件成立,跳转到 当前指令地址 + 当前指令长度 + Ib
最大值:向前跳7f,向后跳80


004082A0 > /70 1B jo short 004082BD ; < 7f 往下跳 >7F 往上跳
004082A2 |71 11 jno short 004082B5
004082A4 |72 11 jb short 004082B7
004082A6 |73 11 jnb short 004082B9
004082A8 |74 11 je short 004082BB
004082AA |75 11 jnz short 004082BD ; SE 处理程序安装
004082AC |76 11 jbe short 004082BF
004082AE |77 11 ja short 004082C1
004082B0 |78 11 js short 004082C3
004082B2 |79 11 jns short 004082C5
004082B4 |7A 11 jpe short 004082C7
004082B6 |7B 11 jpo short 004082C9
004082B8 |7C 11 jl short 004082CB
004082BA |7D 11 jge short 004082CD
004082BC |7E 11 jle short 004082CF
004082BE 7F 11 jg short 004082D1

2、0x0F 0x80 - 0x0F 0x8F
条件跳转,后跟四个字节立即数的偏移(有符号),共五个字节。
如果条件成立,跳转到 当前指令地址 + 当前指令长度 + Id
最大值:向前跳7FFFFFFFF,向后跳80000000


004082C0 - 0F80 578965E8 jo E8A60C1D
004082C6 - 0F81 64B14300 jno 00843430
004082CC - 0F82 943000A1 jb A140B366
004082D2 - 0F83 4300C1E8 jnb E901831B
004082D8 0F84 FF000000 je 004083DD
004082DE - 0F85 8943008B jnz 8B40C66D
004082E4 - 0F86 94300081 jbe 8140B37E
004082EA - 0F87 00000089 ja 894082F0
004082F0 - 0F88 8943008B js 8B40C67F
004082F6 - 0F89 894300C1 jns C140C685
004082FC 0F8A 03152089 jpe 89609805
00408302 0F8B 89151889 jpo 89589891
00408308 - 0F8C A1148943 jl 43C997AF
0040830E - 0F8D E81025FF jge FF6593FC
00408314 - 0F8E 00A31489 jle 8955261A
0040831A - 0F8F 6A00E8CD jg CE28838A


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
3、其他指令										

0xE0 LOOPNE/LOOPNZ Ib (Jb) 共2字节
ECX = ECX - 1 当ZF = 0 && ECX!=0 时跳转到 当前指令地址 + 当前指令长度 + Ib

0XE1 LOOPE/LOOPZ Ib (Jb) 共2字节
ECX = ECX - 1 当ZF = 1 && ECX != 0 时跳转到 当前指令地址 + 当前指令长度 + Ib

0XE2 LOOP Ib (Jb) 共2字节
ECX = ECX - 1 当 ECX!=0 时跳转到 当前指令地址 + 当前指令长度 + Ib

0XE3 JrCXZ Ib (Jb) (在32位模式中,rCX为ECX) 共2字节
当 ECX = 0 时跳转到 当前指令地址 + 当前指令长度 + Ib
(自己控制步长)

0xE8 CALL Id (Jd) 共5字节
CALL指令的下一条指令地址入栈后,跳转到 当前指令地址 + 当前指令长度 + Id

0xE9 JMP Id (Jd) 共5字节
跳转到 当前指令地址 + 当前指令长度 + Id

8个段寄存器: ES CS SS DS FS GS LDTR TR (顺序固定)
(段寄存器实际是个结构体,共96位,其中仅16位是汇编指令可以访问到的)

0xEA JMP Ap (Ap:六字节长度的直接地址) 共7字节
JMP CS:Id 将Ap中的高2位赋值给CS,低4位直接赋值给EIP, 即跳转

004183D7 > EA 12345678 1B00 JMP FAR 001B:78563412

0xEB JMP Ib (Jb)
跳转到 当前指令地址 + 当前指令长度 + Ib

0xC3 RET 共1字节
EIP出栈

0xC2 RET Iw 共3字节
EIP出栈后,ESP = ESP + Iw

0XCB RETF (return far) 共1字节
出栈8个字节,低4个字节赋值给EIP,高4个字节中低2位赋值给CS

0xCA RETF Iw 共3字节
出栈8个字节,低4个字节赋值给EIP,高4个字节中低2位赋值给CS后,ESP = ESP + Iw


ret -> pop eip
retf -> pop eip, pop cs


00408320 EA 12345678 0B00 jmp far 000B:78563412
00408327 - E9 750A6A1C jmp 1CAA8DA1
0040832C ^ EB CF jmp short 004082FD


# 经典变长指令

1
2
3
4
5
6
7
0x88  	MOV Eb, Gb			G:通用寄存器			
0x89 MOV Ev, Gv E:寄存器/内存
0x8A MOV Gb, Eb b:字节
0x8B MOV Gv, Ev v:Word, doubleword or quadword 取决于平台

00A291BC 880400 mov byte ptr ds:[eax+eax],al

1
ModRM:

image-20230923180030444

image-20230923180307438

Mod (第 6、7 位) 和 R/M (第 0、1、2 位) 共同描述指令中的 E 部分,即寄存器 / 内存

image-20230923180807179

image-20230923181238014

8815 没有这条 ebp 的指令,被 32 位偏移代替 00010101

00A291D0 8815 B59D0500 mov byte ptr ds:[0x59DB5],dl

00A291D5 8810 mov byte ptr ds:[eax],dl

mod 决定了后面的立即数是几位的

opcode 决定了后面有没有 modrm

modrm 决定了后面有没有 sib

如果 rm 是 4,那么后面就有 1 字节的 sib,就是上图中带 [–][–] 的

image-20230927205322395

image-20230927205431293

image-20230927222338794

88 84 48

al 由 modrm 确定

ds;[] 由 sib 确定

mov byte ptr ds:[eax+ecx*2+0x78563412],al

89 84 84 11111111

mov dword ptr ss:[esp+eax*4 + 0x11111111],eax

esp 或者 ebp 的时候是 ss:[]

89 2C 15

mod == 00 [*] 变成了 32 位偏移

mov dword ptr ds:[edx+0x78563412] ,ebp

89 AC 15

mod == 10

mov dowrd ptr ds:[edx+ebp+0x12345678],ebp

89 84 61

sib 中 none 代表啥都不加

mov dword ptr ds:[ecx],eax

注意,他还是占 7 个字节

00CA187D 898461 8BFF558B mov dword ptr ds:[ecx+0x8B55FF8B],eax

# modrm 中 3,4,5 为 opcode 情况:

table A-2 是硬编码主表

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
80 81 82 83 这几个编码,并没有明确给出具体的操作码是什么。						

特别说明:凡是出现Grp的,均参见TableA-6

见到grp,那么modrm中 345为opcode

举例说明:

80 65 08 FF

查表步骤:

1、第一个字节为80 查Table-2表,得到对应结构:Eb,Ib

2、第二个字节为ModR/M字段,所以查分65:

01 100 101

Mod 与 R/M字段 查Table2-2 得到对应的结构:[EBP+DIS8]

3、100 字段 查表TableA-6 得到对应操作码为:AND

4、最终的指令格式:

AND [ebp+dis8],Ib

AND BYTE PTR SS:[EBP+08],0xFF

img

img

img

Table A-6

image-20230928161513596

rAX == RAX / EAX / AX

eAX == AX / EAX

指令长度最多 15 字节,opcode 可能是 1,2,3 字节

table a-2 中只有 0F 是两字节,其余都是 1 字节,0F 查看 table A-3

0F + table-A3 至少都是两字节

0f 38 / 0f 3a 指令长度 3 字节,其余都是两字节 ,查看 table-A4 和 table-A5

table A-3

img

img

img

img

# 指令前缀

image-20230928164340783

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
1、段前缀:	

段寄存器的作用:早期8086cpu寻址范围小,Inter遍通过段寄存器来拓展内存.即通过段寄存器基址+偏移的方式来寻址。

[]中的地址为有效地址(Effect Address),有效地址+段寄存器基址才是实际地址LA(线性地址 Line Address)。

线性地址 = 段基址 + 有效地址

在后来的80386时,cpu的寻址范围大大提升,这些段寄存器便被用作了其他用途。但是DS:[]类似

这种寻址格式却被保留了下来。

实际上操作码已经决定了寻址时使用哪个段寄存器作为基址,不需要其他字节描述。

1、如果没有特别说明,[]前为DS,即DS:[]
2、PUSH POP指令,以及在[]中使用ESP/EBP的,使用SS段
3、在[Base + Index*2Scale + I]中,以Base为判断条件,没有特别说明,用DS。如果Base为ESP/EBP,则用SS段.
4、串操作指令一般使用ES。MOV ES:[EDI] DS:[ESI]中,目标EDI使用ES段,其他使用DS段.
5、EIP指向当前指令,EIP取指令时使用的是CS段.
6、如果指令加段寄存器前缀,则该条指令一律用这个段,如果加多个段寄存器前缀,默认只看op前的那个.

2、操作指令前缀

0x66 将操作数改为16字节。例子50为 PUSH EAX, 而66 50则为 PUSH AX

004183DA 50 PUSH EAX
004183DB 66:50 PUSH AX

3、操作指令前缀:修改默认寻址方式

0x67 将操作数改为16字节。例子50为 PUSH EAX, 而66 50则为 PUSH AX

004183FD 8801 MOV BYTE PTR DS:[ECX],AL
004183FF 67:8801 MOV BYTE PTR DS:[BX+DI],AL

指令前缀更多信息参见:35页

4.锁前缀


指令前缀是互斥的,一共有四组

【2021.01.09】Intel Table_intel table a-1 表说明 - CSDN 博客

# winapi

# MessageBoxA/W

1
2
3
4
5
6
7
8
MessageBoxA(
_In_opt_ HWND hWnd,
_In_opt_ LPCSTR lpText,
_In_opt_ LPCSTR lpCaption,
_In_ UINT uType);

MessageBox(0,0,0,0); //错误
MessageBox(NULL,TEXT("选择进程"),TEXT("出错啦"),MB_OK); // 出错啦是标题,选择进程是弹窗内容

# CreateThread

1
2
3
4
5
6
7
HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);	

ThreadProc 是回调函数
DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
return 0;
}

# SuspendThread

1
2
//挂起线程
::SuspendThread(hThread);

# ResumeThread

1
2
//恢复线程
::ResumeThread(hThread);

# GetDlgItem

1
2
//获取页面空间
HWND hEdit = GetDlgItem(hDlg,IDC_EDIT);

# SetWindowText

1
SetWindowText(hEdit,"1000");

# GetWindowText

1
2
3
4
TCHAR szBuffer[10];
memset();
GetWindowText(句柄,数据缓冲区,长度);

# sscanf

1
2
3
//字符串转整数
sscanf( szBuffer, "%d", &dwTimer );

# sprintf

1
2
3
4
//数字转字符串
memset(szBuffer);
sprintf(数据缓冲区,"%d",数字);

# IDA

1
2
3
4
5
6
7
8
9
10
IDA 创建工程时会一起创建四个文件
后缀分别为 id0 id1 nam til

关闭ida时可以选择不同存储方式
dont pack 会保持四个文件
store后会生成一个idb文件,下次直接打开idb文件

collect grabage 清理内存

dont save 回退到打开前版本
1
2
空格切换视图 流程图 / 反汇编代码

1
2
3
4
A  字符串显示数据
D 数据显示 dd word ddd dword
C 指令显示
U undefined
1
G 跳转
1
ALT + T  搜索
1
n  改名
1
2
3
4
5
6
新增结构体  edit 添加结构体类型
d 结构体中新增成员 d dd ddd
n改名
ALT+Q 添加结构体变量,改变结构体
右键 添加数组
t 选择结构体
1
2
3
;
:
函数 + ;
1
cross_reference  交叉引用

image-20230910212324077

1
2
3
4
5
2、配置双机调试流程
1)在本机安装Windbg(WDK 7600)
2)在虚拟机中(XP)修改boot.ini设置虚拟机
4)修改Windbg运行参数指向虚拟机

image-20230910213126254

Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

John Doe WeChat Pay

WeChat Pay

John Doe Alipay

Alipay

John Doe PayPal

PayPal