内容简介:以代表性的 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 */
.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); }
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,状态寄存器 |
各种检测
dex 校验
SHA1 值。
反调试
- 读取 /proc/pid/status 的 State 是否为 t
- 读取 /proc/pid/status 的 TracerPid 是否不为0
- 读取 /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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
创投之巅——中国创投精彩案例
投资界网站 / 人民邮电出版社 / 2018-11 / 69.00
中国的科技产业发展,与创投行业密不可分。在过去的几十年间,资本与科技的结合,缔造了众多创业“神话”。回顾这些科技巨头背后的资本路径,可以给如今的国内创业者很多有益的启发。 本书从风险投资回报率、投资周期、利润水平、未来趋势等多个维度,筛选出了我国过去几十年中最具代表性的创业投资案例,对其投资过程和企业成长过程进行复盘和解读,使读者可以清晰地看到优秀创业公司的价值与卓越投资人的投资逻辑。一起来看看 《创投之巅——中国创投精彩案例》 这本书的介绍吧!