目录

任鸟飞笔记

小知识

16进制 4x xx xx xx一般是浮点数(正数)

16进制 Cx xx xx xx一般是浮点数(负数)

st0 st1 st2 … 是浮点栈

sar 算术右移 每右移一位,相当于除以一次2

shl 算术左移 每左移一位,相当于乘以一次2

1
2
mov ecx,[edx+esi]
#当出现两个寄存器的时候,99%其中一个是0

执行到返回 代表已经到达最外层 或者 VM阻断,可堆栈跳出

汇编

OD指令

dd 查看dword

db 查看字节集

寄存器

EAX call的返回值

ECX call的参数

汇编指令

  • AND 指令在两个操作数的对应位之间进行(按位)逻辑与(AND)操作,并将结果存放在目标操作数中。

  • XOR 指令在两个操作数的对应位之间进行(按位)逻辑异或(XOR)操作,并将结果存放在目标操作数中.

    1
    2
    
    xor  eax,eax	 #eax清0
    xor  eax,A3F838C	#简单的异或加密(可直接异或解密)
    
  • TEST 指令在两个操作数的对应位之间进行 AND 操作,并根据运算结果设置符号标志位、零标志位和奇偶标志位。

    1
    
    test eax,eax	#判断eax是否为0
    
  • FLD类似于 PUSH指令 将浮点数据压入协处理器的堆栈中

    1
    2
    
    fld dword ptr [eax+4]
    #将[eax+4]的值传入st0
    
  • FSTP类似于 POP指令 与FST相类似,FSTP执行完后,需要进行堆栈的弹出操作,堆栈将发生变化。

    1
    2
    
    fstp dword ptr [ebp-28]
    #将st0中的值传入后面的地址[ebp-28],并出栈
    
  • FST 将协处理器堆栈栈顶的数据传送到目标操作数中。在进行数据传送时,系统自动根据控制寄存器中舍入控制位的设置把栈顶浮点数舍入成相应精度的数据。

    1
    2
    
    fst dword ptr [ebp-28]
    #将st0中的值传入后面的地址[ebp-28],不出栈
    
  • FADD类似于 ADD指令 源操作数与目的操作数相加

  • FDIV 指令执行目的操作数除以源操作数,被除数保存在目的操作数中。目的操作数总是一个寄存器,源操作数可以为寄存器或者内存操作数。其语法与 FADD 和 FSUB 相同。

  • FXCH 交换寄存器内容

    1
    2
    
    FXCH ;ST0和ST1内容交换
    FXCH ST(i) ;ST0和STi内容交换
    
  • MOVZX 指令(进行全零扩展并传送)将源操作数复制到目的操作数,并把目的操作数 0 扩展到 16 位或 32 位。这条指令只用于无符号整数.

  • MOVSX 指令(进行符号扩展并传送)将源操作数内容复制到目的操作数,并把目的操作数符号扩展到 16 位或 32 位。这条指令只用于有符号整数.

发包函数

  • send
  • WSASend
  • sendto

send在64位系统上是ws2_32.send

WSPSend (在同一个电脑上位置是固定的)

DXF单机版 私服

找到WSPSend

  1. 附加上口袋西游(或者任何的send发包的软件都可以),跳到send位置
  2. send 第三个call下断(win7是第三个,win10是第四个),断下后F7进入,这就是WSPSend的位置。
  3. 附加到目标进程,跳转到这个地址,下断。
  4. 有时WSASend胡乱断,不是真正的发包 ,根据它的特征码,去搜索。查找–>所有命令 ,输入特征码,然后右键–>在每个命令上下设置断点。然后在所有被断下的位置做标记(注释),并取消断点。然后去执行喊话等发包操作,看在哪断下。如果不断,说明在刚才被标记的里面。之后逐一下断测试。

线程发包

1
2
3
graph RL

d[ ]-->e["检测是否真的需要发包(封包的加密解密)"]-->f[send]

线程发包的特点:

  1. 找到发包函数

  2. 下断会立刻断下,并且非常频繁

  3. 无论做任何动作断下的,我们的返回堆栈都是一样的

  4. 找到跳出线程的办法

确定堆栈平衡

方法:

  1. 在call的栈上双击
  2. F8步过call
  3. 此时看栈上差值

应该熟悉的汇编指令

mov 赋值 和 lea 传址

push 入栈 和 pop 出栈

pushad 和 popad 调用call之前pushad,调用call之后popad,保证寄存器不受影响

add 加法 和 sub 减法

inc 自加1 和 dec 自减1

mul 乘法

div 除法

call 和 retn

xor 异或 相同为0,不同为1

and 都是1为1

or 有一个是1为1

xor 是可逆运算,即 A xor B == C 那么 A xor C == B

