{"id":13462833,"url":"https://github.com/kiyadesu/android-reversing-challenges","last_synced_at":"2025-03-25T06:31:26.018Z","repository":{"id":215882252,"uuid":"94738045","full_name":"kiyadesu/android-reversing-challenges","owner":"kiyadesu","description":"there are some CTF challenges or some other things helping improving android reversing skills.","archived":false,"fork":false,"pushed_at":"2019-02-27T08:17:38.000Z","size":9924,"stargazers_count":70,"open_issues_count":0,"forks_count":22,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-10-29T13:49:56.455Z","etag":null,"topics":["android","reverse-engineering"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kiyadesu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2017-06-19T04:58:27.000Z","updated_at":"2024-10-18T18:18:40.000Z","dependencies_parsed_at":null,"dependency_job_id":"f2ed0348-a3ac-4cfb-abcd-2efed4846c76","html_url":"https://github.com/kiyadesu/android-reversing-challenges","commit_stats":null,"previous_names":["kiyadesu/android-reversing-challenges"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kiyadesu%2Fandroid-reversing-challenges","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kiyadesu%2Fandroid-reversing-challenges/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kiyadesu%2Fandroid-reversing-challenges/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kiyadesu%2Fandroid-reversing-challenges/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kiyadesu","download_url":"https://codeload.github.com/kiyadesu/android-reversing-challenges/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245413753,"owners_count":20611353,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","reverse-engineering"],"created_at":"2024-07-31T13:00:37.724Z","updated_at":"2025-03-25T06:31:23.266Z","avatar_url":"https://github.com/kiyadesu.png","language":null,"funding_links":[],"categories":["Mobile CTF challenges","\u003ca id=\"8c5a692b5d26527ef346687e047c5c21\"\u003e\u003c/a\u003e收集"],"sub_categories":[],"readme":"\n为代表性的 crackme 总结相关知识点。一緒に頑張りましょう！\n\n[TOC]\n\n# 工具列表\n\n|function|name|How to get|\n|-|-|-|\n|apk 分析|jadx|https://github.com/skylot/jadx/releases|\n|Java方法调用追踪(与 DDMS method profiling 配合使用)|TraceReader|https://github.com/panhongwei/TraceReader|\n|逆向工具|Hopper|https://down.52pojie.cn/Tools/Disassemblers/|\n|逆向工具|jeb|https://down.52pojie.cn/Tools/Android_Tools/|\n|逆向工具|Ida|https://down.52pojie.cn/Tools/Disassemblers/|\n|逆向工具|radare2|https://github.com/radare/radare2|\n|so 修复|ThomasKing rebuild_section|https://bbs.pediy.com/thread-192874.htm|\n|jar 包查看|JD-GUI|https://github.com/java-decompiler/jd-gui/releases|\n|修改 ro.debuggable|netsniffer mprop|https://bbs.pediy.com/thread-215311.htm|\n|汇编字节码|ARM ⇌ Hex|http://armconverter.com/|\n|汇编框架|keystone|http://www.keystone-engine.org/|\n|二进制查看|010 Editor|https://down.52pojie.cn/Tools/Editors/|\n|文件格式模板|010 templates|http://www.sweetscape.com/010editor/templates/|\n|抓包|Charles|https://down.52pojie.cn/Tools/Network_Analyzer/|\n|抓包|Burp Suite|https://down.52pojie.cn/Tools/Network_Analyzer/|\n|证书校验|JustTrustMe|https://github.com/Fuzion24/JustTrustMe/releases|\n|操作 apk|aapt|in sdk build-tools|\n|apk 签名|Google signapk|https://github.com/kiya-z/Android/tree/master/tools/signapk|\n|hook 框架|xposed|http://repo.xposed.info/module/de.robv.android.xposed.installer|\n|hook 框架|frida|https://www.frida.re/|\n|frida on burp suite|Brida|https://github.com/federicodotta/Brida|\n|DDMS|Android Device Monitor|in sdk tools|\n|gdb 调试|gdb|in ndk toolchains (ndk \u003c= r10)|\n|gdb 调试|gdbserver|in ndk prebuilt (ndk \u003c= r10)|\n|开发工具|Android Studio|https://developer.android.com/studio|\n|反编译 apk|ShakaApktool|https://github.com/rover12421/ShakaApktool|\n|调试 smali|smalidea|https://bitbucket.org/JesusFreke/smali/downloads/|\n|smali -\u003e dex|smali|https://bitbucket.org/JesusFreke/smali/downloads/|\n|dex -\u003e smali|baksmali|https://bitbucket.org/JesusFreke/smali/downloads/|\n|解析 android manifest|axmlprinter|https://github.com/rednaga/axmlprinter/releases|\n|查看 .class 文件, 可配合 010 的 class template 手动修改 class bytecode|jclasslib|https://github.com/ingokegel/jclasslib|\n|帮助修改 java 字节码|javassist|https://github.com/jboss-javassist/javassist/releases|\n|luac 反编译|unluac|https://sourceforge.net/projects/unluac/|\n|sql 加解密|sqlcipher|https://github.com/sqlcipher/sqlcipher|\n|ab 文件解压|android-backup-extractor|https://github.com/nelenkov/android-backup-extractor|\n|llvm 混淆|o-llvm|https://github.com/obfuscator-llvm/obfuscator/|\n|逆向框架|Miasm|https://github.com/cea-sec/miasm|\n|符号执行|angr|https://github.com/angr/angr|\n|符号执行|trigon|https://github.com/JonathanSalwan/Triton|\n|二进制分析|barf|https://github.com/programa-stic/barf-project|\n\n# [mobicrackNDK.apk](https://github.com/kiya-z/android-reversing-challenges/tree/master/apks/mobicrackNDK.apk)\n\n\u003e来自福建海峡两岸CTF 2015。\n\n## JNI_Onload 中通过 RegisterNatives 动态注册 jni 函数\n\n**相关函数**：\n\n```\nsigned int __fastcall JNI_OnLoad(_JavaVM *a1)\n\n((int (__fastcall *)(_JavaVM *, _JNIEnv **, signed int))v1-\u003efunctions-\u003eGetEnv)(v1, \u0026v8, 65540)  \n    /*  v1:JavaVM  v8:JniEnv  65540:jni version */\n\n((int (__fastcall *)(_JNIEnv *, char *))v3-\u003efunctions-\u003eFindClass)(v3, v4)   \n    /*  v3:JNIEnv  v4:类名    */\n\n((int (__fastcall *)(_JNIEnv *, int, char **, signed int))v3-\u003efunctions-\u003eRegisterNatives)(v3, v5, off_400C, 2)\n    /*  v3:JniEnv  v5:FindClass得到的jclass对象  off_400C:要注册的methods  2:注册的methods个数\n        method的格式为：函数名 函数描述(smali格式) 函数指针\n        例如(in ida)：\n            DCD aHello              ; \"hello\"\n            DCD aLjavaLangStr_1     ; \"()Ljava/lang/String;\"\n            DCD native_hello+1\n    */\n```\n\n## .init_array\n\n根据 linker 源码, section 的执行顺序为 `.preinit_array` -\u003e `.init` -\u003e `.init_array` 。但 so 是不会执行 `.preinit_array` 的, 可以忽略。\n\n`.init_array` 是一个函数指针数组。编写代码时在函数声明时加上 `__attribute__((constructor))` 使之成为共享构造函数，即可使该函数出现在 `.init_array` section 中。\n\nIDA 动态调试时 'ctrl+s' 查看 section 信息即可定位这两个 setction，特别的，对于 `.init_array`，可通过搜索 `Calling %s @ %p for '%s'` 定位。\n\n**部分源码**:\n\n```\nvoid soinfo::CallConstructors() {\n    ...\n    // DT_INIT should be called before DT_INIT_ARRAY if both are present.\n    CallFunction(\"DT_INIT\", init_func);\n    CallArray(\"DT_INIT_ARRAY\", init_array, init_array_count, false);    // CallArray 中也会调用 CallFunction 函数\n}\n\nvoid soinfo::CallFunction(const char* function_name UNUSED, linker_function_t function) {\n  if (function == NULL || reinterpret_cast\u003cuintptr_t\u003e(function) == static_cast\u003cuintptr_t\u003e(-1)) {\n    return;\n  }\n\n  TRACE(\"[ Calling %s @ %p for '%s' ]\", function_name, function, name);\n  function();\n  TRACE(\"[ Done calling %s @ %p for '%s' ]\", function_name, function, name);\n\n  // The function may have called dlopen(3) or dlclose(3), so we need to ensure our data structures\n  // are still writable. This happens with our debug malloc (see http://b/7941716).\n  set_soinfo_pool_protection(PROT_READ | PROT_WRITE);\n}\n```\n\n# [misc.apk](https://github.com/kiya-z/android-reversing-challenges/tree/master/apks/misc.apk)\n\n\u003e来自 RCTF 2015。\n\n## dex 结构（修复dex）\n\n快速简记：\n\n|结构|单位结构体占字节|共计字节|\n|---|---|---|\n|DexHeader|-|0x70h|\n|String Table|4|-|\n|Type Table|4|-|\n|Proto Table|12|-|\n|Field Table|8|-|\n|Method Table|8|-|\n|Class Def Table|32|-|\n|Data Section(含Map Section)|-|-|\n\n# [EasyRe.apk](https://github.com/kiya-z/android-reversing-challenges/tree/master/apks/EasyRe.apk)\n\n\u003e来自 0CTF 2015。\n\n## hook 系统函数\n\n常规方法静态分析\n\n## dump 内存搜索 flag\n\n### 1. 利用 ddms 的 `dump HPROF file` 功能 (带箭头的油桶图标)\n\n搜索：`strings easyre.sjl.gossip.easyre.hprof | grep 0ctf`\n\n### 2. 利用 gore\n\ngdb 附加进程后直接执行 `gcore` dump，搜索：`strings core.7967 | grep 0ctf`\n\n# [Timer.apk](https://github.com/kiya-z/android-reversing-challenges/tree/master/apks/Timer.apk)\n\n\u003e来自 AliCTF 2016。\n\n## 修改 smali 代码\n\n指令参考这里👉[dalvik bytecode](https://source.android.com/devices/tech/dalvik/dalvik-bytecode)\n\n# [LoopAndLoop.apk](https://github.com/kiya-z/android-reversing-challenges/tree/master/apks/LoopAndLoop.apk)\n\n\u003e来自 AliCTF 2016。\n\n## ARM 的参数传递规则\n\nR0、R1、R2、R3， 在调用函数时，用来存放前4个函数参数；如果函数的参数多于 4 个，则多余参数存放在堆栈当中；\n低于32位的函数返回值存于 R0。\n\n## ARM 的寄存器规则\n\n|寄存器|作用|\n|-|-|\n|R0 ~ R3|调用函数时，用来存放前4个函数参数|\n|R0|函数返回时，存放低于32位的函数返回值|\n|R4 ~ R11|保存局部变量。进入函数时必须保存所用到的局部变量寄存器的值，在返回前必须恢复这些寄存器的值；对于函数中没有用到的寄存器则不必进行这些操作。\u003cbr\u003e在Thumb中，通常只能使用寄存器 R4~R7来保存局部变量，\u003cbr\u003e所以函数内部通用的入栈出栈代码可以为：\u003cbr\u003eSTMFD sp!,\\{r4-r11,lr\\}\u003cbr\u003e// body of ASM code\u003cbr\u003eLDMFD sp!,\\{r4-r11,pc\\}|\n|R12|用作 IP，内部调用暂时寄存器|\n|R13|用作 SP，栈指针，sp 中存放的值在退出被调用函数时必须与进入时的值相同。|\n|R14|用作 LR，链接寄存器，保存函数的返回地址；如果在函数中保存了返回地址，寄存器R14 则可以用作其他用途|\n|R15|用作 PC，程序计数器|\n|R16|CPSR，状态寄存器|\n\n\n# [KXCTF.apk](https://github.com/kiya-z/android-reversing-challenges/tree/master/apks/KXCTF.apk)\n\n\u003e来自 看雪CTF 2017。\n\n## dex 校验\n\nSHA1 值。\n\n## 反调试\n\n1. 读取 /proc/pid/status 的 State 是否为 t\n2. 读取 /proc/pid/status 的 TracerPid 是否不为0\n3. 读取 /proc/pid/wchan 是否有 ptrace_stop\n\n## DES 加密\n\n对称性加密，典型的 DES 以`64 位二进制为分组`对数据加密。\n如果明文不是 64 位（16个16进制位）的整数倍，则加密前，这段文本必须`在尾部补充一些额外的字节`。\n在运算时需要根据`特定的表格`以 64 位为单位对明文和秘钥分别进行`置换操作`。\n\n## RC6 加密\n\n对称性加密。主要操作是`异或和循环左移`。\n\n```\n// Encryption/Decryption with RC6-w/r/b\n//\n// Input:   Plaintext stored in four w-bit input registers A, B, C \u0026 D\n//  r is the number of rounds\n//  w-bit round keys S[0, ... , 2r + 3]\n//\n// Output: Ciphertext stored in A, B, C, D\n//\n// '''Encryption Procedure:'''\n\n  B = B + S[0]\n  D = D + S[1]\n  for i = 1 to r do\n  {\n    t = (B*(2B + 1)) \u003c\u003c\u003c lg w\n    u = (D*(2D + 1)) \u003c\u003c\u003c lg w\n    A = ((A ⊕ t) \u003c\u003c\u003c u) + S[2i]\n    C = ((C ⊕ u) \u003c\u003c\u003c t) + S[2i + 1]\n                (A, B, C, D)  =  (B, C, D, A)\n  }\n  A = A + S[2r + 2]\n  C = C + S[2r + 3]\n```\n\n# [rfchen.apk](https://github.com/kiya-z/android-reversing-challenges/tree/master/apks/rfchen.apk)\n\n\u003e来自 看雪CTF 2017。\n\n\n## 花指令\n\n本例中的花指令有以下几种：\n\n```\nB               loc_XXXX\n```\n\n```\nPUSH            {R0,R4,R5,R7,LR}\nSUB             SP, SP, #8\nMOV             R2, R2\nADD             SP, SP, #8\nADD.W           R0, R0, #1\nSUB.W           R0, R0, #1\nMOV             R3, R3\nPOP.W           {R0,R4,R5,R7,LR}\nADD.W           R1, R1, #1\nSUB.W           R1, R1, #1\n```\n\n```\nPUSH.W          {R4-R10,LR}\nPOP.W           {R4-R10,LR}\n```\n\n去花即将规律的花指令 nop 掉并修复跳转，ida 中的去花脚本编写可参考 IDA 的 idc 或 idapython API。\n\n为了使 IDA 识别某个函数X，需要在 Functions Window **统统删除**之前函数X中误将 junk code 识别为函数的垃圾函数，手动**设置函数X的结尾**（Edit - Functions - set function end）。\n\n**函数尾部特征：**\n\n1. `BLX   __stack_chk_fail`  -\u003e 堆栈保护\n2. `POP   {R4-R7,PC} (与函数头 PUSH {R4-R7,LR} 对应)`  -\u003e 堆栈平衡\n\n本例去花可参考：[1（ HideArea 方便分析）](http://bbs.pediy.com/thread-217889.htm)、[2（ NOP并修改跳转 ）](http://bbs.pediy.com/thread-218432.htm)\n\n## RC4 加密\n\n对称性加密。由`伪随机数生成器和异或运算`组成。密钥长度范围是[1,255]。\nRC4一个字节一个字节地加解密。给定一个密钥，伪随机数生成器接受密钥并产生一个S盒。S盒用来加密数据，而且在加密过程中S盒会变化。\n\n*伪代码：*\n\n```\n for i from 0 to 255\n     S[i] := i\n endfor\n j := 0\n for( i=0 ; i\u003c256 ; i++)\n     j := (j + S[i] + key[i mod keylength]) % 256\n     swap values of S[i] and S[j]\n endfor\n\n i := 0\n j := 0\n while GeneratingOutput:\n     i := (i + 1) mod 256   //a\n     j := (j + S[i]) mod 256 //b\n     swap values of S[i] and S[j]  //c\n     k := inputByte ^ S[(S[i] + S[j]) % 256]\n     output K\n endwhile\n```\n\n# [WantAShell.apk](https://github.com/kiya-z/android-reversing-challenges/tree/master/apks/WantAShell.apk)\n\n\u003e来自 LCTF 2016.\n\n## SMC (self-modifying code) - 运行时自篡改代码\n\n在自身应用中，Java 代码在被执行时权限为只读，SMC 只会发生在 NDK 层。一般步骤如下：\n\n1. 通过搜索 DEX 特征码来找到 DEX 的起始地址；\n2. 解析 dex 格式定位到具体的类以及方法，找到要修改的 dalvik 字节码；\n3. 重新映射内存段，修改内存。\n\n**对于本例**：(只对 dalvik 有效)\n\n读取 self maps 文件找到 odex 的内存地址 -\u003e 解析dex -\u003e 遍历 classDefs 找到两个函数地址 -\u003e mprotect 修改内存属性 -\u003e 函数替换 -\u003e 将内存属性改回。\n\n\u003e论文参考：**《基于 SMC 的 Android 软件保护研究与实现》**\n\n# [AN.apk](https://github.com/kiya-z/android-reversing-challenges/tree/master/apks/AN.apk)\n\n\u003e来自 NJCTF 2017.\n\n## NativeActivity\n\nNativeActivity 是 android SDK 自带的一个 activity，本例将其作为主 activity，使得 dex 中没有 Java 代码。\n\nNativeActivity 所在的 so 在 manifest 中有注册，固定格式：\n\n```\n\u003cmeta-data android:name=\"android.app.lib_name\" android:value=\"SONAME\" /\u003e\n```\n\n入口函数是 `android_main()`。可以这样找到它：\n\n1. 函数 `ANativeActivity_onCreate`\n2. `j_j_pthread_create((pthread_t *)v4 + 20, \u0026attr, (void *(*)(void *))sub_XXX, v4);`\n3. 进入 sub_XXX ，即可看到 `android_main(v1);`\n\n关于 NativeActivity 原理，参考[这里](http://blog.csdn.net/ldpxxx/article/details/9253369)。\n\n## ollvm\n\n# reference\n\n[CTF-Mobile](https://github.com/toToCW/CTF-Mobile)\n\n[write-ups-2015](https://github.com/ctfs/write-ups-2015)\n\n[write-ups-2016](https://github.com/ctfs/write-ups-2016)\n\n[看雪论坛](http://bbs.pediy.com/)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkiyadesu%2Fandroid-reversing-challenges","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkiyadesu%2Fandroid-reversing-challenges","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkiyadesu%2Fandroid-reversing-challenges/lists"}