arm汇编逆向

本文介绍ARM及AARCH64架构下的汇编语言编程基础,包括环境搭建、基本指令集、数据类型处理、函数调用约定等,并探讨了如何利用汇编语言进行系统级开发和调试。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

环境搭建,与初步认识

下载android ndk,将clang添加都环境变量中

#include<stdio.h>
int main()
{
	printf("hello arm\n");
	return 0;
}

编译

64位	
clang -target aarch64-linux-android22 hello.c -o hello

32位
clang -target armv7a-linux-android22 hello.c -o hello
clang -target arm-linux-android22 hello.c -o hello

thumb
clang -target arm-linux-android22 -mthumb hello.c -o hello

clang -target arm-linux-android22 -E hello.c -o hello.i
clang -target arm-linux-android22 -S hello.i -o hello.S
clang -target arm-linux-android22 -c hello.S -o hello.o
clang -target arm-linux-android22 hello.o -o hello

静态编译
clang++ -target arm-linux-android22 hello.cpp -o hello -static-libstdc++
去除符号
strip hello

调试 : 使用android ndk中一套的clang,gdbserver,gdb

下载gdb的插件 git clone https://github.com/hugsy/gef-legacy.git
最新gef的很费事,需要的gdb是与python3绑定编译的,但是android ndk最新版的gdb是python2.7绑定的,上面的gef是python2.7绑定的

# 手机端
# 将android ndk中的gdbserver推送到手机
./gdbserver :11678 ./hello

# 电脑端
# 端口转发
adb forward tcp:11678 tcp:11678

# 进入gdb ~/Library/Android/sdk/ndk/22.0.7026061/prebuilt/darwin-x86_64/bin
target remote localhost:11678
# 就可以进行远端调试了

一些调试指令

下断 : b main
下地址断点 : b *0xabcde
显示汇编 : disass...  main
继续执行 : c,执行到断点
单步步过 
   n 源码层面的单步步过
   ni  在汇编层面的单步步过
info b 查看断点

一些指令

LSL 逻辑左移
LSR 逻辑右移
ROR 循环右移 
ASR 算术右移
RRX 扩展的循环右移

B   强制跳转指令
BL  带返回的跳转指令, LR
BLX 带返回和带状态切换的跳转指令
BX  带状态切换的跳转指令

数据处理指令
    mov, add, sub, and, eor, orr, bic

    mov r0, r1
    add r0, r1, r2  @ r0 = r1 + r2
    sub r0, r1, r2
    and r0, r1, r2  @ r0 = r1 & r2
    eor r0, r1, r2  @ r0 = r1 ^ r2
    orr r0, r1, r2  @ r0 = r0 | r2
    bic r0, r1, #0xF @0x12345678 -> 0x12345670

乘法指令
    MUL r0, r1, r2   @ r0 = r1 * r2
    MLA r0, r1, r2, r3  @ r0 = r1 * r2 + r3
    SMULL r0, r1, r2, r3  @ r0 = (r2 * r3)的低32位,r1 = (r2 * r3)的高32位,
    SMLAL r0, r1, r2, r3  @ r0 = (r2 * r3)的低32位 + r0,r1 = (r2 * r3)的高32位 + r1,
    UMULL r0, r1, r2, r3  @ r0 = (r2 * r3)的低32位,r1 = (r2 * r3)的高32位,
    UMLAL r0, r1, r2, r3  @ r0 = (r2 * r3)的低32位 + r0,r1 = (r2 * r3)的高32位 + r1,

内存访问指令
    ldr  4字节读取
    ldrb 1字节读取
    ldrh 2字节读取

    str  4字节写入
    strb 1字节写入
    strh 2字节写入

指令集

arm官方文档 :https://developer.arm.com/documentation#sort=relevancy
在线解析opcode & arm指令 :https://armconverter.com/

armv7指令
在这里插入图片描述
1、imm24会扩展为32位 :imm24后添加两个bit的0,再在前面添加扩展为32位 -> imm32
2、再加上3级流水线 :imm32的实际地址 <= imm32 + 8

当imm24为 1 :
0、				00 0000 0000 0000 0000 0000 01
1、0000 0000 0000 0000 0000 0000 0000 0100
2、0000 0000 0000 0000 0000 0000 0000 1100