堆栈详解

  1. 什么是堆栈

    堆栈就是一种数据项按序排列的数据结构,只能在栈顶对数据项进行插入和删除,其它位置只能改变数值。

    特点是先进后出,像个杯子。

  2. 堆栈指针寄存器

    ESP 栈顶指针

    EBP 栈底指针(本层CALL的栈底)

    ESI 堆栈地址

  3. 改变堆栈的操作

    影响esp的操作

    push eax == sub esp,4 + mov [esp],eax

    pop eax == mov eax,[esp] + add esp,4

    call 1234 == push eip + jmp 1234

    retn 8 == pop eip + add esp,8

    add esp,xxx

    sub esp,xxx

  4. 堆栈中储存的都是 参数、局部变量、操作中间数

    那么我们怎么去找他的来源

    例如[ebp-4] [ebp+8] [esp+20]

    实例来说明

    1. 通过ebp来传递参数和局部变量

      头部有push ebp

      1
      2
      3
      4
      5
      6
      
      push	ebp
      mov 	ebp,esp  #头部
      
      
      mov 	esp,ebp  #尾部
      pop 	ebp
      

      [ebp-8] 第二个局部变量

      [ebp-4] 第一个局部变量

      [ebp] 保存着上一层的ebp值

      [ebp+4] call返回到xxxxxxxx == call xxxxxxxx 相当于 push eip

      [ebp+8] 第一个参数

      [ebp+c] 第二个参数

      同时知道了 ebp为什么是栈底指针

      四种情况:

      • ebp+10 如果ESP与EBP相差的远,说明ebp不是堆栈地址,而是普通寄存器,10就是偏移,追ebp就可以了
      • ebp+10 如果相差不远,ebp就是堆栈地址,返回上一层追参数
      • ebp+0或者ebp+4 需要注意一下,这可能是堆栈检测
      • ebp-x 说明是局部变量,来源必须是本条到函数头部,或者是这之前的call里面。
    2. 通过esp来传递参数和局部变量

      头部没有push ebp

      1
      2
      3
      
      mov 	ebpesp	#这样的代码,往往是用esp来表示
      
      sub 	espxxxx 	#开辟局部变量
      

      算到头部,如果是[esp+],那么是参数

      如果是[esp-],那么是局部变量,是在本层找来源

      更简单的方法是,直接断下看堆栈,看看这个堆栈指针,指向的位置是,上一层返回的上面还是下面。

      但是不用全部计算,往往一个call都是自身堆栈平衡的。

      我们只需要计算头部的一些堆栈处理。

      [esp-8] 第二个局部变量

      [esp-4] 第一个局部变量

      [esp] 指向call返回到

      [esp+4] 第一个参数

      [esp+8] 第二个参数

      1. 首先要确定 我们要追的[esp+]是参数还是局部变量

        两种方法可以确定

        • 一条一条往上推,推到最上面,如果是esp+,那就是参数,如果是esp-那就是局部变量

        • 直接下断,看堆栈,如果在第一个call返回到的下面是参数,在上面是局部变量

      2. 如果是局部变量,有两种追法

        • 一条一条向上堆,并根据 影响esp的操作 来计算偏移
        • 需要下两个断点,第一个断点为当前[esp+xx]处,第二个断点为:在头部位置找到 sub esp xxxx,在它的下一条下断。连断两次之后,看esp变化是多少,比如是n,那么在头部要追的就是[esp+xx-n]。 在堆栈内双击esp+xx-n,做个定位,然后在头部断点位置按F8,找哪句改变了堆栈内esp+xx-n的值即可。
      3. 如果是参数

        看它在堆栈内,第一个call返回到的下面第几个位置,就代表它是上层第几个参数

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      
      graph 
      z(["首先要确定<br>我们要追的[esp+]<br>是参数<br>还是局部变量"]) --> z1 & z4
      z1{"方法1<br>一条一条往上推<br>推到最上面"} --> z2 & z3
      z2["如果是esp+"]-->z6
      z3["如果是esp-"]-->z5
      z4{"方法2<br>直接下断<br>看堆栈"} --> z4a & z4b
      z4a[如果在第一个call<br>返回到的上面]--> z5
      z4b[如果在第一个call<br>返回到的下面]--> z6
      z5["局部变量"]-->z7 & z9
      z6["参数"]-->z14
      z7["方法1<br>一条一条向上堆"]-->
      z8["并根据影响esp的<br>操作来计算偏移"]
      z9["方法2<br>需要下两个断点"]-->
      z10["第一个断点为:<br>当前[esp+xx]处"]-->
      z11["第二个断点为:<br>在头部位置找到<br>sub esp xxxx<br>在它的下一条下断"]-->
      z12["连断两次之后,<br>看esp变化是多少,<br>比如是n,<br>那么在头部要追的就是<br>[esp+xx-n]"]-->
      z13["在堆栈内双击esp+xx-n,<br>做个定位,<br>然后在头部断点位置按F8,<br>找哪句改变了堆栈内<br>esp+xx-n的值即可。"]
      z14["看它在堆栈内<br>第一个call返回到<br>的下面第几个位置,<br>就代表它是上层<br>第几个参数"]
      

