# C 与汇编的关系

# 应用层技术栈:

  1. 加密与解密(四)看雪论坛
  2. X86/X64/arm 汇编语言
  3. windows 核心编程
  4. c 语言从入门到精通 (第三版) (清华)

# 学习逆向环境配置

工具类:

  • 吾爱破解工具类
  • Hxd (十六进制编辑工具)

开发类:

  1. VS2008 IDE
  2. VC6.0 SP6 IDE

# 进制转换

  1. 十进制的定义:由十个符号组成,分别是 0 1 2 3 4 5 6 7 8 9 逢十进一
  2. 九进制的定义:由九个符号组成,分别是 0 1 2 3 4 5 6 7 8 逢九进一
  3. 十六进制的定义:由十六个符号组成,分别是 0 1 2 3 4 5 6 7 8 9 A B C D E F
  4. N 进制的定义:由 N 个符号组成 逢 N 进一

# 数据类型与逻辑运算

在计算机中,由于硬件的制约,数据是有长度限制的,超过数据宽度的数据会被丢弃

同一个数据,表示无符号数和有符号数则其含义不同

  1. 无符号数:正数
  2. 有符号数:正数、负数

# 常见的数据类型(重要)

  • BYTE 字节 8BIT
  • WORD 字 16BIT 2 字节
  • DWORD 双字 32BIT 4 字节

# 常见的运算符类型(重要)

或运算(or |)

只要有一个为 1 则结果为 1

与运算(and &)

两个都是 1 结果才为 1

异或运算(xor ^)

相同为 0 不同为 1

非运算 (not !)

取反 1 是 0 0 是 1

# 32 位寄存器

EAX

EBX

ECX

EDX

只有通用寄存器能拆成高位和低位

1
2
mov byte [local.3+2],ah     //由于只是高位地址,因此是3+2
mov byte [local.3+3],ah //同时注意是byte,所以是2

command 中

?al 可以显示 al 中的值,即查看寄存器中的值

dd 内存 display dword

dw 内存

db 内存

# CPU 计算 2+3

1. 先异或,得到 R

2. 在与运算,将结果左移一位

3. 如果 (2) 的结果为全 0,那么 R 就是最终答案,如果不是,将 R 赋值给 x,将 (2) 的结果赋值给 y

4. 重复操作直至 (2) 的结果为全 0,那么此时 R 就是我们的答案

# CPU 计算 2-3

1. 异或,得到 R

2. 在与运算,将结果再左移一位

3. 如果结果为全 0,那么 R 就是结果,否则重复

在 debug 的模式下是用常量来分配的 print hello world

# 堆栈的原理:

  1. 临时存放一些寄存器已无法存放的数据,比如超过内存对齐的数据类型
  2. 存放临时数据,野指针,指针初始化时的数据

Windows 分配栈时 是从高地址往低地址分配:

  1. MOV EBX,0x13FFDC BASE
  2. MOV EDX,0x13FFDC TOP

EBP 和 ESP:

ESP:该指针永远指向系统栈最上面一个栈帧的栈顶。

EBP:该指针永远指向系统栈最上面一个栈帧的底部。

栈底和栈顶原理:

  1. 控制栈顶和栈底分别为两个固定的寄存器 (EBP 基址指针寄存器 和 ESP 堆栈指针寄存器)

ebp = esp

esp -= 0x40

img

ESP 和 EBP 不够的时候会相互借用,两个都用作传参

初始状态下,ebp 指向高地址,ESP 指向低地址

image-20221105095016154

push eax

先把 esp-4,再把 eax 中的值压栈

image-20221105095049237

pop eax

将 esp 指向的值给 eax,esp+4

image-20221105095144317

# 一个 helloworld 的 main 函数

一般来说,VC 6.0 的 main 函数在 call 00401005

image-20221105093859109

1
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
00401005  /$ /E9 06000000   jmp     main
0040100A | |CC int3
0040100B | |CC int3
0040100C | |CC int3
0040100D | |CC int3
0040100E | |CC int3
0040100F | |CC int3
00401010 >|> \55 push ebp
00401011 |. 8BEC mov ebp,esp
00401013 |. 83EC 40 sub esp,0x40
00401016 |. 53 push ebx
00401017 |. 56 push esi
00401018 |. 57 push edi
00401019 |. 8D7D C0 lea edi,[local.16]
0040101C |. B9 10000000 mov ecx,0x10
00401021 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401026 |. F3:AB rep stos dword ptr es:[edi]
00401028 |. 68 1C204200 push 0042201C ; /format = "Hello World!
"
0040102D |. E8 2E000000 call printf ; \printf
00401032 |. 83C4 04 add esp,0x4
00401035 |. 33C0 xor eax,eax
00401037 |. 5F pop edi
00401038 |. 5E pop esi
00401039 |. 5B pop ebx
0040103A |. 83C4 40 add esp,0x40
0040103D |. 3BEC cmp ebp,esp
0040103F |. E8 9C000000 call _chkesp
00401044 |. 8BE5 mov esp,ebp
00401046 |. 5D pop ebp
00401047 \. C3 retn


假设当前我们有这样一个程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// add_main.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

void func(int m, int n) {
int a, b;
a = m;
b = n;
}

int main(int argc, char* argv[])
{
int m=3,n=4;
func(m,n);
printf("Hello World!\n");
return 0;
}

在调用 func 之前,main 的栈帧:

image-20221105100143457

从低地址 esp 到高地址 ebp 的这块区域,就是当前 main 函数的栈帧。当 main 中调用 func 时,写成汇编大致是:

1
2
3
push m
push n; 两个参数压入栈
call func; 调用func,将返回地址填入栈,并跳转到func
1
2
3
4
5
6
7
8
9
10
11
12
00401078  |.  C745 FC 03000>mov     dword ptr ss:[ebp-0x4],0x3
0040107F |. C745 F8 04000>mov dword ptr ss:[ebp-0x8],0x4
00401086 |. 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
00401089 |. 50 push eax
0040108A |. 8B4D FC mov ecx,dword ptr ss:[ebp-0x4]
0040108D |. 51 push ecx
0040108E |. E8 72FFFFFF call 00401005
00401093 |. 83C4 08 add esp,0x8
00401096 |. 68 1C204200 push 0042201C ; /format = "Hello World!
"
0040109B |. E8 30000000 call printf ; \printf

image-20221105100732747

当跳转到了 func,来看看 func 的汇编大致的样子:

1
2
3
4
5
6
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

push ebp 需要保存 main 的栈底,因为现在到了一个新的函数,但栈顶不需要保存,因为上一个栈帧的顶部讲会是 func 的栈帧底部。(两栈帧相邻的)

image-20221105101018994

到这里,新的栈帧开始了

1
2
3
4
mov     eax,dword ptr ss:[ebp+0x8]
mov dword ptr ss:[ebp-0x4],eax
mov ecx,dword ptr ss:[ebp+0xC]
mov dword ptr ss:[ebp-0x8],ecx

image-20221105101240579

retn ; 返回,然后 8 是什么意思呢,就是参数占用的字节数,当返回后,esp-n,释放参数 m,n 的空间

由此可见,通过 ebp,能够很容易定位到上面的参数。当从 func 函数返回时,首先 esp 移动到栈帧底部(即释放局部变量),然后把上一个函数的栈帧底部指针弹出到 ebp, 再弹出返回地址到 cs:ip 上,esp 继续移动划过参数,这样,ebp,esp 就回到了调用函数前的状态,即现在恢复了原来的 main 的栈帧

大佬的文章:栈帧 ebp,esp 详解_TuxedoLinux 的博客 - CSDN 博客_ebp esp

# 提升堆栈

对应语句为

1
2
3
00401060 >/> \55            push    ebp
00401061 |. 8BEC mov ebp,esp
00401063 |. 83EC 48 sub esp,0x40

将堆栈提升了 0x40

将 esp 的值减去 0x40=64,我们这里的相差的数据宽度为 4 即 16,64/4=16,因此堆栈图里多了 16 格(蓝色部分),这种操作常被叫做提升堆栈,此时堆栈图为:

image-20210228212917496

此时 ESP 指向新的栈顶,蓝色区域为我们 CALL 0040100A 之后新的栈,下面的区域为原来的栈

此时蓝色中的数据为脏数据,因为栈中放的是临时数据,可能是之前没有清理的数据

# 保护现场

对应语句为

1
2
3
00401066  |.  53            push    ebx
00401067 |. 56 push esi
00401068 |. 57 push edi

将 ebx、esi、edi 三个通用寄存器保存到堆栈中,前面的 push ebp 其实也属于保护现场

# 初始化提升的堆栈

1
2
3
4
00401069  |.  8D7D B8       lea     edi,dword ptr ss:[ebp-0x48]  ;就是将刚开始的esp给的edi
0040106C |. B9 12000000 mov ecx,0x12 ;ecx是为了之后的rep做准备的,ecx是rep循环次数
00401071 |. B8 CCCCCCCC mov eax,0xCCCCCCCC ;使用CC防止缓冲溢出
00401076 |. F3:AB rep stos dword ptr es:[edi] ;将eax的值赋值给edi所指向的内存地址里的值,并且每执行一次edi都会增加4(D标志位为0所以是增加

这里将我们提升的堆栈中的内容全部初始化为 CCCCCCCC

为什么是初始化为 CC?防止缓冲溢出

CC 的硬编码对应的指令为 int 3,即断点

这么做有什么好处呢?当程序执行超过缓冲区时,遇到 int 3 就会自动停下来

# 执行实际的内容

1
2
00401058 |. 8B45 08    mov eax,dword ptr ss:[ebp+0x8]
0040105B |. 0345 0C add eax,dword ptr ss:[ebp+0xC]

就是将前面压入的参数 2 和 1 进行相加得到 3

# 恢复现场

1
2
3
4
5
00401145  |.  5F            pop     edi
00401146 |. 5E pop esi
00401147 |. 5B pop ebx
00401148 |. 8BE5 mov esp,ebp
0040114A |. 5D pop ebp

与前面保护现场相对应

image-20210228224826624

# 返回

1
0040114B  \.  C3            retn

# CALL 返回后

1
0040112B  |.  83C4 0C       add     esp,0xC

作用为平衡堆栈

注意 CALL 之前和 CALL 之后,其前后环境要一致,这就是所谓的堆栈平衡

# 堆栈平衡

如果通过堆栈传递参数了。那么在程序执行完毕后,要平衡因参数导致的堆栈变化

当我们使用堆栈传参的时候,在程序执行完毕后这些参数应该一起被清理掉,也就是加了几条参数,就要在堆栈中加几个地址,这么做可以理解为清理垃圾。

处理方法:

第一种 :在函数外部添加 ADD 处理

第二种:在函数内部添加 ret8

# 逆推 C 语言代码

根据我们前面的分析,我们不难发现这其实就是个简单的加法函数

1
2
3
4
5
6
void func(int m, int n) {
int a, b;
a = m;
b = n;
}

逆向基础笔记七 堆栈图(重点) - 『软件调试区』 - 吾爱破解 - LCG - LSG | 安卓破解 | 病毒分析 | www.52pojie.cn

我们最后在总结一下:

1
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
00401060 >/> \55            push    ebp
;现在到了一个新的函数,需要有自己的栈底,因此需要把上一个栈底保存起来,栈顶不用保存,因为栈顶是新函数的栈底
00401061 |. 8BEC mov ebp,esp
00401063 |. 83EC 48 sub esp,0x48
;堆栈提升
00401066 |. 53 push ebx
00401067 |. 56 push esi
00401068 |. 57 push edi
;保护现场,为了retn后回恢复
00401069 |. 8D7D B8 lea edi,[local.18]
0040106C |. B9 12000000 mov ecx,0x12
00401071 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401076 |. F3:AB rep stos dword ptr es:[edi]
;初始化堆栈,防止脏数据
00401078 |. C745 FC 03000>mov [local.1],0x3
0040107F |. C745 F8 04000>mov [local.2],0x4
00401086 |. 8B45 F8 mov eax,[local.2]
00401089 |. 50 push eax
0040108A |. 8B4D FC mov ecx,[local.1]
0040108D |. 51 push ecx
;形参压入栈中
0040108E |. E8 72FFFFFF call 00401005
;调用add函数
00401093 |. 83C4 08 add esp,0x8
;堆栈平衡,将esp带回call以前的位置,call之前push了两次,2*4=8
00401096 |. 68 1C204200 push 0042201C ; /format = "Hello World!
0040109B |. E8 30000000 call printf ; \printf
;嗲用printf函数,操作和上面调用add一样
004010A0 |. 83C4 04 add esp,0x4
;堆栈平衡
004010A3 |. 33C0 xor eax,eax
004010A5 |. 5F pop edi
004010A6 |. 5E pop esi
004010A7 |. 5B pop ebx
;恢复现场
004010A8 |. 83C4 48 add esp,0x48
004010AB |. 3BEC cmp ebp,esp
004010AD |. E8 9E000000 call _chkesp
004010B2 |. 8BE5 mov esp,ebp
004010B4 |. 5D pop ebp
004010B5 \. C3 retn


分配堆栈向下减少。堆栈传递参数向上相加

# 堆栈结构图(重点)

  1. 调用 CALL 又可以分为六个部分:
  2. 提升堆栈
  3. 保护现场
  4. 初始化提升的堆栈
  5. 执行实际内容
  6. 恢复现场
  7. 返回

# 如何右键启动 od

image-20221108181646294

通过以上方式右键启动 od

# 标志寄存器

image-20221108181822816

image-20221108193731856
  1. 进位标志 CF (Carry Flag)
  2. 奇偶标志 PF (Parity Flag)
  3. 辅助进位标志 AF (Auxiliary Carry Flag)
  4. 零标志 ZF (Zero Flag)
  5. 符号标志 SF (Sign Flag)
  6. 溢出标志 OF (Overflow Flag)
  7. 方向标志 DF (Direction Flag)

# 进位标志 CF (Carry Flag)

如果运算结果的最高位产生了一个进位或借位,那么,其值为 1,否则其值为 0

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

int main(int argc, char* argv[])
{
__asm{
pushad //通用寄存器入栈
pushfd //标志寄存器入栈
mov al,0xff
add al,1 //C,P,A,Z = 1
popad
popfd
}
return 0;
}

# 奇偶标志 PF (Parity Flag)

奇偶标志 PF 用于反映运算结果中最低有效字节中 “1” 的个数的奇偶性,如果 “1” 的个数为偶数,则 PF 的值为 1,否则其值为 0。

1
2
3
4
MOV AX,803
ADD AX,1
0x804: 0000 1000 0000 0100 总共2个1 ,PF应为1,但实际运行结果PF为0
因为PF是根据最低有效字节来看,即804后面04的这部分

# 辅助进位标志 AF (Auxiliary Carry Flag)

1
2
3
4
在发生下列情况时,辅助进位标志AF的值被置为1,否则其值为0:

在字操作时,发生低字节向高字节进位或借位时
在字节操作时,发生低4位向高4位进位或借位时

AF 与数据宽度相关

32 位时 FFFF F FFF

16 位时 FF F F

8 位时 F F

加黑的字体为 AF 标志位判断的位置,如果该位置要向前进位则 AF 为 1,否则为 0,和 CF 相似,不过判断的位置不同

# 零标志 ZF (Zero Flag)

1
2
3
4
5
6
零标志ZF用来反映运算结果是否为0
如果运算结果为0,则其值为1,否则其值为0
作用:在判断运算结果是否为0时,可使用此标志位
XOR EAX,EAX //通过xor将eax清零,会改变zf标志位为1
MOV EAX,0 //通过MOV将EAX赋值为0,非运算,不改变zf标志位

# 符号标志 SF (Sign Flag)

符号标志 SF 用来反映运算结果的符号位,它与运算结果的最高位相同

# 溢出标志 OF (Overflow Flag)

溢出标志 OF 用于反映有符号数加减运算所得结果是否溢出

进位标志表示无符号数运算结果是否超出范围.

溢出标志表示有符号数运算结果是否超出范围.

  • 正 + 正 = 正 如果结果是负数,则说明有溢出
  • 负 + 负 = 负 如果结果是正数,则说明有溢出
  • 正 + 负 永远都不会有溢出

CPU 如何计算 OF

  • 符号位有进位
  • 最高有效数值位向符号位产生的进位

对于一个有符号数:如 0x80 和 0xC0

符号位有进位

0x80:1 000 0000

0xC0:1 100 0000

最高有效数值位向符号位产生的进位

0x80:1 0 00 0000

0xC0:1 1 00 0000

就是运算 0x80+0xc0

0x80:1 0 00 0000

0xC0:1 1 00 0000

符号位 1+1 有产生进位,于是符号位有进位为 1

最高有效数值位向符号位产生的进位 0+1 没有产生进位,于是最高有效数值位向符号位产生的进位为 0

OF = 符号位有进位 xor 最高有效数值位向符号位产生的进位

OF = 1 xor 0 = 1 所以此时 OF=1

# 方向标志 DF (Direction Flag)

DF=1 时串操作为减地址方式 DF=0 为增地址方式

# 相关汇编指令

ADC 指令:带进位加法

SBB 指令:带借位减法

XCHG 指令:交换数据

格式:XCHG R/M,R/M 两边不能同时为内存 数据宽度要一样

1
2
3
4
5
MOV AL,1
MOV CL,2
XCHG AL,CL
执行前:AL=1 CL=2
执行后:AL=2 CL=1

MOVS 指令:移动数据 内存 - 内存

MOVS 指令常用于复制字符串

可以将不同宽度的指令简写为 movsb movsw movsd

1
2
3
4
5
MOV EDI,12FFD8
MOV ESI,12FFD0
MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI]
EDI内存里的值被修改为ESI内存里的值,且EDI和ESI各加4,和DOWRD数据宽度相关,如果为WORD 则各加2
由DF(Direction Flag)方向标志位决定,当DF位为1时为减,当DF位为0时,则为加

movsx 操作数 A,操作数 B 符号扩展传送

movzx 零扩展传送

movsx,movzx 操作数 A 的空间必须大于操作数 b

如果大的给小的,会产生截断

STOS 指令

将 Al/AX/EAX 的值存储到 [EDI] 指定的内存单元,和数据宽度相关

1
2
3
4
5
6
STOS BYTE PTR ES:[EDI]                将AL存储到[EDI]
STOS WORD PTR ES:[EDI] 将AX存储到[EDI]
STOS DWORD PTR ES:[EDI] 将EAX存储到[EDI]
注意这里使用的是ES
当后面为[EDI]时要使用ES
存储完数据后EDI地址的变化方向也受DF标志控制,1减0增
1
2
3
4
5
00401019  |.  8D7D C0       lea     edi,[local.16]
0040101C |. B9 10000000 mov ecx,0x10
00401021 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
00401026 |. F3:AB rep stos dword ptr es:[edi]
现在在看看这几行代码,先把eax的值变为cccccccc,stos将其存储到es中,通过rep循环存储

REP 指令

按计数寄存器 (ECX) 中指定的次数重复执行指令

1
2
3
4
MOV ECX,10
REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS:[ESI] 也可以写成REP MOVSD
代码将会重复执行16次
因为每执行一次EDI和ESI都会变化4,变化方向由DF决定

LEA 指令

地址传送指令

将操作数 B 的有效地址传送到指定的某个寄存器,操作数 A 必须是寄存器

# 跳转和比较指令

# JCC 指令

cc 代表 condition code (状态码)

Jcc 不是单个指令,它只是描述了跳转之前检查条件代码的跳转助记符

image-20221031210918994

https://www.cs.princeton.edu/courses/archive/fall12/cos375/IA32Jcc.pdf

JCC 指令用于改变 EIP(CPU 要读取的指令地址)

# JMP 指令

JMP 指令:修改 EIP 的值

JMP 指令只影响了 EIP,不影响堆栈和其它通用寄存器

JMP 寄存器 / 立即数相当于 MOV EIP, 寄存器 / 立即数

# CALL 指令

CALL 指令和 JMP 指令都会修改 EIP 的值

但 CALL 指令会将返回地址(CALL 指令的下一条指令地址)压入堆栈

因此也会引起 esp 的变化

# RET 指令

call 调用跳转后执行完相关代码完要返回到 call 的下一条指令时使用 ret 指令

ret 指令相当于 pop eip

# CMP 指令

指令格式:CMP R/M,R/M/IMM

CMP 指令只改变标志寄存器的值

该指令是比较两个操作数,实际上,它相当于 SUB 指令,但是相减的结果并不保存到第一个操作数中

只是根据相减的结果来改变 ZF 零标志位的,当两个操作数相等的时候,零标志位置 1

# TEST 指令

指令格式:TEST R/M,R/M/IMM

该指令在一定程度上和 CMP 指令时类似的,两个数值进行与操作,结果不保存,但是会改变相应标志位

常见用法:用这个指令,可以确定某寄存器是否等于 0

观察 ZF(零标志位)就可以判断 EAX 是否为 0

# if 指令编译结果

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