thumb && 32-thumb
.code 16 表示接下来的指令是thumb
在这里插入图片描述

ldr	r1, [sp, #8]
1001 1 001  0000 0010 

32-thumb
在这里插入图片描述

movs	r2, #0 (movs.w r2, #0 :w寻址较大,32-thumb)
1111 0 00  0101 1111 0        0010 
1111 0000 0101 1111 0000 0010 0000 0000 

0x F05F 0200
5F F0 00 02

区分 arm , thumb, 32-thumb
arm = 32位
thumb = 16位
32-thumb = 16位 + 16位

aarch64 & arm
x0(64位的寄存器) - w0(低32位寄存器)
x0 - x30
x0-x7 : 参数
x0 :返回值
x8 : 结果位置寄存器
x9 - x15 : 临时寄存器
x16 - x17 : 内部过程调用寄存器
x19 - x28 : 备份
x29 : FP
x30 : LR
专门的: SP , PC , CPSR

返回:ret
在这里插入图片描述
sf == 1 为 64位

逆向识别

数据类型

strb:存储一个字节
char ch1 = 'a';
char ch2 = 'b';

mov	r0, #97
strb	r0, [r11, #-13]
mov	r0, #98
strb	r0, [r11, #-14]

strh:存储两个字节
short s1 = 0x10;
short s2 = 0x20;

mov	r0, #16
strh	r0, [r11, #-16]
mov	r0, #32
strh	r0, [r11, #-18]

int n1 = 0x100;
int n2 = 0x200;

str:存储四个字节
mov	r0, #256
str	r0, [r11, #-24]
mov	r0, #512
str	r0, [r11, #-28]

浮点型(实际逆向,看不出来,靠猜)
float f1 = 9.0f; // 9.0 -> 1001 -> 1.001 * 2^3 -> 最高位(31位)0表示正,指数位(30~23位)3+127,小数位001(22~0)后面用0补全
float f2 = 19.0f;

mov	r0, #17825792
orr	r0, r0, #1073741824
str	r0, [r11, #-32]

mov	r0, #26738688
orr	r0, r0, #1073741824
str	r0, [sp, #36]

double
double d1 = 29.0; 11101 > 1.1101 * 2^4 ->指数位(30~20位)4+1023 , 小数位1101(19~0)后面用0补全
double d2 = 39.0;

// 没有优化过的 double d1 = 29.0;
mov	r0, #3997696
orr	r0, r0, #1073741824
str	r0, [sp, #28]
// 优化过的 double d2 = 39.0;
str	r2, [sp, #24]
ldr	r0, .LCPI0_0

.LCPI0_0:
	.long	1078165504              @ 0x40438000

运算符

// 加
ldrb	r0, [r11, #-13]
ldrb	r1, [r11, #-14]
add	r0, r0, r1
strb	r0, [sp, #15]

// 减
ldrh	r0, [r11, #-16]
ldrh	r1, [r11, #-18]
sub	r0, r0, r1
strh	r0, [sp, #12]

// 乘
ldr	r0, [r11, #-24]
ldr	r1, [r11, #-28]
mul	r2, r0, r1
str	r2, [sp, #8]

// 除
mov	r0, #4096
str	r0, [sp, #8]
ldr	r0, [sp, #8]
ldr	r1, .LCPI0_1
smull	r2, r3, r0, r1   @ r3高32位,r2低32位,smull,符号填充
add	r1, r3, r0
asr	r3, r1, #2
add	r1, r3, r1, lsr #31
sub	r1, r1, r1, lsl #3
add	r0, r0, r1
str	r0, [sp, #4]

分支与跳转

// if-else
ldr	r0, [sp, #4]
cmp	r0, #5
bgt	.LBB0_2
b	.LBB0_1

// case :
// case少的时候,就像if-else
// case多的时候,查表

// goto
b	.LBB0_1	(有b的,大部分不是goto,是循环,switch,if等等)

循环

// for - 比较for循环的最大值
.LBB0_1:                                @ =>This Inner Loop Header: Depth=1
	ldr	r0, [sp, #16]
	cmp	r0, #19
	bgt	.LBB0_4
	b	.LBB0_2
.LBB0_2:                                @   in Loop: Header=BB0_1 Depth=1
	ldr	r1, [sp, #16]
	ldr	r0, .LCPI0_0
.LPC0_0:
	add	r0, pc, r0
	bl	printf
	b	.LBB0_3
.LBB0_3:                                @   in Loop: Header=BB0_1 Depth=1
	ldr	r0, [sp, #16]
	add	r0, r0, #1
	str	r0, [sp, #16]
	b	.LBB0_1

// while - 先判断,再执行。将比较的内容减,在开始的位置比较是否等于0
.LBB0_4:
	mov	r0, #30
	str	r0, [sp, #12]
	b	.LBB0_5
.LBB0_5:                                @ =>This Inner Loop Header: Depth=1
	ldr	r0, [sp, #12]
	cmp	r0, #0
	beq	.LBB0_7
	b	.LBB0_6
.LBB0_6:                                @   in Loop: Header=BB0_5 Depth=1
	ldr	r1, [sp, #12]
	ldr	r0, .LCPI0_1
.LPC0_1:
	add	r0, pc, r0
	bl	printf
	ldr	r1, [sp, #12]
	sub	r1, r1, #1
	str	r1, [sp, #12]
	b	.LBB0_5

// do-while  - 先执行,再判断。将比较的内容减,在结束的位置比较是否等于0
.LBB0_7:
	mov	r0, #40
	str	r0, [sp, #8]
	b	.LBB0_8
.LBB0_8:                                @ =>This Inner Loop Header: Depth=1
	ldr	r0, [sp, #8]
	sub	r1, r0, #1
	str	r1, [sp, #8]
	ldr	r1, .LCPI0_2
.LPC0_2:
	add	r1, pc, r1
	str	r0, [sp, #4]            @ 4-byte Spill
	mov	r0, r1
	ldr	r1, [sp, #4]            @ 4-byte Reload
	bl	printf
	b	.LBB0_9
.LBB0_9:                                @   in Loop: Header=BB0_8 Depth=1
	ldr	r0, [sp, #8]
	cmp	r0, #0
	bne	.LBB0_8

函数

ARM中 参数较多是,前4个参数放R0~r3寄存器,其余的放到栈中
func_more_arg('c',15,1,2,3,4,5);

mov	r0, sp
mov	r1, #5
str	r1, [r0, #8]
mov	r1, #4
str	r1, [r0, #4]
mov	r1, #3
str	r1, [r0]
mov	r0, #99
mov	r1, #15
mov	r2, #1
mov	r3, #2
bl	func_more_arg

aarch64寄存器多,直接寄存器传,超过8个参数,还是放到栈中
func_arg('a',12,3333,11.0,22.0);
func_more_arg('c',15,1,2,3,4,5,6,7);

mov	w0, #97
mov	w1, #12
mov	w2, #3333
fmov	s0, #11.00000000
fmov	d1, #22.00000000
bl	func_arg

mov	w0, #99
mov	w1, #15
mov	w2, #1
mov	w3, #2
mov	w4, #3
mov	w5, #4
mov	w6, #5
mov	w7, #6
mov	x8, sp
mov	w9, #7
str	w9, [x8]
bl	func_more_arg

函数的返回值给R0
aarch64,返回较复杂的结构体时,x8作为结构的地址寄存器

变量

静态变量,全局变量,默认在data分区

数组与指针

__aeabi_memclr4 数组初始化

结构体

取值,赋值,返回值
union : 同一个地址赋值取值

ida python pycharm编写插件环境

在这里插入图片描述
添加路径
/Applications/IDA Pro 7.0/ida.app/Contents/MacOS/python

cpp逆向

1、没有任何方法和成员变量的类(空类) : 实例化只占1个字节
2、成员变量
只有一个char成员变量的类:只占一个字节
只有一个int成员变量的类 : 只占四个字节
3、成员函数,有一个隐藏的this指针
去除符号之后,start->__libc_init的参数就是main函数
arm 中,r0为this指针
成员函数,不占对象的内存
4、构造函数
构造函数,在反编译中,以r0作为返回值
5、全局对象
在静态分析的时候,没有被初始化,看不到啥东西
在.init_array中进行初始化 (.init_array比main的调用时机早)
6、静态对象
在创建的代码中初始化,只初始化一下
反汇编中会看到:判断静态对象有没有被初始化
可以通过判断代码,识别是不是一个静态变量 :__cxa_guard_acquire …
7、析构函数
在函数末尾,__cax_atexit,主动释放时调用
8、虚函数
虚函数的地址放在虚表中
在构造函数中:将虚表的地址放在对象的R0地址上
识别:
LDR r1,[r0]
LDR r1,[r0,#8]
mov LR,PC
BX R1
给人看-把虚表当做结构体:
定义结构体,名字就是类名_virtual_table,结构体的成员名就是虚函数名
选中,按T,转换为定义的结构体成员
9、继承/覆盖/重载
10、虚表继承,覆盖、重载
11、纯虚函数
12、RTTI
通过RTTI找到基类,子类的关系
13、异常
__cxa_allocate_exception __cxa_throw
__cxa_begin_cache __cxa__end_cache
无符号:__Unwind_Resume ,unwind_phase2(抛出异常的地方)特征

汇编开发

内联汇编

//内联汇编
//https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
//内联汇编调用syscall, naked
//CMakeLists.txt 加载汇编文件
// frida libc open, openat
long test_inline_asm(long m) {
    long result = 0;
#if defined(__arm__)
    //arm
    __asm__ __volatile__( // __volatile__ 方式被优化
            "mov r0, %[m]\r\n"
            "add r0, r0 \r\n"
            "mov %[result], r0\r\n"
            :[result] "=r" (result)     //传出来的结果
            :[m] "r" (m)                //传进去的参数
            );
#elif defined(__aarch64__)
    __asm__ __volatile__(
            "mov x0, %[m]\r\n"
            "add x0, x0, #40\r\n"
            "mov %[result], x0\r\n"
            :[result] "=r" (result)
            :[m] "r" (m)
            );
#endif
    return result;
}

调用syscall

//http://androidxref.com/9.0.0_r3/xref/bionic/libc/kernel/uapi/asm-arm/asm/unistd-common.h
//#define __NR_SYSCALL_BASE 0
//#define __NR_read (__NR_SYSCALL_BASE + 3)
//#define __NR_open (__NR_SYSCALL_BASE + 5)
//#define __NR_close (__NR_SYSCALL_BASE + 6)
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

char buffer[0x100] = {0};
syscall(__NR_read, fd, buffer, 0x100);

C代码中写syscall

__attribute__((naked))
long raw_syscall(long __number, ...) {
    __asm__ __volatile__("MOV             R12, SP\r\n"
                         "STMFD           SP!, {R4-R7}\r\n"
                         "MOV             R7, R0\r\n"
                         "MOV             R0, R1\r\n"
                         "MOV             R1, R2\r\n"
                         "MOV             R2, R3\r\n"
                         "LDMIA           R12, {R3-R6}\r\n"
                         "SVC             0\r\n"    // 进入内核
                         "LDMFD           SP!, {R4-R7}\r\n"
                         "mov             pc, lr");
}

直接在汇编文件中写

    .text
    .global raw_syscall
    .type raw_syscall,@function

raw_syscall:
        MOV             X8, X0
        MOV             X0, X1
        MOV             X1, X2
        MOV             X2, X3
        MOV             X3, X4
        MOV             X4, X5
        MOV             X5, X6
        SVC             0
        RET
extern "C" long raw_syscall(long __number, ...);

CMakeLists.txt

enable_language(C ASM)
if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch")
	set_source_files_properties(syscall64.s PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp")
	add_library( # Sets the name of the library.
             native-lib
             # Sets the library as a shared library.
             SHARED
             syscall64.s
             # Provides a relative path to your source file(s).
             native-lib.cpp )
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "arm")
    set_source_files_properties(syscall32.s PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp")
    add_library( # Sets the name of the library.
            native-lib
            # Sets the library as a shared library.
            SHARED
            syscall32.s
            # Provides a relative path to your source file(s).
            native-lib.cpp )
endif()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值