luckfollowme arm学习篇 9 --在C内写arm介绍
# luckfollowme arm学习篇 9 --在C内写arm介绍这章是为了给 手写 **inline Hook** 做些准备的,要手写 **inline hook**无非两种,
一种就像 dobby 那样插入字节码
```c
void EmitInt64(int64_t value) {
buffer_->Emit64(value);
}
```
还有一种就是 **内联汇编 asm** 或者 通过 **gnu assembly**
本次目标就是做到能够在 **C内联 ARM**
## Inline ASM
**Inline Assembly**一般来说 c/c++ 的编译器都自带 汇编编译器
最明显的关键字就是 **asm**
为了我们能够使用 **asm**
您需要在 **CMakeLists.txt** 中加入:
```cmake
# 启动支持 asm 汇编
enable_language(ASM)
```
而 **asm** 一般跟**volatile** 一起使用,来告诉编译器编译的时候不要进行优化。
并且它还可以根据 **输出操作数** 和 **输入操作数** 来跟 **C/C++** 的变量进行交互
```
asm volatile("add %0,%1,%2":输出操作符:输入操作符1,输入操作符2)
```
组合起来就是这样:
```c
int a = 100;
int b = 10;
int sum;
asm volatile("add %0,%1,%2":"=r"(sum):"r"(a),"r"(b)); # sum = a + b
```
其中 **r** 代表通用寄存器**=r** 表示输出到的位置
下面是一个完整的例子:
首先您创建一个 **AssemblyDemo.cpp**
代码如下:
```c
#include "stdio.h"
#include "stdint.h"
uint64_t _global_sum;
int sum(int a,int b){
int _sum;
asm volatile("add %w0 ,%w1,%w2":"=r"(_sum):"r"(a),"r"(b));
return _sum;
}
int main()
{
//1. 测试b 指令 死循环
while (true)
{
int ret = sum(10,99);
//3. 测试传参 和 调用方法
printf("sum:%d\n",ret);
getchar();
}
return 0;
}
```
随后您修改 **ndk_builder.py** 和 **CMakeLists.txt**
```cmake
# CMakeLists.txt
set(assmbly_demo1
./source/AssemblyDemo1.cpp
)
add_executable(assmblyDemo1 ${assmbly_demo1})
```
记住,我们只测试 **arm64** 的,您应该修改 **abis** 否则 如果变成其他 **arch** 架构的平台 ,您写的汇编指令肯定是不通过的。
```python
# abis = ["x86", "x86_64", "armeabi-v7a", "arm64-v8a"]
abis = ["arm64-v8a"]
```
编译出来后,我们拖入到 **ida pro** 静态分析一下:
</p>
</p>
可以看到 生成的 **add** 貌似并不是一条指令,这是正常的,因为它涉及到 **读取和写入局部变量** 也就是 **输出操作符和输出操作符**的位置
```asm
# 分配局部变量空间
SUB SP, SP, #0x10
# 储存 x0-x1 的参数
STR W0,
STR W1,
# 读取 x0-x1 的参数
LDR W8,
LDR W9,
# a + b
ADD W8, W8, W9
# _sum = a + b
STR W8,
# 读取 _sum 到 x0 准备返回值
LDR W0,
# 释放局部变量空间
ADD SP, SP, #0x10
# 返回
RET
```
## GAS
一般来说 写 gas 更方便一点,它的全称叫 **GNU ASM**
它里面有很多 **Assembly Directive(汇编指示符)** 来帮助我们编写各种格式的汇编
汇编指示符有点多,直接说长篇大论有点麻烦,也不容易理解,我们通过 将 原有代码 转换成 **GAS** 的汇编文件进行分析
为了分析的更方便,后面我都写成 **.c** 文件 而不是 **.cpp**
1.首先我们编写简单的**HelloAssembly.c**文件
```c
#include <stdio.h>
int add(int a,int b){
return a + b + 100;
}
int main(int argc, char const *argv[])
{
while (1)
{
int a = 10;
int b = 30;
int sum = add(a,b);
printf("sum:%d\n",sum);
getchar();
}
/* code */
return 0;
}
```
2.通过**llvm**的**aarch64**交叉编译器转换成**gas**的汇编文件
aarch64 的教程编译器 我们使用clang 它位于 ndk 的
**ndk\25.1.8937393\toolchains\llvm\prebuilt\windows-x86_64\bin\aarch64-linux-android21-clang.cmd**
使用方式如下:
```
aarch64-linux-android21-clang -S 转换成assembly -o 输出位置 来源文件
```
以下是我写的 **arrch64-assembly.bat**下的脚本:
```
C:\Users\hp\AppData\Local\Android\Sdk\ndk\25.1.8937393\toolchains\llvm\prebuilt\windows-x86_64\bin\aarch64-linux-android21-clang-S -o ./helloassembly.S HelloAssembly.c
```
运行完您的 ``helloassmbly.S``内容如下:
```asm
.text
.file "HelloAssembly.c"
.globl add // -- Begin function add
.p2align 2
.type add,@function
add: // @add
.cfi_startproc
// %bb.0:
sub sp, sp, #16
.cfi_def_cfa_offset 16
str w0,
str w1,
ldr w8,
ldr w9,
add w8, w8, w9
add w0, w8, #100
add sp, sp, #16
ret
.Lfunc_end0:
.size add, .Lfunc_end0-add
.cfi_endproc
// -- End function
.globl main // -- Begin function main
.p2align 2
.type main,@function
main: // @main
.cfi_startproc
// %bb.0:
sub sp, sp, #48
stp x29, x30, // 16-byte Folded Spill
add x29, sp, #32
.cfi_def_cfa w29, 16
.cfi_offset w30, -8
.cfi_offset w29, -16
stur wzr,
stur w0,
str x1,
b .LBB1_1
.LBB1_1: // =>This Inner Loop Header: Depth=1
mov w8, #10
str w8,
mov w8, #30
str w8,
ldr w0,
ldr w1,
bl add
str w0,
ldr w1,
adrp x0, .L.str
add x0, x0, :lo12:.L.str
bl printf
bl getchar
b .LBB1_1
.Lfunc_end1:
.size main, .Lfunc_end1-main
.cfi_endproc
// -- End function
.type .L.str,@Object // @.str
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "sum:%d\n"
.size .L.str, 8
.ident "Android (8490178, based on r450784d) clang version 14.0.6 (https://android.googlesource.com/toolchain/llvm-project 4c603efb0cca074e9238af8b4106c30add4418f6)"
.section ".note.GNU-stack","",@progbits
```
然后我们通过这个 **.S** 的 **gas** 汇编文件 通过 cmake 生成:
```cmake
set(assmbly_gas_demo1
./source/assembly/helloassembly.S
)
add_executable(assmblygasdemo1 ${assmbly_gas_demo1})
```
您可以发现它依旧可以生成可执行程序。
也就是说我们可以通过 **gas** 汇编文件写出跟 **c** 一样的效果
汇编各位目前都看的懂吧,那么就差一点, gas 的 **assembly directive** 汇编助记符
下章我们将会逐步分析 熟悉的汇编哈哈哈 学习学习 这里sum函数汇编需要知道申请函数空间,栈顶指针sub就是申请空间,add就是函数执行完恢复之前的lr返回上函数,中间就是取参数和做加法了{:1_892:} debug_cat 发表于 2023-12-11 16:48
这里sum函数汇编需要知道申请函数空间,栈顶指针sub就是申请空间,add就是函数执行完恢复之前的lr返回上函 ...
这里错了,如果叶子函数是不会恢复lr,非叶子函数需要恢复现场,跑到上一个函数调用结束位置继续跑{:1_906:}
页:
[1]