# 滴水 reverse
# link
阿里云盘分享 (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
此处可使用 PEID 或 ExeinfoPE 进行代替
32 位计算机最大内存 4G,寻址宽度 32 位
push esp-4 , 压栈,EIP +4
call EIP 跳转,esp-4 压栈
ret 将栈中的地址弹出到 EIP,esp-4
arr [6] 是 ebp+4,放的是函数返回值的地址,修改了返回值在函数返回时会自动执行
全局变量在 pe 头的数据中,局部变量在堆栈中
# 汇编
# 进制
进制的本质是查数
# 数据宽度
4 位宽度表示
8 位宽度表示
16 位宽度表示
32 位宽度表示
6、几个重要的计量单位:
BYTE 字节 8BIT
WORD 字 16BIT 2 字节
DWORD 双字 32BIT 4 字节
or
and
xor
not
2+3
# 寄存器
mov ebx,0x123456789 //ebx=23456789 忽略高位,1 是高位
我们可以打补丁或者其他方式拓展内存
# 内存
栈中每四个内存单元一组
数据窗口中以小端存储以 db 查看时
0012FFDC = E4
0012FFDD = 7C
在数据窗口是小端在前
# 堆栈
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
只计算最低有效字节中的 1 的个数
1 2 3 看当前数据宽度/2 中间的位是否进位 12345678 看5是否产生进位 16位,8位一样
test 和 xor 的区别
1 2 adc al,cl al = al + cl + 进位标志carry(1)
Borrow
1 2 3 movs stos 移动方向由D标志位决定 D = 0 + D = 1 -
# JCC
test 可以判断寄存器中值是否为 0
call 执行时首先将 call 的下一条命令入栈,通过当前地址 + 当前指令长度,指令长度通过硬编码确定,此时 esp-4,作为 call ret 之后的下一条执行,EIP 变为 call 的地址
ret 时将栈顶的返回地址给 EIP,esp+4
因此一个函数在被调用时,堆栈栈顶就是他返回后执行的下一条指令
# 堆栈图
定位到 winMain
有如下函数
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
注意 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 -
# 函数
计算机的函数,是一个固定的一个程序段,或称其为一个子程序,它东可以实现固定运算功能的同时还带有一入口和一个出口,所谓的入口,就是函数所带的各个参数,我们可以通过这个入口,把函数的参数值代入子程序,供计算机处理,所谓出口,就是指函数的计算结果,也称为返回值,在计算机求得之后,由此口带回给调用它的程序。
函数传参包括寄存器传参 (比如 this 指针) 和内存传参
函数返回结果可以通过寄存器返回 (一般为 EAX) 或者内存
Windows 堆栈特点
1. 现进后出
2. 向低地址扩展
windows 中的堆栈,是一块普通的内存,主要用来存储一些临时的数据和参数等可以把 Windows 中的堆栈想象成是一个公用的书箱,函数就像是使用箱子的人函数在执行的时候,会用到这个书箱,把一些数据存到里面,但用完的时候一定要记得把书拿走,否则会乱的,也就是说,你放进去几本书,走的时候也要拿走几本书,这个就是堆栈平衡.
# C 语言
# VC6.0
快捷键
F7 编译
F5 执行
shift + F5 结束
函数名、参数名、变量名的命名规则:只能包含字母、数字和_且不能以数字开头.
函数返回值
1. 无参数,无返回值的格式形式
当函数没有参数时不需要提升和降低堆栈
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()
更改入口点
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 ],78 h short k = 0x12345678 k = 5678 整数类型 unsigned signed unsigned char a = 0xFF ; 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 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 中的判断条件是相反的
还原反汇编
函数内部功能分析:
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 之后的代码
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 };0040 D740 >/> \55 push ebp0040 D741 |. 8B EC mov ebp,esp0040 D743 |. 83 EC 44 sub esp,0x44 0040 D746 |. 53 push ebx0040 D747 |. 56 push esi0040 D748 |. 57 push edi0040 D749 |. 8 D7D BC lea edi,[local.17 ]0040 D74C |. B9 11000000 mov ecx,0x11 0040 D751 |. B8 CCCCCCCC mov eax,0xCCCCCCCC 0040 D756 |. F3:AB rep stos dword ptr es:[edi]0040 D758 |. C645 FC 01 mov byte ptr ss:[ebp-0x4 ],0x1 0040 D75C |. C645 FD 02 mov byte ptr ss:[ebp-0x3 ],0x2 0040 D760 |. C645 FE 03 mov byte ptr ss:[ebp-0x2 ],0x3 0040 D764 |. 5F pop edi0040 D765 |. 5 E pop esi0040 D766 |. 5B pop ebx0040 D767 |. 8B E5 mov esp,ebp0040 D769 |. 5 D pop ebp0040 D76A \. C3 retnchar a[4 ] = {1 ,2 ,3 ,4 };0040 D740 >/> \55 push ebp0040 D741 |. 8B EC mov ebp,esp0040 D743 |. 83 EC 44 sub esp,0x44 0040 D746 |. 53 push ebx0040 D747 |. 56 push esi0040 D748 |. 57 push edi0040 D749 |. 8 D7D BC lea edi,[local.17 ]0040 D74C |. B9 11000000 mov ecx,0x11 0040 D751 |. B8 CCCCCCCC mov eax,0xCCCCCCCC 0040 D756 |. F3:AB rep stos dword ptr es:[edi]0040 D758 |. C645 FC 01 mov byte ptr ss:[ebp-0x4 ],0x1 0040 D75C |. C645 FD 02 mov byte ptr ss:[ebp-0x3 ],0x2 0040 D760 |. C645 FE 03 mov byte ptr ss:[ebp-0x2 ],0x3 0040 D764 |. C645 FF 04 mov byte ptr ss:[ebp-0x1 ],0x4 0040 D768 |. 5F pop edi0040 D769 |. 5 E pop esi0040 D76A |. 5B pop ebx0040 D76B |. 8B E5 mov esp,ebp0040 D76D |. 5 D pop ebp0040 D76E \. C3 retn数组声明是需要常量来提升堆栈,使用时[]内可以是变量 char i[3 ] char i[4 ] short i[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]; } 0040 D770 >/> \55 push ebp0040 D771 |. 8B EC mov ebp,esp0040 D773 |. 83 EC 74 sub esp,0x74 ;0x74 -0x40 =0x34 =52 /4 =13 = 10 + 1 + 1 + 1 0040 D776 |. 53 push ebx0040 D777 |. 56 push esi0040 D778 |. 57 push edi0040 D779 |. 8 D7D 8 C lea edi,[local.29 ]0040 D77C |. B9 1 D000000 mov ecx,0x1D 0040 D781 |. B8 CCCCCCCC mov eax,0xCCCCCCCC 0040 D786 |. F3:AB rep stos dword ptr es:[edi]0040 D788 |. C745 FC 01000000 mov [local.1 ],0x1 0040 D78F |. C745 F8 02000000 mov [local.2 ],0x2 0040 D796 |. C745 CC 01000000 mov [local.13 ],0x1 ;低地址在上0040 D79D |. C745 D0 02000000 mov [local.12 ],0x2 0040 D7A4 |. C745 D4 03000000 mov [local.11 ],0x3 0040 D7AB |. C745 D8 04000000 mov [local.10 ],0x4 0040 D7B2 |. C745 DC 05000000 mov [local.9 ],0x5 0040 D7B9 |. C745 E0 06000000 mov [local.8 ],0x6 0040 D7C0 |. C745 E4 07000000 mov [local.7 ],0x7 0040 D7C7 |. C745 E8 08000000 mov [local.6 ],0x8 0040 D7CE |. C745 EC 09000000 mov [local.5 ],0x9 0040 D7D5 |. C745 F0 00000000 mov [local.4 ],0x0 ; r = arr[1 ]; 0040 D7DC |. 8B 45 D0 mov eax,[local.12 ] ; 反汇编中写的是定值,直接取值0040 D7DF |. 8945 F4 mov [local.3 ],eax ;r = arr[1 ] = 2 ;; r = arr[x]; 0040 D7E2 |. 8B 4D FC mov ecx,[local.1 ] ; ecx = 1 0x34 = 52 0040 D7E5 |. 8B 548D CC mov edx,dword ptr ss:[ebp+ecx*4 -0x34 ] ; edx = 4 -34 =ebp-0x30 =[local.12 ]=2 0040 D7E9 |. 8955 F4 mov [local.3 ],edx ; r = 2 0040 D7EC |. 8B 45 FC mov eax,[local.1 ] ; 1 0040 D7EF |. 0345 F8 add eax,[local.2 ] ; eax = 1 +2 = 3 0040 D7F2 |. 8B 4C85 CC mov ecx,dword ptr ss:[ebp+eax*4 -0x34 ] ; ecx = [ebp-0x28 ] = [local.10 ] = 4 0040 D7F6 |. 894 D F4 mov [local.3 ],ecx ; r = 4 0040 D7F9 |. 8B 55 FC mov edx,[local.1 ] ; edx = 1 0040 D7FC |. 8B 45 F8 mov eax,[local.2 ] ; eax = 2 0040 D7FF |. 8 D0C50 lea ecx,dword ptr ds:[eax+edx*2 ] ; ecx = 4 0040 D802 |. 8B 548D CC mov edx,dword ptr ss:[ebp+ecx*4 -0x34 ] ; edx = [local.9 ] = 5 0040 D806 |. 8955 F4 mov [local.3 ],edx ; r= 5 0040 D809 |. 5F pop edi0040 D80A |. 5 E pop esi0040 D80B |. 5B pop ebx0040 D80C |. 8B E5 mov esp,ebp0040 D80E |. 5 D pop ebp0040 D80F \. C3 retn[ebp+ecx*4 -0x34 ] 每次减去0x34 的原因,0x34 = 52 ,是该函数内所有局部变量一共分配的空间 ecx的值即为数组下标,去对应数组的下标即可取到对应的值,注意下标从0 开始 short ecx * 2 int a[10 ] = {1 ,2 ,3 ,4 ,5 } 二维数组在汇编中和一维数组存储方式一样 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 ; }
# 结构体
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; 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 ebp00401031 |. 8B EC mov ebp,esp00401033 |. 83 EC 40 sub esp,0x40 00401036 |. 53 push ebx00401037 |. 56 push esi00401038 |. 57 push edi00401039 |. 8 D7D C0 lea edi,[local.16 ]0040103 C |. B9 10000000 mov ecx,0x10 00401041 |. B8 CCCCCCCC mov eax,0xCCCCCCCC 00401046 |. F3:AB rep stos dword ptr es:[edi]00401048 |. C705 507 C4200>mov dword ptr ds:[0x427C50 ],0xA 00401052 |. 66 :C705 547 C4>mov word ptr ds:[0x427C54 ],0x14 0040105B |. C605 567 C4200>mov byte ptr ds:[0x427C56 ],0x1E 00401062 |. C705 587 C4200>mov dword ptr ds:[0x427C58 ],0x1 0040106 C |. C705 5 C7C4200>mov dword ptr ds:[0x427C5C ],0x2 00401076 |. 5F pop edi00401077 |. 5 E pop esi00401078 |. 5B pop ebx00401079 |. 8B E5 mov esp,ebp0040107B |. 5 D pop ebp0040107 C \. C3 retn00401090 >/> \55 push ebp00401091 |. 8B EC mov ebp,esp00401093 |. 83 EC 4 C sub esp,0x4C 00401096 |. 53 push ebx00401097 |. 56 push esi00401098 |. 57 push edi00401099 |. 8 D7D B4 lea edi,[local.19 ]0040109 C |. B9 13000000 mov ecx,0x13 004010 A1 |. B8 CCCCCCCC mov eax,0xCCCCCCCC 004010 A6 |. F3:AB rep stos dword ptr es:[edi]004010 A8 |. A1 507 C4200 mov eax,dword ptr ds:[0x427C50 ]004010 AD |. 8945 FC mov [local.1 ],eax004010B 0 |. 0F BF0D 547 C42>movsx ecx,word ptr ds:[0x427C54 ]004010B 7 |. 894 D F8 mov [local.2 ],ecx004010B A |. 0F BE15 567 C42>movsx edx,byte ptr ds:[0x427C56 ]004010 C1 |. 8955 F4 mov [local.3 ],edx004010 C4 |. 5F pop edi004010 C5 |. 5 E pop esi004010 C6 |. 5B pop ebx004010 C7 |. 8B E5 mov esp,ebp004010 C9 |. 5 D pop ebp004010 CA \. 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 ebp00401021 |. 8B EC mov ebp,esp00401023 |. 83 EC 7 C sub esp,0x7C 00401026 |. 53 push ebx00401027 |. 56 push esi00401028 |. 57 push edi00401029 |. 8 D7D 84 lea edi,[local.31 ]0040102 C |. B9 1F 000000 mov ecx,0x1F 00401031 |. B8 CCCCCCCC mov eax,0xCCCCCCCC 00401036 |. F3:AB rep stos dword ptr es:[edi]00401038 |. C745 C4 0 A000000 mov dword ptr ss:[ebp-0x3C ],0xA 0040103F |. 66 :C745 C8 1400 mov word ptr ss:[ebp-0x38 ],0x14 00401045 |. C645 CA 1 E 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 0040105 E |. 5F pop edi0040105F |. 5 E pop esi00401060 |. 5B pop ebx00401061 |. 8B E5 mov esp,ebp00401063 |. 5 D pop ebp00401064 \. C3 retn 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 ebp00401021 |. 8B EC mov ebp,esp00401023 |. 83 EC 4 C sub esp,0x4C 00401026 |. 53 push ebx00401027 |. 56 push esi00401028 |. 57 push edi00401029 |. 8 D7D B4 lea edi,[local.19 ]0040102 C |. B9 13000000 mov ecx,0x13 00401031 |. B8 CCCCCCCC mov eax,0xCCCCCCCC 00401036 |. F3:AB rep stos dword ptr es:[edi]00401038 |. C745 F4 0 A000000 mov [local.3 ],0xA 0040103F |. C745 F8 14000000 mov [local.2 ],0x14 00401046 |. C745 FC 1E000000 mov [local.1 ],0x1E 0040104 D |. 5F pop edi0040104 E |. 5 E pop esi0040104F |. 5B pop ebx00401050 |. 8B E5 mov esp,ebp00401052 |. 5 D pop ebp00401053 \. C3 retnfunc2 00401080 >/> \55 push ebp00401081 |. 8B EC mov ebp,esp00401083 |. 83 EC 4 C sub esp,0x4C 00401086 |. 53 push ebx00401087 |. 56 push esi00401088 |. 57 push edi00401089 |. 8 D7D B4 lea edi,[local.19 ]0040108 C |. B9 13000000 mov ecx,0x13 00401091 |. B8 CCCCCCCC mov eax,0xCCCCCCCC 00401096 |. F3:AB rep stos dword ptr es:[edi]00401098 |. C745 FC 0 A000000 mov [local.1 ],0xA 0040109F |. C745 F8 14000000 mov [local.2 ],0x14 004010 A6 |. C745 F4 1E000000 mov [local.3 ],0x1E 004010 AD |. 5F pop edi004010 AE |. 5 E pop esi004010 AF |. 5B pop ebx004010B 0 |. 8B E5 mov esp,ebp004010B 2 |. 5 D pop ebp004010B 3 \. C3 retnfunc3 004106F 0 >/> \55 push ebp004106F 1 |. 8B EC mov ebp,esp004106F 3 |. 83 EC 4 C sub esp,0x4C 004106F 6 |. 53 push ebx004106F 7 |. 56 push esi004106F 8 |. 57 push edi004106F 9 |. 8 D7D B4 lea edi,[local.19 ]004106F C |. B9 13000000 mov ecx,0x13 00410701 |. B8 CCCCCCCC mov eax,0xCCCCCCCC 00410706 |. F3:AB rep stos dword ptr es:[edi]00410708 |. C745 F4 0 A000> mov [local.3 ],0xA 0041070F |. C745 F8 14000 > mov [local.2 ],0x14 00410716 |. C745 FC 1E000 > mov [local.1 ],0x1E 0041071 D |. 5F pop edi0041071 E |. 5 E pop esi0041071F |. 5B pop ebx00410720 |. 8B E5 mov esp,ebp00410722 |. 5 D pop ebp00410723 \. 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
1 2 3 4 5 6 对齐原则 原则一:数据成员对齐规则。结构的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储). 原则二:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。 原则三:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b, b里有char,int,double等元素,那b应该从8的整数倍开始存储.) 原则四:对齐参数如果比结构体成员的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 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
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 ); 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 ); return 0 ; }
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 ebp00401021 8B EC mov ebp,esp00401023 83 EC 44 sub esp,44 h00401026 53 push ebx00401027 56 push esi00401028 57 push edi00401029 8 D 7 D BC lea edi,[ebp-44 h]0040102 C B9 11 00 00 00 mov ecx,11 h00401031 B8 CC CC CC CC mov eax,0 CCCCCCCCh00401036 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 ],eax0040103 E 83 7 D FC 01 cmp dword ptr [ebp-4 ],1 00401042 74 0 E je func+32 h (00401052 )00401044 83 7 D FC 02 cmp dword ptr [ebp-4 ],2 00401048 74 17 je func+41 h (00401061 )0040104 A 83 7 D FC 03 cmp dword ptr [ebp-4 ],3 0040104 E 74 20 je func+50 h (00401070 )00401050 EB 2 D jmp func+5F h (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 (0040108 c) 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 (0040108 c) 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 (0040108 c) 0040107F 68 1C 20 42 00 push offset string "5" (0042201 c) 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 (004011 c0) 00401099 8B E5 mov esp,ebp 0040109B 5D pop ebp 0040109C C3 ret 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 ebp00401021 8B EC mov ebp,esp00401023 83 EC 44 sub esp,44 h00401026 53 push ebx00401027 56 push esi00401028 57 push edi00401029 8 D 7 D BC lea edi,[ebp-44 h]0040102 C B9 11 00 00 00 mov ecx,11 h00401031 B8 CC CC CC CC mov eax,0 CCCCCCCCh00401036 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 ],eax0040103 E 8B 4 D FC mov ecx,dword ptr [ebp-4 ]00401041 83 E9 01 sub ecx,1 00401044 89 4 D FC mov dword ptr [ebp-4 ],ecx00401047 83 7 D FC 03 cmp dword ptr [ebp-4 ],3 0040104B 77 46 ja $L539+0F h (00401093 )0040104 D 8B 55 FC mov edx,dword ptr [ebp-4 ]00401050 FF 24 95 B1 10 40 00 jmp dword ptr [edx*4 +4010B 1h]$L533: 00401057 68 2 C 20 42 00 push offset string "1" (0042202 c)0040105 C E8 DF 00 00 00 call printf (00401140 ) 00401061 83 C4 04 add esp,4 00401064 EB 3A jmp $L539+1Ch (004010 a0) $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+1 Ch (004010 a0)$L537: 00401075 68 24 20 42 00 push offset string "3" (00422024 )0040107 A E8 C1 00 00 00 call printf (00401140 )0040107F 83 C4 04 add esp,4 00401082 EB 1 C jmp $L539+1 Ch (004010 a0)$L539: 00401084 68 20 20 42 00 push offset string "4" (00422020 )00401089 E8 B2 00 00 00 call printf (00401140 )0040108 E 83 C4 04 add esp,4 00401091 EB 0 D jmp $L539+1 Ch (004010 a0)00401093 68 1 C 20 42 00 push offset string "5" (0042201 c)00401098 E8 A3 00 00 00 call printf (00401140 )0040109 D 83 C4 04 add esp,4 004010 A0 5F pop edi004010 A1 5 E pop esi004010 A2 5B pop ebx004010 A3 83 C4 44 add esp,44 h004010 A6 3B EC cmp ebp,esp004010 A8 E8 13 01 00 00 call __chkesp (004011 c0)004010 AD 8B E5 mov esp,ebp004010 AF 5 D pop ebp004010B 0 C3 ret //case顺序交换不影响大表的生成,即可以是无序的
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 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 ; } } 0040 D7D0 55 push ebp0040 D7D1 8B EC mov ebp,esp0040 D7D3 83 EC 44 sub esp,44 h0040 D7D6 53 push ebx0040 D7D7 56 push esi0040 D7D8 57 push edi0040 D7D9 8 D 7 D BC lea edi,[ebp-44 h]0040 D7DC B9 11 00 00 00 mov ecx,11 h0040 D7E1 B8 CC CC CC CC mov eax,0 CCCCCCCCh0040 D7E6 F3 AB rep stos dword ptr [edi]0040 D7E8 8B 45 08 mov eax,dword ptr [ebp+8 ]0040 D7EB 89 45 FC mov dword ptr [ebp-4 ],eax0040 D7EE 8B 4 D FC mov ecx,dword ptr [ebp-4 ]0040 D7F1 81 E9 2 D 01 00 00 sub ecx,12 Dh0040 D7F7 89 4 D FC mov dword ptr [ebp-4 ],ecx0040 D7FA 83 7 D FC 09 cmp dword ptr [ebp-4 ],9 0040 D7FE 0F 87 A6 00 00 00 ja $L551+0F h (0040 d8aa)0040 D804 8B 55 FC mov edx,dword ptr [ebp-4 ]0040 D807 FF 24 95 C8 D8 40 00 jmp dword ptr [edx*4 +40 D8C8h]$L533: 0040 D80E 68 C0 2F 42 00 push offset string "1" (00422f c0)0040 D813 E8 28 39 FF FF call printf (00401140 ) 0040D818 83 C4 04 add esp,4 0040D81B E9 97 00 00 00 jmp $L551+1Ch (0040 d8b7) $L535: 0040 D820 68 BC 2F 42 00 push offset string "2" (00422f bc)0040 D825 E8 16 39 FF FF call printf (00401140 )0040 D82A 83 C4 04 add esp,4 0040 D82D E9 85 00 00 00 jmp $L551+1 Ch (0040 d8b7)$L537: 0040 D832 68 B8 2F 42 00 push offset string "3" (00422f b8)0040 D837 E8 04 39 FF FF call printf (00401140 )0040 D83C 83 C4 04 add esp,4 0040 D83F EB 76 jmp $L551+1 Ch (0040 d8b7)$L539: 0040 D841 68 B4 2F 42 00 push offset string "4" (00422f b4)0040 D846 E8 F5 38 FF FF call printf (00401140 )0040 D84B 83 C4 04 add esp,4 0040 D84E EB 67 jmp $L551+1 Ch (0040 d8b7)$L541: 0040 D850 68 64 2F 42 00 push offset string "5" (00422f 64)0040 D855 E8 E6 38 FF FF call printf (00401140 )0040 D85A 83 C4 04 add esp,4 0040 D85D EB 58 jmp $L551+1 Ch (0040 d8b7)$L543: 0040 D85F 68 3 C 21 42 00 push offset string "6" (0042213 c)0040 D864 E8 D7 38 FF FF call printf (00401140 )0040 D869 83 C4 04 add esp,4 0040 D86C EB 49 jmp $L551+1 Ch (0040 d8b7)$L545: 0040 D86E 68 2 C 20 42 00 push offset string "7" (0042202 c)0040 D873 E8 C8 38 FF FF call printf (00401140 )0040 D878 83 C4 04 add esp,4 0040 D87B EB 3 A jmp $L551+1 Ch (0040 d8b7)$L547: 0040 D87D 68 28 20 42 00 push offset string "8" (00422028 )0040 D882 E8 B9 38 FF FF call printf (00401140 )0040 D887 83 C4 04 add esp,4 0040 D88A EB 2B jmp $L551+1 Ch (0040 d8b7)$L549: 0040 D88C 68 24 20 42 00 push offset string "9" (00422024 )0040 D891 E8 AA 38 FF FF call printf (00401140 )0040 D896 83 C4 04 add esp,4 0040 D899 EB 1 C jmp $L551+1 Ch (0040 d8b7)$L551: 0040 D89B 68 20 20 42 00 push offset string "A" (00422020 )0040 D8A0 E8 9B 38 FF FF call printf (00401140 )0040 D8A5 83 C4 04 add esp,4 0040 D8A8 EB 0 D jmp $L551+1 Ch (0040 d8b7)0040 D8AA 68 1 C 20 42 00 push offset string "sb" (0042201 c)0040 D8AF E8 8 C 38 FF FF call printf (00401140 )0040 D8B4 83 C4 04 add esp,4 0040 D8B7 5F pop edi0040 D8B8 5 E pop esi0040 D8B9 5B pop ebx0040 D8BA 83 C4 44 add esp,44 h0040 D8BD 3B EC cmp ebp,esp0040 D8BF E8 FC 38 FF FF call __chkesp (004011 c0)0040 D8C4 8B E5 mov esp,ebp0040 D8C6 5 D pop ebp0040 D8C7 C3 ret0040 D8C8 0 E D8 40 00 .谸.0040 D8CC 20 D8 40 00 谸.0040 D8D0 32 D8 40 00 2 谸.0040 D8D4 41 D8 40 00 A谸.0040 D8D8 50 D8 40 00 P谸.0040 D8DC 5F D8 40 00 _谸.0040 D8E0 6 E D8 40 00 n谸.0040 D8E4 7 D D8 40 00 }谸.0040 D8E8 8 C D8 40 00 屫@.0040 D8EC 9B D8 40 00 涁@. //case不连续且差值不大 //如果中间出现不连续的case会用default补大表空缺 //空缺的地方会用case补,下面的两个0040 D886 0040 D8A4 0 E D8 40 00 .谸.0040 D8A8 86 D8 40 00 嗀@.0040 D8AC 86 D8 40 00 嗀@.0040 D8B0 1 D D8 40 00 .谸.0040 D8B4 2 C D8 40 00 ,谸.0040 D8B8 3B D8 40 00 ; 谸.0040 D8BC 4 A D8 40 00 J谸.0040 D8C0 59 D8 40 00 Y谸.0040 D8C4 68 D8 40 00 h谸.0040 D8C8 77 D8 40 00 w谸. 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 ; } } 0040 D7D0 55 push ebp0040 D7D1 8B EC mov ebp,esp0040 D7D3 83 EC 44 sub esp,44 h0040 D7D6 53 push ebx0040 D7D7 56 push esi0040 D7D8 57 push edi0040 D7D9 8 D 7 D BC lea edi,[ebp-44 h]0040 D7DC B9 11 00 00 00 mov ecx,11 h0040 D7E1 B8 CC CC CC CC mov eax,0 CCCCCCCCh0040 D7E6 F3 AB rep stos dword ptr [edi]0040 D7E8 8B 45 08 mov eax,dword ptr [ebp+8 ]0040 D7EB 89 45 FC mov dword ptr [ebp-4 ],eax0040 D7EE 8B 4 D FC mov ecx,dword ptr [ebp-4 ]0040 D7F1 81 E9 2 D 01 00 00 sub ecx,12 Dh0040 D7F7 89 4 D FC mov dword ptr [ebp-4 ],ecx0040 D7FA 83 7 D FC 09 cmp dword ptr [ebp-4 ],9 0040 D7FE 77 4 E ja $L539+0F h (0040 d84e)0040 D800 8B 45 FC mov eax,dword ptr [ebp-4 ]0040 D803 33 D2 xor edx,edx0040 D805 8 A 90 80 D8 40 00 mov dl,byte ptr (0040 d880) [eax] 0040D80B FF 24 95 6C D8 40 00 jmp dword ptr [edx*4+40D86Ch] $L533: 0040 D812 68 2 C 20 42 00 push offset string "1" (0042202 c)0040 D817 E8 24 39 FF FF call printf (00401140 )0040 D81C 83 C4 04 add esp,4 0040 D81F EB 3 A jmp $L539+1 Ch (0040 d85b)$L535: 0040 D821 68 28 20 42 00 push offset string "8" (00422028 )0040 D826 E8 15 39 FF FF call printf (00401140 )0040 D82B 83 C4 04 add esp,4 0040 D82E EB 2B jmp $L539+1 Ch (0040 d85b)$L537: 0040 D830 68 24 20 42 00 push offset string "9" (00422024 )0040 D835 E8 06 39 FF FF call printf (00401140 )0040 D83A 83 C4 04 add esp,4 0040 D83D EB 1 C jmp $L539+1 Ch (0040 d85b)$L539: 0040 D83F 68 20 20 42 00 push offset string "A" (00422020 )0040 D844 E8 F7 38 FF FF call printf (00401140 )0040 D849 83 C4 04 add esp,4 0040 D84C EB 0 D jmp $L539+1 Ch (0040 d85b)0040 D84E 68 1 C 20 42 00 push offset string "sb" (0042201 c)0040 D853 E8 E8 38 FF FF call printf (00401140 )0040 D858 83 C4 04 add esp,4 0040 D85B 5F pop edi0040 D85C 5 E pop esi0040 D85D 5B pop ebx0040 D85E 83 C4 44 add esp,44 h0040 D861 3B EC cmp ebp,esp0040 D863 E8 58 39 FF FF call __chkesp (004011 c0)0040 D868 8B E5 mov esp,ebp0040 D86A 5 D pop ebp0040 D86B C3 ret 0040 D86C 12 D8 40 00 .谸.0040 D870 21 D8 40 00 !谸.0040 D874 30 D8 40 00 0 谸.0040 D878 3F D8 40 00 ?谸.0040 D87C 4 E D8 40 00 N谸.0040 D880 00 04 04 04 ....0040 D884 04 04 04 01 ....0040 D888 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 ; } } 0040 D7D0 55 push ebp0040 D7D1 8B EC mov ebp,esp0040 D7D3 83 EC 44 sub esp,44 h0040 D7D6 53 push ebx0040 D7D7 56 push esi0040 D7D8 57 push edi0040 D7D9 8 D 7 D BC lea edi,[ebp-44 h]0040 D7DC B9 11 00 00 00 mov ecx,11 h0040 D7E1 B8 CC CC CC CC mov eax,0 CCCCCCCCh0040 D7E6 F3 AB rep stos dword ptr [edi]0040 D7E8 8B 45 08 mov eax,dword ptr [ebp+8 ]0040 D7EB 89 45 FC mov dword ptr [ebp-4 ],eax0040 D7EE 81 7 D FC 45 28 00 00 cmp dword ptr [ebp-4 ],2845 h0040 D7F5 7F 1 D jg func+44 h (0040 d814)0040 D7F7 81 7 D FC 45 28 00 00 cmp dword ptr [ebp-4 ],2845 h0040 D7FE 74 3 D je func+6 Dh (0040 d83d)0040 D800 81 7 D FC 15 05 00 00 cmp dword ptr [ebp-4 ],515 h0040 D807 74 16 je func+4F h (0040 d81f)0040 D809 81 7 D FC BC 14 00 00 cmp dword ptr [ebp-4 ],14B Ch0040 D810 74 1 C je func+5 Eh (0040 d82e)0040 D812 EB 47 jmp func+8B h (0040 d85b)0040 D814 81 7 D FC 56 4F 00 00 cmp dword ptr [ebp-4 ],4F 56h0040 D81B 74 2F je func+7 Ch (0040 d84c)0040 D81D EB 3 C jmp func+8B h (0040 d85b)0040 D81F 68 2 C 20 42 00 push offset string "1" (0042202 c)0040 D824 E8 17 39 FF FF call printf (00401140 ) 0040D829 83 C4 04 add esp,4 0040D82C EB 3A jmp func+98h (0040 d868) 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 (0040 d868) 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 (0040 d868) 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 (0040 d868) 0040D85B 68 1C 20 42 00 push offset string "sb" (0042201 c) 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 (004011 c0) 0040D875 8B E5 mov esp,ebp 0040D877 5D pop ebp 0040D878 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 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); } } 0040 D920 55 push ebp0040 D921 8B EC mov ebp,esp0040 D923 83 EC 44 sub esp,44 h0040 D926 53 push ebx0040 D927 56 push esi0040 D928 57 push edi0040 D929 8 D 7 D BC lea edi,[ebp-44 h]0040 D92C B9 11 00 00 00 mov ecx,11 h0040 D931 B8 CC CC CC CC mov eax,0 CCCCCCCCh0040 D936 F3 AB rep stos dword ptr [edi] 0040 D938 C7 45 FC 00 00 00 00 mov dword ptr [ebp-4 ],0 ;int i = 0 ;0040 D93F EB 09 jmp func2+2 Ah (0040 d94a)0040 D941 8B 45 FC mov eax,dword ptr [ebp-4 ]0040 D944 83 C0 01 add eax,1 0040 D947 89 45 FC mov dword ptr [ebp-4 ],eax0040 D94A 83 7 D FC 0 A cmp dword ptr [ebp-4 ],0 Ah ;i<=10 0040 D94E 7F 13 jg func2+43 h (0040 d963) ;i>10 ,ret0040 D950 8B 4 D FC mov ecx,dword ptr [ebp-4 ] ;i<=10 0040 D953 51 push ecx ;ecx = i0040 D954 68 C4 2F 42 00 push offset string "%d\n" (00422f c4)0040 D959 E8 E2 37 FF FF call printf (00401140 )0040 D95E 83 C4 08 add esp,8 0040 D961 EB DE jmp func2+21 h (0040 d941)0040 D963 5F pop edi0040 D964 5 E pop esi0040 D965 5B pop ebx0040 D966 83 C4 44 add esp,44 h0040 D969 3B EC cmp ebp,esp0040 D96B E8 50 38 FF FF call __chkesp (004011 c0)0040 D970 8B E5 mov esp,ebp0040 D972 5 D pop ebp0040 D973 C3 retvoid func3 (){ int i = 0 ; while (i<=10 ) { printf ("%d\n" ,i); i++; } } 0040 D980 55 push ebp0040 D981 8B EC mov ebp,esp0040 D983 83 EC 44 sub esp,44 h0040 D986 53 push ebx0040 D987 56 push esi0040 D988 57 push edi0040 D989 8 D 7 D BC lea edi,[ebp-44 h]0040 D98C B9 11 00 00 00 mov ecx,11 h0040 D991 B8 CC CC CC CC mov eax,0 CCCCCCCCh0040 D996 F3 AB rep stos dword ptr [edi] 0040 D998 C7 45 FC 00 00 00 00 mov dword ptr [ebp-4 ],0 0040 D99F 83 7 D FC 0 A cmp dword ptr [ebp-4 ],0 Ah0040 D9A3 7F 1 C jg func3+41 h (0040 d9c1)0040 D9A5 8B 45 FC mov eax,dword ptr [ebp-4 ]0040 D9A8 50 push eax0040 D9A9 68 C4 2F 42 00 push offset string "%d\n" (00422f c4)0040 D9AE E8 8 D 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 (0040 d99f) 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 (004011 c0) 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 ); } 0040 D980 55 push ebp0040 D981 8B EC mov ebp,esp0040 D983 83 EC 44 sub esp,44 h0040 D986 53 push ebx0040 D987 56 push esi0040 D988 57 push edi0040 D989 8 D 7 D BC lea edi,[ebp-44 h]0040 D98C B9 11 00 00 00 mov ecx,11 h0040 D991 B8 CC CC CC CC mov eax,0 CCCCCCCCh0040 D996 F3 AB rep stos dword ptr [edi] 0040 D998 C7 45 FC 00 00 00 00 mov dword ptr [ebp-4 ],0 0040 D99F 83 7 D FC 0 A cmp dword ptr [ebp-4 ],0 Ah0040 D9A3 7F 1 C jg func3+41 h (0040 d9c1)0040 D9A5 8B 45 FC mov eax,dword ptr [ebp-4 ]0040 D9A8 50 push eax0040 D9A9 68 C4 2F 42 00 push offset string "%d\n" (00422f c4)0040 D9AE E8 8 D 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 (0040 d99f) 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 (004011 c0) 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 ; } 0040 D740 55 push ebp0040 D741 8B EC mov ebp,esp0040 D743 83 EC 54 sub esp,54 h0040 D746 53 push ebx0040 D747 56 push esi0040 D748 57 push edi0040 D749 8 D 7 D AC lea edi,[ebp-54 h]0040 D74C B9 15 00 00 00 mov ecx,15 h0040 D751 B8 CC CC CC CC mov eax,0 CCCCCCCCh0040 D756 F3 AB rep stos dword ptr [edi]0040 D758 C7 45 FC 01 00 00 00 mov dword ptr [ebp-4 ],1 0040 D75F C7 45 F8 02 00 00 00 mov dword ptr [ebp-8 ],2 0040 D766 C7 45 F4 03 00 00 00 mov dword ptr [ebp-0 Ch],3 0040 D76D C7 45 F0 04 00 00 00 mov dword ptr [ebp-10 h],4 0040 D774 C7 45 EC 05 00 00 00 mov dword ptr [ebp-14 h],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 void func2 () { char * x; short * y; int * z; x = (char *)1 ; y = (short *)2 ; z = (int *)3 ; x++; y++; z++; 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); }
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); 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); 总结: 1. 带*类型的变量可以加、减一个整数,但不能乘或者除.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 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******
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 }; int * x = &arr[0 ]; int * px = arr; for (int i = 0 ; i<5 ;i++){ printf ("%d " ,arr[i]); printf ("%d " ,*(px+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 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)); } }
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 y[] = "China" ; void func () { *(x+1 ) = "A" ; x = "abx" ; y[1 ] = 'A' ; } void func () { char * x = "China" ; char y[] = "China" ; *(x+1 ) = "A" ; y[1 ] = 'A' ; } int strlen (char * s) { 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) { 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; while ((*dest++) = (*src++)); 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 ; }
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)); }
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)); }
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)); px = ((*px)[5 ])10 ; 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 +8b yte+4b yte 1 +3 *(*(px+3 )+3 ) == 4 +3 *8b yte+3 *4b yte 3 *2 +3 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 ) pFun++ int Function (int a,int b) { }pFun = Function; int sb = pFun (1 ,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 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; }
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);
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 ); if (ptr == NULL ) return 0 ; memset (ptr,0 ,sizeof (int )*128 ); *(ptr) = 1 ; free (ptr); ptr = NULL ;
1 2 3 4 fopen fseek 设置文件指针 ftell 文件大小 fclose
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.节省内存,不在复制全部节
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 typedef struct _IMAGE_DOS_HEADER { WORD e_magic; 5 A4D WORD e_cblp; 0090 WORD e_cp; 0003 WORD e_crlc; 0000 WORD e_cparhdr; 0004 WORD e_minalloc; 0000 WORD e_maxalloc; FFFF WORD e_ss; 0000 WORD e_sp; 00B 8 WORD e_csum; 0000 WORD e_ip; 0000 WORD e_cs; 0000 WORD e_lfarlc; 0040 WORD e_ovno; 0000 WORD e_res[4 ]; 0000 0000 0000 0000 WORD e_oemid; 0000 WORD e_oeminfo; 0000 WORD e_res2[10 ]; 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 DWORD e_lfanew; 0000 00F 8 } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; 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; 8664 WORD NumberOfSections; 0007 DWORD TimeDateStamp; AC6AAA87 DWORD PointerToSymbolTable; 00000000 DWORD NumberOfSymbols; 00000000 WORD SizeOfOptionalHeader; 00F 0 WORD Characteristics; 0022 } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; typedef struct _IMAGE_OPTIONAL_HEADER { WORD Magic; 020B BYTE MajorLinkerVersion; 0 E BYTE MinorLinkerVersion; 14 DWORD SizeOfCode; 00023400 DWORD SizeOfInitializedData; 00079400 DWORD SizeOfUninitializedData; 00000000 DWORD AddressOfEntryPoint; 00023520 DWORD BaseOfCode; 00001000 DWORD BaseOfData; 40000000 DWORD ImageBase; 00000100 DWORD SectionAlignment; 00001000 DWORD FileAlignment; 00000002 WORD MajorOperatingSystemVersion; 000 A WORD MinorOperatingSystemVersion; 0000 WORD MajorImageVersion; 000 A WORD MinorImageVersion; 0000 WORD MajorSubsystemVersion; 000 A WORD MinorSubsystemVersion; 0000 DWORD Win32VersionValue; 00000000 DWORD SizeOfImage; 000 A2000 DWORD SizeOfHeaders; 00000400 DWORD CheckSum; 0005 D8CD 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;
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
将文件读取到内存中,此时称 FileBuffer,如果内存对齐和文件对齐一样,那么不拉伸,但还是不能直接加载
如果两者不一样,需要先在内存中进行拉伸,此事成为 image buffer
image buffer 从 imagebase 开始
imagebase 用于模块对齐和保护指针
正常情况下 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); if (*((PDWORD)((DWORD)pFileBuffer + pDosHeader->e_lfanew)) != IMAGE_NT_SIGNATURE) { printf ("不是有效的PE标志\n" ); free (pFileBuffer); return ; } 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); 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;
MISC = a - b
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--; }
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.存盘 如果不往代码区里填,需要改属性,判断拉伸后剩余空间是否够 在任意节里添加,封装函数 属性通过 | 取并集
用函数写
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)); }
# 代码节空白区添加代码
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);
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 ; } 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 ("---------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); 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--; } free (pFileBuffer); } DWORD RvaToFileOffset (IN LPVOID pFileBuffer, DWORD dwRva) { LPVOID pImageBuffer = NULL ; 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)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++) { if ((dwRva >= pSectionTemp->VirtualAddress) && (dwRva < pSectionTemp->VirtualAddress + pSectionTemp->Misc.VirtualSize)) { return dwRva - pSectionTemp->VirtualAddress + pSectionTemp->PointerToRawData; } } } return 0 ; #endif #if 1 DWORD MemOffset = (dwRva); int NumOfSec = 0 ; DWORD thisVA = 0 ; DWORD thisFile = 0 ; DWORD thisoffset = 0 ; 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; break ; } pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader + IMAGE_SIZEOF_SECTION_HEADER); pFileHeader->NumberOfSections--; NumOfSec++; } thisoffset = MemOffset - thisVA; FilePos = thisoffset + thisFile; return FilePos; #endif } DWORD CopyFileBufferToImageBuffer (IN LPVOID pFileBuffer, OUT LPVOID* 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)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 ; LPVOID pTempImageBuffer = NULL ; pTempImageBuffer = (LPVOID)malloc (pOptionHeader->SizeOfImage); if (pTempImageBuffer == NULL ) { cout << "为imagebuffer申请内存失败" ; return NULL ; } memset (pTempImageBuffer, 0 , pOptionHeader->SizeOfImage); memcpy (pTempImageBuffer, pFileBuffer, pOptionHeader->SizeOfHeaders); TotalSize += pOptionHeader->SizeOfHeaders; 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; } DWORD CopyImageBufferToNewBuffer (LPVOID pImageBuffer, LPVOID *pNewBuffer) { 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); int secNum = pFileHeader->NumberOfSections; DWORD pointerdata = 0 ; DWORD sizedata = 0 ; 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); memcpy (*pNewBuffer, pImageBuffer, pOptionHeader->SizeOfHeaders); 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); } 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 ; 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 (pSectionHeader->SizeOfRawData - pSectionHeader->Misc.VirtualSize < sizeof (shellcode)/sizeof (shellcode[0 ])) { cout << "空闲区不够!!\n" ; free (pFileBuffer); free (pImageBuffer); return ; } PBYTE addPos = (PBYTE)((DWORD)pImageBuffer + pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize); memcpy (addPos ,shellcode, sizeof (shellcode)/sizeof (shellcode[0 ])); DWORD e8 = MessageAddr - (pOptionHeader->ImageBase + pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize +8 +5 ); printf ("%X\n" ,e8); memcpy (addPos+9 ,&e8,sizeof (DWORD)); 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)); 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 ; 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 (NumOfSec > pFileHeader->NumberOfSections) { cout << "超出最大节数量\n" ; return ; } if (pSectionHeader[NumOfSec-1 ].SizeOfRawData - pSectionHeader[NumOfSec-1 ].Misc.VirtualSize < sizeof (shellcode)/sizeof (shellcode[0 ])) { cout << "空闲区不够!!\n" ; free (pFileBuffer); free (pImageBuffer); return ; } PBYTE addPos = (PBYTE)((DWORD)pImageBuffer + pSectionHeader[NumOfSec-1 ].VirtualAddress + pSectionHeader[NumOfSec-1 ].Misc.VirtualSize); memcpy (addPos ,shellcode, sizeof (shellcode)/sizeof (shellcode[0 ])); DWORD e8 = MessageAddr - (pOptionHeader->ImageBase + pSectionHeader[NumOfSec-1 ].VirtualAddress + pSectionHeader[NumOfSec-1 ].Misc.VirtualSize +8 +5 ); printf ("%X\n" ,e8); memcpy (addPos+9 ,&e8,sizeof (DWORD)); 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)); 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 ; 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 = (PIMAGE_SECTION_HEADER)((DWORD)pSectionHeader+(pFileHeader->NumberOfSections)*IMAGE_SIZEOF_SECTION_HEADER); memcpy (MyBegin, SecBegin, IMAGE_SIZEOF_SECTION_HEADER); LPVOID ZeroBegin = (PIMAGE_SECTION_HEADER)MyBegin + 1 ; memset (ZeroBegin, 0 , 40 ); pFileHeader->NumberOfSections += 1 ; pOptionHeader->SizeOfImage += ((AddNumber / pOptionHeader->SectionAlignment) + 1 )* pOptionHeader->SectionAlignment; 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 ; 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; cout << "提升并覆盖dos stub!\n" ; LPVOID dest = (LPVOID)((DWORD)pImageBuffer + 0x40 ); LPVOID source = (LPVOID)((DWORD)pImageBuffer + pDosHeader->e_lfanew); DWORD size = pDosHeader->e_lfanew - 0x40 ; cout << size << "\n" ; DWORD shabi = (4 + IMAGE_SIZEOF_FILE_HEADER + pFileHeader->SizeOfOptionalHeader + pFileHeader->NumberOfSections * IMAGE_SIZEOF_SECTION_HEADER); memcpy (dest, source, shabi); pDosHeader->e_lfanew = elfanew - size; pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pImageBuffer + pDosHeader->e_lfanew); pFileHeader = (PIMAGE_FILE_HEADER)(((DWORD)pNTHeader) + 4 ); pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pFileHeader + IMAGE_SIZEOF_FILE_HEADER); pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pFileHeader->SizeOfOptionalHeader); LPVOID SecBegin = (LPVOID)pSectionHeader; LPVOID MyBegin = (LPVOID)((DWORD)SecBegin + pFileHeader->NumberOfSections * 40 ); memcpy (MyBegin, SecBegin, 40 ); memset ((PBYTE)MyBegin + 40 , 0 , 40 ); pFileHeader->NumberOfSections += 1 ; #if 1 pOptionHeader->SizeOfImage += ((AddNumber / sectionalignment) + 1 )* sectionalignment; 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 size1 = CopyImageBufferToNewBuffer (pImageBuffer, &pNewBuffer); bool isSucc = MemeryToFile (pNewBuffer, size1, lpszOutFile); if (isSucc == true ) cout << "Success\n" ; else cout << "woshishabi\n" ; #endif } void TestExpandLastSec (IN LPSTR lpszFile, IN DWORD AddNumber, OUT LPSTR lpszOutFile) { LPVOID pFileBuffer = NULL ; LPVOID pImageBuffer = NULL ; LPVOID pNewBuffer = NULL ; 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); LPVOID newspace = (LPVOID)malloc (pOptionHeader->SizeOfImage + AddNumber); DWORD newvirtualsize = (pSectionHeader[pFileHeader->NumberOfSections-1 ].Misc.VirtualSize / pOptionHeader->SectionAlignment +1 ) * pOptionHeader->SectionAlignment; 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" ; pOptionHeader->SizeOfImage = \ (((pSectionHeader[pFileHeader->NumberOfSections - 1 ].Misc.VirtualSize + pSectionHeader[pFileHeader->NumberOfSections - 1 ].VirtualAddress) / pOptionHeader->SectionAlignment) + 1 ) * pOptionHeader->SectionAlignment; 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); } DWORD Align (int x, int y) { return ((x/y)+1 )*y; } void TestMergeSec (IN LPSTR lpszFile, OUT LPSTR lpszOutFile) { LPVOID pFileBuffer = NULL ; LPVOID pImageBuffer = NULL ; LPVOID pNewBuffer = NULL ; 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); } pFileHeader->NumberOfSections = 1 ; #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 ; 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); 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);
手动连接
#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” 导出:
如果加 extern “C” :
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
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、如何根据函数的名字获取一个函数的地址?
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 ; 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); DWORD FOA = RvaToFileOffset (pFileBuffer, pOptionHeader->DataDirectory[0 ].VirtualAddress); 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" ; 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" ; 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" ; 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); 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 ); 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); 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 ; }
# 重定位表
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;
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
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; 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)); printf ("Relocation Table Rva: %#x\n" , pRelocationTable->VirtualAddress); int i = 0 ; 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 ); for (int k=0 ;k<(pRelocationTable->SizeOfBlock - 8 )/2 ;k++) { DWORD pRepair_RvaOffset = (recAddr[k] & 0x0FFF ) + RvaToFileOffset(pFileBuffer,pRelocationTable->VirtualAddress); WORD type = recAddr[k] >> 12 ; if (type!=0 ) { printf ("RVA:%X,type:%X\r\n" ,pRepair_RvaOffset,type); } } 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
# 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
重定位表只能保证自己程序中的地址是正确的
IAT 是保证调用 dll 的时候 call 的地址是正确的
E8 call 是偏移
FF 15 call 是绝对地址
只要用 dll 函数,全都是通过 [x] 调用,这样形成的表叫做 IAT 表
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 文件加载前
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,且内容完全一样
加载后
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
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
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 ; 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); 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 0 C sub esp,0 Ch0040106B 8B C4 mov eax,esp0040106 D 8B 4 D F4 mov ecx,dword ptr [ebp-0 Ch]00401070 89 08 mov dword ptr [eax],ecx00401072 8B 55 F8 mov edx,dword ptr [ebp-8 ]00401075 89 50 04 mov dword ptr [eax+4 ],edx00401078 8B 4 D FC mov ecx,dword ptr [ebp-4 ]0040107B 89 48 08 mov dword ptr [eax+8 ],ecx0040107 E E8 82 FF FF FF call @ILT+0 (fun) (00401005 )00401083 83 C4 0 C add esp,0 Ch结构体作为参数时传的是结构体的副本,应该使用结构体指针 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); } }; void init (int x, int y) { this ->x = x; this ->y = y; } return *(int *)this ;
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 ; } 0040 D768 C7 45 F8 05 00 00 00 mov dword ptr [ebp-8 ],5 0040 D76F C7 45 FC 02 00 00 00 mov dword ptr [ebp-4 ],2 0040 D776 8 D 4 D F8 lea ecx,[ebp-8 ]0040 D779 E8 B4 38 FF FF call @ILT+45 (Cal::Add) (00401032 )0040 D77E 89 45 F4 mov dword ptr [ebp-0 Ch],eax0040 D781 8B 45 F4 mov eax,dword ptr [ebp-0 Ch]0040 D784 50 push eax0040 D785 68 1 C 20 42 00 push offset string "%d %d %d %d\n" (0042201 c)0040 D78A E8 31 39 FF FF call printf (004010 c0) 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 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)); 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)); 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 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 ); return 0 ; } 0040 D768 6 A 05 push 5 0040 D76A 6 A 04 push 4 0040 D76C 6 A 03 push 3 0040 D76E 8 D 4 D F4 lea ecx,[ebp-0 Ch]0040 D771 E8 C1 38 FF FF call @ILT+50 (Cal::init) (00401037 )0040 D7C9 5B pop ebx0040 D7CA 8B E5 mov esp,ebp0040 D7CC 5 D pop ebp0040 D7CD C2 0 C 00 ret 0 Ch
# 构造函数
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
多重继承增加了程序的复杂度,容易出错
微软建议使用单继承,如果需要多重继承可以改为多层继承
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 8 D 4 D FC lea ecx,[ebp-4 ]0040139B E8 F1 FC FF FF call @ILT+140 (Test::Test) (00401091 )24 : Test* pt = &t;004013 A0 8 D 45 FC lea eax,[ebp-4 ]004013 A3 89 45 F8 mov dword ptr [ebp-8 ],eax25 : pt->func1 ();004013 A6 8B 4 D F8 mov ecx,dword ptr [ebp-8 ]004013 A9 E8 61 FC FF FF call @ILT+10 (Test::func1) (0040100f )26 : pt->func2 ();004013 AE 8B 4 D F8 mov ecx,dword ptr [ebp-8 ]004013B 1 8B 11 mov edx,dword ptr [ecx]004013B 3 8B F4 mov esi,esp004013B 5 8B 4 D F8 mov ecx,dword ptr [ebp-8 ]004013B 8 FF 12 call dword ptr [edx] sizeof (Test) = 12 ;虚函数 + 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 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*)⊂ 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*)⊂ 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; }
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*)⊂ 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; 有几个直接分类就有几个虚函数表
多重继承有覆盖
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,通过虚表实现
动态绑定就是多态,相同函数体现出了不同的行为
多态实现了父类的指针访问子类的方法
虚析构在继承的时候有用,把父类的析构定义成虚函数,这样子类在析构时不会调用父类的析构
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 ; }
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 (); 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 >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; };
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++) { 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" ; Test2 (&a); std::cout<< a << "\n" ; } 174 : Test (a);0040181F 8 D 45 FC lea eax,[ebp-4 ]00401822 50 push eax00401823 E8 04 F9 FF FF call @ILT+295 (Test) (0040112 c)00401828 83 C4 04 add esp,4 176 : Test2 (&a);00401847 8 D 55 FC lea edx,[ebp-4 ]0040184 A 52 push edx0040184B 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
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、读懂每一个方法的反汇编实现
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 () ; DWORD erase (DWORD dwIndex) ; DWORD size () ; 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 ) { 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) { 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; } 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 ) ; haha.push_back (5 ); haha.push_back (4 ); haha.push_back (3 ); haha.push_back (2 ); haha.push_back (1 ); 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 () ; 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; 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; } 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++) { 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; 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)); 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; 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; m_dwLength++; return SUCCESS; } template <class T >DWORD LinkList<T>::Delete (IN DWORD dwIndex) { if (m_pList == NULL || m_dwLength == 0 ) { return ERROR; } if (dwIndex <0 || dwIndex > m_dwLength) { cout << "index out of range\n" ; return INDEX_ERROR; } PNODE ptempNode = m_pList; if (dwIndex == 0 && m_dwLength == 1 ) { delete m_pList; m_pList = NULL ; m_dwLength--; return SUCCESS; } if (dwIndex == 0 ) { delete m_pList; m_pList = ptempNode->pNext; m_dwLength--; return SUCCESS; } ptempNode = m_pList; for (size_t i = 0 ; i < dwIndex-1 ; i++) { 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 >* shabi = new LinkList<int >; shabi->Insert (0 ,9 ); shabi->Insert (1 ,8 ); shabi->Insert (7 ); shabi->Insert (6 ); cout << "\n" ; shabi->Delete (2 ); cout << "\n" ; shabi->clear (); cout << "\n" ; printf ("Hello World!\n" ); return 0 ; }
滴水逆向 ——C++_链表 - 灰信网(软件开发博客聚合) (freesion.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 #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. 若左子树不空,则左子树上所有结点的值均小于它的根结点的值
即 左小右大
搜索二叉树中序遍历后就是排序后的结果
删除叶子节点,直接删
删除节点只有一个子树,直接把下面节点提上去
既有左也有右,用右子树最小节点取代原节点,递归删除最小节点 、 或者左边最大
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 ; } }; 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) ; TreeNode<T>* GetMaxNode (TreeNode<T>* 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; if (data>=pNode->element) { pMin = (TreeNode<T>*)pNode->element; } GetMinNode (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); pNode->element = (int )pMin; DeleteNode (pNode->element, pCurrent); } } else if (element > pNode->element) { 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; } if (pNode->pLeft == NULL && pNode->element>element ) { pNode->pLeft = pElement; size++; return SUCCESS; } if (pNode->pRight == NULL &&pNode->element<element ) { pNode->pRight = pElement; size++; return SUCCESS; } 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 >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; } 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);
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
创建 win32application
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" int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { return 0 ; } #include <stdio.h> .h void __cdecl OutputDebugStringF (const char * format, ...) ;#ifdef _DEBUG #define DbgPrintf OutputDebugStringF #else #define DbgPrintf #endif .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;
系统消息队列与应用程序消息队列
根据窗口句柄将消息分发到不同应用程序队列
判断应用程序队列中消息 类型是不是我关心的是就处理不是让 windows 去处理
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;
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 #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) { 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 , className, TEXT("Learn to Program Windows" ), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL , NULL , hInstance, NULL ); 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); 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,系统执行到这里就会产生中断,
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
# 入口程序分析
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
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); 00401191 |. 50 push eax ; /pWndClass00401192 |. C74424 4 C 0E0 >mov dword ptr ss:[esp+0x4C ],0xE ; |0040119 A |. C74424 34 501 >mov dword ptr ss:[esp+0x34 ],00401250 ; |004011 A2 |. 895424 54 mov dword ptr ss:[esp+0x54 ],edx ; |004011 A6 |. 897424 40 mov dword ptr ss:[esp+0x40 ],esi ; |004011 AA |. FF15 C8604000 call dword ptr ds:[<&USER32.RegisterClassA>] ; \RegisterClassA
# 子窗口
按钮本质也是子窗口
[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 #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, 10 , 10 , 80 , 20 , hwnd, (HMENU)1001 , hAppInstance, NULL ); TCHAR szBuffer[0x20 ]; GetClassName(hwndPushButton, szBuffer, 0x20 ); WNDCLASS wc; GetClassInfo(hAppInstance, szBuffer, &wc); OutputDebugStringF("-->%s\n" , wc.lpszClassName); OutputDebugStringF("-->%x\n" , wc.lpfnWndProc); hwndCheckBox = CreateWindow( TEXT("button" ), TEXT("复选框" ), WS_CHILD | WS_VISIBLE | BS_CHECKBOX | BS_AUTOCHECKBOX, 10 , 40 , 80 , 20 , hwnd, (HMENU)1002 , hAppInstance, NULL ); hwndRadio = CreateWindow( TEXT("button" ), TEXT("单选按钮" ), WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON, 10 , 70 , 80 , 20 , hwnd, (HMENU)1003 , hAppInstance, NULL ); } int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { 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 , className, TEXT("Learn to Program Windows" ), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL , NULL , hInstance, NULL ); 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) { CREATESTRUCT* createst = (CREATESTRUCT*)lParam; DWORD xPos; DWORD yPos; switch (uMsg) { case WM_CREATE: return 0 ; case WM_MOVE: xPos = (int )(short )LOWORD(lParam); yPos = (int )(short )HIWORD(lParam); return 0 ; case WM_SIZE: xPos = (int )(short )LOWORD(lParam); yPos = (int )(short )HIWORD(lParam); 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
# 资源文件
使用 dialog 之后只需要写创建窗口和消息处理函数 DialogBox ()
对话框处理返回 true,没有处理或者不关心返回 false
1 2 3 4 5 6 7 8 9 10 11 12 13 0040112F 90 nop00401130 /$ 8B 4424 04 mov eax,dword ptr ss:[esp+0x4 ]00401134 |. 6 A 00 push 0x0 ; /lParam = NULL 00401136 |. 68 00104000 push 00401000 ; |DlgProc = win32_di.00401000 0040113B |. 6 A 00 push 0x0 ; |hOwner = NULL 0040113 D |. 6 A 66 push 0x66 ; |pTemplate = 0x66 0040113F |. 50 push eax ; |hInst00401140 |. A3 F0844000 mov dword ptr ds:[0x4084F0 ],eax ; |00401145 |. FF15 9 C504000 call dword ptr ds:[<&USER32.DialogBoxParamA>] ; \DialogBoxParamADialogBox (hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL ,DialogProc) ;
系统的消息处理函数一定会调用我们写的消息处理函数
消息断点
LBUTTONUP
本质上就是条件断点 [esp+8] == LBUTTONUP
在代码段下内存访问断点
继续执行程序
判断是否是对应动作触发的断点
1 2 3 0018F780 76E662FA 返回到 user32.76E662FA 0018F784 0017031C 0018F788 00000135 # 111 WM_COMMAND 才是对应的消息
一直往下跟,又回到 7xx
继续执行 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, UINT uMsg, WPARAM wParam, LPARAM lParam ) { HWND hEditUser = NULL ; HWND hEditPass = NULL ; TCHAR szUserBuff[0x50 ]; TCHAR szUserPass[0x50 ]; switch (uMsg) { case WM_INITDIALOG: 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; } int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL ,DialogProc); return 0 ; }
# 提取图标
设置 icon
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, UINT uMsg, WPARAM wParam, LPARAM lParam ) { 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; } int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { hAPPInstance = hInstance; DialogBox(hInstance,MAKEINTRESOURCE(IDD_DIALOG_MAIN),NULL ,DialogProc); return 0 ; }
LoadPE 解析资源
resource hack 展示资源
可以产生 dmp 文件,直接改 dmp 的后缀不能直接显示
需要添加对应的头信息,拼成对应的格式
PE - 资源表_pe 文件自定义资源_megaparsec 的博客 - CSDN 博客
绿的是_IMAGE_RESOURCE_DIRECTORY 资源目录
1 2 3 NumberOfNamedEntries; NumberOfIdEntries; 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; DWORD TimeDateStamp; WORD MajorVersion; WORD MinorVersion; WORD NumberOfNamedEntries; WORD NumberOfIdEntries; } IMAGE_RESOURCE_DIRECTORY, *PIMAGE_RESOURCE_DIRECTORY; typedef struct _IMAGE_RESOURCE_DIRECTORY_ENTRY { union { struct { DWORD NameOffset:31 ; DWORD NameIsString:1 ; }; DWORD Name; WORD Id; }; union { DWORD OffsetToData; struct { DWORD OffsetToDirectory:31 ; DWORD DataIsDirectory:1 ; }; }; } IMAGE_RESOURCE_DIRECTORY_ENTRY, *PIMAGE_RESOURCE_DIRECTORY_ENTRY; 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; printf ( "\nProcess ID: %u\n" , processID ); hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID ); if (NULL == hProcess) return 1 ; if ( EnumProcessModules(hProcess, hMods, sizeof (hMods), &cbNeeded)) { for ( i = 0 ; i < (cbNeeded / sizeof (HMODULE)); i++ ) { TCHAR szModName[MAX_PATH]; if ( GetModuleFileNameEx( hProcess, hMods[i], szModName, sizeof (szModName) / sizeof (TCHAR))) { _tprintf( TEXT("\t%s (0x%08X)\n" ), szModName, hMods[i] ); } } } CloseHandle( hProcess ); return 0 ; } DWORD cProcesses; DWORD* GetProcess () { DWORD aProcesses[1024 ]; DWORD cbNeeded; if ( !EnumProcesses( aProcesses, sizeof (aProcesses), &cbNeeded ) ) return NULL ; cProcesses = cbNeeded / sizeof (DWORD); 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 ; 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 ; 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) { 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 ; } 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)); 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 ; SendMessage(hListProcess,LVM_INSERTCOLUMN,0 ,(DWORD)&lv); lv.pszText = TEXT("PID" ); lv.cx = 60 ; lv.iSubItem = 1 ; 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 ; SendMessage(hListProcess1,LVM_INSERTCOLUMN,0 ,(DWORD)&lv1); lv1.pszText = TEXT("模块位置" ); lv1.cx = 210 ; lv1.iSubItem = 1 ; 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) { 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; UINT code; } 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
# 创建线程
一个进程中至少有一个线程。
多线程会产生多个堆栈,每个线程都有自己的栈的空间
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, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, OUT LPDWORD lpThreadId ) ;
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++) { 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 ; }
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++) { 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++) { 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) { 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 { DWORD ContextFlags; DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; FLOATING_SAVE_AREA FloatSave; DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; DWORD Ebp; DWORD Eip; DWORD SegCs; DWORD EFlags; DWORD Esp; DWORD SegSs; 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) { 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); 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, DWORD dwMilliseconds ) ; 功能说明: 等待函数可使线程自愿进入等待状态,直到一个特定的内核对象变为已通知状态为止. 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); 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 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, CONST HANDLE *lpHandles, BOOL bWaitAll, DWORD dwMilliseconds ) ; 功能说明: 同时查看若干个内核对象的已通知状态 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效率没有临界区高.
# 内核对象
进程、线程、文件、文件映射、事件、互斥体等等都是内核对象
内核对象会保留一个计数器,只有当计数器为 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, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName ) ; 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) { g_hEvent = CreateEvent(NULL , TRUE, FALSE, NULL ); HANDLE hThread[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, 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++ 获取窗口句柄和类,但是这个会变。
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 ) { 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
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; } 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 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、跳转到入口执行
# 内存写入
进程 A 只知道有进程写了自己的进程。但不知道是谁写的,写了什么。
相比注入,这个在进程的模块中是找不到的。
exe 扔到 exe 里~
虽然已经修复了重定位表,IAT 会修复一部分。比如 [412345] 会被重定位修复后变为 [512345]
但是 [ ] 得出的值会变,即 api 地址发生变化。而且两个进程加载的 dll 不一定一样。
还需要将 [512345] 算出的值变为进程 A 中的 dll 的地址
# hook
hook 修改
1. 监控:可以获取原来的函数参数和返回值
2. 改行为
# IAT hook
更改 [x] == y 中 y 改成自己的函数的地址
注意函数改之后不能破环堆栈平衡
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 都需要卸载
# 进程通信
# 自定义消息
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 { 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; hMapObject = CreateFileMapping ((HANDLE)0xFFFFFFFF ,NULL ,PAGE_READWRITE,0 ,0x1000 ,TEXT ("shared" )); if (!hMapObject) { MessageBox (NULL ,TEXT ("共享内存失败" ),TEXT ("Error" ),MB_OK); return FALSE; } 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; hMapObject = CreateFileMapping ((HANDLE)0xFFFFFFFF ,NULL ,PAGE_READWRITE,0 ,0x1000 ,TEXT ("shared" )); if (!hMapObject) { MessageBox (NULL ,TEXT ("共享内存失败" ),TEXT ("Error" ),MB_OK); return FALSE; } 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 共享节实现进程通信
# 硬编码
指令编码结构
每一条指令,最短 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 取决于平台00 A291BC 880400 mov byte ptr ds:[eax+eax],al
Mod (第 6、7 位) 和 R/M (第 0、1、2 位) 共同描述指令中的 E 部分,即寄存器 / 内存
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,就是上图中带 [–][–] 的
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
Table A-6
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
# 指令前缀
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 3 4 A 字符串显示数据 D 数据显示 dd word ddd dword C 指令显示 U undefined
1 2 3 4 5 6 新增结构体 edit 添加结构体类型 d 结构体中新增成员 d dd ddd n改名 ALT+Q 添加结构体变量,改变结构体 右键 添加数组 t 选择结构体
1 2 3 4 5 2、配置双机调试流程 1)在本机安装Windbg(WDK 7600) 2)在虚拟机中(XP)修改boot.ini设置虚拟机 4)修改Windbg运行参数指向虚拟机