int main(int argc, char* argv[])
{
int a = 1;
if (a == 1) {
printf("%d\n",11);
}
else {
printf("%d\n",22);
}
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
0040D440 >/> \55            push    ebp
0040D441 |. 8BEC mov ebp,esp
0040D443 |. 83EC 44 sub esp,0x44
0040D446 |. 53 push ebx
0040D447 |. 56 push esi
0040D448 |. 57 push edi
0040D449 |. 8D7D BC lea edi,[local.17]
0040D44C |. B9 11000000 mov ecx,0x11
0040D451 |. B8 CCCCCCCC mov eax,0xCCCCCCCC
0040D456 |. F3:AB rep stos dword ptr es:[edi]
//上面都是堆栈提升,初始化堆栈,if从这里开始

// a=1
0040D458 |. C745 FC 01000>mov dword ptr ss:[ebp-0x4],0x1
// a==1?a=11:a=22
// 如果a!=1 jmp 0040D476
// 如果a!=1 printf(11) jmp(0040D485)
// jmp(0040D485)就是跳转到if判断结束的地方,继续执行下面的语句
0040D45F |. 837D FC 01 cmp dword ptr ss:[ebp-0x4],0x1
0040D463 |. 75 11 jnz short 0040D476
0040D465 |. 6A 0B push 0xB ; /<%d> = B (11.)
0040D467 |. 68 0C214200 push 0042210C ; |format = "%d

0040D46C |. E8 4F020000 call printf ; \printf
0040D471 |. 83C4 08 add esp,0x8
0040D474 |. EB 0F jmp short 0040D485
0040D476 |> 6A 16 push 0x16 ; /<%d> = 16 (22.)
0040D478 |. 68 0C214200 push 0042210C ; |format = "%d

0040D47D |. E8 3E020000 call printf ; \printf
0040D482 |. 83C4 08 add esp,0x8
0040D485 |> 33C0 xor eax,eax
0040D487 |. 5F pop edi
0040D488 |. 5E pop esi
0040D489 |. 5B pop ebx
0040D48A |. 83C4 44 add esp,0x44
0040D48D |. 3BEC cmp ebp,esp
0040D48F |. E8 BC3BFFFF call _chkesp
0040D494 |. 8BE5 mov esp,ebp
0040D496 |. 5D pop ebp
0040D497 \. C3 retn


# 反汇编分析 C 语言

有了之前画堆栈图的经验,我们不难看出,尽管我们的函数是个空函数,但其汇编代码依然完成了以下流程:

  1. 提升堆栈
  2. 保护现场
  3. 初始化提升的堆栈
  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
提升堆栈
复制代码 隐藏代码
00401010 push ebp
00401011 mov ebp,esp
00401013 sub esp,40h
保护现场
复制代码 隐藏代码
00401016 push ebx
00401017 push esi
00401018 push edi
PS:前面的push ebp也是保护现场

初始化提升的堆栈
复制代码 隐藏代码
00401019 lea edi,[ebp-40h]
0040101C mov ecx,10h
00401021 mov eax,0CCCCCCCCh
00401026 rep stos dword ptr [edi]
恢复现场
复制代码 隐藏代码
00401028 pop edi
00401029 pop esi
0040102A pop ebx
0040102B mov esp,ebp
0040102D pop ebp

image-20221108201930358

# 内联汇编

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

int main(int argc, char* argv[])
{
__asm{
pushad //通用寄存器入栈
pushfd //标志寄存器入栈
mov al,0xff
add al,1 //C,P,A,Z = 1
popad
popfd
}
return 0;
}

使用 VS2008 创建一个 win32 application

image-20221108192317976

img

使用多线程 /mt 之后,用户运行时不需要库既可运行,但文件会变大

编译一个内联函数

nake 声明一个裸函数,即堆栈的申请释放都需要我们自己进行

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


int __declspec (naked) Plus(){
__asm{
pushad
xor eax,eax
cmp byte ptr ds:[0x4F040],al
retn
popad
}
}
int _tmain(int argc, _TCHAR* argv[])
{
Plus();
return 0;
}

Plus () 对应的汇编代码为

1
2
3
00FC2240 > > \60            pushad
00FC2241 . 33C0 xor eax,eax
00FC2243 61 popad

正常的函数:

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

int Plus()
{
int x=1+2;
return 1;
}
int _tmain(int argc, _TCHAR* argv[])
{
Plus();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
003813A0 >  55              push    ebp
003813A1 8BEC mov ebp,esp
003813A3 81EC CC000000 sub esp,0xCC
003813A9 53 push ebx
003813AA 56 push esi
003813AB 57 push edi
003813AC 8DBD 34FFFFFF lea edi,dword ptr ss:[ebp-0xCC]
003813B2 B9 33000000 mov ecx,0x33
003813B7 B8 CCCCCCCC mov eax,0xCCCCCCCC
003813BC F3:AB rep stos dword ptr es:[edi]
003813BE C745 F8 0300000>mov dword ptr ss:[ebp-0x8],0x3
003813C5 B8 01000000 mov eax,0x1
003813CA 5F pop edi
003813CB 5E pop esi
003813CC 5B pop ebx
003813CD 8BE5 mov esp,ebp
003813CF 5D pop ebp
003813D0 C3 retn

正常的函数系统会自动帮我们进行堆栈的操作我们只需要关系自己的代码即可

# 寻找 C 程序入口

为什么能看到 wmain 的名称

因为有 pdb 文件,od 读取 pdb 读取 debug 信息

PDB 全称为程序数据库文件,存储了被编译文件的调试信息,一般用在调式程序中

1. 找到 wmainCRTStartup

image-20221108210348946

2. 找到__tmainCRTStartup

image-20221108210432400

3. 这个 call 002C108C 就是我们的 main 函数

1
2
3
002C260E   .  FF35 70F02D00 push    dword ptr ds:[__wargv]
002C2614 . FF35 68F02D00 push dword ptr ds:[__argc]
002C261A . E8 6DEAFFFF call 002C108C

4.wmain 就是我们的 main 函数

image-20221108210639094

mainCRTStartup 和 wmainCRTStartup 是控制台环境下多字节编码和 Unicode 编码的启动函数

而 WinMainCRTStartup 和 wWinMainCRTStartup 是 windows 环境下多字节编码和 Unicode 编码的启动函数

mainCRTStartup 做了哪些事:

image-20221108210737730

  1. 前面我们已经知道了 mainCRTStartup 也就是程序入口,那么如何通过 mainCRTStartup 来找到 main 函数入口
  2. 根据函数的参数来进行判断
  3. main 函数貌似只有两个参数,但实际上 main 函数一共有三个参数,只不过一般第三个参数我们并没有用到,于是在使用 main 函数时并没有加上,完整的 main 函数原型如下:
1
int main(int argc,char *argv[],char *envp[]){}

这里的 argv 和 envp 对应 mainCRTStartup 里_setargv () 和_setenvp ()

main 中参数

image-20221108210850705

另一种歪门邪道:

找一个有标示性的断点

image-20221111213357060

下断,跳转到断点,F9 执行到断点

image-20221111213436905

在堆栈窗口中选中,enter 建进入 main

或者在有 printf 或者 MessageBox 时可以 F9 CTRL+F9

# 调试方式

1.OD 直接打开

2. 附加到已经打开的进程

# OD 常用操作

F9 程序运行到所设断点处

F2 设置软件断点

ALT+C 初始页面

bp 指令地址 设置软件断点

bc 清除断点

dd ecx / 内存地址 查看 ecx 寄存器,寄存器中值 以双字显示

dw 以字显示

db 以字节显示

dc 以字符串显示数据

? 计算表达式

F8 单步步过

F7 单步步入

CTRL + F9 执行到返回

ALT + F9 执行到用户代码

CTRL+F2 重新开始

转到地址 Ctrl +G

断点窗口 Alt +B

- 到上一步

* 转到当前指令地址

+ 转到下一步

选中汇编,按空格,输入汇编代码

# 函数调用

注意先关闭 c/c++ 优化

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

int add(int a, int b)
{
return a+b;
}
int aaa=1;

int _tmain(int argc, _TCHAR* argv[])
{
MessageBox(0,NULL,NULL,MB_OK);
aaa = add(3,4);
return 0;
}

# release

优化的

1
2
3
4
5
6
7
8
00371000 >/$  6A 00         push    0x0                              ; /Style = MB_OK|MB_APPLMODAL
00371002 |. 6A 00 push 0x0 ; |Title = NULL
00371004 |. 6A 00 push 0x0 ; |Text = NULL
00371006 |. 6A 00 push 0x0 ; |hOwner = NULL
00371008 |. FF15 A4203700 call dword ptr ds:[<&USER32.MessageBo>; \MessageBoxW
0037100E |. C705 18303700>mov dword ptr ds:[aaa],0x7
00371018 |. 33C0 xor eax,eax
0037101A \. C3 retn

未被优化的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
012B1010 >/$  55            push    ebp
012B1011 |. 8BEC mov ebp,esp
012B1013 |. 6A 00 push 0x0 ; /Style = MB_OK|MB_APPLMODAL
012B1015 |. 6A 00 push 0x0 ; |Title = NULL
012B1017 |. 6A 00 push 0x0 ; |Text = NULL
012B1019 |. 6A 00 push 0x0 ; |hOwner = NULL
012B101B |. FF15 E4802B01 call dword ptr ds:[<&USER32.MessageBoxW>] ; \MessageBoxW
012B1021 |. 6A 04 push 0x4
012B1023 |. 6A 03 push 0x3
012B1025 |. E8 D6FFFFFF call add
012B102A |. 83C4 08 add esp,0x8
012B102D |. A3 40AC2B01 mov dword ptr ds:[aaa],eax
012B1032 |. 33C0 xor eax,eax
012B1034 |. 5D pop ebp
012B1035 \. C3 retn

如果我们此时将代码改为如下:

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

int add(int a, int b)
{
return a+b;
}
int aaa=1;

int _tmain(int argc, _TCHAR* argv[])
{
MessageBox(0,NULL,NULL,MB_OK);
aaa = add(aaa,4);
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
00EB13F0 >  55              push    ebp
00EB13F1 8BEC mov ebp,esp
00EB13F3 81EC C0000000 sub esp,0xC0
00EB13F9 53 push ebx
00EB13FA 56 push esi
00EB13FB 57 push edi
00EB13FC 8DBD 40FFFFFF lea edi,dword ptr ss:[ebp-0xC0]
00EB1402 B9 30000000 mov ecx,0x30
00EB1407 B8 CCCCCCCC mov eax,0xCCCCCCCC
00EB140C F3:AB rep stos dword ptr es:[edi]
00EB140E 8BF4 mov esi,esp
00EB1410 6A 00 push 0x0
00EB1412 6A 00 push 0x0
00EB1414 6A 00 push 0x0
00EB1416 6A 00 push 0x0
00EB1418 FF15 3883EB00 call dword ptr ds:[<&USER32.MessageBoxW>] ; user32.MessageBoxW
00EB141E 3BF4 cmp esi,esp
00EB1420 E8 25FDFFFF call 00EB114A
00EB1425 6A 04 push 0x4
00EB1427 A1 0070EB00 mov eax,dword ptr ds:[aaa]
00EB142C 50 push eax
00EB142D E8 69FCFFFF call 00EB109B
00EB1432 83C4 08 add esp,0x8
00EB1435 A3 0070EB00 mov dword ptr ds:[aaa],eax
00EB143A 33C0 xor eax,eax
00EB143C 5F pop edi
00EB143D 5E pop esi
00EB143E 5B pop ebx
00EB143F 81C4 C0000000 add esp,0xC0
00EB1445 3BEC cmp ebp,esp
00EB1447 E8 FEFCFFFF call 00EB114A
00EB144C 8BE5 mov esp,ebp
00EB144E 5D pop ebp
00EB144F C3 retn


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
00EB13B0 >  55              push    ebp
00EB13B1 8BEC mov ebp,esp
00EB13B3 81EC C0000000 sub esp,0xC0
00EB13B9 53 push ebx
00EB13BA 56 push esi
00EB13BB 57 push edi
00EB13BC 8DBD 40FFFFFF lea edi,dword ptr ss:[ebp-0xC0]
00EB13C2 B9 30000000 mov ecx,0x30
00EB13C7 B8 CCCCCCCC mov eax,0xCCCCCCCC
00EB13CC F3:AB rep stos dword ptr es:[edi]
00EB13CE 8B45 08 mov eax,dword ptr ss:[ebp+0x8]
00EB13D1 0345 0C add eax,dword ptr ss:[ebp+0xC]
00EB13D4 5F pop edi ; 0038FB84
00EB13D5 5E pop esi
00EB13D6 5B pop ebx
00EB13D7 8BE5 mov esp,ebp
00EB13D9 5D pop ebp
00EB13DA C3 retn


# 进制与内存单元长度修饰

1
2
3
4
5
6
add [ebx],0x333
然在实际汇编出来: add byte ptr [ebx], 33

eax是双字
内存单元访问,默认byte
因此会截断

# 补充

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int a = 0x109     //a = local.1
int b = 8 //b = local.2

sub 会影响zf

je/jz 如果zf=1跳转
jne/jnz

__asm {
mob eax,3
sub eax,a
jz end
end:

}

无符号JL/JNGE 小于/不大于等于跳转 根据sf,of跳转
有符号JNG/JLE 不大于/小于等于 sf=1||zf=1|||OF=1 ZF=1 || sf!=of
有符号JG/JNLE 大于/不小于等于 ZF=0 && SF=OF
无符号JA/JG 无符号大于
无符号JBE/JNA 小于等于/大于

image-20221115213554357

image-20221115213632575

EBP 栈底指针 base pointer

ESP 栈顶指针

EBP -/+ 偏移量可以访问 call 里的局部变量

ESP 栈顶指针与 EBP 构成了一段空间,一般就是本 call 局部变量的空间大小总和

空函数中

1
2
3
4
push ebp   //保存上一个函数的栈底,上一个函数的栈顶就是新的ebp,即原来的esp
mov ebp,esp //确定新的栈底
pop ebp
ret

image-20221116195359372

image-20221116200409461

1
2
3
4
5
int a = 3
int b = 5
char c = 1

char s[17] //如果微软编译器可能会多分配几个字节防止数组越界导致程序崩溃
1
2
3
4
5
6
7
sub esp,0C   //0c因为内存对齐
mov dword ptr [local.1],3 //local.1 == ebp-4
mov dword ptr [local.2],5 //local.2 == ebp-4
mov byte ptr [local.3+3],1 //local.3 == ebp+3 //因为只使用了一个字节,从顶往下减少三个
mov esp,ebp
pop ebp
ret

每个 call 分配独立的栈段空间,供局部变量使用

call 平衡,pop 后 ebp 和 esp 不变

栈的上面小,下面大

push ebp //sub esp,4 mov esp,[ebp]

pop ebp //mov ebp,[esp] add esp,4

参数入栈又右向左入栈

ecx 放 this 指针,eax 放返回值

call 执行 push eIP

retn pop eip

32 位内存中,EIP (32 位) 已经能表达所有内存地址。而 16 位系统中的内存地址是 20 位,所以需要 IP 和 CS 同时入栈

参数局部变量的表示 [EBP - x] 本 call 中的局部变量 [EBP+x] 上一个 call 的局部变量作为传入参数

[ebp +4] 是父函数 EBP 的值,[EBP+8] 大概率是外部传进来的参数值

调用约定:

​ __cdecl 是 c declaration 缩写,所有参数从右到左依次入栈,这些参数由调用者清除,称为手动清栈,即清栈是在 call 后面 call 0010000h add esp+x VC 默认约定

1
int __cdecl add(int a, int b)

​ __stdcall api 函数调用约定,是 c++ 标准调用方式,所有参数从右到左依次入栈,如果调用类成员,最后一个入栈的是 this 指针,这些堆栈由被调用者在返回后清除,retx,x 代表参数占用字节数,cpu 在 ret 之后自动弹出 x 个栈空间,称为自动清栈

1
int __stdcall add2(int a, int b)

​ __fastcall 调用约定,是编译器指定的快速调用方式。fastcall 规定将前两个 (若干个参数由寄存器传递,其余参数通过堆栈传递),返回方式和 stdcall 一样,如果只有两个参数,不用栈,不需要堆栈平衡,如果有很多参数,还需要用堆栈平衡部分堆栈

1
int __fastcall add3(int a, int b)
image-20221116223933037

c 中不加默认说明为_cedcl 方式,c++ 也一样,默认调用方式可以在 ide 中设置

带有可变参数的函数必须使用_cdecl 方式,比如 printf (char *fmtstr, …) scanf

# if-else

if-else:

1
2
3
4
5
6
7
8
9
10
11
int _tmain(int argc, _TCHAR* argv[])
{
printf("haha");
int a=1, b=2;
if (a>b)
{
printf("shabi");
} else {
printf("zhizhang");
}
}

debug

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
00EF13A0 >  55              push    ebp
00EF13A1 8BEC mov ebp,esp
00EF13A3 81EC D8000000 sub esp,0xD8
00EF13A9 53 push ebx
00EF13AA 56 push esi
00EF13AB 57 push edi
00EF13AC 8DBD 28FFFFFF lea edi,dword ptr ss:[ebp-0xD8]
00EF13B2 B9 36000000 mov ecx,0x36
00EF13B7 B8 CCCCCCCC mov eax,0xCCCCCCCC
00EF13BC F3:AB rep stos dword ptr es:[edi]
//堆栈操作上面有写,自己看啊

00EF13BE 8BF4 mov esi,esp
00EF13C0 68 5057EF00 push 00EF5750 ; ASCII "haha"
00EF13C5 FF15 BC82EF00 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00EF13CB 83C4 04 add esp,0x4
//printf调用结束后保护现场,属于手动清栈

00EF13CE 3BF4 cmp esi,esp
00EF13D0 E8 70FDFFFF call 00EF1145
//00EF1145 /E9 16030000 jmp _RTC_CheckEsp

00EF13D5 C745 F8 0100000>mov dword ptr ss:[ebp-0x8],0x1
00EF13DC C745 EC 0200000>mov dword ptr ss:[ebp-0x14],0x2
//局部变量a,b入栈

00EF13E3 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
00EF13E6 3B45 EC cmp eax,dword ptr ss:[ebp-0x14]
比较a 和 b的大小
00EF13E9 7E 19 jle short 00EF1404
00EF13EB 8BF4 mov esi,esp
00EF13ED 68 4857EF00 push 00EF5748 ; ASCII "shabi"
00EF13F2 FF15 BC82EF00 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00EF13F8 83C4 04 add esp,0x4
00EF13FB 3BF4 cmp esi,esp
00EF13FD E8 43FDFFFF call 00EF1145
00EF1402 EB 17 jmp short 00EF141B
00EF1404 8BF4 mov esi,esp
00EF1406 68 3C57EF00 push 00EF573C ; ASCII "zhizhang"
00EF140B FF15 BC82EF00 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00EF1411 83C4 04 add esp,0x4
00EF1414 3BF4 cmp esi,esp
00EF1416 E8 2AFDFFFF call 00EF1145
00EF141B 33C0 xor eax,eax
00EF141D 5F pop edi
00EF141E 5E pop esi
00EF141F 5B pop ebx
00EF1420 81C4 D8000000 add esp,0xD8
00EF1426 3BEC cmp ebp,esp
00EF1428 E8 18FDFFFF call 00EF1145
00EF142D 8BE5 mov esp,ebp
00EF142F 5D pop ebp
00EF1430 C3 retn

release

1
2
3
4
5
6
7
8
9
10
000E1000 >/$  56            push    esi
000E1001 |. 8B35 A0200E00 mov esi,dword ptr ds:[<&MSVCR90.printf>] ; msvcr90.printf
000E1007 |. 68 F4200E00 push 000E20F4 ; /format = "haha"
000E100C |. FFD6 call esi ; \printf
000E100E |. 68 FC200E00 push 000E20FC ; ASCII "zhizhang"
000E1013 |. FFD6 call esi
000E1015 |. 83C4 08 add esp,0x8
000E1018 |. 33C0 xor eax,eax
000E101A |. 5E pop esi
000E101B \. C3 retn

OD 文件夹中有 UDD 作为缓存

# switch case

switch-case

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int _tmain(int argc, _TCHAR* argv[])
{
printf("haha");
int a=1, b=2;
switch(a)
{
case 1:
printf("1111");
break;
case 2:
printf("2222");
break;
case 3:
printf("3333");
break;
default:
printf("shabi");
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
63
64
65
66
67
68
69
70
71
72
00FF13BE    8BF4            mov     esi,esp
00FF13C0 68 5C57FF00 push 00FF575C ; ASCII "haha"
00FF13C5 FF15 BC82FF00 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00FF13CB 83C4 04 add esp,0x4
00FF13CE 3BF4 cmp esi,esp
00FF13D0 E8 70FDFFFF call 00FF1145

00FF13D5 C745 F8 0100000>mov dword ptr ss:[ebp-0x8],0x1
00FF13DC C745 EC 0200000>mov dword ptr ss:[ebp-0x14],0x2
//局部变量a,b入栈
00FF13E3 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
00FF13E6 8985 24FFFFFF mov dword ptr ss:[ebp-0xDC],eax
//[ebp-0xdc] = a


00FF13EC 83BD 24FFFFFF 0>cmp dword ptr ss:[ebp-0xDC],0x1
00FF13F3 74 14 je short 00FF1409
00FF13F5 83BD 24FFFFFF 0>cmp dword ptr ss:[ebp-0xDC],0x2
00FF13FC 74 24 je short 00FF1422
00FF13FE 83BD 24FFFFFF 0>cmp dword ptr ss:[ebp-0xDC],0x3
00FF1405 74 34 je short 00FF143B
//比较a和case中值,如果相等那么跳转

00FF1407 EB 4B jmp short 00FF1454
//case

00FF1409 8BF4 mov esi,esp
00FF140B 68 5457FF00 push 00FF5754 ; ASCII "1111"
00FF1410 FF15 BC82FF00 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00FF1416 83C4 04 add esp,0x4

00FF1419 3BF4 cmp esi,esp
00FF141B E8 25FDFFFF call 00FF1145

00FF1420 EB 49 jmp short 00FF146B
//break

00FF1422 8BF4 mov esi,esp
00FF1424 68 4C57FF00 push 00FF574C ; ASCII "2222"
00FF1429 FF15 BC82FF00 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00FF142F 83C4 04 add esp,0x4

00FF1432 3BF4 cmp esi,esp
00FF1434 E8 0CFDFFFF call 00FF1145

00FF1439 EB 30 jmp short 00FF146B

00FF143B 8BF4 mov esi,esp
00FF143D 68 4457FF00 push 00FF5744 ; ASCII "3333"
00FF1442 FF15 BC82FF00 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00FF1448 83C4 04 add esp,0x4
00FF144B 3BF4 cmp esi,esp
00FF144D E8 F3FCFFFF call 00FF1145
00FF1452 EB 17 jmp short 00FF146B
00FF1454 8BF4 mov esi,esp
00FF1456 68 3C57FF00 push 00FF573C ; ASCII "shabi"
00FF145B FF15 BC82FF00 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00FF1461 83C4 04 add esp,0x4
00FF1464 3BF4 cmp esi,esp
00FF1466 E8 DAFCFFFF call 00FF1145

00FF146B 33C0 xor eax,eax
00FF146D 5F pop edi
00FF146E 5E pop esi
00FF146F 5B pop ebx
00FF1470 81C4 DC000000 add esp,0xDC
00FF1476 3BEC cmp ebp,esp
00FF1478 E8 C8FCFFFF call 00FF1145
00FF147D 8BE5 mov esp,ebp
00FF147F 5D pop ebp
00FF1480 C3 retn

release

1
2
3
4
5
6
7
8
9
10
011C1000 >/$  56            push    esi
011C1001 |. 8B35 A0201C01 mov esi,dword ptr ds:[<&MSVCR90.printf>] ; msvcr90.printf
011C1007 |. 68 F4201C01 push 011C20F4 ; /format = "haha"
011C100C |. FFD6 call esi ; \printf
011C100E |. 68 FC201C01 push 011C20FC ; ASCII "1111"
011C1013 |. FFD6 call esi
011C1015 |. 83C4 08 add esp,0x8
011C1018 |. 33C0 xor eax,eax
011C101A |. 5E pop esi
011C101B \. C3 retn

如果 case 很多的时候,会使用导出表

用 case 最大的 - case 最小的,得出的范围称为 k,如果要判断的值 - case 最小的值不在范围 k 之内,直接 default

其实这样的,edx 存放的是 a 与 case 中最小值的差值,而 eax*4 + 地址存放的是每个 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
int _tmain(int argc, _TCHAR* argv[])
{
printf("haha");
int a=5;
switch(a)
{
case 1:
printf("1");
case 5:
printf("5");
case 9:
printf("9");
case 2:
printf("2");
case 7:
printf("7");
break;
case 13:
printf("13");
break;
default:
printf("shabi");
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
00F713A0 >  55              push    ebp
00F713A1 8BEC mov ebp,esp
00F713A3 81EC D0000000 sub esp,0xD0
00F713A9 53 push ebx
00F713AA 56 push esi
00F713AB 57 push edi
00F713AC 8DBD 30FFFFFF lea edi,dword ptr ss:[ebp-0xD0]
00F713B2 B9 34000000 mov ecx,0x34
00F713B7 B8 CCCCCCCC mov eax,0xCCCCCCCC
00F713BC F3:AB rep stos dword ptr es:[edi]
00F713BE 8BF4 mov esi,esp
00F713C0 68 5C57F700 push 00F7575C ; ASCII "haha"
00F713C5 FF15 BC82F700 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00F713CB 83C4 04 add esp,0x4
00F713CE 3BF4 cmp esi,esp
00F713D0 E8 70FDFFFF call 00F71145
00F713D5 C745 F8 0500000>mov dword ptr ss:[ebp-0x8],0x5 ; a = 5
00F713DC 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
00F713DF 8985 30FFFFFF mov dword ptr ss:[ebp-0xD0],eax
00F713E5 8B8D 30FFFFFF mov ecx,dword ptr ss:[ebp-0xD0] ; ecx = eax = 5
00F713EB 83E9 01 sub ecx,0x1 ; ecx = ecx - 1 计算a与case中最小值的差值
00F713EE 898D 30FFFFFF mov dword ptr ss:[ebp-0xD0],ecx ; [ebp-0x0d] = 4 4在13-1的范围之间
00F713F4 83BD 30FFFFFF 0>cmp dword ptr ss:[ebp-0xD0],0xC ; 将a 与case 中最小值的差1值和case最大最小差值12相比
00F713FB 0F87 A2000000 ja 00F714A3 ; 如果当前要比较的值和case最小值的差值>12,直接default
00F71401 8B95 30FFFFFF mov edx,dword ptr ss:[ebp-0xD0] ; edx中存放的就是a与case最小值的差值 0xF714EC存放导出表地址
00F71407 0FB682 EC14F700 movzx eax,byte ptr ds:[edx+0xF714EC] ; eax=2
00F7140E FF2485 D014F700 jmp dword ptr ds:[eax*4+0xF714D0] ; 2*4 + 0xF714D0
00F71415 8BF4 mov esi,esp
00F71417 68 5857F700 push 00F75758 ; UNICODE "1"
00F7141C FF15 BC82F700 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00F71422 83C4 04 add esp,0x4
00F71425 3BF4 cmp esi,esp
00F71427 E8 19FDFFFF call 00F71145
00F7142C 8BF4 mov esi,esp
00F7142E 68 5457F700 push 00F75754 ; UNICODE "5"
00F71433 FF15 BC82F700 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00F71439 83C4 04 add esp,0x4
00F7143C 3BF4 cmp esi,esp
00F7143E E8 02FDFFFF call 00F71145
00F71443 8BF4 mov esi,esp
00F71445 68 5057F700 push 00F75750 ; UNICODE "9"
00F7144A FF15 BC82F700 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00F71450 83C4 04 add esp,0x4
00F71453 3BF4 cmp esi,esp
00F71455 E8 EBFCFFFF call 00F71145
00F7145A 8BF4 mov esi,esp
00F7145C 68 4C57F700 push 00F7574C ; UNICODE "2"
00F71461 FF15 BC82F700 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00F71467 83C4 04 add esp,0x4
00F7146A 3BF4 cmp esi,esp
00F7146C E8 D4FCFFFF call 00F71145
00F71471 8BF4 mov esi,esp
00F71473 68 4857F700 push 00F75748 ; UNICODE "7"
00F71478 FF15 BC82F700 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00F7147E 83C4 04 add esp,0x4
00F71481 3BF4 cmp esi,esp
00F71483 E8 BDFCFFFF call 00F71145
00F71488 EB 30 jmp short printf
00F7148A 8BF4 mov esi,esp
00F7148C 68 4457F700 push 00F75744 ; ASCII "13"
00F71491 FF15 BC82F700 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00F71497 83C4 04 add esp,0x4
00F7149A 3BF4 cmp esi,esp
00F7149C E8 A4FCFFFF call 00F71145
00F714A1 EB 17 jmp short printf
00F714A3 8BF4 mov esi,esp
00F714A5 68 3C57F700 push 00F7573C ; ASCII "shabi"
00F714AA FF15 BC82F700 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00F714B0 83C4 04 add esp,0x4
00F714B3 3BF4 cmp esi,esp
00F714B5 E8 8BFCFFFF call 00F71145
00F714BA > 33C0 xor eax,eax
00F714BC 5F pop edi
00F714BD 5E pop esi
00F714BE 5B pop ebx
00F714BF 81C4 D0000000 add esp,0xD0
00F714C5 3BEC cmp ebp,esp
00F714C7 E8 79FCFFFF call 00F71145
00F714CC 8BE5 mov esp,ebp
00F714CE 5D pop ebp
00F714CF C3 retn

如果 a=9, 9-1=8
db 8+0xF714EC == 4 db 8+0xF714EC

image-20221119102654407

dd 4 * 4 + 0xF714D0 ==

image-20221119102748365\

image-20221119102822429`

也就是说 F714EC 中存放的就是导出表

image-20221119103208537·

# for

for 循环

1
2
3
for (int i=1;i<=10;++i) {
printf("%d",i);
}

debug,禁止优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
013013BE    C745 F8 0100000>mov     dword ptr ss:[ebp-0x8],0x1
013013C5 EB 09 jmp short 013013D0
013013C7 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
013013CA 83C0 01 add eax,0x1
013013CD 8945 F8 mov dword ptr ss:[ebp-0x8],eax
013013D0 837D F8 0A cmp dword ptr ss:[ebp-0x8],0xA
013013D4 7F 1D jg short 013013F3
013013D6 8BF4 mov esi,esp
013013D8 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
013013DB 50 push eax
013013DC 68 3C573001 push 0130573C ; ASCII "%d"
013013E1 FF15 BC823001 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
013013E7 83C4 08 add esp,0x8
013013EA 3BF4 cmp esi,esp
013013EC E8 54FDFFFF call 01301145
013013F1 ^ EB D4 jmp short 013013C7
013013F3 33C0 xor eax,eax ;eax清零,作为返回值
013013F5 5F pop edi
013013F6 5E pop esi
013013F7 5B pop ebx
013013F8 81C4 CC000000 add esp,0xCC
013013FE 3BEC cmp ebp,esp
01301400 E8 40FDFFFF call 01301145
01301405 8BE5 mov esp,ebp
01301407 5D pop ebp
01301408 C3 retn

release,O/2 优化

最大化速度优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
00361000 >/$  56            push    esi
00361001 |. 68 84B33600 push 0036B384 ; /format = "shabi"
00361006 |. E8 2F000000 call printf ; \printf
0036100B |. 83C4 04 add esp,0x4
0036100E |. BE 01000000 mov esi,0x1
00361013 |> 56 /push esi ; /<%d>
00361014 |. 68 8CB33600 |push 0036B38C ; |format = "%d"
00361019 |. E8 1C000000 |call printf ; \printf
0036101E |. 46 |inc esi
0036101F |. 83C4 08 |add esp,0x8
00361022 |. 83FE 0A |cmp esi,0xA
00361025 |.^ 7E EC \jle short 00361013
00361027 |. 33C0 xor eax,eax
00361029 |. 5E pop esi
0036102A \. C3 retn


release,O/1 优化

大小最小化优化情况下,for

1
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
01161000 >/$  56            push    esi
01161001 |. 57 push edi ; 保护edi
01161002 |. 8B3D A0201601 mov edi,dword ptr ds:[<&MSVCR90.printf>] ; printf给edi,调用时直接call edi,用寄存器代替内存
01161008 |. 68 F4201601 push 011620F4 ; /format = "shabi",printf参数入栈
0116100D |. FFD7 call edi ; call printf
0116100F |. 33F6 xor esi,esi ; esi = 0
01161011 |. 59 pop ecx ; 堆栈平衡,可以理解为esp+4
01161012 |. 46 inc esi

//循环代码段
01161013 |> 56 /push esi ; i 入栈
01161014 |. 68 FC201601 |push 011620FC ; ASCII "%d"入栈
01161019 |. FFD7 |call edi ; call printf 编译器判断第一次可以执行,直接优化
0116101B |. 46 |inc esi
0116101C >|. 83FE 0A |cmp esi,0xA
0116101F |. 59 |pop ecx
01161020 |. 59 |pop ecx ; 两次pop堆栈平衡 esp+8
01161021 |.^ 7E F0 \jle short 01161013

01161023 |. 5F pop edi
01161024 |. 33C0 xor eax,eax
01161026 >|. 5E pop esi
01161027 \. C3 retn

//inc 一个字节,pop edi一个字节,
//add 三个字节,

release,关闭优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
00191000 > $  55            push    ebp
00191001 . 8BEC mov ebp,esp
00191003 . 51 push ecx
//这个ecx在最后没有出栈,所以不是用来保护的,而是用来分配堆栈空间的

00191004 . C745 FC 01000>mov dword ptr ss:[ebp-0x4],0x1
0019100B . EB 09 jmp short 00191016
0019100D > 8B45 FC mov eax,dword ptr ss:[ebp-0x4]
00191010 . 83C0 01 add eax,0x1
00191013 . 8945 FC mov dword ptr ss:[ebp-0x4],eax
00191016 > 837D FC 0A cmp dword ptr ss:[ebp-0x4],0xA
0019101A . 7F 14 jg short 00191030
0019101C . 8B4D FC mov ecx,dword ptr ss:[ebp-0x4]
0019101F . 51 push ecx ; 根据从右到左的入栈数据,i先入栈,%d后入
00191020 . 68 F4201900 push 001920F4 ; ASCII "%d"
00191025 FF db FF
00191026 > . 15 A0201900 adc eax,<&MSVCR90.printf>
0019102B . 83C4 08 add esp,0x8 ; printf有两个参数,因此堆栈平衡时需要+8
0019102E .^ EB DD jmp short 0019100D
00191030 > 33C0 xor eax,eax ; eax作为函数返回值,此处将eax清零
00191032 . 8BE5 mov esp,ebp
00191034 . 5D pop ebp
00191035 > . C3 retn

release,完全优化 / Ox

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
010E1000 >/$  56            push    esi
010E1001 |. 68 84B30E01 push 010EB384 ; /format = "shabi"
010E1006 |. E8 2F000000 call printf ; \printf
010E100B |. 83C4 04 add esp,0x4
010E100E |. BE 01000000 mov esi,0x1
010E1013 |> 56 /push esi ; /<%d>
010E1014 |. 68 8CB30E01 |push 010EB38C ; |format = "%d"
010E1019 |. E8 1C000000 |call printf ; \printf
010E101E |. 46 |inc esi
010E101F |. 83C4 08 |add esp,0x8
010E1022 |. 83FE 0A |cmp esi,0xA
010E1025 |.^ 7E EC \jle short 010E1013
010E1027 |. 33C0 xor eax,eax
010E1029 |. 5E pop esi
010E102A \. C3 retn

# ++ –

inc a //i++ ++i 速度比 add 快,占用空间小,指令执行影响 AS,OF,PF,SF,ZF 标志位,不影响 CF

dec a

1
2
010E100B  |.  83C4 04       add     esp,0x4
01161012 |. 46 inc esi
1
2
3
4
5
6
7
8
9
10
int _tmain(int argc, _TCHAR* argv[])
{
printf("shabi");
int i=0;
i++;
i--;
return 0;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
003A1000 >/$  55            push    ebp
003A1001 |. 8BEC mov ebp,esp
003A1003 |. 51 push ecx
003A1004 |. 68 F4203A00 push 003A20F4 ; /format = "shabi"
003A1009 |. FF15 A0203A00 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
003A100F |. 83C4 04 add esp,0x4
003A1012 |. C745 FC 00000>mov [local.1],0x0
003A1019 |. 8B45 FC mov eax,[local.1]
003A101C |. 83C0 01 add eax,0x1
003A101F |. 8945 FC mov [local.1],eax
003A1022 |. 8B4D FC mov ecx,[local.1]
003A1025 |. 83E9 01 sub ecx,0x1
003A1028 |. 894D FC mov [local.1],ecx
003A102B |. 33C0 xor eax,eax
003A102D |. 8BE5 mov esp,ebp
003A102F |. 5D pop ebp
003A1030 \. C3 retn


速度最大化的时候因为 i 没有实际用途,所以直接返回

1
2
3
4
5
01251000 >/$  68 F4202501   push    012520F4                         ; /format = "shabi"
01251005 |. FF15 A0202501 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
0125100B |. 83C4 04 add esp,0x4
0125100E |. 33C0 xor eax,eax
01251010 \. C3 retn
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
01201000 >/$  56            push    esi
01201001 |. 8B35 A0202001 mov esi,dword ptr ds:[<&MSVCR90.printf>] ; msvcr90.printf
01201007 |. 68 F4202001 push 012020F4 ; /format = "shabi"
0120100C |. FFD6 call esi ; \printf
0120100E |. 6A 01 push 0x1
01201010 |. 68 FC202001 push 012020FC ; ASCII "%d"
01201015 |. FFD6 call esi
01201017 |. 6A 00 push 0x0
01201019 |. 68 FC202001 push 012020FC ; ASCII "%d"
0120101E |. FFD6 call esi
01201020 |. 83C4 14 add esp,0x14 ; 堆栈平衡,2+2+1=20/4
01201023 |. 33C0 xor eax,eax
01201025 |. 5E pop esi
01201026 \. C3 retn

//添加打印i后的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
012B1000 >/$  51            push    ecx
012B1001 |. 56 push esi
012B1002 |. 8B35 A0202B01 mov esi,dword ptr ds:[<&MSVCR90.printf>] ; msvcr90.printf
012B1008 |. 57 push edi
012B1009 |. 68 F4202B01 push 012B20F4 ; /format = "shabi"
012B100E |. FFD6 call esi ; \printf
012B1010 |. 8B7C24 0C mov edi,dword ptr ss:[esp+0xC] ; msvcr90.__winitenv
012B1014 |. 47 inc edi
012B1015 |. 57 push edi
012B1016 |. 68 FC202B01 push 012B20FC ; ASCII "%d"
012B101B |. FFD6 call esi
012B101D |. 4F dec edi
012B101E |. 57 push edi
012B101F |. 68 FC202B01 push 012B20FC ; ASCII "%d"
012B1024 |. FFD6 call esi
012B1026 |. 83C4 14 add esp,0x14
012B1029 |. 5F pop edi
012B102A |. 33C0 xor eax,eax
012B102C |. 5E pop esi
012B102D |. 59 pop ecx
012B102E \. 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
int _tmain(int argc, _TCHAR* argv[])
{
printf("shabi");
int i;
i++;
printf("%d",i);
i--;
printf("%d",i);

int j=0;
j=i++;
printf("i++%d",j);
j=++i;
printf("++i%d",j);
j=i--;
printf("i--%d",j);
j=--i;
printf("--i%d",j);



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
01281000 >/$  55            push    ebp
01281001 |. 8BEC mov ebp,esp
01281003 |. 83EC 08 sub esp,0x8
01281006 |. 68 F4202801 push 012820F4 ; /format = "shabi"
0128100B |. FF15 A0202801 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
01281011 |. 83C4 04 add esp,0x4
01281014 |. 8B45 FC mov eax,[local.1]
01281017 |. 83C0 01 add eax,0x1
0128101A |. 8945 FC mov [local.1],eax
0128101D |. 8B4D FC mov ecx,[local.1]
01281020 |. 51 push ecx ; /<%d>
01281021 |. 68 FC202801 push 012820FC ; |format = "%d"
01281026 |. FF15 A0202801 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
0128102C |. 83C4 08 add esp,0x8
0128102F |. 8B55 FC mov edx,[local.1]
01281032 |. 83EA 01 sub edx,0x1
01281035 |. 8955 FC mov [local.1],edx
01281038 |. 8B45 FC mov eax,[local.1]
0128103B |. 50 push eax ; /<%d>
0128103C |. 68 00212801 push 01282100 ; |format = "%d"
01281041 |. FF15 A0202801 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
01281047 |. 83C4 08 add esp,0x8
0128104A |. C745 F8 00000>mov [local.2],0x0
01281051 |. 8B4D FC mov ecx,[local.1]
01281054 |. 894D F8 mov [local.2],ecx
01281057 |. 8B55 FC mov edx,[local.1]
0128105A |. 83C2 01 add edx,0x1
0128105D |. 8955 FC mov [local.1],edx
01281060 |. 8B45 F8 mov eax,[local.2]
01281063 |. 50 push eax ; /<%d>
01281064 |. 68 04212801 push 01282104 ; |format = "i++%d"
01281069 |. FF15 A0202801 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
0128106F |. 83C4 08 add esp,0x8
01281072 |. 8B4D FC mov ecx,[local.1]
01281075 |. 83C1 01 add ecx,0x1
01281078 |. 894D FC mov [local.1],ecx
0128107B |. 8B55 FC mov edx,[local.1]
0128107E |. 8955 F8 mov [local.2],edx
01281081 |. 8B45 F8 mov eax,[local.2]
01281084 |. 50 push eax ; /<%d>
01281085 |. 68 0C212801 push 0128210C ; |format = "++i%d"
0128108A |. FF15 A0202801 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
01281090 |. 83C4 08 add esp,0x8
01281093 |. 8B4D FC mov ecx,[local.1]
01281096 |. 894D F8 mov [local.2],ecx
01281099 |. 8B55 FC mov edx,[local.1]
0128109C |. 83EA 01 sub edx,0x1
0128109F |. 8955 FC mov [local.1],edx
012810A2 |. 8B45 F8 mov eax,[local.2]
012810A5 |. 50 push eax ; /<%d>
012810A6 |. 68 14212801 push 01282114 ; |format = "i--%d"
012810AB |. FF15 A0202801 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
012810B1 |. 83C4 08 add esp,0x8
012810B4 |. 8B4D FC mov ecx,[local.1]
012810B7 |. 83E9 01 sub ecx,0x1
012810BA |. 894D FC mov [local.1],ecx
012810BD |. 8B55 FC mov edx,[local.1]
012810C0 |. 8955 F8 mov [local.2],edx
012810C3 |. 8B45 F8 mov eax,[local.2]
012810C6 |. 50 push eax ; /<%d>
012810C7 |. 68 1C212801 push 0128211C ; |format = "--i%d"
012810CC |. FF15 A0202801 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
012810D2 |. 83C4 08 add esp,0x8
012810D5 |. 33C0 xor eax,eax
012810D7 |. 8BE5 mov esp,ebp
012810D9 |. 5D pop ebp
012810DA \. C3 retn

# do - while

1
2
3
4
5
6
7
8
9
10
11
12
13
int _tmain(int argc, _TCHAR* argv[])
{
int i = 0;
do
{
i++;
}
while (i<=10);
printf("%d",i);

return 0;
}

/O2

1
2
3
4
5
6
7
8
00071000 >/$  6A 0B         push    0xB                                      ; /<%d> = B (11.)
00071002 |. 68 F4200700 push 000720F4 ; |format = "%d"
00071007 |. FF15 A0200700 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
0007100D |. 83C4 08 add esp,0x8
00071010 |. 33C0 xor eax,eax
00071012 \. C3 retn


禁用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
> 00251000 >/$  55            push    ebp
> 00251001 |. 8BEC mov ebp,esp
> 00251003 |. 51 push ecx
> 00251004 |. C745 FC 00000>mov [local.1],0x0
> 0025100B |> 8B45 FC /mov eax,[local.1]
> 0025100E |. 83C0 01 |add eax,0x1
> 00251011 |. 8945 FC |mov [local.1],eax
> 00251014 |. 837D FC 0A |cmp [local.1],0xA
> 00251018 |.^ 7E F1 \jle short 0025100B
> 0025101A |. 8B4D FC mov ecx,[local.1]
> 0025101D |. 51 push ecx ; /<%d>
> 0025101E |. 68 F4202500 push 002520F4 ; |format = "%d"
> 00251023 |. FF15 A0202500 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
> 00251029 |. 83C4 08 add esp,0x8
> 0025102C |. 33C0 xor eax,eax
> 0025102E |. 8BE5 mov esp,ebp
> 00251030 |. 5D pop ebp
> 00251031 \. C3 retn


# 浮点指令

1
2
3
4
5
6
7
8
int _tmain(int argc, _TCHAR* argv[])
{
printf("shabi");
float a=8.123f;
a++;
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

012B1000 >/$ 55 push ebp
012B1001 |. 8BEC mov ebp,esp
012B1003 |. 51 push ecx
012B1004 |. 68 F4202B01 push 012B20F4 ; /format = "shabi"
012B1009 |. FF15 A0202B01 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
012B100F |. 83C4 04 add esp,0x4
012B1012 |. D905 08212B01 fld dword ptr ds:[_real]
012B1018 |. D95D FC fstp [local.1]
012B101B |. D945 FC fld [local.1]
012B101E |. DC05 00212B01 fadd qword ptr ds:[_real]
012B1024 |. D95D FC fstp [local.1]
012B1027 |. 33C0 xor eax,eax
012B1029 |. 8BE5 mov esp,ebp
012B102B |. 5D pop ebp
012B102C \. C3 retn


浮点寄存器 - fpu - 浮点运算单元

image-20221120195516311

精度为 80 位,10 字节

MMX - 用于处理多媒体功能

image-20221120195604566

st0 至 st7 80 位的两用寄存器 MMX - FPU

3.FLD,FSTP,FADD

fld dword ptr ds:[_real] //fld dword ptr ds:[0x12B2108]

类似于 push 指令,将数字放入 st 中

image-20221120200304416

image-20221120200412594

fstp [local.1]

类似于 pop 指令,将 st 中的值给 local.1

调用多次 fld 时,会使用多个 st 寄存器

fadd qword ptr ds:[_real] //fadd qword ptr ds:[0x12B2100]

类似于 add 指令,做加法 qword 64 位,8 各字

4.fsub

类似于 sub 指令

5.fmul

类似于乘法

6.fdiv

类似于除法

7.FILD 指令

整数入栈指令,将整数转为浮点数

1
2
3
4
5
6
7
8
int _tmain(int argc, _TCHAR* argv[])
{
printf("shabi");
float a=8.123f;
int b = 2;
a= a + b;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
012F1000 >/$  55            push    ebp
012F1001 |. 8BEC mov ebp,esp
012F1003 |. 83EC 08 sub esp,0x8
012F1006 |. 68 F4202F01 push 012F20F4 ; /format = "shabi"
012F100B |. FF15 A0202F01 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
012F1011 |. 83C4 04 add esp,0x4
012F1014 |. D905 FC202F01 fld dword ptr ds:[_real]
012F101A |. D95D FC fstp [local.1]
012F101D |. C745 F8 02000>mov [local.2],0x2
012F1024 |. DB45 F8 fild [local.2]
012F1027 |. D845 FC fadd [local.1]
012F102A |. D95D FC fstp [local.1]
012F102D >|. 33C0 xor eax,eax
012F102F |. 8BE5 mov esp,ebp
012F1031 |. 5D pop ebp
012F1032 \. C3 retn

image-20221120202918750

8.cvttsd2si eax,dword ptr ss:[ebp-4]

浮点转整数指令,运用截断处理将 mmx/m32 中的一个单精度浮点值转换成 r32 中的一个有符号的双字整数

# 位移指令

1. 逻辑位移

shr 逻辑右移指令

相当于整除 2,用 0 来补位

shl 逻辑右移指令

相当于乘 2,用 0 来补位,有可能移除

1
2
3
4
5
6
7
8
int _tmain(int argc, _TCHAR* argv[])
{
printf("shabi");
unsigned int i = 0x77883322;
i = i >> 8;
i = i >> 2;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
011D1000 >/$  55            push    ebp
011D1001 |. 8BEC mov ebp,esp
011D1003 |. 51 push ecx
011D1004 |. 68 F4201D01 push 011D20F4 ; /format = "shabi"
011D1009 |. FF15 A0201D01 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
011D100F |. 83C4 04 add esp,0x4
011D1012 |. C745 FC 22330>mov [local.1],0x3322
011D1019 |. 8B45 FC mov eax,[local.1]
011D101C |. C1E8 08 shr eax,0x8 ; 0x 00 77 88 33 ÷(2^8)
011D101F |. 8945 FC mov [local.1],eax
011D1022 |. 8B4D FC mov ecx,[local.1]
011D1025 |. C1E9 02 shr ecx,0x2 ; ÷ 4
011D1028 |. 894D FC mov [local.1],ecx
011D102B |. 33C0 xor eax,eax
011D102D |. 8BE5 mov esp,ebp
011D102F |. 5D pop ebp
011D1030 \. C3 retn

2. 算术位移

SAR 算术右移指令

​ SAR 右移时保留操作数符号,用符号位补

​ SHR 右移时用 0 部位

SAL 算术左移指令

​ SAL 与 SHL 功能一样

1
2
3
4
5
6
7
8
9
10
int _tmain(int argc, _TCHAR* argv[])
{
printf("shabi");
int i = -100000000;
i = i << 5;
printf("%d",i); //1094967296
getchar();
return 0;
}

3. 循环位移指令

ROL 循环左移

ROR 循环右移

# 逻辑运算符

逻辑或 ||

截断原理,如果前面为真,那么不在判断后面的。因此在代码优化层面,把可能性大的放在前面提高效率

按位或 |

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
01321000 >/$  55            push    ebp
01321001 |. 8BEC mov ebp,esp
01321003 |. 83EC 10 sub esp,0x10
01321006 |. 68 F4203201 push 013220F4 ; /format = "shabi"
0132100B |. FF15 A0203201 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
01321011 |. 83C4 04 add esp,0x4
01321014 |. C745 FC 05000>mov [local.1],0x5
0132101B |. C745 F8 03000>mov [local.2],0x3
01321022 |. 837D FC 00 cmp [local.1],0x0
01321026 |. 75 0F jnz short 01321037
01321028 |. 837D F8 00 cmp [local.2],0x0
0132102C |. 75 09 jnz short 01321037
0132102E |. C745 F0 00000>mov [local.4],0x0
01321035 |. EB 07 jmp short 0132103E
01321037 |> C745 F0 01000>mov [local.4],0x1
0132103E |> 8B45 F0 mov eax,[local.4]
01321041 |. 8945 F4 mov [local.3],eax
01321044 |. 33C0 xor eax,eax
01321046 |. 8BE5 mov esp,ebp
01321048 |. 5D pop ebp
01321049 \. C3 retn

逻辑与 &&

按位与 &

非运算 !

按位取反 ~

sete al // 取 ZF 位的值,保存到寄存器中

setne al //zf 取反保存到寄存器中

1
2
3
4
5
6
7
8
9
int _tmain(int argc, _TCHAR* argv[])
{
printf("shabi");
int a =5,b=4,c,d;

c = !a;
d = ~b;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
00FC1000 >/$  55            push    ebp
00FC1001 |. 8BEC mov ebp,esp
00FC1003 |. 83EC 10 sub esp,0x10
00FC1006 |. 68 F420FC00 push 00FC20F4 ; /format = "shabi"
00FC100B |. FF15 A020FC00 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
00FC1011 |. 83C4 04 add esp,0x4
00FC1014 |. C745 FC 05000>mov [local.1],0x5
00FC101B |. C745 F8 04000>mov [local.2],0x4
00FC1022 |. 33C0 xor eax,eax
00FC1024 |. 837D FC 00 cmp [local.1],0x0
00FC1028 |. 0F94C0 sete al
00FC102B |. 8945 F0 mov [local.4],eax
00FC102E |. 8B4D F8 mov ecx,[local.2]
00FC1031 |. F7D1 not ecx
00FC1033 |. 894D F4 mov [local.3],ecx
00FC1036 |. 33C0 xor eax,eax
00FC1038 |. 8BE5 mov esp,ebp
00FC103A |. 5D pop ebp
00FC103B \. C3 retn

异或运算

按位异或 ^

xor eax,eax //eax 清零

可以看成没有进位的加法

交换 a,b

1
2
3
4
5
6
7
8
9
a = a + b
b = a - b
a = a - b

//
a = a ^ b
b = a ^ b
a = a ^ b

# 字符操作相关指令

strcmp

需要将优化中的启动内部函数设为否,关闭优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
00F81000 >/$  55            push    ebp
00F81001 |. 8BEC mov ebp,esp
00F81003 |. 83EC 08 sub esp,0x8
00F81006 |. 68 F420F800 push 00F820F4 ; /format = "shabi"
00F8100B |. FF15 A420F800 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
00F81011 |. 83C4 04 add esp,0x4
00F81014 |. C745 F8 FC20F>mov [local.2],00F820FC ; ASCII "123456"
00F8101B |. C745 FC 0421F>mov [local.1],00F82104 ; ASCII "shabishabi"
00F81022 |. 8B45 FC mov eax,[local.1]
00F81025 |. 50 push eax ; /s2
00F81026 |. 8B4D F8 mov ecx,[local.2] ; |
00F81029 |. 51 push ecx ; |s1
00F8102A |. E8 19000000 call strcmp ; \strcmp
00F8102F |. 83C4 08 add esp,0x8
00F81032 |. 33C0 xor eax,eax
00F81034 |. 8BE5 mov esp,ebp
00F81036 |. 5D pop ebp
00F81037 \. C3 retn

strcmp

1
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
74D6B8E0 >  8B5424 04       mov     edx,dword ptr ss:[esp+0x4]
74D6B8E4 8B4C24 08 mov ecx,dword ptr ss:[esp+0x8]
74D6B8E8 F7C2 03000000 test edx,0x3
74D6B8EE 75 3C jnz short 74D6B92C
74D6B8F0 8B02 mov eax,dword ptr ds:[edx]
74D6B8F2 3A01 cmp al,byte ptr ds:[ecx]
74D6B8F4 75 2E jnz short 74D6B924
74D6B8F6 0AC0 or al,al
74D6B8F8 74 26 je short 74D6B920
74D6B8FA 3A61 01 cmp ah,byte ptr ds:[ecx+0x1]
74D6B8FD 75 25 jnz short 74D6B924
74D6B8FF 0AE4 or ah,ah
74D6B901 74 1D je short 74D6B920
74D6B903 C1E8 10 shr eax,0x10
74D6B906 3A41 02 cmp al,byte ptr ds:[ecx+0x2]
74D6B909 75 19 jnz short 74D6B924
74D6B90B 0AC0 or al,al
74D6B90D 74 11 je short 74D6B920
74D6B90F 3A61 03 cmp ah,byte ptr ds:[ecx+0x3]
74D6B912 75 10 jnz short 74D6B924
74D6B914 83C1 04 add ecx,0x4
74D6B917 83C2 04 add edx,0x4
74D6B91A 0AE4 or ah,ah
74D6B91C ^ 75 D2 jnz short 74D6B8F0
74D6B91E 8BFF mov edi,edi
74D6B920 33C0 xor eax,eax
74D6B922 C3 retn


每次取四个字节依次比较

SCASB

SCAS byte ptr es:[EDI] 用于查找 AL 的字符在 edi 中的位置

相当于

cmp byte ptr [edi],al

对标志位的影响相当于 sub 指令,还会修改 edi 的值

DF=0 ,inc edi

DF=1, dec edi

REPNE

repne scasb

ecx !=0 ,zf=0,重复执行后面指令,每执行一次 ecx-1

REPNE 与 REPNZ 是同一条指令不同助记符

// 通过 repne 计算字符串长度

1
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
int _tmain(int argc, _TCHAR* argv[])
{
printf("shabi");
char *s1 = "123456";
__asm { //cal length
mov al,0
mov edi,s1
mov ecx,-1
repnz scasb
mov eax,-1
sub eax,ecx

}
__asm { //find index
xor eax,rax
mov al,0x33
mov edi,s1
mov ecx,-1
repnz scasb
not ecx
dec ecx

}

std::string str1 = s1;
int haha = str1.find("3");

return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
00901000 >/$  55            push    ebp
00901001 |. 8BEC mov ebp,esp
00901003 |. 51 push ecx
00901004 |. 57 push edi
00901005 |. 68 F4209000 push 009020F4 ; /format = "shabi"
0090100A |. FF15 A0209000 call dword ptr ds:[<&MSVCR90.printf>] ; \printf
00901010 |. 83C4 04 add esp,0x4
00901013 |. C745 FC FC209>mov [local.1],009020FC ; ASCII "123456"
0090101A |. B0 00 mov al,0x0
0090101C |. 8B7D FC mov edi,[local.1]
0090101F |. B9 FFFFFFFF mov ecx,-0x1
00901024 |. F2:AE repne scas byte ptr es:[edi]
00901026 |. B8 FFFFFFFF mov eax,-0x1
0090102B |. 2BC1 sub eax,ecx
0090102D |. 33C0 xor eax,eax
0090102F |. 5F pop edi
00901030 |. 8BE5 mov esp,ebp
00901032 |. 5D pop ebp
00901033 \. C3 retn


SCASB SCASW SCASD

image-20221124212255214

mov ax,L’7’

实例运用

1. 计算字符串长度

2. 定位特定字符串位置(索引)

3. 在内存中定位一串特征码

REPNZ/REPNE 与 SCASB 指令结合使用,表示当串未结束 (ECX!=0),且当对应串元素不相同 (ZF=0) 时,继续重复执行串比较指令

REPE/REPZ 和 CMPSB,CMPSW,CMPSD

1.CMPS

cmps byte ptr [edi], byte ptr [esi]

对标志位的影响相当于 sub 指令,同时还会修改 edi 和 esi 的值

如果 df 为 0,则 edi,esi 按相对大小递增

df 为 1,递减

2.REPE、REPZ

repe/repz cmpsb 当 ecx!=0,zf=1,重复执行后面指令,每执行一次 ecx-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
__asm {
xor al,al
mov edi,s1
mov ecx,-1
repnz scasb //finish position
not ecx //cal length

mov edi,s1
mov esi,s2
repz cmpsb //compare equal

xor al,al
sete al
cmp al,1
jnz result1
cmp ecx,0
jz result2
}
result1:
printf("S1!=S2");
goto end
result2:
printf("s1==s2");
end:
getchar();

汇编编写比较函数

__declspec (naked) 纯汇编编译函数,不添加寄存器保护和堆栈平衡代码

窄字节版

1
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
__declspec(naked) int strcmpa(char *s1, char *s2)
{
__asm{
push ebp //esp-4-4
mov ebp,esp //s1:ebp+4+4 s2:ebp+4+8
push ecx
push edi
push esi
push edx


xor al,al
mov edi,s1
CLD
mov ecx,-1
repnz scasb //finish position
not ecx //cal length

mov edi,s1
mov esi,s2
repz cmpsb //compare equal

xor eax,eax
xor edx,edx
mov al,[edi-1]
mov dl,[esi-1]
sub eax,edx
pop edx
pop esi
pop edi
pop ecx
pop ebp
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
__declspec(naked) int strcmpW(wchar_t *s1, wchar_t *s2)
{
__asm{
push ebp //esp-4-4
mov ebp,esp //s1:ebp+4+4 s2:ebp+4+8
push ecx
push edi
push esi
push edx


xor ax,ax
mov edi,s1
mov ecx,-1
CLD
repnz scasw //finish position
not ecx //cal length

mov edi,s1
mov esi,s2
repz cmpsw //compare equal

xor eax,eax
xor edx,edx
mov al,[edi-2]
mov dl,[esi-2]
sub eax,edx
pop edx
pop esi
pop edi
pop ecx
pop ebp
retn
}
}

ESP+4 是 call 的下一个命令的地址

ESP+8 是外部传进来的第一个变量

image-20221126203249743

STD/CLD

修改 DF

STD DF=1

CLD DF=0

串存储和加载指令

1. 串存储指令 STOSB STOSW STOSD

stos btye ptr [edi]

相当于 mov byte ptr [edi],al

rep stosb //rep stos byte ptr [edi]

用 al 的值填充 byte ptr [edi],每次 ecx-1,edi+1

1
2
3
4
5
6
7
8
9
#include <cstdio>

int main()
{
int a[0x22];
a[0] = 1;
a[0x12] = 222;
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
013F13A0    55              push    ebp
013F13A1 8BEC mov ebp,esp
013F13A3 81EC 50010000 sub esp,0x150
013F13A9 53 push ebx
013F13AA 56 push esi
013F13AB 57 push edi
013F13AC 8DBD B0FEFFFF lea edi,dword ptr ss:[ebp-0x150]
013F13B2 B9 54000000 mov ecx,0x54
013F13B7 B8 CCCCCCCC mov eax,0xCCCCCCCC
013F13BC F3:AB rep stos dword ptr es:[edi]
013F13BE C785 74FFFFFF 0>mov dword ptr ss:[ebp-0x8C],0x1
013F13C8 C745 BC DE00000>mov dword ptr ss:[ebp-0x44],0xDE
013F13CF 33C0 xor eax,eax
013F13D1 52 push edx
013F13D2 8BCD mov ecx,ebp
013F13D4 50 push eax
013F13D5 8D15 EC133F01 lea edx,dword ptr ds:[0x13F13EC]
013F13DB E8 A7FCFFFF call 013F1087

手动初始化数组

1
2
3
4
5
6
__asm {
mov ecx,0x22
xor eax,eax
lea edi,a
rep stosd
}

debug 找 main 函数

退出函数

1
013F199F    FF15 7C823F01   call    dword ptr ds:[<&MSVCR90D.exit>]                      ; msvcr90d.exit

退出函数的上一个函数即使 main 函数

release 中程序中断位置不在 main 上,在 jmp 上才时 main

image-20221126213639997

image-20221126213710284

看 exit 找 main 或者看 main 的 argv ,argc 参数找 main 也可以

这边没删 pdb,删了一样

同时注意,main 有三个参数,argv 参数个数,argc 参数数组,envp 环境变量

envp 是二级指针,需要两次寻址,存放系统环境变量

image-20221127190321276

argv 也是二级指针,存在文件路径

image-20221127190413354

argc 存放 argv 中参数个数

image-20221127190503930

串载入指令

loadSB ,LOADSW,LOASSD

相当于 mov al,byte ptr [esi] 将内存中数据放到寄存器中

rep loadsb

rep loads word ptr [esi]

用 byte ptr [esi] 填充 al,每次 ecx-1,esi+1

循环控制指令

loop start

1.ecx-1

2.ecx!=0 转移到标号处循环执行

3. 直至 ecx=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
int _tmain(int argc, _TCHAR* argv[])
{
int i = 100;
char *s = "%d";
__asm mov ecx,i

__asm {
start:
push ecx

push ecx
mov eax,s
push eax
call dword ptr [printf]
add esp,8

pop ecx

loop start

}
getchar();
return 0;
}

条件置位指令

sete setz

sete al 取 zf 标志位的值

setnz 、 setne

将 zf 的值取反保存

setg al

大于比较时 ZF =0&& sf=0 && OF=1 时 al=1

setl al

小于比较

sf=1 || of=1 ,al=1

setge

操作数可以是一个字节存储单元,可以是一个字节宽度寄存器

>= 时,操作数值 1,否则为 0,一般与 cmp 结合使用

SF=OF ,操作数置 1

setle

操作数可以是一个字节存储单元,可以是一个字节宽度寄存器

<= 时,操作数值 1,否则为 0,一般与 cmp 结合使用

ZF = 1 || sf!= of 操作数置 1

# 实战

# 1. 游戏 call

调用 MessageBoxA 函数 call

1. 找到 messageboxA call 地址

1
76C4FD1E >  8BFF            mov     edi,edi

2. 找到 MessageBoxA call 参数

1
2
3
4
5
6
7
8
9
10
11
12
int MessageBox(
[in, optional] HWND hWnd,
[in, optional] LPCTSTR lpText,
[in, optional] LPCTSTR lpCaption,
[in] UINT uType
);

我们用四个push 0,因为都可以传0
push 0
push 0
push 0
push 0

3. 注入代码测试调用 MessageBoxA call

image-20221127205600961

image-20221127205625663

或者在第二个和第三个参数传入字符指针,,第一个参数改变窗口格式

1
2
3
4
5
push 0
push 01005148
push 01005148
push 0
call 76C4FD1E

2. 窗口菜单类 call 分析

image-20221127212738634

follow poc,定位窗口处理函数位置

image-20221127214017807

窗口处理函数原型

1
2
3
4
5
6
LRESULT CALLBACK WindowProc(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
);

菜单被点击的时候发送的是 WM_COMMAND 消息

当点击菜单的时候,WindowProc 会被系统调用,

uMsg = WM_COMMAND

wPARAM = 窗口菜单的 id

在 WindowProc 函数下条件断点

条件时:uMSG == WM_COMMAND

如 edx == WM_COMMAND 条件断点

点击菜单时条件断点被中断

image-20221128192802977

点击初级时 edx == VMCOMMAND == 0x111

1
2
3
4
5
6
7
8
9
01001BC9  /.  55            push    ebp
01001BCA |. 8BEC mov ebp,esp
01001BCC |. 83EC 40 sub esp,0x40
01001BCF |. 8B55 0C mov edx,[arg.2] ; uMsg
01001BD2 >|. 8B4D 14 mov ecx,[arg.4] ; edx == WM_COMMAND
01001BD5 |. 53 push ebx
01001BD6 |. 56 push esi
01001BD7 |. 33DB xor ebx,ebx
01001BD9 |. 57 push edi

分析出各项菜单被点击时四个参数的值

wParam(菜单 ID)参数:

​ 初级菜单 ID:

1
2
3
4
EBP+8    >|0011029C   HWND hwnd
EBP+C >|00000111 uMsg
EBP+10 >|00000209 wParam
EBP+14 >|00000000 lParam

​ 中级菜单 ID:

1
2
3
4
EBP+8    >|0011029C
EBP+C >|00000111
EBP+10 >|0000020A
EBP+14 >|00000000

​ 高级菜单 ID:

1
2
3
4
EBP+8    >|0011029C
EBP+C >|00000111
EBP+10 >|0000020B
EBP+14 >|00000000

image-20221128194129162

注入代码调试

1
2
3
4
5
push 0
push 0x20b
push 0x111
push 0x001A03DA
call 0x01001BC9

image-20221128204020367

# 2. 分析游戏基地址

基地址概念

全局变量,字符常量等的地址

1. 使用 CE 内存查找工具查找数据的地址

2. 在 od 中验证是否为基地址

​ 通过下内存断点方式验证

通过 ce 找到基地址,new scan 后改变数值进行 next scan

1. 首次先让要查找的数据稳定在某个范围 (或者某个精确的值)

2. 改变要查找的数据,根据变化筛选出数据的地址

image-20221128205517107

在扫雷中,小旗子剩余数量的基地址为 01005194

1
0100346E  |.  0105 94510001 add     dword ptr ds:[0x1005194],eax

立即数寻址的一般都是基地址

# 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
void CsaoleiDlg::OnBnClickedButton1()
{
HWND hwnd = ::FindWindow(NULL, _T("扫雷")); //找到扫雷窗口
if (NULL == hwnd) //处理未找到时操作
{
::MessageBox(NULL, _T("未打开"), _T("错误"), MB_OK);
return;
}
::SendMessage(hwnd, WM_COMMAND, 0x20A, 0); //发送WindowProc的参数

}

void CsaoleiDlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
HWND hwnd = ::FindWindow(NULL, _T("扫雷")); //找到扫雷窗口
if (NULL == hwnd) //处理未找到时操作
{
::MessageBox(NULL, _T("未打开"), _T("错误"), MB_OK);
return;
}
::SendMessage(hwnd, WM_COMMAND, 0x20B, 0); //发送WindowProc的参数
}

void CsaoleiDlg::OnBnClickedButton3()
{
// TODO: 在此添加控件通知处理程序代码
while (TRUE)
{
OnBnClickedButton1();
Sleep(1000);
OnBnClickedButton2();
Sleep(1000);
}
}
void CsaoleiDlg::OnBnClickedButton4()
{
// TODO: 在此添加控件通知处理程序代码
/*
BOOL ReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead
);
HANDLE OpenProcess(
DWORD fdwAccess,
BOOL fInherit,
DWORD IDProcess
);
DWORD GetWindowThreadProcessId(
HWND hWnd,
LPDWORD lpdwProcessId
);
*/
DWORD pid;
HWND hWnd = ::FindWindow(NULL, _T("扫雷")); //找到扫雷窗口
if (NULL == hWnd) //处理未找到时操作
{
::MessageBox(NULL, _T("未打开"), _T("错误"), MB_OK);
return;
}

GetWindowThreadProcessId(hWnd, &pid); //通过窗口句柄拿到进程ID
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); //通过进程ID拿到进程句柄
if (NULL == hProcess)
{
::MessageBox(NULL, _T("未打开"), _T("错误"), MB_OK);
return;
}
//m_editbase1 是编辑框的绑定变量,类别为value,变量类型为int,变量名为m_editbase1
ReadProcessMemory(hProcess, (LPCVOID)0x1005194, &m_editbase1, sizeof(m_editbase1), &pid);

UpdateData(FALSE);
}

image-20221130104547356

# 4. 扫雷游戏辅助编写

1. 雷区数据分析

中级 char a [24][32] //ReadProcessMemory

数据雷区基地址 0x01005361 == 扫雷.exe+5361

找基地址:new scan 时使用未知数,之后每一次 next scan 根据 change 或者 unchange 进行 next scan

初级 9*9 ,内存中隔行存放,因为每一行占两行内存,10 代表结束

只有点击第一下之后才开始布雷,winxp 扫雷特性

0x8F 是雷,40 空白,40 对应 1,0x10 代表行 / 列结束标识

宽基地址 0x01005334

高基地址 0x01005338

image-20221130111727184

1
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
void CsaoleiDlg::OnBnClickedButton1()
{
HWND hwnd = ::FindWindow(NULL, _T("扫雷")); //找到扫雷窗口
if (NULL == hwnd) //处理未找到时操作
{
::MessageBox(NULL, _T("未打开"), _T("错误"), MB_OK);
return;
}
::SendMessage(hwnd, WM_COMMAND, 0x20A, 0); //发送WindowProc的参数

}

void CsaoleiDlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
HWND hwnd = ::FindWindow(NULL, _T("扫雷")); //找到扫雷窗口
if (NULL == hwnd) //处理未找到时操作
{
::MessageBox(NULL, _T("未打开"), _T("错误"), MB_OK);
return;
}
::SendMessage(hwnd, WM_COMMAND, 0x20B, 0); //发送WindowProc的参数
}

void CsaoleiDlg::OnBnClickedButton3()
{
// TODO: 在此添加控件通知处理程序代码
while (TRUE)
{
OnBnClickedButton1();
Sleep(1000);
OnBnClickedButton2();
Sleep(1000);
}
}

void CsaoleiDlg::OnBnClickedButton4()
{
// TODO: 在此添加控件通知处理程序代码
/*
BOOL ReadProcessMemory(
HANDLE hProcess,
LPCVOID lpBaseAddress,
LPVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead
);
HANDLE OpenProcess(
DWORD fdwAccess,
BOOL fInherit,
DWORD IDProcess
);
DWORD GetWindowThreadProcessId(
HWND hWnd,
LPDWORD lpdwProcessId
);
*/
DWORD pid;
HWND hWnd = ::FindWindow(NULL, _T("扫雷")); //找到扫雷窗口
if (NULL == hWnd) //处理未找到时操作
{
::MessageBox(NULL, _T("未打开"), _T("错误"), MB_OK);
return;
}

GetWindowThreadProcessId(hWnd, &pid); //通过窗口句柄拿到进程ID
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); //通过进程ID拿到进程句柄
if (NULL == hProcess)
{
::MessageBox(NULL, _T("未打开"), _T("错误"), MB_OK);
return;
}
ReadProcessMemory(hProcess, (LPCVOID)0x1005194, &m_editbase1, sizeof(m_editbase1), &pid);

UpdateData(FALSE);
}

void CsaoleiDlg::OnBnClickedButton5()
{
// TODO: 在此添加控件通知处理程序代码
// TODO: 在此添加控件通知处理程序代码
DWORD pid;
HWND hWnd = ::FindWindow(NULL, _T("扫雷")); //找到扫雷窗口
if (NULL == hWnd) //处理未找到时操作
{
::MessageBox(NULL, _T("未打开"), _T("错误"), MB_OK);
return;
}

GetWindowThreadProcessId(hWnd, &pid); //通过窗口句柄拿到进程ID
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); //通过进程ID拿到进程句柄
if (NULL == hProcess)
{
::MessageBox(NULL, _T("未打开"), _T("错误"), MB_OK);
return;
}

/*
> 0x8F是雷,40空白,40对应1,0x10代表行/列结束标识
>
> 宽基地址 0x01005334
>
> 高基地址 0x01005338
*/

unsigned char gamedata[24][32] = { 0 };
if (!ReadProcessMemory(hProcess, (LPCVOID)0x01005361, &gamedata, 32*24, &pid))
{
::MessageBox(NULL, _T("未打开12"), _T("错误"), MB_OK);
return;
}
int dwHigth = 0;
m_strshowdata.Empty();
if (!ReadProcessMemory(hProcess, (LPCVOID)0x01005338, &dwHigth, sizeof(dwHigth), &pid))
{
::MessageBox(NULL, _T("未打开13"), _T("错误"), MB_OK);
return;
}
CString strTemp = _T("");
//第一个格子位置
short gamex = 20;
short gamey = 60;
unsigned short xypos[2] = { 0 };
for (int i = 0; i < dwHigth; i++)
{
for (int j = 0; j < 32; j++)
{
if (0x10 == gamedata[i][j])
break;
xypos[0] = gamex + j * 16;
xypos[1] = gamey + i * 16;
if (0x8F != gamedata[i][j])
{
::PostMessage(hWnd, WM_LBUTTONDOWN,MK_LBUTTON,*(int *)xypos);
::PostMessage(hWnd, WM_LBUTTONUP,0,*(int *)xypos);
}
strTemp.Format(_T("%02X "), gamedata[i][j]);
m_strshowdata += strTemp;
}
m_strshowdata += _T("\r\n");
}

UpdateData(FALSE);
}

# 5. 植物大战僵尸

1. 找阳光地址和基地址

某一个程序每次运行都不改变的地址(基地址)里面存着阳光的地址

我们需要找到不会改变的地址,通过偏移拿到阳光的地址

1. 通过 ce 找到阳光地址,并且在 od 中定位

dd 0F2EE158

2. 下内存写入断点

要是 CE 找的地址在 OD 中没有这个地址的话请在 CE 地址右键谁改写了此地址然后对照着到 OD 找

image-20221201211117079

第一个偏移地址

阳光地址 0EDD32F8

当前阳光地址 = eax + 0x5560 eax=0EDCDD98

00430A11 |. 0188 60550000 add dword ptr [eax+5560], ecx

image-20221202195211747

第二个偏移地址,找到谁改了 eax=0EDCDD98 的值

还是去下内存断点

00538090 |> \8B01 |mov eax, dword ptr [ecx] ; PlantsVs.00656CA8

ecx = 0EDCDD98

eax = [ecx+5560]

第三个偏移地址

0053807D |. 8B49 08 |mov ecx, dword ptr [ecx+8]

eax = [[ecx+8]+5560] 0EDCDD98

基地址 [[6A9EC0]+768]+5560

2. 分析金币基地址

通过 ce 工具收集游戏金币基地址

可能和金币 1:1,也可能是其他比例

金币显示的值不是数字 1:1 对应关系,我们只能通过范围搜素定位金币地址

金币的地址不是绿色的基地址所有,所以我们需要找到基地址 + 偏移地址表示他

第一个偏移地址 06BA60A0

0041A3BB - 8B 41 28 - mov eax,[ecx+28] ECX=06BA6078

金币的地址 = ecx + 28

第二个偏移地址

0040EB41 - 8B 88 2C080000 - mov ecx,[eax+0000082C] EAX=02101E48

金币地址 = [eax+0000082c]+28

第三个偏移地址

00467B00 - 8B 0D C09E6A00 - mov ecx,[006A9EC0]

金币地址 =[[006A9EC0]+82c]+28

3. 分析植物安放冷却时间

第一步

1
2
3
4
5
6
7
8
9
10
11
0048728C - 83 47 24 01           - add dword ptr [edi+24],01 { 1 }
00487290 - 8B 47 24 - mov eax,[edi+24]
00487293 - 3B 47 28 - cmp eax,[edi+28]
00487296 - 7E 14 - jle 004872AC
00487298 - C7 47 24 00000000 - mov [edi+24],00000000 { 0 }
0048729F - C6 47 49 00 - mov byte ptr [edi+49],00 { 0 }
004872A3 - C6 47 48 01 - mov byte ptr [edi+48],01 { 1 }
004872A7 - E8 E4FEFFFF - call 00487190
004872AC - 8B 47 3C - mov eax,[edi+3C]

0BE14EC0

CD 计数器地址 = edi + 24

CD 计数器上线 = edi + 28

思路一:该计数器上限的值,缩短冷却时间

思路二:改写代码

1
2
3
4
5
6
7
8
9
10
11
0048728C - 83 47 24 01           - add dword ptr [edi+24],01 { 1 }
00487290 - 8B 47 24 - mov eax,[edi+24]
00487293 - 3B 47 28 - cmp eax,[edi+28]
00487296 - 90 - nop
00487297 - 90 - nop
00487298 - C7 47 24 00000000 - mov [edi+24],00000000 { 0 }
0048729F - C6 47 49 00 - mov byte ptr [edi+49],00 { 0 }
004872A3 - C6 47 48 01 - mov byte ptr [edi+48],01 { 1 }
004872A7 - E8 E4FEFFFF - call 00487190
004872AC - 8B 47 3C - mov eax,[edi+3C]

3. 分析大嘴花吞噬时间代码

1. 定位大嘴花吞噬时间计数器变量的地址

2. 通过 ce 或者 od 定位谁访问了这个地址,从而定位到判断大嘴花吞噬冷却时间的代码

00461561 - 83 7F 54 00 - cmp dword ptr [edi+54],00

00461565 - 75 5F - jne 004615C6

4. 分析种植植物 call

思路:找出安放植物的 call

1. 通过鼠标右键选中植物的地址

2. 分析 call 代码参数及堆栈

1
2
3
4
5
6
7
8
00410A91 - 8B 40 28              - mov eax,[eax+28]
00410A94 - 52 - push edx
00410A95 - 50 - push eax
00410A96 - 8B 44 24 20 - mov eax,[esp+20]
00410A9A - 57 - push edi
00410A9B - 55 - push ebp
00410A9C - E8 7FC6FFFF - call 0040D120

1
2
3
4
5
6
push -1
push 3
mov eax,1
push 2
push 0EC01BD0
call 0040D120

3. 注入代码,将自己写的植物安放的汇编代码注入到游戏进程空间中,验证

1
2
3
4
5
6
7
push -1     ;固定的值
push 2 ;植物类型id,樱桃是2
mov eax,4 ;x
push 4 ;y
push 147E5FF8 ;[[6A9EC0]+768]
push 0040D120

坐标范围为 [4][8] = 5 * 9 = 45

1
2
3
4
5
6
7
8
push -1     ;固定的值
push 2 ;植物类型id,樱桃是2,坚果3
mov eax,0 ;y 0-4
push 4 ;x 0-8
mov ebx,[6A9EC0]
mov ebx,[ebx+768]
push ebx
push 0040D120
1
2
3
4
5
6
7
8
push -1
push 2
mov eax,0
push 2
mov ebx,[6A9EC0]
mov ebx,[ebx+768]
push ebx
call 0040D120

5. 远程注入代码

1. 打开目标进程 OpenProcess,获取目标进程句柄

2. 在目标进程分配内存空间 VirtualAllocEx

3. 往分配的内存空间中写入代码

4. 远程调用 CreateRemoteThread 执行目标进程指定地址的代码

5. 等待远程线程执行完成 WaitForSingleObject

6. 释放目标进程空间 VirtualFreeEx

7. 关闭目标进程句柄

植物血量

后台运行 有一个标识 1,0

关键代码位置 0054E1C2, 0F 95 C0 -> B1 01 90

毁灭菇

image-20221210004852205

image-20221210004902774

排错思路:

image-20221210211445273

到 od 中找到对应位置,查看我们插入的额汇编是否正确

冰冻

土豆

1
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
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
// plantDlg.cpp : 实现文件
//

#include "stdafx.h"
#include "plant.h"
#include "plantDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// 用于应用程序“关于”菜单项的 CAboutDlg 对话框

class CAboutDlg : public CDialog
{
public:
CAboutDlg();

// 对话框数据
enum { IDD = IDD_ABOUTBOX };

protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持

// 实现
protected:
DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// CplantDlg 对话框




CplantDlg::CplantDlg(CWnd* pParent /*=NULL*/)
: CDialog(CplantDlg::IDD, pParent)
, m_edit_sun(9999)
, m_edit_gold(9999)
, m_nocd(FALSE)
, m_bigmouth_nocd(FALSE)
, m_x(0)
, m_y(0)
, m_noblood(FALSE)
, no_run(FALSE)
, no_pit(FALSE)
, m_nomubei(FALSE)
, m_tudou(FALSE)
, m_frozen(FALSE)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CplantDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
DDX_Text(pDX, IDC_EDIT1, m_edit_sun);
DDX_Text(pDX, IDC_EDIT2, m_edit_gold);
DDX_Check(pDX, IDC_CHECK1, m_nocd);
DDX_Check(pDX, IDC_CHECK2, m_bigmouth_nocd);
DDX_Text(pDX, IDC_EDIT3, m_x);
DDX_Text(pDX, IDC_EDIT4, m_y);
DDX_Control(pDX, IDC_COMBO1, m_cbPlantsChoose);
DDX_Check(pDX, IDC_CHECK3, m_noblood);
DDX_Check(pDX, IDC_CHECK4, no_run);
DDX_Check(pDX, IDC_CHECK5, no_pit);
DDX_Check(pDX, IDC_CHECK6, m_nomubei);
DDX_Check(pDX, IDC_CHECK7, m_tudou);
DDX_Check(pDX, IDC_CHECK8, m_frozen);
}

BEGIN_MESSAGE_MAP(CplantDlg, CDialog)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
//}}AFX_MSG_MAP
ON_BN_CLICKED(IDC_BUTTON1, &CplantDlg::OnBnClickedButton1)
ON_BN_CLICKED(IDC_BUTTON2, &CplantDlg::OnBnClickedButton2)
ON_BN_CLICKED(IDC_BUTTON3, &CplantDlg::OnBnClickedButton3)
ON_BN_CLICKED(IDC_BUTTON4, &CplantDlg::OnBnClickedButton4)
ON_BN_CLICKED(IDC_CHECK1, &CplantDlg::OnBnClickedCheck1)
ON_BN_CLICKED(IDC_CHECK2, &CplantDlg::OnBnClickedCheck2)
ON_BN_CLICKED(IDC_BUTTON5, &CplantDlg::OnBnClickedButton5)
ON_BN_CLICKED(IDC_BUTTON6, &CplantDlg::OnBnClickedButton6)
ON_BN_CLICKED(IDC_CHECK3, &CplantDlg::OnBnClickedCheck3)
ON_BN_CLICKED(IDC_CHECK4, &CplantDlg::OnBnClickedCheck4)
ON_BN_CLICKED(IDC_CHECK5, &CplantDlg::OnBnClickedCheck5)
ON_BN_CLICKED(IDC_CHECK6, &CplantDlg::OnBnClickedCheck6)
ON_BN_CLICKED(IDC_BUTTON7, &CplantDlg::OnBnClickedButton7)
ON_BN_CLICKED(IDC_CHECK7, &CplantDlg::OnBnClickedCheck7)
ON_BN_CLICKED(IDC_CHECK8, &CplantDlg::OnBnClickedCheck8)
END_MESSAGE_MAP()


// CplantDlg 消息处理程序

BOOL CplantDlg::OnInitDialog()
{
CDialog::OnInitDialog();

// 将“关于...”菜单项添加到系统菜单中。

// IDM_ABOUTBOX 必须在系统命令范围内。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);

enableDebugPriv();

CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}

// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
// 执行此操作
SetIcon(m_hIcon, TRUE); // 设置大图标
SetIcon(m_hIcon, FALSE); // 设置小图标

// TODO: 在此添加额外的初始化代码
addPlantsToCombox();

return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
}

void CplantDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}

// 如果向对话框添加最小化按钮,则需要下面的代码
// 来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
// 这将由框架自动完成。

void CplantDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用于绘制的设备上下文

SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

// 使图标在工作区矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;

// 绘制图标
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialog::OnPaint();
}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CplantDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}

DWORD CplantDlg::FindGameProcessidByWndTitle(CString strTitle)
{
DWORD pid = 0;
HWND hWnd = ::FindWindow(NULL, strTitle.GetBuffer()); //找到窗口
if (NULL == hWnd) //处理未找到时操作
{
::MessageBox(NULL, _T("未找到指定游戏"), _T("错误"), MB_OK);
return 0;
}

GetWindowThreadProcessId(hWnd, &pid); //通过窗口句柄拿到进程ID
return pid;
}


void CplantDlg::OnBnClickedButton1()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
DWORD dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess) //处理未找到时操作
{
::MessageBox(NULL, _T("进程打开失败"), NULL, MB_OK);
return;
}
// 计算基地址 [[6A9EC0]+768]+5560
DWORD dwTemp = 0;
if (!ReadProcessMemory(hProcess, (LPCVOID)0x6A9EC0, (LPVOID)&dwTemp, sizeof(DWORD), &dwPid))
{
::MessageBox(NULL, _T("读取数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}

dwTemp += 0x768;
if (!ReadProcessMemory(hProcess, (LPVOID)dwTemp, (LPVOID)&dwTemp, sizeof(DWORD), &dwPid))
{
::MessageBox(NULL, _T("读取数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
DWORD dwsun = 99999;
dwTemp += 0x5560;
if (!::WriteProcessMemory(hProcess, (LPVOID)(dwTemp), &dwsun, sizeof(DWORD), NULL))
{
::MessageBox(NULL, _T("写入数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
CloseHandle(hProcess);
}




void CplantDlg::OnBnClickedButton2()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
DWORD dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess) //处理未找到时操作
{
::MessageBox(NULL, _T("进程打开失败"), NULL, MB_OK);
return;
}
// 计算基地址 [[006A9EC0]+82c]+28
DWORD dwTemp = 0;
if (!ReadProcessMemory(hProcess, (LPCVOID)0x6A9EC0, (LPVOID)&dwTemp, sizeof(DWORD), &dwPid))
{
::MessageBox(NULL, _T("读取数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}

dwTemp += 0x82c;
if (!ReadProcessMemory(hProcess, (LPVOID)dwTemp, (LPVOID)&dwTemp, sizeof(DWORD), &dwPid))
{
::MessageBox(NULL, _T("读取数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
DWORD dwGold = 99999;
dwTemp += 0x28;
if (!::WriteProcessMemory(hProcess, (LPVOID)(dwTemp), &dwGold, sizeof(DWORD), NULL))
{
::MessageBox(NULL, _T("写入数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
CloseHandle(hProcess);
}

void CplantDlg::OnBnClickedButton3()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
DWORD dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess) //处理未找到时操作
{
::MessageBox(NULL, _T("进程打开失败"), NULL, MB_OK);
return;
}
// 计算基地址 [[6A9EC0]+768]+5560
DWORD dwTemp = 0;
if (!ReadProcessMemory(hProcess, (LPCVOID)0x6A9EC0, (LPVOID)&dwTemp, sizeof(DWORD), &dwPid))
{
::MessageBox(NULL, _T("读取数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}

dwTemp += 0x768;
if (!ReadProcessMemory(hProcess, (LPVOID)dwTemp, (LPVOID)&dwTemp, sizeof(DWORD), &dwPid))
{
::MessageBox(NULL, _T("读取数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
DWORD dwsun = m_edit_sun;
dwTemp += 0x5560;
if (!::WriteProcessMemory(hProcess, (LPVOID)(dwTemp), &dwsun, sizeof(DWORD), NULL))
{
::MessageBox(NULL, _T("写入数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
CloseHandle(hProcess);
UpdateData(FALSE);

}

void CplantDlg::OnBnClickedButton4()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
DWORD dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess) //处理未找到时操作
{
::MessageBox(NULL, _T("进程打开失败"), NULL, MB_OK);
return;
}
// 计算基地址 [[006A9EC0]+82c]+28
DWORD dwTemp = 0;
if (!ReadProcessMemory(hProcess, (LPCVOID)0x6A9EC0, (LPVOID)&dwTemp, sizeof(DWORD), &dwPid))
{
::MessageBox(NULL, _T("读取数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}

dwTemp += 0x82c;
if (!ReadProcessMemory(hProcess, (LPVOID)dwTemp, (LPVOID)&dwTemp, sizeof(DWORD), &dwPid))
{
::MessageBox(NULL, _T("读取数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
DWORD dwGold = m_edit_gold;;
dwTemp += 0x28;
if (!::WriteProcessMemory(hProcess, (LPVOID)(dwTemp), &dwGold, sizeof(DWORD), NULL))
{
::MessageBox(NULL, _T("写入数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
CloseHandle(hProcess);
UpdateData(FALSE);
}

void CplantDlg::OnBnClickedCheck1()
{
// TODO: 在此添加控件通知处理程序代码
// TODO: 在此添加控件通知处理程序代码
// 00487296 - 7E 14 - jle 004872AC 植物cd判断跳转代码
UpdateData(TRUE);
DWORD dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess) //处理未找到时操作
{
::MessageBox(NULL, _T("进程打开失败"), NULL, MB_OK);
return;
}

UCHAR buf[2] = { 0 };
if (m_nocd)
{
//nop nop
//禁用cd
buf[0] = 0x90;
buf[1] = 0x90;
}
else
{
//7e 14
//启用cd时间
buf[0] = 0x7E;
buf[1] = 0x14;
}


if (!::WriteProcessMemory(hProcess, (LPVOID)(0x00487296), buf, sizeof(buf), NULL))
{
::MessageBox(NULL, _T("写入数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
CloseHandle(hProcess);
UpdateData(FALSE);
}

void CplantDlg::OnBnClickedCheck2()
{
// TODO: 在此添加控件通知处理程序代码
//00461565 - 75 5F - jne 004615C6
UpdateData(TRUE);
DWORD dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess) //处理未找到时操作
{
::MessageBox(NULL, _T("进程打开失败"), NULL, MB_OK);
return;
}

UCHAR buf[2] = { 0 };
if (m_bigmouth_nocd)
{
//nop nop
//禁用cd
buf[0] = 0x90;
buf[1] = 0x90;
}
else
{
//7e 14
//启用cd时间
buf[0] = 0x75;
buf[1] = 0x5f;
}


if (!::WriteProcessMemory(hProcess, (LPVOID)(0x00461565), buf, sizeof(buf), NULL))
{
::MessageBox(NULL, _T("写入数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
CloseHandle(hProcess);
UpdateData(FALSE);
}

# if 0
// 裸函数,不需要编译器添加任何代码
__declspec(naked) void putPlant()
{
__asm
{
pushad
push -1
push 2
mov eax, 4
push 4
mov ebx, ds:[0x6A9EC0]
mov ebx, ds:[ebx + 0x768]
push ebx
mov edx, 0x40D120
call edx
popad
ret
}
}

void CplantDlg::OnBnClickedButton5()
{
// TODO: 在此添加控件通知处理程序代码
// 打开目标进程
DWORD dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);


// 在目标进程分配内存空间
PVOID ThreadFunAdd = VirtualAllocEx(hProcess, NULL, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);

// 往分配的内存空间中写入代码
DWORD byWrite = 0;
bool bret = WriteProcessMemory(hProcess, ThreadFunAdd, putPlant, 4096, &byWrite);

// 执行目标进程指定地址的代码
CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)ThreadFunAdd, NULL, NULL, NULL);
}
#endif



bool CplantDlg::enableDebugPriv()
{
HANDLE hToken;
LUID sedebugnameValue;
TOKEN_PRIVILEGES tkp;

if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
return false;
}

if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &sedebugnameValue))
{
CloseHandle(hToken);
return false;
}

tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = sedebugnameValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL))
{
CloseHandle(hToken);
return false;
}

return true;
}


void CplantDlg::addPlantsToCombox()
{
int i = 0;
i = m_cbPlantsChoose.AddString(_T("樱桃炸弹"));
m_cbPlantsChoose.SetItemData(i, 2);
i = m_cbPlantsChoose.AddString(_T("毁灭菇"));
m_cbPlantsChoose.SetItemData(i, 15);
i = m_cbPlantsChoose.AddString(_T("寒冰菇"));
m_cbPlantsChoose.SetItemData(i, 14);
i = m_cbPlantsChoose.AddString(_T("魅惑菇"));
m_cbPlantsChoose.SetItemData(i, 12);
i = m_cbPlantsChoose.AddString(_T("坚果墙"));
m_cbPlantsChoose.SetItemData(i, 3);
}

typedef struct PutPlantsPareame
{
UINT u_x;
UINT u_y;
UINT u_plantID;
}PutPlantsParame,*PPutPlantsPareame;



DWORD __stdcall CplantDlg::PutPlants(LPVOID lpThreadParame)
{
PPutPlantsPareame pParame = (PPutPlantsPareame)lpThreadParame;
UINT u_x = pParame->u_x;
UINT u_y = pParame->u_x;
UINT u_plantid = pParame->u_plantID;

__asm
{
pushad
push -1
push u_plantid
mov eax, u_x
push u_y
mov ebx, dword ptr ds:[0x6A9EC0]
mov ebx, dword ptr ds:[ebx + 0x768]
push ebx
mov edx, 0x0040D120
call edx
popad
}
return 0;
}

int CplantDlg::GetPlantsIDByName(CString name)
{
int i = m_cbPlantsChoose.FindString(-1, name);
return m_cbPlantsChoose.GetItemData(i);
}

void CplantDlg::OnBnClickedButton5()
{
UpdateData(TRUE); //从页面读取变量
CString strPlantName = _T("");
DWORD dwPlantID = 0;
DWORD dwPid = 0;

m_cbPlantsChoose.GetWindowText(strPlantName);
dwPlantID = GetPlantsIDByName(strPlantName);

//打开进程
dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));

//远程调用参数
PutPlantsPareame parame;
parame.u_plantID = dwPlantID;
parame.u_x = m_x;
parame.u_y = m_y;


if (!InjectRemoteFunc(dwPid, PutPlants, &parame, sizeof(parame)))
MessageBox(_T("远程调用失败"), NULL, 0);


}

BOOL CplantDlg::InjectRemoteFunc(DWORD dwPid, LPVOID mFunc, LPVOID pRemoteParam, DWORD dwParameSize, DWORD dwWaitTime)
{
HANDLE hProcess = NULL;
LPVOID ThreadAdd = NULL;
DWORD lpNumberofBytes = 0;
BOOL bret = FALSE, bSucc = TRUE;
LPVOID ParamAdd = NULL;
HANDLE hThread = NULL;
DWORD dwWait = 0;

do
{
//打开被注入的进程
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (NULL == hProcess) break;

//装载函数的空间
ThreadAdd = VirtualAllocEx(hProcess, NULL, 4096, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (NULL == ThreadAdd) break;

//写入函数
bret = WriteProcessMemory(hProcess, ThreadAdd, mFunc, 4096, &lpNumberofBytes);
if (FALSE == bret) break;

if (0 != dwParameSize)
{
//装在函数所需要的参数空间
ParamAdd = VirtualAllocEx(hProcess, NULL, dwParameSize, MEM_COMMIT, PAGE_READWRITE);
if (NULL == ParamAdd) break;

//写入参数地址
bret = WriteProcessMemory(hProcess, ParamAdd, pRemoteParam, dwParameSize, &lpNumberofBytes);
if (FALSE == bret) break;
}

// 创建远程线程
hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)ThreadAdd, ParamAdd, NULL, &lpNumberofBytes);
if (NULL == hThread) break;

bSucc = TRUE;

} while (FALSE);

//等待线程结束
if (bSucc)
{
dwWait = WaitForSingleObject(hThread, dwWaitTime);
if (WAIT_TIMEOUT == dwWait) goto end; //如果超时放弃释放资源,防止目标进程在使用
}

//释放目标线程中分配的资源
if (bSucc && ThreadAdd && hProcess)
VirtualFreeEx(hProcess, ThreadAdd, 0, MEM_RELEASE);

if (bSucc && (0 != dwParameSize) && pRemoteParam && hProcess)
{
VirtualFreeEx(hProcess, pRemoteParam, 0, MEM_RELEASE);
}

end:
if (NULL != hThread) CloseHandle(hThread);
if (NULL != hProcess) CloseHandle(hProcess);

return bSucc;

//如果使用stdcall,,然后WriteProcessMemory使用单独计算的地址,我的程序会崩溃,只知道是写入多了很多编译器加的代码,但这些代码为什么会出错暂时没找到原因
}



void CplantDlg::OnBnClickedButton6()
{
// TODO: 在此添加控件通知处理程序代码
// TODO: 在此添加控件通知处理程序代码

}

void CplantDlg::OnBnClickedCheck3()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
DWORD dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess) //处理未找到时操作
{
::MessageBox(NULL, _T("进程打开失败"), NULL, MB_OK);
return;
}

UCHAR buf[4] = { 0 };
if (m_noblood)
{
//nop nop
//禁用cd
buf[0] = 0x83;
buf[1] = 0x46;
buf[2] = 0x40;
buf[3] = 0x01;

}
else
{
//7e 14
//启用cd时间
buf[0] = 0x83;
buf[1] = 0x46;
buf[2] = 0x40;
buf[3] = 0xFC;
}


if (!::WriteProcessMemory(hProcess, (LPVOID)(0x0052FCF0), buf, sizeof(buf), NULL))
{
::MessageBox(NULL, _T("写入数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
CloseHandle(hProcess);
UpdateData(FALSE);
}

void CplantDlg::OnBnClickedCheck4()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
DWORD dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess) //处理未找到时操作
{
::MessageBox(NULL, _T("进程打开失败"), NULL, MB_OK);
return;
}

UCHAR buf[3] = { 0 };
if (m_noblood)
{
//nop nop
//禁用cd
buf[0] = 0x90;
buf[1] = 0x90;
buf[2] = 0x90;
}
else
{
//7e 14
//启用cd时间
buf[0] = 0x0F;
buf[1] = 0x95;
buf[2] = 0xC0;
}


if (!::WriteProcessMemory(hProcess, (LPVOID)(0x0052FCF0), buf, sizeof(buf), NULL))
{
::MessageBox(NULL, _T("写入数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
CloseHandle(hProcess);
UpdateData(FALSE);
}

void CplantDlg::OnBnClickedCheck5()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
DWORD dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess) //处理未找到时操作
{
::MessageBox(NULL, _T("进程打开失败"), NULL, MB_OK);
return;
}

UCHAR buf[2] = { 0 };
if (no_pit)
{
//nop nop
//禁用cd
buf[0] = 0x90;
buf[1] = 0x90;
}
else
{
//7e 14
//启用cd时间
buf[0] = 0x75;
buf[1] = 0x05;
}


if (!::WriteProcessMemory(hProcess, (LPVOID)(0x0041D79E), buf, sizeof(buf), NULL))
{
::MessageBox(NULL, _T("写入数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
CloseHandle(hProcess);
UpdateData(FALSE);
}

void CplantDlg::OnBnClickedCheck6()
{

UpdateData(TRUE);
DWORD dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess) //处理未找到时操作
{
::MessageBox(NULL, _T("进程打开失败"), NULL, MB_OK);
return;
}

UCHAR buf[2] = { 0 };
if (m_nomubei)
{
//nop nop
//禁用cd
buf[0] = 0x90;
buf[1] = 0x90;
}
else
{
//7e 14
//启用cd时间
buf[0] = 0x75;
buf[1] = 0x5F;
}


if (!::WriteProcessMemory(hProcess, (LPVOID)(0x0045FD07), buf, sizeof(buf), NULL))
{
::MessageBox(NULL, _T("写入数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
CloseHandle(hProcess);
UpdateData(FALSE);
}

DWORD __stdcall CplantDlg::PutAllPlants(LPVOID lpThreadParame)
{
__asm
{
pushad
push -1
push 2
mov eax, 1
push 1
mov ebx, dword ptr ds:[0x6A9EC0]
mov ebx, dword ptr ds:[ebx + 0x768]
push ebx
mov edx, 0x0040D120
call edx
popad
}
return 0;
}

void CplantDlg::OnBnClickedButton7()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE); //从页面读取变量
CString strPlantName = _T("");
DWORD dwPlantID = 0;
DWORD dwPid = 0;

m_cbPlantsChoose.GetWindowText(strPlantName);
dwPlantID = GetPlantsIDByName(strPlantName);

//打开进程
dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));

//远程调用参数
PutPlantsPareame parame;
parame.u_plantID = dwPlantID;
parame.u_x = m_x;
parame.u_y = m_y;


if (!InjectRemoteFunc(dwPid, PutAllPlants, &parame, sizeof(parame)))
MessageBox(_T("远程调用失败"), NULL, 0);
}

void CplantDlg::OnBnClickedCheck7()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
DWORD dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess) //处理未找到时操作
{
::MessageBox(NULL, _T("进程打开失败"), NULL, MB_OK);
return;
}

UCHAR buf[6] = { 0 };
if (m_tudou)
{
//nop nop
//禁用cd
buf[0] = 0x90;
buf[1] = 0x90;
buf[2] = 0x90;
buf[3] = 0x90;
buf[4] = 0x90;
buf[5] = 0x90;
}
else
{
//7e 14
//启用cd时间
buf[0] = 0x0F;
buf[1] = 0x85;
buf[2] = 0xFA;
buf[3] = 0x01;
buf[4] = 0x00;
buf[5] = 0x00;
}


if (!::WriteProcessMemory(hProcess, (LPVOID)(0x0045FE53), buf, sizeof(buf), NULL))
{
::MessageBox(NULL, _T("写入数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
CloseHandle(hProcess);
UpdateData(FALSE);
}

void CplantDlg::OnBnClickedCheck8()
{
// TODO: 在此添加控件通知处理程序代码
UpdateData(TRUE);
DWORD dwPid = FindGameProcessidByWndTitle(_T("植物大战僵尸中文版"));
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (!hProcess) //处理未找到时操作
{
::MessageBox(NULL, _T("进程打开失败"), NULL, MB_OK);
return;
}

UCHAR buf[3] = { 0 };
if (m_frozen)
{
//nop nop
//禁用cd
buf[0] = 0x83;
buf[1] = 0xC0;
buf[2] = 0x01;
}
else
{
//7e 14
//启用cd时间
buf[0] = 0x83;
buf[1] = 0xC0;
buf[2] = 0xFD;
}


if (!::WriteProcessMemory(hProcess, (LPVOID)(0x0052B41F), buf, sizeof(buf), NULL))
{
::MessageBox(NULL, _T("写入数据失败"), NULL, MB_OK);
CloseHandle(hProcess);
return;
}
CloseHandle(hProcess);
UpdateData(FALSE);
}

# 6. 网游辅助实战训练

LoadLibrary // 加载模块

FreeLibrary // 卸载模块

CreateRemoteThread // 创建远程线程

GetModuleHandle // 通过模块名称获取模块句柄

# 角色人物分析

角色名

血量

魔法

等级

职业

找人物的属性信息,其实是找 this 指针,通过偏移来找人物属性

1. 人物真气

1.CE 搜索真气值

1
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
00B2CC25 - 89 86 8C070000        - mov [esi+0000078C],eax
eax = 00000212
esi = 15E71F60 5B14C038
当前血量 = [esi + 78C]

00B2CB07 - 8B F1 - mov esi,ecx
ECX = 514FA270
当前血量 = [ECX + 78C]

00B37131 - 56 - push esi
00B37132 - E8 C959FFFF - call 00B2CB00
00B37137 - B0 01 - mov al,01 { 1 }
ecx = 514FA270

00BEF846 - 8B 10 - mov edx,[eax]
00BEF848 - FF 52 68 - call dword ptr [edx+68]
00BEF84B - B0 01 - mov al,01 { 1 }
ecx = 514FA270

00BEF844 - 8B C8 - mov ecx,eax
eax = 514FA270
当前血量 = [EAX + 78C]

00BED9C0 - 8B 41 08 - mov eax,[ecx+08]
eax = 265489C0 ecx = 063A9708
00BED9C3 - 8B 40 30 - mov eax,[eax+30]
00BED9C6 - C3 - ret
eax = 514FA270
当前血量 = [[[ecx+08]+30] + 78C]

00BEFDBB | E8 00DCFFFF | call elementclient.BED9C0 |

00BEFDB9 | 8BCB | mov ecx,ebx |
当前血量 = [[[ebx+08]+30] + 78C]

00BEFBCB | 8BD9 | mov ebx,ecx |
当前血量 = [[[ecx+08]+30] + 78C]
BCB F51

009BBED5 | FFD0 | call eax |

009BBECB | 8B0F | mov ecx,dword ptr ds:[edi] |
当前血量 = [[[[edi]+08]+30] + 78C]

009BBEC1 | 83C7 1C | add edi,1C |
当前血量 = [[[[edi+1c]+08]+30] + 78C]

009BBEB9 | 8B78 1C | mov edi,dword ptr ds:[eax+1C] |
当前血量 = [[[[[eax+1C]+1c]+08]+30] + 78C]

009BBEAE | 8B40 1C | mov eax,dword ptr ds:[eax+1C] | eax:"ff&?"
当前血量 = [[[[[[eax+1C]+1C]+1c]+08]+30] + 78C]

009BBEA4 | A1 A8DD4F01 | mov eax,dword ptr ds:[14FDDA8] |
当前血量 = [[[[[[[14FDDA8]+1C]+1C]+1C]+08]+30] + 78C]

1
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
00B2E1C5 - 89 86 8C070000        - mov [esi+0000078C],eax
esi = 4098A518
当前血量 = [esi + 78C]

00B2E0A7 | 8BF1 | mov esi,ecx |
当前血量 = [ecx + 78C]

00B38612 | E8 895AFFFF | call elementclient.B2E0A0 |
ecx = 4098A518

00BF0808 | FF52 68 | call dword ptr ds:[edx+68] |

00BF0804 | 8BC8 | mov ecx,eax |
eax = 4098A518
当前血量 = [eax + 78C]

00BEE980 | 8B41 08 | mov eax,dword ptr ds:[ecx+8] |
00BEE983 | 8B40 30 | mov eax,dword ptr ds:[eax+30] |
00BEE986 | C3 | ret |
当前血量 = [[[ecx+8]+30] + 78C]

00BE3ED9 | FF50 34 | call dword ptr ds:[eax+34] |

00BE3ECB | 8B4C88 1C | mov ecx,dword ptr ds:[eax+ecx*4+1C] |
当前血量 = [[[[eax+ecx*4+1C]+8]+30] + 78C]
eax = 08D8EF00
ecx = 00000001

00BE3EC1 | 8B46 04 | mov eax,dword ptr ds:[esi+4] |
00BE3EC4 | 8B40 1C | mov eax,dword ptr ds:[eax+1C] |
当前血量 = [[[[[[esi+4]+1C]+1*4+1C]+8]+30] + 78C]

00BE3E98 | 8BF1 | mov esi,ecx |
当前血量 = [[[[[[ecx+4]+1C]+1*4+1C]+8]+30] + 78C]

00A4A55E | E8 0D991900 | call elementclient.BE3E70 |

00A4A553 | 8B8F 98000000 | mov ecx,dword ptr ds:[edi+98] |
当前血量 = [[[[[[[edi+98]+4]+1C]+1*4+1C]+8]+30] + 78C]

00A4A4E4 | 8BF9 | mov edi,ecx |
当前血量 = [[[[[[[ecx+98]+4]+1C]+1*4+1C]+8]+30] + 78C]

00A4A1BF | E8 1C030000 | call elementclient.A4A4E0 |


00A4A1B9 | 8BCE | mov ecx,esi |
当前血量 = [[[[[[[esi+98]+4]+1C]+1*4+1C]+8]+30] + 78C]

00A4A0B8 | 8BF1 | mov esi,ecx |
当前血量 = [[[[[[[ecx+98]+4]+1C]+1*4+1C]+8]+30] + 78C]

00A3F231 | E8 7AAE0000 | call elementclient.A4A0B0 |

00A3F22E | 8B4F 1C | mov ecx,dword ptr ds:[edi+1C] |
当前血量 = [[[[[[[[edi+1C]+98]+4]+1C]+1*4+1C]+8]+30] + 78C]

00A3EF57 | 8BF9 | mov edi,ecx |
当前血量 = [[[[[[[[ecx+1C]+98]+4]+1C]+1*4+1C]+8]+30] + 78C]

00A3F840 | E8 0BF7FFFF | call elementclient.A3EF50 |

00A3F83E | 8BCF | mov ecx,edi |
当前血量 = [[[[[[[[edi+1C]+98]+4]+1C]+1*4+1C]+8]+30] + 78C]

00A3F7A5 | 8BF9 | mov edi,ecx |
当前血量 = [[[[[[[[ecx+1C]+98]+4]+1C]+1*4+1C]+8]+30] + 78C]

00D35072 | E8 29A7D0FF | call elementclient.A3F7A0 |

00D3506D | B9 F8845001 | mov ecx,elementclient.15084F8 |
当前血量 = [[[[[[[[0x15084F8+1C]+98]+4]+1C]+1*4+1C]+8]+30] + 78C]
[[[[[[[[0x1508514]+98]+4]+1C]+1*4+1C]+8]+30] + 78C]


$ + 78C 为血量

$ + 7E4 为血量上限

$ + 790 为真气

$ + 7E8 为真气上限

$ + 784 为等级

$ + 794 为经验

$ + 798 为元神

??? $ + 79C 为元气上限

$ + B00 / $ + B04 为角色姓名

$ + 930 为银币

image-20221222191354658

如图可知,this 指针的基地址为 5F7FB0D8

$ + 78C 为血量

$ + 7E4 为血量上限

$ + 790 为真气

$ + 7E8 为真气上限

$ + 784 为等级

$ + 794 为经验

$ + 798 为元神

$ + 79C 为元气上限

34416B58

1
2
3
773       77+400=477   +3C  x
177 17 +40 z
-349 -34+550=516 +44 y

搜索角色名称

方法 1

1.ce 搜索角色名称,注意,宽字节和窄字节都要考虑

2. 使用定位基地址的方式,一个一个关键点网上找

方法 2(在已知任务角色信息基地址的前提下)

在 ce 里面搜索

逐个分析这些地址,找出与任务角色基地址信息相近的地址,然后计算偏移

image-20221227170809963

34417B58-34416B58=B00

image-20221227170851601

释放技能 call 查找

思路一:技能应该有技能 ID,索引,技能文字信息和描述

可以从技能文字信息为入口,用 CE 搜索字符信息,可以根据内存读写断点。定位技能的关键 call

思路二:分析游戏按键和鼠标点击处理机制,找到技能 call

思路三:针对网游,一般网络游戏会将攻击和伤害的计算放在服务器端处理

失望技能需要与服务端进行网络通信,通过游戏数据网络数据收发机制,跟踪 send,recv 函数,用条件过滤,保证 send 的是释放技能的数据,看函数堆栈反推上层函数,找到技能 call

bp send, 对网络发送数据的函数下断点

排除其他数据包发送的干扰,定位到使用技能时触发的数据包的发送

ctrl + F9 定位到游戏程序模块 (OD 上 module 为游戏进程名) 然后逐层分析函数,看谁更适合作为技能 call

1
2
3
4
5
6
7
8
9
10
11
12
UserSkill ()

{
xxx()
{
send()
//send处下断点,根据堆栈回溯可以定位技能call
}



}
1
2
3
4
00CC3F37  | 57                      | push edi                              |
00CC3F38 | 56 | push esi | esi:L")q"
00CC3F39 | 8B49 20 | mov ecx,dword ptr ds:[ecx+20] |
00CC3F3C | E8 0F780B00 | call elementclient.D7B750 |

发送消息或者触发事件 SendNotyxxx

消息事件处理线程

1
2
3
4
5
6
7
8
9
while(1)
{
switch
case xxx;
break;
case yyy;
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
63
64
65
66
67
68
69
70
71
72
73
764B58A0  | 8BFF                   | mov edi,edi                           |

00D7AD35 | 6A 00 | push 0 |
00D7AD37 | 6A 01 | push 1 |
00D7AD39 | 68 31AC2F01 | push elementclient.12FAC31 |
00D7AD3E | FF35 7CAD5001 | push dword ptr ds:[150AD7C] |
00D7AD44 | FF15 486A1E01 | call dword ptr ds:[<&send>] |

00D7AC71 |. FF75 10 push [arg.3]
00D7AC74 |. FF75 0C push [arg.2]
00D7AC77 |. E8 24000000 call ElementC.00D7ACA0

00D7B9CC |> \FF75 0C push [arg.2]
00D7B9CF |. 8B8E AC000000 mov ecx,dword ptr ds:[esi+0xAC]
00D7B9D5 |. 57 push edi
00D7B9D6 |. FFB6 B0000000 push dword ptr ds:[esi+0xB0]
00D7B9DC |. E8 0FF2FFFF call ElementC.00D7ABF0

00D7B7D1 |. 66:8906 mov word ptr ds:[esi],ax
00D7B7D4 |> 6A 00 push 0x0
00D7B7D6 |. 8D45 DC lea eax,[local.9]
00D7B7D9 |. 8BCF mov ecx,edi
00D7B7DB |. 50 push eax
00D7B7DC |. E8 9F000000 call ElementC.00D7B880
//以上三个除了技能也能中断

00CC3F31 |> \8B0D 38EE4F01 mov ecx,dword ptr ds:[0x14FEE38] ; ElementC.015084F8
00CC3F37 |. 57 push edi 0C
00CC3F38 |. 56 push esi 04229034
00CC3F39 |. 8B49 20 mov ecx,dword ptr ds:[ecx+0x20] 24BC4028
00CC3F3C |. E8 0F780B00 call ElementC.00D7B750
//没有技能ID

009AC4F5 |. FF75 14 push [arg.4] 0019EE7C
009AC4F8 |. C701 00000000 mov dword ptr ds:[ecx],0x0 [24A1B180]
009AC4FE |. FF75 10 push [arg.3] 01
009AC501 |. FF75 0C push [arg.2] 215AAB00
009AC504 |. FF75 08 push [arg.1] 7D
009AC507 |. E8 D4793100 call ElementC.00CC3EE0
//技能不可用

00B42E62 . 50 push eax 0019EE7C
00B42E63 . 8B87 00280000 mov eax,dword ptr ds:[edi+0x2800] 4E492C40
00B42E69 . 6A 01 push 0x1
00B42E6B . FF75 C8 push dword ptr ss:[ebp-0x38] 215AAB00
00B42E6E . FF70 08 push dword ptr ds:[eax+0x8] 7D 44EE61B8 + 08
00B42E71 . A1 38EE4F01 mov eax,dword ptr ds:[0x14FEE38] 015084F8
00B42E76 . 8B48 20 mov ecx,dword ptr ds:[eax+0x20] 24B53028
00B42E79 . 81C1 EC000000 add ecx,0xEC
00B42E7F . E8 3C96E6FF call ElementC.009AC4C0
//技能不可用


00B69E06 |. 6A 00 push 0x0 0
00B69E08 |. 50 push eax 0
00B69E09 |. FF76 08 push dword ptr ds:[esi+0x8] 80001A0B 80001C4D
00B69E0C |. E8 5F87FDFF call ElementC.00B42570
//无技能ID
//以下执行频率很高,消息或者类似事件派发的一个地方

00B697CF |> \8B40 0C mov eax,dword ptr ds:[eax+0xC]
00B697D2 |. FFD0 call eax

00B6A4CF . 8BCE mov ecx,esi
00B6A4D1 . E8 9AF2FFFF call ElementC.00B69770

00B5F6E4 |. 8B03 |mov eax,dword ptr ds:[ebx]
00B5F6E6 |. 8BCB |mov ecx,ebx
00B5F6E8 |. FF75 08 |push [arg.1]
00B5F6EB |. FF50 04 |call dword ptr ds:[eax+0x4]

00B55D68 |. 53 push ebx
00B55D69 |. E8 12990000 call ElementC.00B5F680
1
2
3
4
5
6
7
8
9
10
11
12
00CE9231 - FF 71 08  - push [ecx+08]
00CE92C7 - FF 70 08 - push [eax+08]
00CE92DE - FF 70 08 - push [eax+08]
00B3E488 - FF 73 08 - push [ebx+08]
00B3E62E - FF 73 08 - push [ebx+08]
00B41F83 - FF 70 08 - push [eax+08]
00B425F8 - FF 70 08 - push [eax+08]
00B42E6E - FF 70 08 - push [eax+08]
00B356C7 - FF 70 08 - push [eax+08]
00B356DB - 8B 78 08 - mov edi,[eax+08]
00B5E550 - FF 70 08 - push [eax+08]

1
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
00CE922A  | A1 38EE4F01            | mov eax,dword ptr ds:[14FEE38]        | 015084F8
00CE922F | 56 | push esi | 06CBDD48
00CE9230 | 57 | push edi | 52B640C8
00CE9231 | FF71 08 | push dword ptr ds:[ecx+8] | 0000007D
00CE9234 | 8B40 1C | mov eax,dword ptr ds:[eax+1C] | 1D27F9C0
00CE9237 | 8B70 30 | mov esi,dword ptr ds:[eax+30] | 36FAC5B8
00CE923A | FF15 E8601E01 | call dword ptr ds:[<&?IsGoblinSkill@E | 034CFD30
00CE9240 | 83C4 04 | add esp,4 |
//游戏崩溃

00CE92C1 | 6A FF | push FFFFFFFF | FFFFFFFF
00CE92C3 | 6A 00 | push 0 | 0
00CE92C5 | 6A 00 | push 0 | 0
00CE92C7 | FF70 08 | push dword ptr ds:[eax+8] | 7D
00CE92CA | 8B42 0C | mov eax,dword ptr ds:[edx+C] | 00969460
00CE92CD | FFD0 | call eax |
//游戏崩溃

00CE92D3 | 8B43 08 | mov eax,dword ptr ds:[ebx+8] | 4DB4BCF0
00CE92D6 | 8BCE | mov ecx,esi | 7082C9E8
00CE92D8 | 6A FF | push FFFFFFFF |
00CE92DA | 6A 00 | push 0 |
00CE92DC | 6A 00 | push 0 |
00CE92DE | FF70 08 | push dword ptr ds:[eax+8] | 7D
00CE92E1 | E8 4A4BE5FF | call elementclient.B3DE30 |
//技能call

00B3E487 | 57 | push edi | 687763E8
00B3E488 | FF73 08 | push dword ptr ds:[ebx+8] | 7D
00B3E48B | E8 A0A9E7FF | call elementclient.9B8E30 |
//游戏崩溃

00B3E62D | 50 | push eax | 0
00B3E62E | FF73 08 | push dword ptr ds:[ebx+8] | 7D
00B3E631 | FF75 10 | push dword ptr ss:[ebp+10] | 80001A0B
00B3E634 | 68 886D2701 | push elementclient.1276D88 | 1276D88
00B3E639 | 6A 01 | push 1 |
00B3E63B | E8 C0F83100 | call elementclient.E5DF00 |
00B3E640 | 8B8F F4270000 | mov ecx,dword ptr ds:[edi+27F4] | 6F3CAE58 edi= 5F4F6320 + 0x27F4
00B3E646 | 83C4 14 | add esp,14 |
//无反应

00B41F83 | FF70 08 | push dword ptr ds:[eax+8] | 7D
00B41F86 | E8 75750000 | call elementclient.B49500 |
//无反应

00B425F8 | FF70 08 | push dword ptr ds:[eax+8] | 7D
00B425FB | E8 5034E5FF | call elementclient.995A50
//游戏崩溃


00B42E62 | 50 | push eax | 0019EE7C
00B42E63 | 8B87 00280000 | mov eax,dword ptr ds:[edi+2800] | 5395BDF0
00B42E69 | 6A 01 | push 1 | 1
00B42E6B | FF75 C8 | push dword ptr ss:[ebp-38] | 241D4300
00B42E6E | FF70 08 | push dword ptr ds:[eax+8] | 7D
00B42E71 | A1 38EE4F01 | mov eax,dword ptr ds:[14FEE38] | 015084F8
00B42E76 | 8B48 20 | mov ecx,dword ptr ds:[eax+20] | 29EBB028
00B42E79 | 81C1 EC000000 | add ecx,EC |
00B42E7F | E8 3C96E6FF | call elementclient.9AC4C0 |
//技能不可用

00B356C5 | 6A 00 | push 0 |
00B356C7 | FF70 08 | push dword ptr ds:[eax+8] | 7D
00B356CA | E8 F14C1100 | call elementclient.C4A3C0 |
//无反应

00B5E54D | FF71 04 | push dword ptr ds:[ecx+4] |
00B5E550 | FF70 08 | push dword ptr ds:[eax+8] |
00B5E553 | E8 D8A8E5FF | call elementclient.9B8E30 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
00CE922A  | A1 38EE4F01            | mov eax,dword ptr ds:[14FEE38]                 | 015084F8
00CE922F | 56 | push esi | 06D077F8
00CE9230 | 57 | push edi | 658C4488
00CE9231 | FF71 08 | push dword ptr ds:[ecx+8] | 0000007D
00CE9234 | 8B40 1C | mov eax,dword ptr ds:[eax+1C] | 1D2429C0
00CE9237 | 8B70 30 | mov esi,dword ptr ds:[eax+30] | 7082C9E8

...

00CE92D3 | 8B43 08 | mov eax,dword ptr ds:[ebx+8] | 4DB4BCF0
00CE92D6 | 8BCE | mov ecx,esi | 7082C9E8,ecx的值,人物信息基地址 5F4F6320
00CE92D8 | 6A FF | push FFFFFFFF |
00CE92DA | 6A 00 | push 0 |
00CE92DC | 6A 00 | push 0 |
00CE92DE | FF70 08 | push dword ptr ds:[eax+8] | 7D,技能ID
00CE92E1 | E8 4A4BE5FF | call elementclient.B3DE30 |

[[[14FEE38]+1C]+30] = 7082C9E8 即为this指针
[[[[[[[0x1508514]+98]+4]+1C]+1*4+1C]+8]+30] = 7082C9E8
[[[[[[[[0x15084F8+1C]+98]+4]+1C]+1*4+1C]+8]+30] = 7082C9E8

人物选择目标(玩家,怪物,npc 分析)

选中

当前 this 指针 [[[14FEE38]+1C]+30] = 5F4F6320

image-20230101115006092

image-20230101115309026

image-20230101115332221

1
80001A0B 是戒灵的ID

image-20230101115607140

1
2
3
4
对于不同的囚徒怨魂有不同的ID
80001C4A - 80001C4F
推测某个地域上囚徒怨魂有ID范围,每个ID唯一
戒灵唯一,因此ID也只有一个
1
2
3
009AC900 - 89 B0 04090000  - mov [eax+00000904],esi
00B31E36 - 89 86 04090000 - mov [esi+00000904],eax

思路:

1. 在人物对象偏移地址中找找看是否有与选中的目标相关的信息 (譬如:ID 或者目标对象的地址),然后通过内存断点分析访问这些信息的代码,来确定选中目标的 call

2. 网游可以通过 send 函数断点来分析选中目标的操作 (和释放技能 call 分析类似)

思路一:

1. 上面已经找到一个与任务选中目标的相关信息 (人物选择中的目标 ID)

2. 对这个内存下写入访问 / 写入的断点

1
2
3
4
5
6
7
8
9
10
11
12
13
009AC900  |.  89B0 04090000 mov dword ptr ds:[eax+0x904],esi

00B54582 |> \A1 38EE4F01 mov eax,dword ptr ds:[0x14FEE38] 015084F8
00B54587 |. 56 push esi 80001C56
00B54588 |. 8B48 20 mov ecx,dword ptr ds:[eax+0x20] 11B3C028
00B5458B |. 81C1 EC000000 add ecx,0xEC
00B54591 |. E8 3A83E5FF call ElementC.009AC8D0

mov eax,dword ptr ds:[0x14FEE38]
push 80001A0B
mov ecx,dword ptr ds:[eax+0x20]
add ecx,0x0EC
call 009AC8D0

# 更新 2023.1.5

释放技能 call

1
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
00CE9EE3              | 8B43 08                | mov eax,dword ptr ds:[ebx+8]                   |
00CE9EE6 | 8BCE | mov ecx,esi |
00CE9EE8 | 6A FF | push FFFFFFFF |
00CE9EEA | 6A 00 | push 0 |
00CE9EEC | 6A 00 | push 0 |
00CE9EEE | FF70 08 | push dword ptr ds:[eax+8] |
00CE9EF1 | E8 7A4BE5FF | call elementclient.B3EA70 |

00CE9E3A | A1 38EE4F01 | mov eax,dword ptr ds:[14FEE38] |
00CE9E3F | 56 | push esi |
00CE9E40 | 57 | push edi |
00CE9E41 | FF71 08 | push dword ptr ds:[ecx+8] |
00CE9E44 | 8B40 1C | mov eax,dword ptr ds:[eax+1C] |
00CE9E47 | 8B70 30 | mov esi,dword ptr ds:[eax+30] |


ecx = esi = [eax+30] = [[[14FEE38]+1C]+30]

mov ecx,14FEE38
mov ecx,[ecx]
mov ecx,[ecx+1C]
mov ecx,[ecx+30]
push -1
push 0
push 0
push 7D
call 0x0B3EA70

ecx 就是this指针 [[[14FEE38]+1C]+30] = 65573810 = [[[[[[[0x1508514]+98]+4]+1C]+1*4+1C]+8]+30]

定位怪物 ID

1
2
3
4
5
6
7
8
00B6AC16  |.  6A 00         push 0x0
00B6AC18 |. 50 push eax
00B6AC19 |. FF76 08 push dword ptr ds:[esi+0x8] 80001A0B
00B6AC1C |. E8 8F85FDFF call ElementC.00B431B0

在CE中通过选中怪物切换得到30E32FC4,并且和this指针相邻
65574114 - 65573810 = 904
即人物this指针的+904的位置是所选定怪物

image-20230105200144107

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
009AD0A0 - 89 B0 04090000  - mov [eax+00000904],esi
00B32816 - 89 86 04090000 - mov [esi+00000904],eax


009AD0A0 | 89B0 04090000 | mov dword ptr ds:[eax+904],esi |
009AD0A6 | 8D45 F8 | lea eax,dword ptr ss:[ebp-8] |
009AD0A9 | 50 | push eax |
009AD0AA | 8D45 08 | lea eax,dword ptr ss:[ebp+8] |
009AD0AD | 50 | push eax |
009AD0AE | E8 0D070000 | call elementclient.9AD7C0 |

00B551C2 | A1 38EE4F01 | mov eax,dword ptr ds:[14FEE38] | 015084F8
00B551C7 | 56 | push esi | 80001A0B
00B551C8 | 8B48 20 | mov ecx,dword ptr ds:[eax+20] | 1D01F028
00B551CB | 81C1 EC000000 | add ecx,EC |
00B551D1 | E8 9A7EE5FF | call elementclient.9AD070 |

mov eax,dword ptr ds:[14FEE38]
push 80001A0B
mov ecx,dword ptr ds:[eax+20]
add ecx,0x0EC
call 9AD070

人物技能分析

1
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
00CE9EEE              | FF70 08                | push dword ptr ds:[eax+8]                      |

回城术 A7
458AB5E0

清心咒 71
493BCA60

羽剑 7D
458AB880

龙卷风 7F
4589E0D0

静心咒 72
458AB6F8


3D82F288 493BCA60 `Ê;I
3D82F28C 458AB6F8 ø¶.E
3D82F290 458AB880 .¸.E
3D82F294 4589E0D0 Ðà.E
3D82F298 458AB5E0 àµ.E

493BCA60 01297268 hr).
493BCA64 2E164EA8 ¨N..
493BCA68 00000071 q...
493BCA6C 00000005 ....

2E164EA8 1046C5DC ÜÅF.
2E164EAC 10830198 ....
2E164EB0 00000000 ....
2E164EB4 00000000 ....

10830198 10542A90 .*T. elementskill.&?GetRequiredGenius@ElementSkill@GNET@@UAEPAHH@Z
1083019C 00000071 q...
108301A0 00000007 ....
108301A4 10542AE8 è*T. L"清心咒"
108301A8 10542AF0 ð*T. "清心咒"
108301AC 10542AF8 ø*T. "清心咒.dds"

10542AE8 5FC36E05 .nÃ_
10542AEC 00005492 .T..
10542AF0 C4D0E5C7 ÇåÐÄ
10542AF4 0000E4D6 Öä..
10542AF8 C4D0E5C7 ÇåÐÄ

[[[[3D82F288]+4]+4]+0x0C]

image-20230105204555127

5E5D69F8 - 5E5D41D8 = 2820

人物 this 指针 + 2820 就是技能数组的首地址,通过技能数组的首地址可以找到技能的全部信息

# 更新结束

# 更新 2023.1.11

1
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
1.人物this指针
00B2ECD5 - 89 86 8C070000 - mov [esi+0000078C],eax
当前血量 = [esi + 78C] esi = 5EB45680

00B2EBB7 | 8BF1 | mov esi,ecx |
当前血量 = [ecx + 78C] ecx = 5EB45680

00B391E2 | E8 C959FFFF | call elementclient.B2EBB0 |

00BF23C8 | FF52 68 | call dword ptr ds:[edx+68] |

00BF23C4 | 8BC8 | mov ecx,eax |
当前血量 = [eax + 78C] eax = 5EB45680

00BF0540 | 8B41 08 | mov eax,dword ptr ds:[ecx+8] |
00BF0543 | 8B40 30 | mov eax,dword ptr ds:[eax+30] |
当前血量 = [[[ecx+8]+30] + 78C] ecx = 0648E5A0

00BE5869 | FF50 34 | call dword ptr ds:[eax+34] |

00A4B8AE | E8 4D9F1900 | call elementclient.BE5800 |

00A4B89A | 8B4F 30 | mov ecx,dword ptr ds:[edi+30] |
当前血量 = [[[0150B6FC]+30] + 78C]

$ + 78C 为血量

$ + 7E4 为血量上限

$ + 790 为真气

$ + 7E8 为真气上限

$ + 784 为等级

$ + 794 为经验

$ + 798 为元神

$ + 79C 为元气上限

$ + 7A0 为真元

$ + B00 / $ + B04 为角色姓名

$ + 864 为最大真元

$ + 930 为银币

$ +7A0/864+8FC 为 99/100


2.人物技能call
009ADDF5 | FF75 14 | push dword ptr ss:[ebp+14] | 0019EE7C
009ADDF8 | C701 00000000 | mov dword ptr ds:[ecx],0 | [1DC7CDB8]
009ADDFE | FF75 10 | push dword ptr ss:[ebp+10] | 1
009ADE01 | FF75 0C | push dword ptr ss:[ebp+C] | 01518200
009ADE04 | FF75 08 | push dword ptr ss:[ebp+8] | 0000007D
009ADE07 | E8 84803100 | call elementclient.CC5E90 |

00B43CB2 | 50 | push eax | 0019EE7C
00B43CB3 | 8B87 00280000 | mov eax,dword ptr ds:[edi+2800] | 43336E60
00B43CB9 | 6A 01 | push 1 | 1
00B43CBB | FF75 C8 | push dword ptr ss:[ebp-38] | 181E4F00
00B43CBE | FF70 08 | push dword ptr ds:[eax+8] | 0000007D
00B43CC1 | A1 20205001 | mov eax,dword ptr ds:[1502020] | 0150B6E0
00B43CC6 | 8B48 20 | mov ecx,dword ptr ds:[eax+20] | 1DEB6028
00B43CC9 | 81C1 EC000000 | add ecx,EC |
00B43CCF | E8 ECA0E6FF | call elementclient.9ADDC0 |


00CEB35C - 8B 43 08 - mov eax,[ebx+08]
00CEB35C | 8B43 08 | mov eax,dword ptr ds:[ebx+8] | 43336E60
00CEB35F | 8B11 | mov edx,dword ptr ds:[ecx] | 012787BC
00CEB361 | 6A FF | push FFFFFFFF |
00CEB363 | 6A 00 | push 0 |
00CEB365 | 6A 00 | push 0 |
00CEB367 | FF70 08 | push dword ptr ds:[eax+8] | 0000007D
00CEB36A | 8B42 0C | mov eax,dword ptr ds:[edx+C] | 0096B5A0
00CEB36D | FFD0 | call eax |

00CEB373 - 8B 43 08 - mov eax,[ebx+08]
00CEB373 | 8B43 08 | mov eax,dword ptr ds:[ebx+8] | 43336E60
00CEB376 | 8BCE | mov ecx,esi | 5EB45680
00CEB378 | 6A FF | push FFFFFFFF |
00CEB37A | 6A 00 | push 0 |
00CEB37C | 6A 00 | push 0 |
00CEB37E | FF70 08 | push dword ptr ds:[eax+8] | 0000007D
00CEB381 | E8 FA38E5FF | call elementclient.B3EC80 |

00CEB2D7 | 8B70 30 | mov esi,dword ptr ds:[eax+30] |
esi = [eax + 30]
00CEB2D4 | 8B40 1C | mov eax,dword ptr ds:[eax+1C] |
esi = [[eax+1C] + 30]
00CEB2CA | A1 20205001 | mov eax,dword ptr ds:[1502020] |
esi = [[[1502020] + 1C] + 30]

mov ecx,1502020
mov ecx,[ecx]
mov ecx,[ecx+1C]
mov ecx,[ecx+30]
push -1
push 0
push 0
push 7D
call 0x0B3EC80

3.人物选中call
009AE200 - 89 B0 04090000 - mov [eax+00000904],esi
00B55402 | A1 20205001 | mov eax,dword ptr ds:[1502020] |
00B55407 | 56 | push esi | 80001A0B
00B55408 | 8B48 20 | mov ecx,dword ptr ds:[eax+20] |
00B5540B | 81C1 EC000000 | add ecx,EC |
00B55411 | E8 BA8DE5FF | call elementclient.9AE1D0 |

mov eax,dword ptr ds:[1502020]
push 80001C4A
mov ecx,dword ptr ds:[eax+20]
add ecx,0x0EC
call 9AE1D0

//通过寻找上一层的代码确定call

4.人物技能分析
00CEB37E | FF70 08 | push dword ptr ds:[eax+8] |
eax = 4483FFB0

41930A50 47B991C0 À.¹G
41930A54 4483FE28 (þ.D
41930A58 4483FFB0 °ÿ.D
41930A5C 44832800 .(.D
41930A60 4483FD10 .ý.D

47B991C0 01299498 ..).
47B991C4 30F749F0 ðI÷0
47B991C8 00000071 q...
47B991CC 00000005 ....
47B991D0 00000000 ....

30F749F0 1046E60C .æF.
30F749F4 10833F48 H?..
30F749F8 00000000 ....
30F749FC 00000000 ....

10833F48 105450F8 øPT. elementskill.&?GetRequiredGenius@ElementSkill@GNET@@UAEPAHH@Z
10833F4C 00000071 q...
10833F50 00000007 ....
10833F54 10545150 PQT. L"清心咒"
10833F58 10545158 XQT. "清心咒"
10833F5C 10545160 `QT. "清心咒.dds"

10545150 清心咒

2CAC0CF8 - [[[0150B6FC]+30] = 2CAC0CF8 - 2CABE4D8 = 2820
[[[0150B6FC]+30]+2820] //即可得到技能列表首地址


5.周围怪物列表
00BE8626 - 89 88 2C010000 - mov [eax+0000012C],ecx

00BE8615 > \8B72 0C mov esi,dword ptr ds:[edx+0xC] ; Case 21 of switch 00BE84E0
00BE8618 . FF36 push dword ptr ds:[esi]
00BE861A . E8 F10D0000 call ElementC.00BE9410
00BE861F . 85C0 test eax,eax
00BE8621 . 74 60 je XElementC.00BE8683
00BE8623 . 8B4E 04 mov ecx,dword ptr ds:[esi+0x4]
00BE8626 . 8988 2C010000 mov dword ptr ds:[eax+0x12C],ecx
00BE862C . 8B4E 08 mov ecx,dword ptr ds:[esi+0x8]
00BE862F . 8988 84010000 mov dword ptr ds:[eax+0x184],ecx
00BE8635 . 8B4E 0C mov ecx,dword ptr ds:[esi+0xC]
00BE8638 . 5F pop edi
00BE8639 . 5E pop esi
00BE863A . 8988 0C020000 mov dword ptr ds:[eax+0x20C],ecx

00BF4E10 $ 55 push ebp
00BF4E11 . 8BEC mov ebp,esp
00BF4E13 . 8B45 0C mov eax,dword ptr ss:[ebp+0xC]
00BF4E16 . 33D2 xor edx,edx
00BF4E18 . 56 push esi
00BF4E19 . 8B30 mov esi,dword ptr ds:[eax] 80001C4A
00BF4E1B . 8BC6 mov eax,esi
00BF4E1D . F771 14 div dword ptr ds:[ecx+0x14] ecx = 0A345DEC
00BF4E20 . 8B41 08 mov eax,dword ptr ds:[ecx+0x8] 51A10448
00BF4E23 . 8B0490 mov eax,dword ptr ds:[eax+edx*4] 41AEC578 edx=000002CB
00BF4E26 . 85C0 test eax,eax

00BE9421 |. 8D4E 14 lea ecx,dword ptr ds:[esi+0x14]
ecx = [esi+0x14] = [ecx + 0x14] = [[eax+2*4+0x1C] + 0x14] = [[[[esi+0x4]+0x1C]+2*4+0x1C] + 0x14] = [[[[ecx+0x4]+0x1C]+2*4+0x1C] + 0x14] = [[[[[0150B6FC]+0x98]+0x4]+0x1C]+2*4+0x1C] + 0x14

eax = [eax+edx*4] = 41AEC578

41AEC578 00000000
41AEC57C 2D9E78D0
41AEC580 80001C4A

2D9E78D0 01282E8C ElementC.01282E8C
2D9E78D4 00000012
2D9E78D8 01518328 ElementC.01518328
2D9E78DC 3F7F4E6D
2D9E78E0 80000000
2D9E78E4 3D96A916
2D9E78E8 00000000
2D9E78EC 00000000
2D9E78F0 3F800000
2D9E78F4 00000000
2D9E78F8 00000000
2D9E78FC BD96A917
2D9E7900 00000000
2D9E7904 3F7F4E6F
2D9E7908 00000000
2D9E790C 44339366
2D9E7910 42855E22
2D9E7914 C2B205F5
2D9E7918 3F800000
2D9E791C 3F7F4E6D
2D9E7920 80000000
2D9E7924 3D96A916
2D9E7928 00000000
2D9E792C 00000000
2D9E7930 3F800000
2D9E7934 00000000
2D9E7938 00000000
2D9E793C BD96A917
2D9E7940 00000000
2D9E7944 3F7F4E6F
2D9E7948 00000000
2D9E794C 44339366
2D9E7950 42855E22
2D9E7954 C2B205F5
2D9E7958 3F800000
2D9E795C 00000000
2D9E7960 00000000
2D9E7964 012BC8F8 ElementC.012BC8F8
2D9E7968 01248974 ElementC.01248974
2D9E797C 0000000A
2D9E7980 00000000
2D9E7984 00000006
2D9E7988 420136DA
2D9E798C 75490100 windows_.75490100
2D9E7990 00000000
2D9E7994 BD471982
2D9E7998 80000000
2D9E799C 3F7FB288
2D9E79A0 00000000
2D9E79A4 BD16C33D
2D9E79A8 80000000
2D9E79AC 3F7FD398
2D9E79B0 00000096
2D9E79B4 0000009C
2D9E79B8 0000000F
2D9E79BC 77000100 gdi32ful.77000100
2D9E79C0 BDC16CBC
2D9E79C4 3F77956C
2D9E79C8 3E71C8DE
2D9E79CC BDC16CBC
2D9E79D0 3F77956C
2D9E79D4 3E71C8E3 ASCII "s
2D9E79D8 676F4800 igc32.676F4800
2D9E79DC 01518340 ElementC.01518340
2D9E79E0 41BCBCE0
2D9E79E4 0A345DD8
2D9E79E8 80001C4A
2D9E79EC 0000AE41
2D9E79F0 0000AE41
2D9E79F4 00000010
2D9E79F8 00000000
2D9E79FC 000003BE
2D9E7A54 000003BE
2D9E7ADC FFFFFFFF
2D9E7AE0 3F800000
2D9E7AE4 78443800
2D9E7AE8 0000000C
2D9E7B40 00001388


$+3C 4430814B K.0D ;x
$+40 427230F2 ò0rB ;z
$+44 C2D852B3 ³RØÂ ;y
$+124 00000010 .... ;怪物等级
$+128 00000000 ....
$+12C 000003BE ¾... ;当前血量
$+184 000003BE ¾... ;最大血量
$+270 00001388 ....
$+284 3E312F14 ./1> L"囚徒怨魂"

# 更新结束

5. 人物技能信息分析

1. 每个技能所包含的数据

技能名称 ID

技能冷却时间

可以猜想:有一个技能类,每个技能都是这个类的对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Skill

{

m_skillNamel

m_skillID

m_skillInterval;

m_skillLevel;

m_skillInfo;

};

class skillInfo

{



};
1
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
push [eax+0x08]    
eax = 4C270380 4AAE4A38

46A30A60 01297268 hr).
46A30A64 49C40C18 ..ÄI
46A30A68 00000071 q...
46A30A6C 00000005 ....
46A30A70 00000000 ....
46A30A74 00000000 ....
46A30A78 000003E8 è...
46A30A7C A2B70000 ..·¢
46A30A80 00000000 ....
46A30A84 00000000 ....
46A30A88 005F7800 .x_.
46A30A8C 00000000 ....
46A30A90 00020100 ....
46A30A94 00000000 ....
46A30A98 00000001 ....
46A30A9C 0000001E ....
46A30AA0 0000001F ....


4272ED50
4C270380 01297268 ElementC.01297268
4C270384 553935B0
4C270388 0000007D 技能ID
4C27038C 00000005 技能等级 C
4C270390 00000000
4C270394 00000000 冷却倒计时
4C270398 000003E8 冷却时间 1000ms
4C27039C A2B70000
4C2703A0 00000000
4C2703A4 00000000
4C2703A8 005F7800 入口地址
4C2703AC 00000000
4C2703B0 00020100
4C2703B4 00000000

4C27D890
4C27D890 01297268 ElementC.01297268
4C27D894 55393CB8
4C27D898 0000007F
4C27D89C 00000004
4C27D8A0 00000000
4C27D8A4 00000000
4C27D8A8 00000BB8
4C27D8AC D25FF800
4C27D8B0 00000000
4C27D8B4 00000000
4C27D8B8 00000000
4C27D8BC 00000000
4C27D8C0 00020100
4C27D8C4 00000000
4C27D8C8 00000001
4C27D8CC 00000019
4C27D8D0 0000001F


推测 00CE92DE

1
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
46A30A60 01297268 hr). 
46A30A64 49C40C18 ..ÄI
46A30A68 00000071 q...
46A30A6C 00000005 ....
46A30A70 00000000 ....
46A30A74 00000000 ....
46A30A78 000003E8 è...
46A30A7C A2B70000 ..·¢
46A30A80 00000000 ....
46A30A84 00000000 ....
46A30A88 005F7800 .x_.
46A30A8C 00000000 ....
46A30A90 00020100 ....
46A30A94 00000000 ....
46A30A98 00000001 ....
46A30A9C 0000001E ....
46A30AA0 0000001F ....


495A6148 01297268 ElementC.01297268
495A614C 605D0978
495A6150 0000007D
495A6154 00000005
495A6158 00000000
495A615C 00000000
495A6160 000003E8
495A6164 00726F00 ElementC.00726F00
495A6168 00000000
495A616C 00000000
495A6170 00000000
495A6174 00000000
495A6178 00020100
495A617C 00000000
495A6180 00030000
495A6184 00090006
495A6188 000F000C
495A618C 00190013
495A6190 0021001E
495A6194 00270024
495A6198 002C0029
495A619C 0032002F
495A61A0 003B0036




4272EED8 01297268 hr).
4272EEDC 49C403A8 ¨.ÄI
4272EEE0 0000007F ....
4272EEE4 00000004 ....
4272EEE8 00000000 ....
4272EEEC 00000000 ....
4272EEF0 00000BB8 ¸...
4272EEF4 00726F00 .or.
4272EEF8 00000000 ....
4272EEFC 00000000 ....
4272EF00 00000000 ....
4272EF04 00000000 ....
4272EF08 00020100 ....
4272EF0C 00000000 ....
4272EF10 01297268 hr).
4272EF14 4F3C3E50 P><O
4272EF18 00000C9E ....
4272EF1C 00000001 ....
4272EF20 00000000 ....
4272EF24 00000000 ....
4272EF28 000003E8 è...
4272EF2C 00000000 ....
4272EF30 00000000 ....
4272EF34 00000000 ....
4272EF38 00000000 ....
4272EF3C 00000000 ....
4272EF40 00020100 ....
4272EF44 00000000 ....

1
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
495A6148  01297268  ElementC.01297268
495A614C 605D0978 技能信息
495A6150 0000007D
495A6154 00000005
495A6158 00000000
495A615C 00000000
495A6160 000003E8
495A6164 00726F00 ElementC.00726F00

605D0978 0203A5AC ElementS.0203A5AC
605D097C 023F72B0 ElementS.023F72B0 技能详细信息
605D0980 00000000
605D0984 00000000
605D0988 00000000
605D098C 00000000
605D0990 00000000
605D0994 00000000
605D0998 00000000
605D099C 00000000
605D09A0 00000000
605D09A4 00000000
605D09A8 00000000
605D09AC 0000007D
605D09B0 00000005

023F72B0 02110B04 ElementS.02110B04
023F72B4 0000007D
023F72B8 00000007
023F72BC 020DBFE4 ElementS.020DBFE4 技能名称
023F72C0 020DBFEC ElementS.020DBFEC
023F72C4 020DBFF4 ElementS.020DBFF4
023F72C8 0000000A
023F72CC 00020001
023F72D0 00000000

020DBFE4 7BAD7FBD
020DBFE8 00000000
020DBFEC FDBCF0D3
020DBFF0 00000000
020DBFF4 FDBCF0D3

020DBFE4 羽箭..


猜测:技能列表使用一个数组存储的

通过 OD 中下断点 push dword ptr ds:[eax+0x8] 在 OD 中 eax 的值

491B2710 A7

4B637FE8 71

491B2828 72

491B29B0 7D

491A5200 7F

# 角色人物技能列表

image-20230103134451637

image-20230103134511620

1
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
432CF230  4B637FE8
432CF234 491B2828
432CF238 491B29B0
432CF23C 491A5200
432CF240 491B2710
432CF244 CDD0A3C4
[432CF230]

4B637FE8 01297268 ElementC.01297268
4B637FEC 4C573D48
4B637FF0 00000071
4B637FF4 00000005
4B637FF8 00000000
4B637FFC 00000000
4B638000 000003E8
[[432CF230]+4]

4C573D48 0208A5AC ElementS.0208A5AC
4C573D4C 0244D660 ElementS.0244D660
4C573D50 00000000
4C573D54 00000000
4C573D58 00000000
4C573D5C 00000000
[[[432CF230]+4]+4]

0244D660 02160550 ElementS.02160550
0244D664 00000071
0244D668 00000007
0244D66C 021605A8 ElementS.021605A8
0244D670 021605B0 ElementS.021605B0
0244D674 021605B8 ElementS.021605B8
0244D678 0000000A
0244D67C 00000002
[[[[432CF230]+4]+4]+0x0C]

021605A8 5FC36E05
021605AC 00005492
021605B0 C4D0E5C7
021605B4 0000E4D6
021605B8 C4D0E5C7
021605BC 642EE4D6

021605A8 清心咒.쓐
[[[[432CF230]+4]+4]+0x0C] = 清心咒


image-20230103135837650

2EEDD430 - [[[14FEE38]+1C]+30] = 2EEDD430 - 2EEDAC10 = 2820

1
2
3
4
5
6
7
8
9
10
11
12
2EEDD430  432CF230   人物技能列表
2EEDD434 00000005 人物技能列表长度
2EEDD438 00000010
2EEDD43C 00000010
2EEDD440 01276380 ElementC.01276380
2EEDD444 011ED3B8 ElementC.011ED3B8
2EEDD448 00000000
2EEDD44C 00000000
2EEDD450 00000000
2EEDD454 00000010

dd [[[14FEE38]+1C]+30] + 2820 = 2EEDD430

# 人物周围怪物

猜想:怪物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Monster
{
m_dwMonsterID;
MonsterInfo monIfo;
}

class MonsterInfo
{
m_strMonsterName;
m_dwMonsterID;
m_Mode;
m_MonsterGrade;
m_MonsterShengMing;
m_MonsterMaxShengMing;
m_Monster_X_POS;
m_Monster_Y_POS;
m_Monster_Z_POS;
m_MonsterType; //npc,monster

};

image-20230105211901057

00BE9BA4 - 8B 86 18010000 - mov eax,[esi+00000118] ESI=5F21EBA8

思路一:

在游戏中用 CE 搜索怪物的相关信息 (怪物的 ID,生命,名字等信息) 定位到怪物对象的地址,然后通过 OD 下访问断点,找到相关代码,分析代码逻辑,最终找到怪物列表

思路二:

网游通过 send 函数断点分析相关操作

1. 怪物的信息偏移地址分析

2. 人物周围的怪物列表

80001C4A

1
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
00BE7E26 - 89 88 2C010000  - mov [eax+0000012C],ecx

eax = 3EB9CE70

$+124 00000010 ....
$+128 00000000 ....
$+12C 000003BE ¾...
$+180 00000000 ....
$+184 000003BE ¾...


00C1BD4B - 8B B8 18010000 - mov edi,[eax+00000118]
00C1BD48 | 8B46 04 | mov eax,dword ptr ds:[esi+4] | 3EB978D0
00C1BD4B | 8BB8 18010000 | mov edi,dword ptr ds:[eax+118] | 80001C46
00C1BD51 | 8BB0 B4000000 | mov esi,dword ptr ds:[eax+B4] | 00000006
00C1BD57 | E8 F40D0100 | call elementclient.C2CB50 |

00BE9BA4 - 8B 86 18010000 - mov eax,[esi+00000118]

00C17B6A - 8B BF 18010000 - mov edi,[edi+00000118] 3EB9CE70
00C17B64 | 8BB7 B4000000 | mov esi,dword ptr ds:[edi+B4] | 00000006
00C17B6A | 8BBF 18010000 | mov edi,dword ptr ds:[edi+118] | 80001C4A
00C17B70 | E8 DB4F0100 | call elementclient.C2CB50 |

00BE8603 - 8B 86 18010000 - mov eax,[esi+00000118]
00BE8601 | 8B36 | mov esi,dword ptr ds:[esi] |
00BE8603 | 8B86 18010000 | mov eax,dword ptr ds:[esi+118] |

00BE7E26 - 89 88 2C010000 - mov [eax+0000012C],ecx
00BE7E23 | 8B4E 04 | mov ecx,dword ptr ds:[esi+4] | 00000291
00BE7E26 | 8988 2C010000 | mov dword ptr ds:[eax+12C],ecx |
00BE7E2C | 8B4E 08 | mov ecx,dword ptr ds:[esi+8] | 000003BE
00BE7E2F | 8988 84010000 | mov dword ptr ds:[eax+184],ecx |
00BE7E35 | 8B4E 0C | mov ecx,dword ptr ds:[esi+C] |


00F30DED | 8B89 EC000000 | mov ecx,dword ptr ds:[ecx+EC] |
00F30DF3 | 8B45 08 | mov eax,dword ptr ss:[ebp+8] |
00F30DF6 | 8B0481 | mov eax,dword ptr ds:[ecx+eax*4] |
00F30DF9 | 85C0 | test eax,eax |
00C50D74 | E8 67002E00 | call elementclient.F30DE0 |

00B69BBB - FF B3 18010000 - push [ebx+00000118]

00B69BBB | FFB3 18010000 | push dword ptr ds:[ebx+118] |
00B69BC1 | F3:0F114424 18 | movss dword ptr ss:[esp+18],xmm0 |
00B69BC7 | E8 64BAECFF | call elementclient.A35630 |

00C15CAB - FF B7 18010000 - push [edi+00000118]

00C15CE5 - FF B7 18010000 - push [edi+00000118]
00C15CD5 | 8B75 18 | mov esi,dword ptr ss:[ebp+18] |
00C15CD8 | 8B4D F0 | mov ecx,dword ptr ss:[ebp-10] |
00C15CDB | 53 | push ebx |
00C15CDC | FF75 14 | push dword ptr ss:[ebp+14] |
00C15CDF | 56 | push esi |
00C15CE0 | 6A 00 | push 0 |
00C15CE2 | FF75 08 | push dword ptr ss:[ebp+8] |
00C15CE5 | FFB7 18010000 | push dword ptr ds:[edi+118] |
00C15CEB | E8 9067FCFF | call elementclient.BDC480 |

00C1DE59 - 8B B8 18010000 - mov edi,[eax+00000118]

00BE7E15 > \8B72 0C mov esi,dword ptr ds:[edx+0xC] ; 6FC422FA
00BE7E18 . FF36 push dword ptr ds:[esi] 80001C4A
00BE7E1A . E8 010E0000 call ElementC.00BE8C20
00BE7E1F . 85C0 test eax,eax 2A532888


1
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
00A4AA6F              | E8 1C030000            | call elementclient.A4AD90             |

00A4AD94 | 8BF9 | mov edi,ecx |

00A4AE03 | 8B8F 98000000 | mov ecx,dword ptr ds:[edi+98] |

00A4AE0E | E8 4DA21900 | call elementclient.BE5060 |

00BE5088 | 8BF1 | mov esi,ecx |

00BE50B1 | 8B46 04 | mov eax,dword ptr ds:[esi+4] |
00BE50B4 | 8B40 1C | mov eax,dword ptr ds:[eax+1C] |

00BE50BB | 8B4C88 1C | mov ecx,dword ptr ds:[eax+ecx*4+1C] | eax = 095D9D28 ecx = 2
00BE50BF | 85C9 | test ecx,ecx |
00BE50C1 | 74 29 | je elementclient.BE50EC |
00BE50C3 | 8B01 | mov eax,dword ptr ds:[ecx] |
00BE50C5 | 8D55 D4 | lea edx,dword ptr ss:[ebp-2C] |
00BE50C8 | 52 | push edx |
00BE50C9 | FF50 34 | call dword ptr ds:[eax+34] |

00BE8150 | E8 7BFBFFFF | call elementclient.BE7CD0 |

00BE7E15 > \8B72 0C mov esi,dword ptr ds:[edx+0xC] 1C677902
00BE7E18 . FF36 push dword ptr ds:[esi] 80001C4A
00BE7E1A . E8 010E0000 call ElementC.00BE8C20
00BE7E1F . 85C0 test eax,eax 37327F00
00BE7E21 . 74 60 je XElementC.00BE7E83
00BE7E23 . 8B4E 04 mov ecx,dword ptr ds:[esi+0x4]
00BE7E26 . 8988 2C010000 mov dword ptr ds:[eax+0x12C],ecx
00BE7E2C . 8B4E 08 mov ecx,dword ptr ds:[esi+0x8]
00BE7E2F . 8988 84010000 mov dword ptr ds:[eax+0x184],ecx
00BE7E35 . 8B4E 0C mov ecx,dword ptr ds:[esi+0xC]

call 00BE8C20通过怪物ID得到怪物对象
00BE8C20 /$ 55 push ebp
00BE8C21 |. 8BEC mov ebp,esp
00BE8C23 |. 83EC 08 sub esp,0x8
00BE8C26 |. 56 push esi
00BE8C27 |. 8BF1 mov esi,ecx
00BE8C29 |. 8D45 08 lea eax,[arg.1]
00BE8C2C |. 50 push eax
00BE8C2D |. 8D45 F8 lea eax,[local.2]
00BE8C30 |. 50 push eax
00BE8C31 |. 8D4E 14 lea ecx,dword ptr ds:[esi+0x14] esi = 0A3F6DD8
00BE8C34 |. E8 57130000 call ElementC.00BE9F90

00BE9F90 $ 55 push ebp
00BE9F91 . 8BEC mov ebp,esp
00BE9F93 . 8B45 0C mov eax,dword ptr ss:[ebp+0xC] 0019EECC
00BE9F96 . 33D2 xor edx,edx
00BE9F98 . 56 push esi 0A3F6DD8
00BE9F99 . 8B30 mov esi,dword ptr ds:[eax] 80001C4A
00BE9F9B . 8BC6 mov eax,esi
00BE9F9D . F771 14 div dword ptr ds:[ecx+0x14] eax/301 edx = 000002CB ecx = 0A3F6DEC
00BE9FA0 . 8B41 08 mov eax,dword ptr ds:[ecx+0x8] 58A9B678 怪物列表地址
00BE9FA3 . 8B0490 mov eax,dword ptr ds:[eax+edx*4] 4F46B068
00BE9FA6 . 85C0 test eax,eax
00BE9FA8 . 74 11 je XElementC.00BE9FBB
00BE9FB0 > 3970 08 cmp dword ptr ds:[eax+0x8],esi
00BE9FB3 . 74 18 je XElementC.00BE9FCD
00BE9FB5 . 8B00 mov eax,dword ptr ds:[eax]
00BE9FB7 . 85C0 test eax,eax
00BE9FB9 .^ 75 F5 jnz XElementC.00BE9FB0
00BE9FBB > 8B45 08 mov eax,dword ptr ss:[ebp+0x8]
00BE9FBE . 5E pop esi
00BE9FBF . C700 00000000 mov dword ptr ds:[eax],0x0
00BE9FC5 . C640 04 00 mov byte ptr ds:[eax+0x4],0x0
00BE9FC9 . 5D pop ebp
00BE9FCA . C2 0800 retn 0x8
00BE9FCD | 8B4D 08 | mov ecx,dword ptr ss:[ebp+8] 0019EEBC
00BE9FD0 | 83C0 04 | add eax,4 | 4F46B06C
00BE9FD3 | 5E | pop esi | 0A3F6DD8
00BE9FD4 | 8901 | mov dword ptr ds:[ecx],eax |
00BE9FD6 | 8BC1 | mov eax,ecx | 0019EEBC
00BE9FD8 | C641 04 01 | mov byte ptr ds:[ecx+4],1 |
00BE9FDC | 5D | pop ebp |
00BE9FDD | C2 0800 | ret 8 |




怪物对象通过this指针偏移得到ID,当前血量和最大血量
+ 12C
+ 184
+ 118

0A3F6DEC 00000000 ....
0A3F6DF0 0000001E ....
0A3F6DF4 58A9B678 x¶©X
0A3F6DF8 58A9C27C |©X
0A3F6DFC 00000301 ....
0A3F6E00 00000301 ....
0A3F6E04 00000000 ....
0A3F6E08 00000000 ....
0A3F6E0C 0464E750 Pçd.
0A3F6E10 0464E824 $èd.
ecx + 8 = 58A9B678
58A9C27C - 58A9B678 = C04

ecx = esi + 14 = ecx + 14 = [eax+2*4+1C]+14 = [[[esi+4]+1C]+2*4+1C]+14 = [[[ecx+4]+1C]+2*4+1C]+14
= [[[[edi+98]+4]+1C]+2*4+1C]+14 = [[[[[1508514]+98]+4]+1C]+2*4+1C]+14

$ ==> 4F46B068 00000000 ....
$+4 4F46B06C 37327F00 ..27 ;怪物详细信息
$+8 4F46B070 80001C4A J... ;怪物ID
$+C 4F46B074 00000000 ....
$+10 4F46B078 00000100 ....


$ ==> 37327F00 01280C5C \.(.
$+4 37327F04 00000012 ....
$+8 37327F08 01515140 @QQ.
$+C 37327F0C BF514D40 @MQ¿
$+10 37327F10 00000000 ....
$+14 37327F14 3F136828 (h.?
$+18 37327F18 00000000 ....
$+1C 37327F1C 00000000 ....
$+20 37327F20 3F800000 ...?
$+24 37327F24 00000000 ....
$+28 37327F28 00000000 ....
$+2C 37327F2C BF136828 (h.¿
$+30 37327F30 00000000 ....
$+34 37327F34 BF514D40 @MQ¿
$+38 37327F38 00000000 ....
$+3C 37327F3C 4430814B K.0D ;x
$+40 37327F40 427230F2 ò0rB ;z
$+44 37327F44 C2D852B3 ³RØÂ ;y
$+48 37327F48 3F800000 ...?
$+4C 37327F4C BF514D40 @MQ¿
$+50 37327F50 00000000 ....
$+54 37327F54 3F136828 (h.?
$+58 37327F58 00000000 ....
$+5C 37327F5C 00000000 ....
$+60 37327F60 3F800000 ...?
$+64 37327F64 00000000 ....
$+68 37327F68 00000000 ....
$+6C 37327F6C BF136828 (h.¿
$+70 37327F70 00000000 ....
$+74 37327F74 BF514D40 @MQ¿
$+78 37327F78 00000000 ....
$+7C 37327F7C 4430814B K.0D ;x
$+80 37327F80 427230F2 ò0rB
$+84 37327F84 C2D852B3 ³RØÂ
$+88 37327F88 3F800000 ...?
$+8C 37327F8C 00000000 ....
$+90 37327F90 00000000 ....
$+94 37327F94 012BA6C8 Ȧ+.
$+98 37327F98 01246824 $h$.
$+AC 37327FAC 0000000A ....
$+B0 37327FB0 00000000 ....
$+B4 37327FB4 00000006 .... 怪物类型 6 怪物 7 NPC 9 宠物 A GM
$+B8 37327FB8 4208CC54 TÌ.B
$+BC 37327FBC FFFF0100 ..ÿÿ
$+C0 37327FC0 00000000 ....
$+C4 37327FC4 3F735658 XVs?
$+C8 37327FC8 00000000 ....
$+CC 37327FCC BE9F08C1 Á..¾
$+D0 37327FD0 00000000 ....
$+D4 37327FD4 3F740BDE Þ.t?
$+D8 37327FD8 00000000 ....
$+DC 37327FDC BE9AA083 . .¾
$+E0 37327FE0 00000096 ....
$+E4 37327FE4 00000096 ....
$+E8 37327FE8 000001B6 ¶...
$+EC 37327FEC FF000101 ...ÿ
$+F0 37327FF0 A0A1F1A4 ¤ñ¡ 
$+F4 37327FF4 3F800000 ...?
$+F8 37327FF8 A1D9690E .iÙ¡
$+FC 37327FFC 80000000 ....
$+100 37328000 3F800000 ...?
$+104 37328004 00000000 ....
$+108 37328008 FFFFFF00 .ÿÿÿ
$+10C 3732800C 01515158 XQQ.
$+110 37328010 4DC50270 p.ÅM &"`剂"
$+114 37328014 0A3F6DD8 Øm?.
$+118 37328018 80001C4A J...
$+11C 3732801C 0000AE41 A®..
$+120 37328020 0000AE41 A®..
$+124 37328024 00000010 .... ;怪物等级
$+128 37328028 00000000 ....
$+12C 3732802C 000003BE ¾... ;当前血量
$+184 37328084 000003BE ¾... ;最大血量
$+250 37328028 00000000 .... ;1.移动加速 3.防御强化 4.法术抵抗 5.攻击强化 6.法术强化 7.舍命突击 8.生命强化 9.虚弱
$+270 373281 00001388 ....
$+284 373281 3E312F14 ./1> L"囚徒怨魂"
$+288 373281 00000000 ....

$+2E8 373281 00000000 .... 怪物攻击目标ID
$+2EC 373281 00000000 .... 怪物攻击目标ID
$+320 26674050 ;怪物状态列表基地址
$+324 0000 ;怪物状态个数

# 挂机功能

1. 挂机需要创建一个线程

2. 线程中循环执行

while()

{

​ 选中怪物

​ 靠近怪物 (向怪物移动至最远攻击距离内)

​ 释放技能

​ 判断怪物生命值

​ 判断自己生命值

​ …

}

# 找人物移动 call

思路一:人物移动与目标地点的坐标和人物当前地点坐标数据有联系,所以我们可以从这两个数据入手开始查找

思路二:网友一般将人物移动时的数据的逻辑处理放在服务端,游戏客户端与服务端通过网络通讯

所以通过 send 发包函数来定位人物移动的 call 的代码

1
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
00B503F7 - F3 0F5C 4F 3C  - subss xmm1,[edi+3C]

00B50421 | 8B8F F4270000 | mov ecx,dword ptr ds:[edi+27F4] |
00B50427 | 6A 01 | push 1 |
00B50429 | E8 42F50000 | call elementclient.B5F970 |

00B5042E | 8BF0 | mov esi,eax |
00B50430 | 8D45 D4 | lea eax,dword ptr ss:[ebp-2C] |
00B50433 | 50 | push eax |
00B50434 | 6A 00 | push 0 |
00B50436 | 8BCE | mov ecx,esi |
00B50438 | E8 735C0100 | call elementclient.B660B0 |

00B5043D | 8D85 70FFFFFF | lea eax,dword ptr ss:[ebp-90] |
00B50443 | 8BCE | mov ecx,esi |
00B50445 | 50 | push eax |
00B50446 | 8D85 64FFFFFF | lea eax,dword ptr ss:[ebp-9C] |
00B5044C | 50 | push eax |
00B5044D | E8 2E5A0100 | call elementclient.B65E80 |

00B5051A | 8B8F F4270000 | mov ecx,dword ptr ds:[edi+27F4] |
00B50520 | 6A 00 | push 0 |
00B50522 | 56 | push esi |
00B50523 | 6A 01 | push 1 |
00B50525 | E8 36020100 | call elementclient.B60760 |

$-2C 0019EEA0 44DB58AF
$-28 0019EEA4 43565AF2
$-24 0019EEA8 45889118

$-90 0019EE3C 3D5ECC82
$-8C 0019EE40 3F22F622
$-88 0019EE44 BF44F0D6

$-9C 0019EE30 44DB97A2
$-98 0019EE34 435B1654
$-94 0019EE38 4588AF10


sub esp,0x18
mov ecx,0150B6FC
mov ecx,[ecx]
mov ecx,[ecx+30]
mov ecx,[ecx+27F4]
push 1
call 0x0B5F970

mov esi,eax
mov ebx,44DB58AF
mov [esp+0x0],ebx
mov ebx,43565AF2
mov [esp+0x4],ebx
mov ebx,45889118
mov [esp+0x8],ebx
lea eax,[esp]
push eax
push 0
mov ecx,esi
call 0x0B660B0

mov edx,3D5ECC82
mov [esp+0xC],edx
mov edx,3F22F622
mov [esp+0x10],edx
mov edx,0x0BF44F0D6
mov [esp+0x14],edx
mov ecx,esi
lea eax,[esp+0xC]
push eax
lea eax,[esp]
push eax
call 0x0B65E80

mov ecx,0150B6FC
mov ecx,[ecx]
mov ecx,[ecx+30]
mov ecx,[ecx+27F4]
push 0
push esi
push 1
call 0x0B60760

add esp,0x18






00B661C4 - F3 0F5C 50 3C - subss xmm2,[eax+3C]

# 找人物背包物品列表

思路一:以背包中某一种物品来分析,最好是能叠加的,数量可以改变

物品列表 -> 多种物品 (名字,数量)

1
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
00BA02D6 - 01 41 14  - add [ecx+14],eax

00BA02D0 | 55 | push ebp |
00BA02D1 | 8BEC | mov ebp,esp |
00BA02D3 | 8B45 08 | mov eax,dword ptr ss:[ebp+8] | FFFFFFFF
00BA02D6 | 0141 14 | add dword ptr ds:[ecx+14],eax | ecx = 72221188
00BA02D9 | 8379 14 00 | cmp dword ptr ds:[ecx+14],0 | ecx+14:L"ш"
00BA02DD | 7D 07 | jge elementclient.BA02E6 |
00BA02DF | C741 14 00000000 | mov dword ptr ds:[ecx+14],0 | ecx+14:L"ш"
00BA02E6 | 8B41 18 | mov eax,dword ptr ds:[ecx+18] | 0000270F
00BA02E9 | 3941 14 | cmp dword ptr ds:[ecx+14],eax | ecx+14:L"ш"
00BA02EC | 7E 03 | jle elementclient.BA02F1 |
00BA02EE | 8941 14 | mov dword ptr ds:[ecx+14],eax | ecx+14:L"ш"
00BA02F1 | 8B41 14 | mov eax,dword ptr ds:[ecx+14] | 445
00BA02F4 | 5D | pop ebp |
00BA02F5 | C2 0400 | ret 4 |


$ ==> 722211 0127B5E4 äµ'.
$+4 722211 00000000 ....
$+8 722211 00000009 .... ;物品类型 回血蓝0x09 0x08生产材料 0x1F仙药 0x0F武器 0x3装备 0x2弹药
$+C 722211 00000710 .... ;ID 708(生命) 710(真气)
$+10 722211 00000000 ....
$+14 722211 00000447 G... ;物品数量
$+18 722211 0000270F .'.. ;物品栏最大数量
$+1C 722211 00000000 ....
$+20 722211 00000384 ....

$ ==> 722225B8 4B684590 .EhK
$+4 722225BC 4B684518 .EhK
$+8 722225C0 72220770 p."r
$+C 722225C4 5D938E30 0..]
$+10 722225C8 4B6844A0  DhK
$+14 722225CC 4B684428 (DhK
$+18 722225D0 4B6843B0 °ChK
$+1C 722225D4 722207F8 ø."r
$+20 722225D8 4B684338 8ChK
$+24 722225DC 513FCC68 hÌ?Q
$+28 722225E0 70B166F8 øf±p
$+2C 722225E4 5FE23F60 `?â_
$+30 722225E8 72221D38 8."r
$+34 722225EC 72221CB0 °."r
$+38 722225F0 72221188 .."r
$+3C 722225F4 4B683D20 =hK
$+40 722225F8 4B683CA8 ¨<hK
$+44 722225FC 4B683C30 0<hK
$+48 72222600 099B88E0 à... &" 品"
$+4C 72222604 099B8A28 (... &" 品"
$+50 72222608 099B8B70 p... &" 品"
$+54 7222260C 099B96F8 ø... &"P痈"
$+58 72222610 099B8CB8 ¸... &" 品"
$+5C 72222614 4B683BB8 ¸;hK
$+60 72222618 099B8E00 ....
$+64 7222261C 099B8F48 H... &" 品"
$+68 72222620 099B9090 .... &" 品"
$+6C 72222624 099B91D8 Ø... &" 品"
$+70 72222628 099B9320 ... &" 品"
$+74 7222262C 099B9468 h... &"P痈"
$+78 72222630 099B95B0 °... &"P痈"
$+7C 72222634 00000000 ....

00B77B73 - 8B 47 0C - mov eax,[edi+0C]
00B77B73 | 8B47 0C | mov eax,dword ptr ds:[edi+C] | 722225B8
00B77B76 | 53 | push ebx | 72221188
00B77B77 | 8B1CB0 | mov ebx,dword ptr ds:[eax+esi*4] |
00B77B7A | 85DB | test ebx,ebx |
00B77B7C | 74 1D | je elementclient.B77B9B |
00B77B7E | 8B45 0C | mov eax,dword ptr ss:[ebp+C] | 1
00B77B81 | 8BCB | mov ecx,ebx | 72221188
00B77B83 | F7D8 | neg eax |
00B77B85 | 50 | push eax | -1
00B77B86 | E8 45870200 | call elementclient.BA02D0 | eax = 0x441 edi = 30AD5BF8
[edi+c]

00B34C88 | 51 | push ecx |
00B34C89 | 8BC8 | mov ecx,eax | eax = 30AD5BF8 30AD5BF8

00B34CC2 | 8B4D 08 | mov ecx,dword ptr ss:[ebp+8] |
00B34CCA | 50 | push eax |
00B34CCB | E8 902E0400 | call elementclient.B77B60 |

00B49A78 | FF2485 009BB400 | jmp dword ptr ds:[eax*4+B49B00] |
00B49A7F | 8B81 40180000 | mov eax,dword ptr ds:[ecx+1840] | ecx = 3E1DA010
00B49A85 | 5D | pop ebp |
00B49A86 | C2 0400 | ret 4 |

[[[[[[0150B6FC]+30] + 1840]+C]+x*4] +0x14] x [0-31] //物品数量
[[[[0150B6FC]+30] + 1840]+10]
$ ==> 30AD5BF8 0127AB74 t«'.
$+4 30AD5BFC 0127AB6C l«'.
$+8 30AD5C00 011EF3B8 ¸ó..
$+C 30AD5C04 722225B8 ¸%"r ;背包物品列表
$+10 30AD5C08 00000020 ... ;背包大小
$+14 30AD5C0C 00000020 ...
$+18 30AD5C10 0000000A ....
$+1C 30AD5C14 00000000 ....
$+20 30AD5C18 00010100 ....
$+40 4B6845D0 3F800000 ...?
$+44 4B6845D4 00000000 ....
$+48 4B6845D8 00000000 ....
$+4C 4B6845DC 537177BC ¼wqS L"^aa32ff星云散聚石 (12)\\r^ffffff价格^ffffff 1 银币^ffffff (12) \\r^00ffff\\r死亡保护\\r不可丢在地上\\r不可交易\\r不可放入账号仓库\\r^FF0000占星^ffcb4a必备之物,占星时星盘属性数目、\\r属性内容、属性位置同时随机变化。\\r\\r^96f5ff占星时提供星运经验:1"
$+50 4B6845E0 00000000 ....
$+54 4B6845E4 00000000 ....
$+58 4B6845E8 00000000 ....
$+5C 4B6845EC 00000000 ....
$+60 4B6845F0 00000000 ....
$+64 4B6845F4 00000000 ....
$+68 4B6845F8 28B10B10 ..±( ;物品属性地址
$+6C 4B6845FC 00000000 ....

537177BC ^aa32ff星云散聚石 (12)\r^ffffff价格^fff

$ ==> 28B10B10 0000B985 .¹..
$+4 28B10B14 4E91661F .f.N
$+8 28B10B18 805A6563 ceZ.
$+C 28B10B1C 000077F3 ów..
$+10 28B10B20 00000000 ....


物品类型 = 0x09的偏移 [[[[[[0150B6FC]+30] + 1840]+C]+1*4] +0x68]+4

image-20230125203049839

特征码

使用 *?匹配通配符

根据特征码找游戏更新之后的新 call

# 找使用背包物品 call

自动吃红药

自动吃蓝药

免疫药

无敌药

思路 1:

以某种物品的相关信息为线索 (名称,数量,ID 等),用 CE 监视当使用物品时哪些代码对这些信息有访问,大致定位出使用物品 call 的范围

思路 2:

对于网游,物品相关的逻辑处理一般是放在服务端处理,所以使用物品的时候必定与服务端有网络数据通讯,所以断点通过 send 函数也可以定位物品使用代码的位置

bp sned 对 socket 库中的发包函数下断电

send-> 系统的发送数据的函数

游戏本身 -> 也有自己的发包函数 sendmsg,这函数里面调用了 send

SendData(char* data, int length)

{

​ …;

​ send();

}

1
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
00B5A8AA - FF 77 0C  - push [edi+0C]

00B5BC9F - FF 77 0C - push [edi+0C]
00B5BC97 | 8B5D 0C | mov ebx,dword ptr ss:[ebp+C] | 0
00B5BC9A | 8B4D E0 | mov ecx,dword ptr ss:[ebp-20] | 1C58A028 1C58A028
00B5BC9D | 6A 01 | push 1 | 1
00B5BC9F | FF77 0C | push dword ptr ds:[edi+C] | 710
00B5BCA2 | 81C1 EC000000 | add ecx,EC |
00B5BCA8 | 53 | push ebx |
00B5BCA9 | 6A 00 | push 0 | 0
00B5BCAB | E8 9025E5FF | call elementclient.9AE240 | 0

push 1
push 710
mov ecx,1C58A028
add ecx,0x0EC
push 0
push 0
call 9AE240

00B34C9D - 39 43 0C - cmp [ebx+0C],eax

00D7CC65 | 6A 00 | push 0 |
00D7CC67 | 6A 01 | push 1 |
00D7CC69 | 68 61CE2F01 | push elementclient.12FCE61 |
00D7CC6E | FF35 64DF5001 | push dword ptr ds:[150DF64] |
00D7CC74 | FF15 488A1E01 | call dword ptr ds:[<&send>] |

00D7CBA1 | FF75 10 | push dword ptr ss:[ebp+10] |
00D7CBA4 | FF75 0C | push dword ptr ss:[ebp+C] |
00D7CBA7 | E8 24000000 | call elementclient.D7CBD0 |

00D7D8FC | FF75 0C | push dword ptr ss:[ebp+C] |
00D7D8FF | 8B8E AC000000 | mov ecx,dword ptr ds:[esi+AC] |
00D7D905 | 57 | push edi |
00D7D906 | FFB6 B0000000 | push dword ptr ds:[esi+B0] |
00D7D90C | E8 0FF2FFFF | call elementclient.D7CB20 |

00D7D701 | 66:8906 | mov word ptr ds:[esi],ax | esi:L"萨ā"
00D7D704 | 6A 00 | push 0 |
00D7D706 | 8D45 DC | lea eax,dword ptr ss:[ebp-24] |
00D7D709 | 8BCF | mov ecx,edi | 1C58A028
00D7D70B | 50 | push eax |
00D7D70C | E8 9F000000 | call elementclient.D7D7B0 |

//send packet
00CD2984 | 8A4D 08 | mov cl,byte ptr ss:[ebp+8] |
00CD2987 | B8 28000000 | mov eax,28 | 28:'('
00CD298C | 66:8906 | mov word ptr ds:[esi],ax |
00CD298F | 0FB645 0C | movzx eax,byte ptr ss:[ebp+C] |
00CD2993 | 66:8946 04 | mov word ptr ds:[esi+4],ax |
00CD2997 | 8B45 10 | mov eax,dword ptr ss:[ebp+10] |
00CD299A | 8946 06 | mov dword ptr ds:[esi+6],eax |
00CD299D | 8A45 14 | mov al,byte ptr ss:[ebp+14] |
00CD29A0 | 884E 02 | mov byte ptr ds:[esi+2],cl |
00CD29A3 | 8846 03 | mov byte ptr ds:[esi+3],al |
00CD29A6 | 8B0D 20205001 | mov ecx,dword ptr ds:[1502020] |
00CD29AC | 6A 0A | push A | 包长度
00CD29AE | 56 | push esi | 数据
00CD29AF | 8B49 20 | mov ecx,dword ptr ds:[ecx+20] |
00CD29B2 | E8 C9AC0A00 | call elementclient.D7D680 |

00A4B758 | 50 | push eax |
00A4B759 | A3 CC235001 | mov dword ptr ds:[15023CC],eax |
00A4B75E | E8 AD432800 | call elementclient.CCFB10 |

00A408FE | C787 D4040000 FA00000 | mov dword ptr ds:[edi+4D4],FA | edi+4D4:L" "
00A40908 | FFB7 D4040000 | push dword ptr ds:[edi+4D4] | edi+4D4:L" "
00A4090E | 8B4F 1C | mov ecx,dword ptr ds:[edi+1C] |
00A40911 | E8 EAAA0000 | call elementclient.A4B400 |


009AE282 | FF75 14 | push dword ptr ss:[ebp+14] |
009AE285 | 8911 | mov dword ptr ds:[ecx],edx |
009AE287 | FF75 10 | push dword ptr ss:[ebp+10] |
009AE28A | FF75 0C | push dword ptr ss:[ebp+C] |
009AE28D | FF75 08 | push dword ptr ss:[ebp+8] |
009AE290 | E8 DB463200 | call elementclient.CD2970 |


04B6E034 28 00 00 01 00 00 10 07 00 00 DE FF 3F 01 28 E0 (.........Þÿ?.(à
0028 0100 0000 0710 0000

# 从逆向的角度看 C++

# 虚函数

  • 虚函数地址表(虚表)
    1. 定义:当类中定义有虚函数时,编译器会把该类中所有虚函数的首地址保存在一张地址表中,即虚函数地址表。
    2. 虚表信息在编译后被链接到执行文件中,因此所获得的虚表地址是一个固定的地址。
    3. 虚表中虚函数的地址排列顺序依据虚函数在类中的声明顺序而定。
  • 虚表指针

    • 同时编译器还会在类的每个对象添加一个隐藏数据成员,称为虚表指针,保存着虚表的首地址,用于记录和查找虚函数。
    • 虚表指针的初始化是通过编译器在构造函数中插入代码实现的。由于必须初始化虚表指针,编译器会提供默认的构造函数。
  • 虚函数调用过程

    • 虚表间接寻址访问:
      使用对象的指针或引用调用虚函数。根据对象的首地址,取出相应的虚表指针,在虚表查找对应的虚函数的首地址,并调用执行。
    • 直接调用访问:
      使用对象调用虚函数,和调用普通成员函数一样。
    • 虚函数的识别:
    • 类中隐式定义一个数据成员
    • 数据成员在首地址处,占 4 字节
    • 构造函数初始化该数据成员为某个数组的首地址
    • 地址属于数据区,相对固定的地址
    • 数组的成员是函数指针
    • 函数被调用方式是 thiscall
    • 构造函数与析构函数都会将虚表指针设置为当前对象所属类中的虚表地址。
    • 构造函数中是完成虚表指针的初始化,此时虚表指针并没有指向虚表函数。
    • 执行析构函数时,其对象的虚表指针已经指向某个虚表首地址。虚函数是在还原虚表指针,让其指向自身的虚表首地址,防止在析构函数中调用虚函数时取到非自身虚表

img

img

1
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
#include "stdafx.h"

#include <stdio.h>
#include <windows.h>
#include <iostream>
using namespace std;

class base_class
{
private:
int m_base;
public:
virtual void v_func1()
{
cout << "This is base_class's v_func1()" << endl;
}
virtual void v_func2()
{
cout << "This is base_class's v_func2()" << endl;
}
virtual void v_func3()
{
cout << "This is base_class's v_func3()" << endl;
}
};


int _tmain(int argc, _TCHAR* argv[])
{
base_class Myclass;
Myclass.v_func1();
Myclass.v_func2();
Myclass.v_func3();
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
000A1520 >  55              push    ebp
000A1521 8BEC mov ebp,esp
000A1523 81EC D0000000 sub esp,0xD0
000A1529 53 push ebx
000A152A 56 push esi
000A152B 57 push edi
000A152C 8DBD 30FFFFFF lea edi,dword ptr ss:[ebp-0xD0]
000A1532 B9 34000000 mov ecx,0x34
000A1537 B8 CCCCCCCC mov eax,0xCCCCCCCC
000A153C F3:AB rep stos dword ptr es:[edi]
000A153E 8D4D F4 lea ecx,dword ptr ss:[ebp-0xC]
000A1541 E8 A4FCFFFF call 000A11EA

000A1546 8D4D F4 lea ecx,dword ptr ss:[ebp-0xC]
000A1549 E8 D0FAFFFF call 000A101E

000A154E 8D4D F4 lea ecx,dword ptr ss:[ebp-0xC]
000A1551 E8 62FCFFFF call 000A11B8

000A1556 8D4D F4 lea ecx,dword ptr ss:[ebp-0xC]
000A1559 E8 3CFCFFFF call 000A119A

000A155E 33C0 xor eax,eax
000A1560 52 push edx
000A1561 8BCD mov ecx,ebp
000A1563 50 push eax
000A1564 8D15 88150A00 lea edx,dword ptr ds:[0xA1588]
000A156A E8 4FFBFFFF call 000A10BE
000A156F 58 pop eax
000A1570 5A pop edx
000A1571 5F pop edi
000A1572 5E pop esi
000A1573 5B pop ebx
000A1574 81C4 D0000000 add esp,0xD0
000A157A 3BEC cmp ebp,esp
000A157C E8 50FCFFFF call 000A11D1
000A1581 8BE5 mov esp,ebp
000A1583 5D pop ebp
000A1584 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
//000A11EA处的代码为
013E11EA /E9 61050000 jmp base_class::base_class

013E1750 > 55 push ebp
013E1751 8BEC mov ebp,esp
013E1753 81EC CC000000 sub esp,0xCC
013E1759 53 push ebx
013E175A 56 push esi
013E175B 57 push edi
013E175C 51 push ecx
013E175D 8DBD 34FFFFFF lea edi,dword ptr ss:[ebp-0xCC]
013E1763 B9 33000000 mov ecx,0x33
013E1768 B8 CCCCCCCC mov eax,0xCCCCCCCC
013E176D F3:AB rep stos dword ptr es:[edi]
013E176F 59 pop ecx
013E1770 894D F8 mov dword ptr ss:[ebp-0x8],ecx
013E1773 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
013E1776 C700 7C783E01 mov dword ptr ds:[eax],offset base_class::`vftable'
//此处为虚表的虚表指针eax中的值就是vftable的指针
013E177C 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
013E177F 5F pop edi
013E1780 5E pop esi
013E1781 5B pop ebx
013E1782 8BE5 mov esp,ebp
013E1784 5D pop ebp
013E1785 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
//000A101E处的代码为
013E101E /E9 AD050000 jmp base_class::v_func1

013E15D0 > 55 push ebp
013E15D1 8BEC mov ebp,esp
013E15D3 81EC CC000000 sub esp,0xCC
013E15D9 53 push ebx
013E15DA 56 push esi
013E15DB 57 push edi
013E15DC 51 push ecx
013E15DD 8DBD 34FFFFFF lea edi,dword ptr ss:[ebp-0xCC]
013E15E3 B9 33000000 mov ecx,0x33
013E15E8 B8 CCCCCCCC mov eax,0xCCCCCCCC
013E15ED F3:AB rep stos dword ptr es:[edi]
013E15EF 59 pop ecx
013E15F0 894D F8 mov dword ptr ss:[ebp-0x8],ecx
013E15F3 8BF4 mov esi,esp
013E15F5 A1 10B33E01 mov eax,dword ptr ds:[<&MSVCP90D.std::endl>]
013E15FA 50 push eax
013E15FB 68 00783E01 push 013E7800 ; ASCII 54,"his is base_class's v_func1()"
013E1600 8B0D 0CB33E01 mov ecx,dword ptr ds:[<&MSVCP90D.std::cout>] ; msvcp90d.std::cout
013E1606 51 push ecx
013E1607 E8 6BFBFFFF call 013E1177
013E160C 83C4 08 add esp,0x8
013E160F 8BC8 mov ecx,eax
013E1611 FF15 08B33E01 call dword ptr ds:[<&MSVCP90D.std::basic_ostream<char,std::char_traits<char> >::operator<<>] ; msvcp90d.std::basic_ostream<wchar_t,std::char_traits<wchar_t> >::operator<<
013E1617 3BF4 cmp esi,esp
013E1619 E8 B3FBFFFF call 013E11D1
013E161E 5F pop edi
013E161F 5E pop esi
013E1620 5B pop ebx
013E1621 81C4 CC000000 add esp,0xCC
013E1627 3BEC cmp ebp,esp
013E1629 E8 A3FBFFFF call 013E11D1
013E162E 8BE5 mov esp,ebp
013E1630 5D pop ebp
013E1631 C3 retn

000A11B8
000A119A
分别是func2和func3的代码

image-20221109211943483

# debug 版本和 release 版本

debug 版本:

image-20221109212625495

release 版本

image-20221109212653171

debug main

img

release main

image-20221109213010383

对于刚刚的虚函数

debug

image-20221109213138880

release

image-20221109213209477

# 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
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>

class employee
{
public:
employee() { printf("employee()!\n");}
~employee() { printf("~employee()!\n");}
};

class manager : public employee
{
public:
manager() { printf("manager()!\n");}
~manager() { printf("~maneger()!\n");}
};


int _tmain(int argc, _TCHAR* argv[])
{
manager my;
getchar();
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
010D13D0 >  55              push    ebp
010D13D1 8BEC mov ebp,esp
010D13D3 81EC D8000000 sub esp,0xD8
010D13D9 53 push ebx
010D13DA 56 push esi
010D13DB 57 push edi
010D13DC 8DBD 28FFFFFF lea edi,dword ptr ss:[ebp-0xD8]
010D13E2 B9 36000000 mov ecx,0x36
010D13E7 B8 CCCCCCCC mov eax,0xCCCCCCCC
010D13EC F3:AB rep stos dword ptr es:[edi]
//堆栈操作


010D13EE 8D4D FB lea ecx,dword ptr ss:[ebp-0x5]
010D13F1 E8 22FDFFFF call 010D1118
//010D1118 /E9 63030000 jmp manager::manager


010D13F6 8BF4 mov esi,esp
010D13F8 FF15 C4820D01 call dword ptr ds:[<&MSVCR90D.getchar>] ; msvcr90d.getchar
010D13FE 3BF4 cmp esi,esp
010D1400 E8 54FDFFFF call 010D1159
010D1405 C785 2CFFFFFF 0>mov dword ptr ss:[ebp-0xD4],0x0
010D140F 8D4D FB lea ecx,dword ptr ss:[ebp-0x5]
010D1412 E8 11FCFFFF call 010D1028
//010D1028 /E9 33050000 jmp manager::~manager


010D1417 8B85 2CFFFFFF mov eax,dword ptr ss:[ebp-0xD4]
010D141D 52 push edx
010D141E 8BCD mov ecx,ebp
010D1420 50 push eax
010D1421 8D15 44140D01 lea edx,dword ptr ds:[0x10D1444]
010D1427 E8 6AFCFFFF call 010D1096
010D142C 58 pop eax
010D142D 5A pop edx
010D142E 5F pop edi
010D142F 5E pop esi
010D1430 5B pop ebx
010D1431 81C4 D8000000 add esp,0xD8
010D1437 3BEC cmp ebp,esp
010D1439 E8 1BFDFFFF call 010D1159
010D143E 8BE5 mov esp,ebp
010D1440 5D pop ebp
010D1441 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
010D1118
010D1118 /E9 63030000 jmp manager::manager

010D1480 > 55 push ebp
010D1481 8BEC mov ebp,esp
010D1483 81EC CC000000 sub esp,0xCC
010D1489 53 push ebx
010D148A 56 push esi
010D148B 57 push edi
010D148C 51 push ecx
010D148D 8DBD 34FFFFFF lea edi,dword ptr ss:[ebp-0xCC]
010D1493 B9 33000000 mov ecx,0x33
010D1498 B8 CCCCCCCC mov eax,0xCCCCCCCC
010D149D F3:AB rep stos dword ptr es:[edi]
010D149F 59 pop ecx
010D14A0 894D F8 mov dword ptr ss:[ebp-0x8],ecx
010D14A3 8B4D F8 mov ecx,dword ptr ss:[ebp-0x8]
010D14A6 E8 FAFBFFFF call 010D10A5
//010D10A5 /E9 46040000 jmp employee::employee

-
010D14AB 8BF4 mov esi,esp
010D14AD 68 3C570D01 push 010D573C ; ASCII "manager()!\n"
010D14B2 FF15 BC820D01 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
010D14B8 83C4 04 add esp,0x4
010D14BB 3BF4 cmp esi,esp
010D14BD E8 97FCFFFF call 010D1159
010D14C2 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
010D14C5 5F pop edi
010D14C6 5E pop esi
010D14C7 5B pop ebx
010D14C8 81C4 CC000000 add esp,0xCC
010D14CE 3BEC cmp ebp,esp
010D14D0 E8 84FCFFFF call 010D1159
010D14D5 8BE5 mov esp,ebp
010D14D7 5D pop ebp
010D14D8 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
010D1412    E8 11FCFFFF     call    010D1028
010D1028 /E9 33050000 jmp manager::~manager

010D1560 > 55 push ebp
010D1561 8BEC mov ebp,esp
010D1563 81EC CC000000 sub esp,0xCC
010D1569 53 push ebx
010D156A 56 push esi
010D156B 57 push edi
010D156C 51 push ecx
010D156D 8DBD 34FFFFFF lea edi,dword ptr ss:[ebp-0xCC]
010D1573 B9 33000000 mov ecx,0x33
010D1578 B8 CCCCCCCC mov eax,0xCCCCCCCC
010D157D F3:AB rep stos dword ptr es:[edi]
010D157F 59 pop ecx
010D1580 894D F8 mov dword ptr ss:[ebp-0x8],ecx
010D1583 8BF4 mov esi,esp
010D1585 68 5C570D01 push 010D575C ; ASCII "~maneger()!\n"
010D158A FF15 BC820D01 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
010D1590 83C4 04 add esp,0x4
010D1593 3BF4 cmp esi,esp
010D1595 E8 BFFBFFFF call 010D1159
010D159A 8B4D F8 mov ecx,dword ptr ss:[ebp-0x8]
010D159D E8 8BFAFFFF call 010D102D
//010D102D /E9 9E050000 jmp employee::~employee


010D15A2 5F pop edi
010D15A3 5E pop esi
010D15A4 5B pop ebx
010D15A5 81C4 CC000000 add esp,0xCC
010D15AB 3BEC cmp ebp,esp
010D15AD E8 A7FBFFFF call 010D1159
010D15B2 8BE5 mov esp,ebp
010D15B4 5D pop ebp
010D15B5 C3 retn

private

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>

class employee
{
private:
employee() { printf("employee()!\n");}
~employee() { printf("~employee()!\n");}
};

class manager : public employee
{
private:
manager() { printf("manager()!\n");}
~manager() { printf("~maneger()!\n");}
};


int _tmain(int argc, _TCHAR* argv[])
{
manager my;
getchar();
return 0;
}

# 从反汇编角度看 this 指针

1
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
#include "stdafx.h"

struct MyStruct
{
int x ;
int y ;
};

//函数在结构体外部
void Max(MyStruct* str)
{
if (str->x > str->y)
printf("%d",str->x);
else
printf("%d",str->y);
}
int _tmain(int argc, _TCHAR* argv[])
{
MyStruct haha ;
haha.x = 1 ;
haha.y = 2 ;
Max(&haha);
printf("%d\n",sizeof(haha));
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
012A1450 >  55              push    ebp
012A1451 8BEC mov ebp,esp
012A1453 81EC D0000000 sub esp,0xD0
012A1459 53 push ebx
012A145A 56 push esi
012A145B 57 push edi
012A145C 8DBD 30FFFFFF lea edi,dword ptr ss:[ebp-0xD0]
012A1462 B9 34000000 mov ecx,0x34
012A1467 B8 CCCCCCCC mov eax,0xCCCCCCCC
012A146C F3:AB rep stos dword ptr es:[edi]
012A146E C745 F4 0100000>mov dword ptr ss:[ebp-0xC],0x1
012A1475 C745 F8 0200000>mov dword ptr ss:[ebp-0x8],0x2
012A147C 8D45 F4 lea eax,dword ptr ss:[ebp-0xC]
012A147F 50 push eax
012A1480 E8 CAFCFFFF call 012A114F
//012A114F /E9 5C020000 jmp Max


012A1485 83C4 04 add esp,0x4
012A1488 8BF4 mov esi,esp
012A148A 6A 08 push 0x8
012A148C 68 40572A01 push 012A5740 ; ASCII "%d\n"
012A1491 FF15 BC822A01 call dword ptr ds:[<&MSVCR90D.printf>>; msvcr90d.printf
012A1497 83C4 08 add esp,0x8
012A149A 3BF4 cmp esi,esp
012A149C E8 A4FCFFFF call 012A1145
012A14A1 33C0 xor eax,eax
012A14A3 52 push edx
012A14A4 8BCD mov ecx,ebp
012A14A6 50 push eax
012A14A7 8D15 C8142A01 lea edx,dword ptr ds:[0x12A14C8]
012A14AD E8 DAFBFFFF call 012A108C
012A14B2 58 pop eax
012A14B3 5A pop edx
012A14B4 5F pop edi
012A14B5 5E pop esi
012A14B6 5B pop ebx
012A14B7 81C4 D0000000 add esp,0xD0
012A14BD 3BEC cmp ebp,esp
012A14BF E8 81FCFFFF call 012A1145
012A14C4 8BE5 mov esp,ebp
012A14C6 5D pop ebp
012A14C7 C3 retn


当 haha.x-> 1 和 haha.x -> 2

image-20221110101344910

Max:

1
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
012A13B0 >  55              push    ebp
012A13B1 8BEC mov ebp,esp
012A13B3 81EC C0000000 sub esp,0xC0
012A13B9 53 push ebx
012A13BA 56 push esi
012A13BB 57 push edi
012A13BC 8DBD 40FFFFFF lea edi,dword ptr ss:[ebp-0xC0]
012A13C2 B9 30000000 mov ecx,0x30
012A13C7 B8 CCCCCCCC mov eax,0xCCCCCCCC
012A13CC F3:AB rep stos dword ptr es:[edi]
012A13CE 8B45 08 mov eax,dword ptr ss:[ebp+0x8]
012A13D1 8B4D 08 mov ecx,dword ptr ss:[ebp+0x8]
012A13D4 8B10 mov edx,dword ptr ds:[eax]
012A13D6 3B51 04 cmp edx,dword ptr ds:[ecx+0x4]
012A13D9 7E 1F jle short 012A13FA
012A13DB 8BF4 mov esi,esp
012A13DD 8B45 08 mov eax,dword ptr ss:[ebp+0x8]
012A13E0 8B08 mov ecx,dword ptr ds:[eax]
012A13E2 51 push ecx
012A13E3 68 3C572A01 push 012A573C ; ASCII "%d"
012A13E8 FF15 BC822A01 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
012A13EE 83C4 08 add esp,0x8
012A13F1 3BF4 cmp esi,esp
012A13F3 E8 4DFDFFFF call 012A1145
012A13F8 EB 1E jmp short 012A1418
012A13FA 8BF4 mov esi,esp
012A13FC 8B45 08 mov eax,dword ptr ss:[ebp+0x8]
012A13FF 8B48 04 mov ecx,dword ptr ds:[eax+0x4]
012A1402 51 push ecx
012A1403 68 3C572A01 push 012A573C ; ASCII "%d"
012A1408 FF15 BC822A01 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
012A140E 83C4 08 add esp,0x8
012A1411 3BF4 cmp esi,esp
012A1413 E8 2DFDFFFF call 012A1145
012A1418 5F pop edi
012A1419 5E pop esi
012A141A 5B pop ebx
012A141B 81C4 C0000000 add esp,0xC0
012A1421 3BEC cmp ebp,esp
012A1423 E8 1DFDFFFF call 012A1145
012A1428 8BE5 mov esp,ebp
012A142A 5D pop ebp
012A142B C3 retn


this 看 ecx

ecx 在内存中为:
image-20221110101730211

# 反汇编中构造函数和析构函数的识别

代码如下:

1
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
#include "stdafx.h"
#include <stdio.h>
#include <windows.h>

class MyTest
{
public:
MyTest();
~MyTest();
void SetTest(DWORD dwTest);
DWORD GetTest();
public:
DWORD m_dwTest;
};

MyTest::MyTest()
{
printf("1111\n");

}
MyTest::~MyTest()
{
printf("2222\n");
}


void MyTest::SetTest(DWORD dwTest)
{
this->m_dwTest = dwTest;
}

DWORD MyTest::GetTest()
{
return this->m_dwTest;
}


int _tmain(int argc, _TCHAR* argv[])
{
MyTest Test;
Test.SetTest(1);
int Number = Test.GetTest();
getchar();
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
00D11550 >  55              push    ebp
00D11551 8BEC mov ebp,esp
00D11553 6A FF push -0x1
00D11555 68 E846D100 push 00D146E8
00D1155A 64:A1 00000000 mov eax,dword ptr fs:[0]
00D11560 50 push eax
00D11561 81EC E4000000 sub esp,0xE4
00D11567 53 push ebx
00D11568 56 push esi
00D11569 57 push edi
00D1156A 8DBD 10FFFFFF lea edi,dword ptr ss:[ebp-0xF0]
00D11570 B9 39000000 mov ecx,0x39
00D11575 B8 CCCCCCCC mov eax,0xCCCCCCCC
00D1157A F3:AB rep stos dword ptr es:[edi]
00D1157C A1 0080D100 mov eax,dword ptr ds:[__security_coo>
00D11581 33C5 xor eax,ebp
00D11583 50 push eax
00D11584 8D45 F4 lea eax,dword ptr ss:[ebp-0xC]
00D11587 64:A3 00000000 mov dword ptr fs:[0],eax
00D1158D 8D4D EC lea ecx,dword ptr ss:[ebp-0x14]
00D11590 E8 D3FBFFFF call 00D11168
//00D11168 /E9 73020000 jmp MyTest::MyTest


00D11595 C745 FC 0000000>mov dword ptr ss:[ebp-0x4],0x0
00D1159C 6A 01 push 0x1
00D1159E 8D4D EC lea ecx,dword ptr ss:[ebp-0x14]
00D115A1 E8 CCFBFFFF call 00D11172
00D11172 /E9 49030000 jmp MyTest::SetTest


00D115A6 8D4D EC lea ecx,dword ptr ss:[ebp-0x14]
00D115A9 E8 3CFCFFFF call 00D111EA
00D111EA /E9 21030000 jmp MyTest::GetTest


00D115AE 8945 E0 mov dword ptr ss:[ebp-0x20],eax
00D115B1 8BF4 mov esi,esp
00D115B3 FF15 C492D100 call dword ptr ds:[<&MSVCR90D.getchar>; msvcr90d.getchar
00D115B9 3BF4 cmp esi,esp
00D115BB E8 8AFBFFFF call 00D1114A
00D115C0 C785 14FFFFFF 0>mov dword ptr ss:[ebp-0xEC],0x0
00D115CA C745 FC FFFFFFF>mov dword ptr ss:[ebp-0x4],-0x1
00D115D1 8D4D EC lea ecx,dword ptr ss:[ebp-0x14]
00D115D4 E8 4FFAFFFF call 00D11028
00D115D9 8B85 14FFFFFF mov eax,dword ptr ss:[ebp-0xEC]
00D115DF 52 push edx
00D115E0 8BCD mov ecx,ebp
00D115E2 50 push eax
00D115E3 8D15 1016D100 lea edx,dword ptr ds:[0xD11610]
00D115E9 E8 A3FAFFFF call 00D11091
00D115EE 58 pop eax
00D115EF 5A pop edx
00D115F0 8B4D F4 mov ecx,dword ptr ss:[ebp-0xC]
00D115F3 64:890D 0000000>mov dword ptr fs:[0],ecx
00D115FA 59 pop ecx
00D115FB 5F pop edi
00D115FC 5E pop esi
00D115FD 5B pop ebx
00D115FE 81C4 F0000000 add esp,0xF0
00D11604 3BEC cmp ebp,esp
00D11606 E8 3FFBFFFF call 00D1114A
00D1160B 8BE5 mov esp,ebp
00D1160D 5D pop ebp
00D1160E 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
00D11590    E8 D3FBFFFF     call    00D11168
//00D11168 /E9 73020000 jmp MyTest::MyTest

00D113E0 > 55 push ebp
00D113E1 8BEC mov ebp,esp
00D113E3 81EC CC000000 sub esp,0xCC
00D113E9 53 push ebx
00D113EA 56 push esi
00D113EB 57 push edi
00D113EC 51 push ecx
00D113ED 8DBD 34FFFFFF lea edi,dword ptr ss:[ebp-0xCC]
00D113F3 B9 33000000 mov ecx,0x33
00D113F8 B8 CCCCCCCC mov eax,0xCCCCCCCC
00D113FD F3:AB rep stos dword ptr es:[edi]
00D113FF 59 pop ecx
00D11400 894D F8 mov dword ptr ss:[ebp-0x8],ecx
00D11403 8BF4 mov esi,esp
00D11405 68 3C67D100 push 00D1673C ; ASCII "1111\n"
00D1140A FF15 CC92D100 call dword ptr ds:[<&MSVCR90D.printf>] ; msvcr90d.printf
00D11410 83C4 04 add esp,0x4
00D11413 3BF4 cmp esi,esp
00D11415 E8 30FDFFFF call 00D1114A
00D1141A 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
00D1141D 5F pop edi
00D1141E 5E pop esi
00D1141F 5B pop ebx
00D11420 81C4 CC000000 add esp,0xCC
00D11426 3BEC cmp ebp,esp
00D11428 E8 1DFDFFFF call 00D1114A
00D1142D 8BE5 mov esp,ebp
00D1142F 5D pop ebp
00D11430 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
00D115A1    E8 CCFBFFFF     call    00D11172
00D11172 /E9 49030000 jmp MyTest::SetTest

00D114C0 > 55 push ebp
00D114C1 8BEC mov ebp,esp
00D114C3 81EC CC000000 sub esp,0xCC
00D114C9 53 push ebx
00D114CA 56 push esi
00D114CB 57 push edi
00D114CC 51 push ecx
00D114CD 8DBD 34FFFFFF lea edi,dword ptr ss:[ebp-0xCC]
00D114D3 B9 33000000 mov ecx,0x33
00D114D8 B8 CCCCCCCC mov eax,0xCCCCCCCC
00D114DD F3:AB rep stos dword ptr es:[edi]
00D114DF 59 pop ecx

//以下四句为 Test.SetTest(1);的关键代码
00D114E0 894D F8 mov dword ptr ss:[ebp-0x8],ecx ;ecx此时为this, i = this
00D114E3 8B45 F8 mov eax,dword ptr ss:[ebp-0x8] ;ebp-8是this, eax=this
00D114E6 8B4D 08 mov ecx,dword ptr ss:[ebp+0x8] ;ecx = 1 ,ebp+8是从外面传来的1
00D114E9 8908 mov dword ptr ds:[eax],ecx ;this->m = 1
//

00D114EB 5F pop edi
00D114EC 5E pop esi
00D114ED 5B pop ebx
00D114EE 8BE5 mov esp,ebp
00D114F0 5D pop ebp
00D114F1 C2 0400 retn 0x4


1
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
00D115A9    E8 3CFCFFFF     call    00D111EA
00D111EA /E9 21030000 jmp MyTest::GetTest

00D11510 > 55 push ebp
00D11511 8BEC mov ebp,esp
00D11513 81EC CC000000 sub esp,0xCC
00D11519 53 push ebx
00D1151A 56 push esi
00D1151B 57 push edi
00D1151C 51 push ecx
00D1151D 8DBD 34FFFFFF lea edi,dword ptr ss:[ebp-0xCC]
00D11523 B9 33000000 mov ecx,0x33
00D11528 B8 CCCCCCCC mov eax,0xCCCCCCCC
00D1152D F3:AB rep stos dword ptr es:[edi]
00D1152F 59 pop ecx


//以下三句为int Number = Test.GetTest();的关键代码
00D11530 894D F8 mov dword ptr ss:[ebp-0x8],ecx
00D11533 8B45 F8 mov eax,dword ptr ss:[ebp-0x8]
00D11536 8B00 mov eax,dword ptr ds:[eax]


00D11538 5F pop edi
00D11539 5E pop esi
00D1153A 5B pop ebx
00D1153B 8BE5 mov esp,ebp
00D1153D 5D pop ebp
00D1153E C3 retn


# 动态调试基础

# 断点

1
2
3
4
5
6
7
8
9
10
11
12
DR0~DR3这四个寄存器是断点地址存储器,用于保存断点的地址
DR6是调试状态寄存器用于指明DR0~DR3寄存器中哪一个产生了调试异常
DR6寄存器使用比特位B0~B3来指明DR0~DR3中哪个产生了调试异常
DR7是断点属性控制器
DR7寄存器分别保存着DR0~DR3的断点地址对应的断点的属性,属性有如下几种:
L0~L3:这个比特位等于1,则断点为本地断点.
G0~G3:这个比特位等于1,则断点为全局断点.
R/W0-R/W3:
00:执行断点
01:数据写入断点
10:I/0读写断点
11:读写断点()读取指令不算)

DR0-DR3 硬件断点

img

删除硬件断点 调试 -> 硬件断点

img

设置硬件断点 右键 -> 断点 -> 硬件执行

image-20221112211213663

取消硬件断点: hd 基址

对当前地址做中断,即添加硬件断点: hr 基址

常用断点:软件断点,内存写入,内存访问,硬件执行

img

ALT+M 内存镜像

img

# 常用调试器

# OllyICE

查看在内存在对应的字节码,即指令在内存中的机器码

img

一般来说程序映射到内存中基址从 0040 开始,即 PE 文件头

映射到代码段需要 + 1000,前 1000 是 PE 头信息

img

8087 中浮点运行,反调试会用到

image-20221112214201466

用于轴坐标,准心,多用于游戏分析

image-20221112214252305

# xdbg

1. 线程

image-20221112214836603

stdcall 一般在函数内部做 ret 做堆栈平衡

windbg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
0:004> g
(a5c.9dc): Break instruction exception - code 80000003 (first chance)
eax=7efda000 ebx=00000000 ecx=00000000 edx=77a4f7ea esi=00000000 edi=00000000
eip=779c000c esp=02adff5c ebp=02adff88 iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000246
ntdll!DbgBreakPoint:
779c000c cc int 3
0:004> bp kernel32!OpenProcess
0:004> u kernel32!OpenProcess
kernel32!OpenProcess:
76831986 8bff mov edi,edi
76831988 55 push ebp
76831989 8bec mov ebp,esp
7683198b 5d pop ebp
7683198c eb05 jmp kernel32!OpenProcess+0xd (76831993)
7683198e 90 nop
7683198f 90 nop
76831990 90 nop
0:004> u 76831993
kernel32!OpenProcess+0xd:
76831993 ff2554098376 jmp dword ptr [kernel32+0x10954 (76830954)]
76831999 90 nop
7683199a 90 nop
7683199b 90 nop
7683199c 90 nop
7683199d 90 nop
kernel32!WaitForMultipleObjectsEx:
7683199e 8bff mov edi,edi
768319a0 55 push ebp

VT

需要开启

image-20221113204110511

# 动态调试基础二

ALT+F9 返回调用本函数的函数的代码

rpb radmin.rpb 存在 127.0.0.1 的配置信息

image-20221116181239485

# 硬件断点

  1. Intel 80306 以上的 CPU 给我们提供了调试寄存器用于软件调试,硬件断点是通过设置调试寄存器实现的。

image-20221116183140142

调试时线程挂起

image-20221116183413279

右键 resume all 激活

实现硬件断点,首先要获取当前线程环境

1
2
3
4
//获取线程环境
CONTEXT g_Context = { 0 };
g_Context.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(hThread, &g_Context);

在 CONTEXT 结构体中,存放了诸多当前线程环境的信息

image-20221116184014697

# IDA 动态分析基础

导入表:编译时倒入,通过静态链接到输入表

1.debug -> local 32 debug

image-20221117210719141

IDA 远程调试

将 dbgsrv 放到要调试的文件夹中

# PE 文件结构

image-20221119190434856

# Windows 系统安全基础

进程句柄表

img

当你输入用户名和口令时,Winlogon 将其发送给一个叫做 LSASS 的子进程。如果你执行对服务器或工作站的本地登录,LSASS 进程将在安全数据库(亦即 SAM)中查对有关用户名和口令。Winlogon 有两个子进程,即服务控制器和 LSASS。

image-20221120184811911

image-20221120184825383

Explorer 进程 在 Windows 系列的操作系统中,运行时都会启动一个名为 Explorer.exe 的进程。这个进程主要负责显示系统桌面上的图标以及任务栏。

Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

John Doe WeChat Pay

WeChat Pay

John Doe Alipay

Alipay

John Doe PayPal

PayPal