Android 逆向技巧总结

栏目: IOS · Android · 发布时间: 6年前

内容简介:以代表性的 crackme 为例总结相关知识点。一緒に頑張りましょう!github 仓库例:根据 linker 源码, section 的执行顺序为

以代表性的 crackme 为例总结相关知识点。一緒に頑張りましょう!github 仓库 在此

JNI_Onload 中通过 RegisterNatives 动态注册 jni 函数

相关函数:

signed int __fastcall JNI_OnLoad(_JavaVM *a1)

((int (__fastcall *)(_JavaVM *, _JNIEnv **, signed int))v1->functions->GetEnv)(v1, &v8, 65540)  
    /*  v1:JavaVM  v8:JniEnv  65540:jni version */

((int (__fastcall *)(_JNIEnv *, char *))v3->functions->FindClass)(v3, v4)   
    /*  v3:JNIEnv  v4:类名    */

((int (__fastcall *)(_JNIEnv *, int, char **, signed int))v3->functions->RegisterNatives)(v3, v5, off_400C, 2)
    /*  v3:JniEnv  v5:FindClass得到的jclass对象  off_400C:要注册的methods  2:注册的methods个数
        method的格式为:函数名 函数描述(smali格式) 函数指针
        例如(in ida):
            DCD aHello              ; "hello"
            DCD aLjavaLangStr_1     ; "()Ljava/lang/String;"
            DCD native_hello+1
    */

例: mobicrackNDK.apk

.init_array

根据 linker 源码, section 的执行顺序为 .preinit_array -> .init -> .init_array 。但 so 是不会执行 .preinit_array 的, 可以忽略。

.init_array 是一个函数指针数组。编写代码时在函数声明时加上 __attribute__((constructor)) 使之成为共享构造函数,即可使该函数出现在 .init_array section 中。

IDA 动态调试时 ‘ctrl+s’ 查看 section 信息即可定位这两个 setction,特别的,对于 .init_array ,可通过搜索 Calling %s @ %p for '%s' 定位。

部分源码:

void soinfo::CallConstructors() {
    ...
    // DT_INIT should be called before DT_INIT_ARRAY if both are present.
    CallFunction("DT_INIT", init_func);
    CallArray("DT_INIT_ARRAY", init_array, init_array_count, false);    // CallArray 中也会调用 CallFunction 函数
}

void soinfo::CallFunction(const char* function_name UNUSED, linker_function_t function) {
  if (function == NULL || reinterpret_cast<uintptr_t>(function) == static_cast<uintptr_t>(-1)) {
    return;
  }

  TRACE("[ Calling %s @ %p for '%s' ]", function_name, function, name);
  function();
  TRACE("[ Done calling %s @ %p for '%s' ]", function_name, function, name);

  // The function may have called dlopen(3) or dlclose(3), so we need to ensure our data structures
  // are still writable. This happens with our debug malloc (see http://b/7941716).
  set_soinfo_pool_protection(PROT_READ | PROT_WRITE);
}

例: mobicrackNDK.apk

dex 结构

修复 dexHeader & onCreate

快速简记:

结构 单位结构体占字节 共计字节
DexHeader - 0x70h
String Table 4 -
Type Table 4 -
Proto Table 12 -
Field Table 8 -
Method Table 8 -
Class Def Table 32 -
Data Section(含Map Section) - -

例: misc.apk

hook 系统函数

例: EasyRe.apk

dump 内存搜索 flag

1. 利用 ddms 的 dump HPROF file 功能 (带箭头的油桶图标)

搜索: strings easyre.sjl.gossip.easyre.hprof | grep 0ctf

2. 利用 gore

gdb 附加进程后直接执行 gcore dump,搜索: strings core.7967 | grep 0ctf

例: EasyRe.apk

修改 smali 代码

指令参考这里:point_right: dalvik bytecode

例: Timer.apk

ARM

ARM 的参数传递规则

R0、R1、R2、R3, 在调用函数时,用来存放前4个函数参数;如果函数的参数多于 4 个,则多余参数存放在堆栈当中;

低于32位的函数返回值存于 R0。

ARM 的寄存器规则