数据结构

一般我们接触的数据结构有三种

数组、链表、二叉树,也就是数据不同的排列方式

二叉树

1
2
3
4
5
6
7
graph TD;
 a--"[a+0]"-->b; 
 a--"[a+8]"-->c;
 b --"[[a+0]+0]"--> d;
 b --"[[a+0]+8]"--> e;
 c--"[[a+8]+0]"-->f;
 e--"[[[a+0]+8]+0]"-->g;
1
2
3
4
5
6
7
8
00433FA6	cmp dword ptr [eax+10],esi	#可能是ID
00433FA9	jge short 00433FB0
00433FAB	mov eax,dword ptr [eax+8]		#右
00433FAE	jmp short 00433FB4
00433FB0	mov	edx,eax
00433FB2	mov	eax,dword ptr [eax]			#左
00433FB4	cmp byte ptr [eax+231],0		#判断
00433FBB	je	short 00433FA6

https://cdn.jsdelivr.net/gh/xinqinew/pic@main/img/image-20220224220235821.png

https://cdn.jsdelivr.net/gh/xinqinew/pic@main/img/image-20220224220759338.png

链表

1
2
graph LR
a-->b["[a+x]"] -->c["[[a+x]+x]"]-->d["[[[a+x]+x]+x]"]
1
2
3
4
5
010A1BE0	cmp dword ptr [edx+C],esi		#链表循环
010A1BE3	je 	short 010A1BF8
010A1BE5	mov edx,dword ptr [edx]			#链表循环
010A1BE7	test edx,edx
010A1BE9	jnz short 010A1BE0

转移追踪代码

在call内部栈顶下条件断点

1
2
3
[esp]!=0A2BA8C
或者
[esp]!=0A2BA8C && [esp]!=0A12D1D

在call内部被追踪处下条件断点,断点内容和上面的相同也可以。

跳出系统领空

在OD内存窗口对X地址下断,游戏暂停在系统领空后,跳出系统领空的方法:

  1. 在堆栈中查找X,找到后看是不是程序领空,如果不是的话,ctrl+L继续查找下一个。
  2. 如果找到程度领空的话,ctrl+f9返回。跳到第5步。
  3. 如果找不到的话,可以在堆栈中搜X省略后两位的数据,看能不能搜到。
  4. 实在找不到,就在堆栈里肉眼找相近的地址。(前提是程序领空)
  5. 找到后,前往“返回到”对应的地址。看“相近的地址”是第几个参数,加上相应的偏移。每有一个参数就加4 。之后就可以正常逆向了。

汇编实例

x,z,y [[[D0DF1C]+1C]+28]+3C,40,44

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
    #运行时出错
    mov edx,[00D0DF1C]
    mov eax,[edx+1C]
    mov ecx,[eax+28]
    fld [ecx+44]	#y 入浮点栈
    fld [ecx+40]	#z
    fld [ecx+3C]	#x
    fstp [esp]	#x	出浮点栈
    fstp [esp+4]	#z
    fstp [esp+8]	#y	手动建立一个堆栈的结构体
    push 0
    push 10
    push 00D1F930
    lea eax,[esp+0C]	#结构体指针
    push eax
    mov ecx,00D0DEC8
		call 00445AB0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    mov edx,[00D0DF1C]
    mov eax,[edx+1C]
    mov ecx,[eax+28]
    push [ecx+44]	#y 
    push [ecx+40]	#z
    push [ecx+3C]	#x
    push 0
    push 10
    push 00D1F930
    lea eax,[esp+0C]	#结构体指针
    push eax
    mov ecx,00D0DEC8
		call 00445AB0
		add esp,0C	#堆栈平衡

防检测

调试检测

VM检测(虚拟机)

EXE检测

DLL检测

行为检测

抓特征

CRC检测

数据检测

堆栈检测

Call的其它检测

内存检测

2D 3D朝向算法

  1. 2D平面 传奇 只有8个方向(一个内存地址)

  2. 2D平面 剑灵 勇者 均匀的增加了更多角度 (一个内存地址)

  3. 2D平面 天刀 射雕 (两个内存地址)

    https://ghproxy.com/https://raw.githubusercontent.com/xinqinew/pic/main//img/image-20210820105648894.png

  4. 3D球型 天刀狩猎 (三个内存地址)

    https://ghproxy.com/https://raw.githubusercontent.com/xinqinew/pic/main//img/image-20210820105448503.png

    1/z = 距离/z差 1/x = 距离/x差

    距离的平方=z差平方+l的平方

    l的平方=x差的平方+y差的平方

  5. 3D球型 CF 逆战 (三个内存地址)

    https://ghproxy.com/https://raw.githubusercontent.com/xinqinew/pic/main//img/image-20210820110750521.png