2016976438 发表于 2023-5-1 12:29

luckfollowme arm 学习篇 08 - dobby function inline hook

# luckfollowme arm 学习篇 08 - dobby function inline hook



接下来才是真正的进入主题,也是最关键的地方。



## 查看 dobby inline hook 变化

首先,我们得分析 它在我们 **sum** 方法做了什么,导致跳转到 **fake_sum** 的 **hook** 方法。

又能够通过 **orig_sum** 执行到原方法。



1.首先我们启动下项目,卡在 没有**hook** 的地方

```
adb shell "/data/local/tmp/dobby_test"
输入任意键开始hook...
```



2.然后用ida pro 先分析一遍没有 hook 时候的字节码

</p>



</p>

摘抄出来是这样的,由于之前是分析过了的,您直接看我的注释就可以了

``` asm
# 开辟局部变量空间
SUB             SP, SP, #0x40
# 取出 a9 a10
LDR             X9,
LDR             X8,
# 将 a1 - a8 放入局部变量中
STR             W0,
STR             W1,
STR             W2,
STR             W3,
STR             W4,
STR             W5,
STR             W6,
STR             W7,
STR             X9,
STR             X8,
# sum = a1 + a2 + a3 + a4 + a5 + a6 + a7+ a8
LDR             W8,
LDR             W9,
ADD             W8, W8, W9
LDR             W9,
ADD             W8, W8, W9
LDR             W9,
ADD             W8, W8, W9
LDR             W9,
ADD             W8, W8, W9
LDR             W9,
ADD             W8, W8, W9
LDR             W9,
ADD             W8, W8, W9
LDR             W9,
ADD             W8, W8, W9
# 储存 sum 变量
STR             W8,
# sum2 = a9 + a10
LDR             X8,
LDR             X9,
ADD             X8, X8, X9
STR             X8,
# if (sum > 10)
LDR             W8,
SUBS            W8, W8, #0xA
# if (sum > 10) 条件不成立
B.LE            loc_753EDCE934

# if (sum > 10) 条件成立
B               loc_753EDCE91C

; ---------------------------------------------------------------------------

loc_753EDCE91C
# 读取 并 转换 64 位 的 sum
LDRSW         X8,
# 读取 sum2
LDR             X9,
# sum + sum2
ADD             X8, X8, X9
# 获取 _global_sum 全局变量
ADRP            X9, #_global_sum@PAGE
# _global_sum = sum + sum2
STR             X8,
B               loc_753EDCE934

; ---------------------------------------------------------------------------

loc_753EDCE934                        
# 读取 全局变量
ADRP            X8, #_global_sum@PAGE
LDR             X8,
# 放入 x0 转换 int32
MOV             W0, W8
# 释放局部变量空间
ADD             SP, SP, #0x40 ; '@'
# 返回 x0
RET
```



接下来我们输入任意键 完成 **hook**。

```
输入任意键开始hook...


hook sum:0x753edce888
进入到 hook 方法,参数:1,2,3,4,5,6,7,8,10,20
调用 原方法获取的结果:66
sum:66

```

您在看 **ida pro**可能您并不会看到改变,但是您如果在 **sum** 方法的 **0xC** 字节后下个断点

</p>



</p>



此时您就可以发现到变化了

可以发现它修改了我们的 **0XC** 个字节。

其余的并没有任何的变化。

所以我们分析下这 **12** 个字节

```asm
11 00 00 90 31 A2 28 91       ADRL            X17, _ZL8fake_sumiiiiiiiimm
                                                                  
20 02 1F D6                   BR            X17
```

## Dobby inline asm 分析

dobby 生成的 **ADRL** 明显是个伪指令 。 因为他有着 **8个** 字节的长度 ,ida pro 给我们分析成了符号 **_ZL8fake_sumiiiiiiiimm**

所以这明显就是将 **fake_sum** hook 的方法的 **地址** 赋值给了 **X17**

后面的**BR** 是一个绝对跳转。 相较于 **B #立即数**          绝对跳转指令         **BR(branch register)**可以根据寄存器中的地址 跳转到任意地方,寻址更大。

稍微翻译一下就是

```asm
# 后期 pc 当前内存对齐的地址
adrp         x17, =当前PC内存对齐地址
# 添加 fake_sum 的偏移
add         x17, x17, #0xa28
# 绝对跳转
BR          X17
```



