环境搭建,与初步认识
下载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()