寄存器 作用
R0 ~ R3 调用函数时,用来存放前4个函数参数
R0 函数返回时,存放低于32位的函数返回值
R4 ~ R11 保存局部变量。进入函数时必须保存所用到的局部变量寄存器的值,在返回前必须恢复这些寄存器的值;对于函数中没有用到的寄存器则不必进行这些操作。
在Thumb中,通常只能使用寄存器 R4~R7来保存局部变量,
所以函数内部通用的入栈出栈代码可以为:
STMFD sp!,{r4-r11,lr}
// body of ASM code
LDMFD sp!,{r4-r11,pc}
R12 用作 IP,内部调用暂时寄存器
R13 用作 SP,栈指针,sp 中存放的值在退出被调用函数时必须与进入时的值相同。
R14 用作 LR,链接寄存器,保存函数的返回地址;如果在函数中保存了返回地址,寄存器R14 则可以用作其他用途
R15 用作 PC,程序计数器
R16 CPSR,状态寄存器

例: LoopAndLoop.apk

各种检测

dex 校验

SHA1 值。

反调试

  1. 读取 /proc/pid/status 的 State 是否为 t
  2. 读取 /proc/pid/status 的 TracerPid 是否不为0
  3. 读取 /proc/pid/wchan 是否有 ptrace_stop

例: KXCTF.apk

去花

去花即将规律的花指令 nop 掉并修复跳转,ida 中的去花脚本编写可参考 IDA 的 idc 或 idapython API。

为了使 IDA 识别某个函数X,需要在 Functions Window 统统删除 之前函数X中误将 junk code 识别为函数的垃圾函数,手动 设置函数X的结尾 (Edit - Functions - set function end)。

函数尾部特征:

BLX __stack_chk_fail
POP {R4-R7,PC} (与函数头 PUSH {R4-R7,LR} 对应)

例: rfchen.apk

加密算法

DES 加密

对称性加密,典型的 DES 以 64 位二进制为分组 对数据加密。

如果明文不是 64 位(16个16进制位)的整数倍,则加密前,这段文本必须 在尾部补充一些额外的字节

在运算时需要根据 特定的表格 以 64 位为单位对明文和秘钥分别进行 置换操作

例: KXCTF.apk

RC6 加密

对称性加密。主要操作是 异或和循环左移

// Encryption/Decryption with RC6-w/r/b
//
// Input:   Plaintext stored in four w-bit input registers A, B, C & D
//  r is the number of rounds
//  w-bit round keys S[0, ... , 2r + 3]
//
// Output: Ciphertext stored in A, B, C, D
//
// '''Encryption Procedure:'''

  B = B + S[0]
  D = D + S[1]
  for i = 1 to r do
  {
    t = (B*(2B + 1)) <<< lg w
    u = (D*(2D + 1)) <<< lg w
    A = ((A  t) <<< u) + S[2i]
    C = ((C  u) <<< t) + S[2i + 1]
                (A, B, C, D)  =  (B, C, D, A)
  }
  A = A + S[2r + 2]
  C = C + S[2r + 3]

例: KXCTF.apk

RC4 加密

对称性加密。由 伪随机数生成器和异或运算 组成。密钥长度范围是[1,255]。

RC4一个字节一个字节地加解密。给定一个密钥,伪随机数生成器接受密钥并产生一个S盒。S盒用来加密数据,而且在加密过程中S盒会变化。

伪代码:

for i from 0 to 255
    S[i] := i
endfor
j := 0
for( i=0 ; i<256 ; i++)
    j := (j + S[i] + key[i mod keylength]) % 256
    swap values of S[i] and S[j]
endfor

i := 0
j := 0
while GeneratingOutput:
    i := (i + 1) mod 256   //a
    j := (j + S[i]) mod 256 //b
    swap values of S[i] and S[j]  //c
    k := inputByte ^ S[(S[i] + S[j]) % 256]
    output K
endwhile

例: rfchen.apk


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

PCI Express 体系结构导读

PCI Express 体系结构导读

王齐 / 机械工业 / 2010-3 / 55.00元

《PCI Express 体系结构导读》讲述了与PCI及PCI Express总线相关的最为基础的内容,并介绍了一些必要的、与PCI总线相关的处理器体系结构知识,这也是《PCI Express 体系结构导读》的重点所在。深入理解处理器体系结构是理解PCI与PCI Express总线的重要基础。 读者通过对《PCI Express 体系结构导读》的学习,可超越PCI与PCI Express总线......一起来看看 《PCI Express 体系结构导读》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具