### fake_sum arrch64

我们通过 **ADRL            X17, _ZL8fake_sumiiiiiiiimm** 进去看看 **fake_sum** 生成的汇编指令:

```asm
FF 83 01 D1                   SUB             SP, SP, #0x60
FD 7B 05 A9                   STP             X29, X30,
FD 43 01 91                   ADD             X29, SP, #0x50 ; 'P'
A9 0B 40 F9                   LDR             X9,
A8 0F 40 F9                   LDR             X8,
A0 C3 1F B8                   STUR            W0,
A1 83 1F B8                   STUR            W1,
A2 43 1F B8                   STUR            W2,
A3 03 1F B8                   STUR            W3,
A4 C3 1E B8                   STUR            W4,
A5 83 1E B8                   STUR            W5,
A6 43 1E B8                   STUR            W6,
A7 03 1E B8                   STUR            W7,
E9 17 00 F9                   STR             X9,
E8 13 00 F9                   STR             X8,
A1 C3 5F B8                   LDUR            W1,
A2 83 5F B8                   LDUR            W2,
A3 43 5F B8                   LDUR            W3,
A4 03 5F B8                   LDUR            W4,
A5 C3 5E B8                   LDUR            W5,
A6 83 5E B8                   LDUR            W6,
A7 43 5E B8                   LDUR            W7,
AB 03 5E B8                   LDUR            W11,
EA 17 40 F9                   LDR             X10,
E8 13 40 F9                   LDR             X8,
E9 03 00 91                   MOV             X9, SP
2B 01 00 B9                   STR             W11,
2A 05 00 F9                   STR             X10,
28 09 00 F9                   STR             X8,
C0 FF FF 90 00 08 3B 91       ADRL            X0, unk_753EDC6EC2
8B 79 00 94                   BL            unk_753EDED0D0

08 01 00 F0                   ADRP            X8, #_ZL8orig_sum@page; orig_sum
08 11 47 F9                   LDR             X8, ; orig_sum
A0 C3 5F B8                   LDUR            W0,
A1 83 5F B8                   LDUR            W1,
A2 43 5F B8                   LDUR            W2,
A3 03 5F B8                   LDUR            W3,
A4 C3 5E B8                   LDUR            W4,
A5 83 5E B8                   LDUR            W5,
A6 43 5E B8                   LDUR            W6,
A7 03 5E B8                   LDUR            W7,
EB 17 40 F9                   LDR             X11,
E9 13 40 F9                   LDR             X9,
EA 03 00 91                   MOV             X10, SP
4B 01 00 F9                   STR             X11,
49 05 00 F9                   STR             X9,
00 01 3F D6                   BLR             X8

E0 1F 00 B9                   STR             W0,
E1 1F 40 B9                   LDR             W1,
C0 FF FF 90 00 EC 27 91       ADRL            X0, unk_753EDC69FB
76 79 00 94                   BL            unk_753EDED0D0

E0 1F 40 B9                   LDR             W0,
FD 7B 45 A9                   LDP             X29, X30,
FF 83 01 91                   ADD             SP, SP, #0x60 ; '`'
C0 03 5F D6                   RET
```

代码有点多,我们逐步分析,在此之前你先记住 **fake_name** 方法体内部是啥样子,到时候我们一一对比。

```c
install_hook_name(sum,int, int a1, int a2,int a3,int a4,int a5,int a6,int a7,int a8,uint64_t a9,uint64_t a10){
    printf("进入到 hook 方法,参数:%d,%d,%d,%d,%d,%d,%d,%d,%llu,%llu \n",a1,a2,a3,a4,a5,a6,a7,a8,a9,a10);
    int ret =   orig_sum(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10);
    printf("调用 原方法获取的结果:%d\n",ret);
    return ret;
}
```

### 栈

首先第一行指令是 **SUB             SP, SP, #0x60** 表示 局部变量使用的空间

```
|-60                        <=== SP 指针位置
|-5C
|-58
|-54
|-50
|-4C
|-48
|-44
|-40
|-3C
|-38
|-34
|-30
|-2C
|-28
|-24
|-20
|-1C
|-18
|-14
|-10
|-C
|-8
|-4
| 0               
```



**STP**

**STP (Store Pair)** 用于储存一对寄存器到 寄存器中

所以 下面这条指令执行后

```asm
STP             X29, X30,
```

栈应该是这样的:

```
|-60                        <=== SP 指针位置
|-5C
|-58
|-54
|-50                        
|-4C
|-48                        
|-44
|-40
|-3C
|-38
|-34
|-30
|-2C
|-28
|-24
|-20
|-1C
|-18
|-14
|-10                x29
|-C
|-8                        x30
|-4
| 0                        
```

**x29 和 x30 **是两个 关键的寄存器。

它们分别代表着 **帧指针 FP**和 **链接指针 LR**

LR 各位肯定是知道的。 FP 按照网上的意思是说 它代表着**栈低**SP 代表着**栈顶**

### 参数

下面就是传过来的参数 保存在 栈中,只不过要注意的是,它并不是直接使用 **SP** 而是从栈的 **-10** 的地方开始计算。 这样可以很方便获取栈中 **a9-a10** 的参数



```asm
# 从 sp -10 的地方开算
FD 43 01 91                   ADD             X29, SP, #0x50 ; 'P'
# 根据调用约定 x0-x7 额外的参数放入栈中
# 所以 x9 = a10
A9 0B 40 F9                   LDR             X9,
# x8 = a9
A8 0F 40 F9                   LDR             X8,
# stur 貌似是有符号的 str 指令
# 所以下面储存a1-a8 的参数
A0 C3 1F B8                   STUR            W0,
A1 83 1F B8                   STUR            W1,
A2 43 1F B8                   STUR            W2,
A3 03 1F B8                   STUR            W3,
A4 C3 1E B8                   STUR            W4,
A5 83 1E B8                   STUR            W5,
A6 43 1E B8                   STUR            W6,
A7 03 1E B8                   STUR            W7,
# 储存 a10 和 a9
E9 17 00 F9                   STR             X9,
E8 13 00 F9                   STR             X8,
```

那么现在换算栈中的样子:

```asm
|-60                        <=== SP 指针位置
|-5C
|-58
|-54
|-50                        
|-4C
|-48                        
|-44
|-40                a10
|-3C
|-38                a9
|-34
|-30                a8
|-2C                a7
|-28                a6
|-24                a5
|-20                a4
|-1C                a3
|-18                a2
|-14                a1
|-10                x29               
|-C
|-8                        x30
|-4
| 0                        a10
| 4
| 8                        a9
```

### 调用方法

之前的指令应该都只是对标着传参

```c
int fake_sum(sum,int, int a1, int a2,int a3,int a4,int a5,int a6,int a7,int a8,uint64_t a9,uint64_t a10)
```



后面的代码是:

```c
printf("进入到 hook 方法,参数:%d,%d,%d,%d,%d,%d,%d,%d,%llu,%llu \n",a1,a2,a3,a4,a5,a6,a7,a8,a9,a10);
```

来看看对应的 **arm** 段

```asm
# 首先准备x1 ~ x7 的参数 也就是 a1,a2,a3,a4,a5,a6,a7
LDUR            W1,
LDUR            W2,
LDUR            W3,
LDUR            W4,
LDUR            W5,
LDUR            W6,
LDUR            W7,
# x11 = a8
LDUR            W11,
# 获取 a9 和 a10
LDR             X10,
LDR             X8,
# 获取栈顶 x9 = sp
MOV             X9, SP
# 多余的参数储存在栈中
# = a8
STR             W11,
# = a9
STR             X10,
# = a10
STR             X8,
# 获取 字符串的地址 (进入到 hook 方法,参数...)
ADRL            X0, aHookDDDDDDDDLl   ; "进入到 hook 方法,参数:%d,%d,%d,%d,%d,%d,%d,%d,"...
# 调用 printf 并 记录 LR
BL            printf
```

此时调用后栈应该变成了:

```
|-60                a8      <=== SP 指针位置
|-5C
|-58                a9
|-54
|-50                a10      
|-4C
|-48                        
|-44
|-40                a10
|-3C
|-38                a9
|-34
|-30                a8
|-2C                a7
|-28                a6
|-24                a5
|-20                a4
|-1C                a3
|-18                a2
|-14                a1
|-10                x29               
|-C
|-8                        x30
|-4
| 0                        a10
| 4
| 8                        a9
```





### orig_sum

接下来是重点。我们分析下 **orig_sum** 是如何调用原有的方法。

首先我们看下传参调用的过程:

```asm
# 获取 orig_sum 方法地址放入到 x8 中
ADRP            X8, #_ZL8orig_sum@PAGE; orig_sum
LDR             X8, ; orig_sum
# 准备 x0-x7 的参数
LDUR            W0,
LDUR            W1,
LDUR            W2,
LDUR            W3,
LDUR            W4,
LDUR            W5,
LDUR            W6,
LDUR            W7,
# 其余的参数放入到 栈中
# x11 = a9
LDR             X11,
# x9 = a10
LDR             X9,
# x10 = sp
MOV             X10, SP
# = a9
STR             X11,
# = a10
STR             X9,
# 调用 orig_sum
BLR             X8
```

接下来我们进入 **orig_sum** 看看它做了什么:

</p>



</p>

不知道各位熟悉这个代码么?

如果大伙不记得看下面的图, **(只看前12个字节)**

</p>



</p>

这个图就是之前 **没有hook前** sum方法的图。

由于之前的忘记 按 Q转换 并且没有显示字节码。 但我可以肯定告诉你,它们是一样的



我把 **orig_sum** 截取出来大伙看看

```asm
FF 03 01 D1                   SUB             SP, SP, #0x40 ; '@'
E9 23 40 F9                   LDR             X9,
E8 27 40 F9                   LDR             X8,

                              _753EDCE894
51 00 00 58                   LDR             X17, =0x753EDCE894
20 02 1F D6                   BR            X17
```

**前面12 个字节**都好说,就是之前 **sum** 被替换成 跳转指令之前的样子

下面是目前被替换成的代码:

```asm
# 获取 fake_sum 地址 放入 x17
11 00 00 90 31 A2 28 91       ADRL            X17, _ZL8fake_sumiiiiiiiimm
# 绝对地址跳转 到 fake_sum                                       
20 02 1F D6                   BR            X17
```

那后面的两条指令是跳到哪里呢?

```asm
51 00 00 58                   LDR             X17, =0x753EDCE894
20 02 1F D6                   BR            X17
```

我们在 **ida pro** 按 **G** 跳转下:

</p>



</p>

可以很明显的发现**=0x753EDCE894** 地址就是 原来 **sum**的 **12字节** 后面的地方。

那么当执行完 **sum** 方法后,又如何跳到 我们的 **fake_sum** 接着往下执行呢?

之前说过 , **RET** 指令会根据 **x30(LR)** 链接寄存器的值来跳转。

所以这也是为什么 **fake_sum** 在调用 **orig_sum** 的时候 选择了 **BLR** 指令。

而**orig_sum** 则只使用 **BR** 指令

下面是摘取的 **fake_sum** 和 **orig_sum** 的片段:

```asm
# fake_sum 调用 orig_sum 的片段
ADRP            X8, #_ZL8orig_sum@PAGE; orig_sum
LDR             X8, ; orig_sum
.....
BLR             X8
```

```asm
# orig_sum 跳到 sum 的片段
LDR             X17, =0x753EDCE894
BR            X17
```

### RET

剩余 **fake_sum** 的返回也没啥好说的。 就是正常返回。



```asm
# 将返回值 放入 x0
E0 1F 00 B9                   STR             W0,
# 通过 printf 打印下返回值
E1 1F 40 B9                   LDR             W1,
C0 FF FF 90 00 EC 27 91       ADRL            X0, unk_753EDC69FB
76 79 00 94                   BL            printf

E0 1F 40 B9                   LDR             W0,
# 还原 x29 x30 寄存器
FD 7B 45 A9                   LDP             X29, X30,
# 还原堆栈
FF 83 01 91                   ADD             SP, SP, #0x60 ; '`'
# 返回 x0
C0 03 5F D6                   RET
```



## 总结

基于 dobby 对于 function inline hook 所做的事情简单来说就 2个 步骤

1. **创建 orig_sum 的内存 储存 sum 前12个 字节 并携带 BR 跳回到后12个字节的地方**

2. **修改 sum 的前 12 个字节 跳到 fake_sum 中**



debug_cat 发表于 2023-12-11 16:35

这个就是套娃了,通过跳转指令,把入口转移到新函数中,新函数执行原来函数,执行完lr保存了最最开始的位置继续执行。
最近才有时间去细看跳转和返回,不然真的看不懂文章{:1_937:}
页: [1]
查看完整版本: luckfollowme arm 学习篇 08 - dobby function inline hook