是手机上 AIDE 开发的,那时候不知道什么设计模式,就是单纯的模式识别抄代码(我甚至抄到了 KK 沉浸状态栏的代码,利用一个四边形的彩色 View... 还有一个渐变动画 bling bling 效果的代码),花了一周左右,能用是从完全不懂 Jawa 开始四天后。
然后我发上了酷安 #coolapk 那时候酷安才 v6
现在我在逆向工程破解酷安 v9 的客户端验证令牌生成算法 🤔
希望能成功,如果成功了那对于桌面平台,酷安 API 的『官方』客户端限制就彻底爆破喽
顺便写自己的酷安 — GeekApk
顺便写 GeekSpec,想要弄个可以优雅 Handle 所有 HTTP API 的工具
顺便写了个编写递归下降法 Parser 的工具
偶尔用 Ruby 写工具,喜欢 Kotlin
然后我发上了酷安 #coolapk 那时候酷安才 v6
现在我在逆向工程破解酷安 v9 的客户端验证令牌生成算法 🤔
希望能成功,如果成功了那对于桌面平台,酷安 API 的『官方』客户端限制就彻底爆破喽
顺便写自己的酷安 — GeekApk
顺便写 GeekSpec,想要弄个可以优雅 Handle 所有 HTTP API 的工具
顺便写了个编写递归下降法 Parser 的工具
偶尔用 Ruby 写工具,喜欢 Kotlin
(先熟悉一下分析环境,CoolApk 的 API Spec 待会再写) #reveng #GeekApk #CoolAPK #PL
Spectrum 我觉得非常需要重构,我会给 DSL 制定新的语法规范,因为实际使用中,我能看到很多东西并不是开始我设计 DSL 的时候想的那样... DSL 目前有点太混沌了,新的 GeekSpec 1.0 将能兼容 GeekApk v1b 和 CoolApk v6 两款实际应用的 API,一样简洁富有表现力的语法,但会有更清晰的描述语义和更多的特性(比如返回状态 matching)。
所以 CoolApk 的 SPEC 文件我还是要写,但暂时不打算重构 Spectrum 来支持 CoolApk 的新 API 特性,暂时可以用一些 Workaround 方案支持某些 API,感谢 Ruby 的灵活吧(迫真)
====
新的酷安 liba.so 没有很大区别,编译的时候少加了一个不需要的
看起来,不过,虽然好像业务代码没有用 C++ 异常,还是依赖了
我拿到的 liba.so 是 liba 的 x86 版本,因为比起 ARM 我更熟悉 x86,而且 CISC 机器一般更好分析
这个版本是从 coolapk.com 拿到的官方版本,酷安 v8
== 它包含这些动态链接依赖:
它依赖以下外部函数:memcpy / memset / strlen / sprintf / strcmp / time / strcat
还有这些无关算法本身的依赖:__cxa_finalize / __cxa_atexit / __stack_chk_fail / __stack_chk_guard
它的导出符号:BD / BDL / BE / BEL / MI / MU / MF / r / bd / me / be / Java_com_coolapk_market_util_AuthUtils_getAS
JNI 使用的只有 Java_com_coolapk_market_util_AuthUtils_getAS 这个符号
我逆向重写过的只有 BEL 这个函数,它的定义如下(C):
我觉得运行时替换掉这些函数,添加 log 参数和返回值的代码可能有助于分析算法
编译时采用了 stack guard 反溢出机制
Spectrum 我觉得非常需要重构,我会给 DSL 制定新的语法规范,因为实际使用中,我能看到很多东西并不是开始我设计 DSL 的时候想的那样... DSL 目前有点太混沌了,新的 GeekSpec 1.0 将能兼容 GeekApk v1b 和 CoolApk v6 两款实际应用的 API,一样简洁富有表现力的语法,但会有更清晰的描述语义和更多的特性(比如返回状态 matching)。
所以 CoolApk 的 SPEC 文件我还是要写,但暂时不打算重构 Spectrum 来支持 CoolApk 的新 API 特性,暂时可以用一些 Workaround 方案支持某些 API,感谢 Ruby 的灵活吧(迫真)
====
新的酷安 liba.so 没有很大区别,编译的时候少加了一个不需要的
liblog.so 依赖(实际上它不(向 logd)打印任何 log,这是无用的依赖,但当时我比现在菜所以移除这个依赖甚至对我来说比较难...)看起来,不过,虽然好像业务代码没有用 C++ 异常,还是依赖了
__cxa_finialize (好吧这点是我弄错了,那不是 landingpad personality 函数(迫真))我拿到的 liba.so 是 liba 的 x86 版本,因为比起 ARM 我更熟悉 x86,而且 CISC 机器一般更好分析
这个版本是从 coolapk.com 拿到的官方版本,酷安 v8
com.coolapk.market, platformBuildVersionName="8.8.3", versionCode = 0x1ba289, usesSdk: [0x15..0x1c]
(信息使用 ~/Android/Sdk/build-tools/28.0.3/aapt dump xmltree f81b5d8361e1e197cd336a443710532e-0-o_1crp2etomvdkhmr1kfq1ghh18c6-uid-408649.apk AndroidManifest.xml 获得)== 它包含这些动态链接依赖:
它依赖以下外部函数:memcpy / memset / strlen / sprintf / strcmp / time / strcat
还有这些无关算法本身的依赖:__cxa_finalize / __cxa_atexit / __stack_chk_fail / __stack_chk_guard
它的导出符号:BD / BDL / BE / BEL / MI / MU / MF / r / bd / me / be / Java_com_coolapk_market_util_AuthUtils_getAS
JNI 使用的只有 Java_com_coolapk_market_util_AuthUtils_getAS 这个符号
我逆向重写过的只有 BEL 这个函数,它的定义如下(C):
int BEL(int n) { return (4 * (n - 1) / 3 | 3) + 2; }
借助逆向分析 CoolApk X-App-Token 生成算法的机会,我也顺便学习了一些 X86 汇编的知识。我觉得运行时替换掉这些函数,添加 log 参数和返回值的代码可能有助于分析算法
编译时采用了 stack guard 反溢出机制
Telegram
duangsuse::Echo
探索出了能用的公式,实现了 liba.so!BEL (int BEL(int n))
duangsuse::Echo
(先熟悉一下分析环境,CoolApk 的 API Spec 待会再写) #reveng #GeekApk #CoolAPK #PL Spectrum 我觉得非常需要重构,我会给 DSL 制定新的语法规范,因为实际使用中,我能看到很多东西并不是开始我设计 DSL 的时候想的那样... DSL 目前有点太混沌了,新的 GeekSpec 1.0 将能兼容 GeekApk v1b 和 CoolApk v6 两款实际应用的 API,一样简洁富有表现力的语法,但会有更清晰的描述语义和更多的特性(比如返回状态 matching)。…
从某种意义上来说,第三方要想做『酷安客户端』,真正的困难仅仅在于
X-App-Token,其他都是体力活,他们的接口用 Retrofit 都可以直接绑定上(实际上他们就是用 OkHttp+Retrofit)。(又是 Observable (Stream) 设计模式 hhh)我去,这也太鬼畜梦幻了,他们居然忘了 strip 掉调试符号...
liba.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, BuildID[sha1]=aa1eac616f2abab80d5b9268e955c0d37de0df26, with debug_info, not stripped 这特么简直是作死啊,生怕别人逆向不出来调试不方便了.... 🤪 酷安这位叫做 kjsolo 的老哥啊你也太秀了吧... 要知道在 production 里面出现一行调试代码都会被吐嘈的,何况是在令牌生成算法里保留一大堆调试符号... #security #Android #CoolApk
duangsuse::Echo
我去,这也太鬼畜梦幻了,他们居然忘了 strip 掉调试符号... liba.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, BuildID[sha1]=aa1eac616f2abab80d5b9268e955c0d37de0df26, with debug_info, not stripped 这特么简直是作死啊,生怕别人逆向不出来调试不方便了.... 🤪 酷安这位叫做 kjsolo…
典型的 Android JNI 没好好写... 🤔
到底 liba 有多特殊的需求,Android Make(NDKBuild) 难道不够么,难道他们用 Android CMake? #Android #Java #Native
到底 liba 有多特殊的需求,Android Make(NDKBuild) 难道不够么,难道他们用 Android CMake? #Android #Java #Native
GitHub
duangsuse/e2im
📝 Android library version of ChangeAttr, Apache licensed - duangsuse/e2im
APP_ABI := x86 armeabi-v7a arm64-v8a x86_64#Native #Build 难记... 所以我每次都从某些模板创建这种文件,然后去查 NDK 的文档,这么多宏、变量根本记不住。
APP_PLATFORM := android-14
APP_BUILD_SCRIPT := Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAN_VARS)
LOCAL_CFLAGS := -Wall -Werror -std=c99 -lto -O3 -fno-stack-protector $(EXTRA_FLAGS)
LOCAL_MODULE := e2immutable
LOCAL_MODULE_FILENAME := libe2im.so
LOCAL_SRC_FILES := e2immutable.c
LOCAL_BUILD_SCRIPT := BUILD_EXECUTABLE
LOCAL_MAKEFILE := $(local-makefile)
my := TARGET_
$(call handle-module-built)
LOCAL_MODULE_CLASS := EXECUTABLE
include $(BUILD_SYSTEM)/build-module.mk
然而能写出来依然觉得好大佬啊(迫真
下面是 CMakeLists.txt 的一个栗子,比上面的好记(暴论)
cmake_minimum_required(VERSION 3.6)
project(jni)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(BUILD_USE_64BITS on)
set(SOURCE_TEMPLATES)
set(SOURCE_FILES WinAPI.cpp)
set(SOURCE_HEADERS WinAPI.h)
# add_executable(jni ${SOURCE_FILES})
add_library(
jni SHARED
${SOURCE_FILES}
${SOURCE_TEMPLATES}
${SOURCE_HEADERS})
target_link_libraries(jni)GitHub
ice1000/ice1000.github.io
Personal tech blog. Contribute to ice1000/ice1000.github.io development by creating an account on GitHub.
duangsuse::Echo
(先熟悉一下分析环境,CoolApk 的 API Spec 待会再写) #reveng #GeekApk #CoolAPK #PL Spectrum 我觉得非常需要重构,我会给 DSL 制定新的语法规范,因为实际使用中,我能看到很多东西并不是开始我设计 DSL 的时候想的那样... DSL 目前有点太混沌了,新的 GeekSpec 1.0 将能兼容 GeekApk v1b 和 CoolApk v6 两款实际应用的 API,一样简洁富有表现力的语法,但会有更清晰的描述语义和更多的特性(比如返回状态 matching)。…
所有可以从
== ELF Header
CLASS: ELF32
DATA: 2*, le
VER: 1
OS/ABI: UNIX - SYSV (v0)
TYPE: DYN
ARCH: Intel 80386 (v1)
== misc
BuildID[sha1] = aa1eac616f2abab80d5b9268e955c0d37de0df26
GNU Gold: v1.1
== .symtab / .rel.plt / .dynsym
FILE b.c
- 这个文件应该是 Base64 算法的存放文件(从这里抄来的)
OBJ pr2six 256
- 这个对象的 CTYPE 类型是
- radare2
- 这个对象的 CTYPE 类型是
- radare2
FUN body 3088
FILE a.c
- 这是 liba.so 的源文件名
FUN BDL 96
- 其实就是
- 就是
- 其实就是
- 其实就是
FUN MI 64
FUN MU 324
FUN MF 649
FUN r 125
FUN bd 44
- 这个应该是 b64d
- CTYPE 签名不好给,因为定义和我拿到的 GitHub 开源代码不匹配,是优化过的
- 我可以根据程序逻辑(它只是加了一层栈帧,没有啥实际的业务逻辑)推导出一个可能 Segfault 的类型
- Flase CTYPE:
- 代码经过了高度优化和 inline/DCE,你们知道 inline 对逆向分析来说是个仅仅比位运算稍微好一点的编译优化,当然窥孔优化也很差
- 不幸的是,这个被窥孔优化了
- 幸运的是,我们有调试符号(服服服)
FUN me 249
FUN be 65
- 这个应该是 b64e 这里
- 不幸的是,这个被 DCE 的很严重,我们可以认为优化完只剩下
- False CTYPE:
FUN Java_com_coolapk_market_u 1988
FUN memcpy / memset / strlen / sprintf / strcmp / time / strcat
== Dynamic section
(NEEDED) libstdc++.so liblog.so libm.so libc.so libdl.so
(SONAME) liba.so
(FLAGS) SYMBOLIC BIND_NOW
== ELF sections
.note.gnu.build_id r-- 36
.dynsym r-- 432
.dynstr r-- 271
.hash r-- 184
.rel.dyn r--24
.rel.plt r--80
.plt r-x 176
.text r-x 7.7K
.rodata r-- 565
.eh_frame r-- 724
.eh_frame_hdr r-- 172
.fini_array rw- 8
.init_array rw- 4
.dynamic rw- 256
.got rw- 4
.got.plt rw- 52
.data rw- 4
.bss rw- 0
.comment --- 53
.debug_info --- 13.3K
.debug_abbrev --- 894
.debug_aranges --- 176
.debug_ranges --- 128
.debug_line --- 1.4K
.debug_str --- 5.8K
.note.gnu.gold_version ---28
.symtab --- 704
.strtab --- 426
.shstrtab --- 292
PHDR r-- 256
LOAD0 r-x 10.7K
LOAD1 rw- 328
DYNAMIC rw- 256
NOTE r-- 36
GNU_EH_FRAME r-- 172
GNU_STACK rw- 0
GNU_RELRO rw- 324
ehdr rw- 52
== Debug information (files) radare2
INCLUDEDIR
a.c / b.c / m.c / m.h 这几个文件
readelf -a liba.so 里找出有意思的信息:== ELF Header
CLASS: ELF32
DATA: 2*, le
VER: 1
OS/ABI: UNIX - SYSV (v0)
TYPE: DYN
ARCH: Intel 80386 (v1)
== misc
BuildID[sha1] = aa1eac616f2abab80d5b9268e955c0d37de0df26
GNU Gold: v1.1
== .symtab / .rel.plt / .dynsym
FILE b.c
- 这个文件应该是 Base64 算法的存放文件(从这里抄来的)
OBJ pr2six 256
- 这个对象的 CTYPE 类型是
static const unsigned char pr2six[256];- 这个对象的源代码
- radare2
is; 0x00002500; pc 256
OBJ b6 256- 这个对象的 CTYPE 类型是
static const char b6[64
]; // ASCII, BASIS_64
- 这个对象的源代码 (修改:最后两个 char 换成 "+/")- radare2
is; 0x00002600; pr 64
FILE m.cFUN body 3088
FILE a.c
- 这是 liba.so 的源文件名
FUN BDL 96
- 其实就是
Base64decode_len
- CTYPE: static int BDL(const char *bufcoded);
FUN BD 477- 就是
Base64decode
- CTYPE: static void BD(char *bufplain, const char *bufcoded);
FUN BEL 35- 其实就是
Base64encode_len
- CTYPE: static int BEL(int len);
FUN BE 546- 其实就是
Base64encode
- CTYPE: static void BE(char *encoded, const char *string, int len);
- 进行过检验(确认返回值和第一个参数的数据转送),确认的确应该是复制的逻辑FUN MI 64
FUN MU 324
FUN MF 649
FUN r 125
FUN bd 44
- 这个应该是 b64d
- CTYPE 签名不好给,因为定义和我拿到的 GitHub 开源代码不匹配,是优化过的
- 我可以根据程序逻辑(它只是加了一层栈帧,没有啥实际的业务逻辑)推导出一个可能 Segfault 的类型
- Flase CTYPE:
static void bd(char *out, const char *in);
fn bd(int arg_8, arg_c, arg_14)
auto var_4 @esp +0x4
prologue
push ebx
lea esp -= 20
ebx += 0x23fb
var_4 = arg_c
*esp = arg_8 ; esp +0x0
call BD
esp = arg_14
pop ebx
epilogue
- 对于不会看汇编的小伙伴们可以大概这么理解: static inline void bd(int32_t in, int32_t out) { return BD(in, out); }
- 给不会看汇编的小伙伴们一点提示,对于 esp 和 ebp 的参数来讲,越低的地址会放参数列表里越开始部分的参数(低地址放低参数)、汇编里面指针还是 int32 什么的信息已经丢失了,你得靠动态分析推测是指针还是真的 32 位整形- 代码经过了高度优化和 inline/DCE,你们知道 inline 对逆向分析来说是个仅仅比位运算稍微好一点的编译优化,当然窥孔优化也很差
- 不幸的是,这个被窥孔优化了
- 幸运的是,我们有调试符号(服服服)
FUN me 249
FUN be 65
- 这个应该是 b64e 这里
- 不幸的是,这个被 DCE 的很严重,我们可以认为优化完只剩下
Base64encode(encoded, plain, strlen(plain)); 这行活着- False CTYPE:
static void be(char *out, const char *str);
- 我恨 Android ClangFUN Java_com_coolapk_market_u 1988
FUN memcpy / memset / strlen / sprintf / strcmp / time / strcat
== Dynamic section
(NEEDED) libstdc++.so liblog.so libm.so libc.so libdl.so
(SONAME) liba.so
(FLAGS) SYMBOLIC BIND_NOW
== ELF sections
.note.gnu.build_id r-- 36
.dynsym r-- 432
.dynstr r-- 271
.hash r-- 184
.rel.dyn r--24
.rel.plt r--80
.plt r-x 176
.text r-x 7.7K
.rodata r-- 565
.eh_frame r-- 724
.eh_frame_hdr r-- 172
.fini_array rw- 8
.init_array rw- 4
.dynamic rw- 256
.got rw- 4
.got.plt rw- 52
.data rw- 4
.bss rw- 0
.comment --- 53
.debug_info --- 13.3K
.debug_abbrev --- 894
.debug_aranges --- 176
.debug_ranges --- 128
.debug_line --- 1.4K
.debug_str --- 5.8K
.note.gnu.gold_version ---28
.symtab --- 704
.strtab --- 426
.shstrtab --- 292
PHDR r-- 256
LOAD0 r-x 10.7K
LOAD1 rw- 328
DYNAMIC rw- 256
NOTE r-- 36
GNU_EH_FRAME r-- 172
GNU_STACK rw- 0
GNU_RELRO rw- 324
ehdr rw- 52
== Debug information (files) radare2
id
Source: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/a.c (radare2 iX)INCLUDEDIR
/Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni下面有
a.c / b.c / m.c / m.h 这几个文件
/Users/kjsolo/Library/Android/ndk/toolchains/x86-4.9/prebuilt/darwin-x86_64/lib/gcc/i686-linux-android/4.9/include
stdarg.h
stddef.h
/Users/kjsolo/Library/Android/ndk/platforms/android-21/arch-x86/usr/include
jni.hGitHub
base64url/src/base64.c at master · mllg/base64url
Fast and url-safe base64 encoder and decoder for R - mllg/base64url
duangsuse::Echo
APP_ABI := x86 armeabi-v7a arm64-v8a x86_64 APP_PLATFORM := android-14 APP_BUILD_SCRIPT := Android.mk LOCAL_PATH := $(call my-dir) include $(CLEAN_VARS) LOCAL_CFLAGS := -Wall -Werror -std=c99 -lto -O3 -fno-stack-protector $(EXTRA_FLAGS) LOCAL_MODULE := e2immutable…
OBJ pr2six 这个有意思的东西的意思找到了 — liba.so 使用了这里的源代码
这说明 b6 这个 OBJ 也有可能是 static const char basis_64[]
BD 和 BE 分别可能是 Base64decode / Base64encode
bd 和 be 分别可能是 b64d / b64e
BDL 和 BEL 也分别可能是 Base64decode_len / Base64encode_len
如果真的是这样,那么逆向工程的工作量就大大减小了 🤔
— 事实真的就是这样!
liba.so!BD 里有两行多汇编代码
mov eax, esi
lea esi, [eax + 1]
movzx eax, byte [eax]
movzx eax, al
movzx eax, byte [ebx + eax - 0x1acc]
对应上了这里的
这说明,liba.so!BD 就是 https://github.com/mllg/base64url/blob/master/src/base64.c#L81 的
😂 可以说是非常倒霉了... 本来我们有想重命名复制来的函数的,可是因为忘记 strip 掉调试符号被别人搜索出抄的源码了... 藏叶于林直接被无效化 emmm,,,
这说明 b6 这个 OBJ 也有可能是 static const char basis_64[]
BD 和 BE 分别可能是 Base64decode / Base64encode
bd 和 be 分别可能是 b64d / b64e
BDL 和 BEL 也分别可能是 Base64decode_len / Base64encode_len
如果真的是这样,那么逆向工程的工作量就大大减小了 🤔
— 事实真的就是这样!
liba.so!BD 里有两行多汇编代码
mov eax, esi
lea esi, [eax + 1]
movzx eax, byte [eax]
movzx eax, al
movzx eax, byte [ebx + eax - 0x1acc]
0x00000730 3c3f cmp al, 0x3f(63 的 16 进制表示形式是 0x3f)
0x00000732 76e9 jbe 0x71d
对应上了这里的
while (pr2six[*(bufin++)] <= 63); 循环结构这说明,liba.so!BD 就是 https://github.com/mllg/base64url/blob/master/src/base64.c#L81 的
static void Base64decode(char *bufplain, const char *bufcoded);这个故事告诉我们:千万千万不要在自己的对象文件里保留调试符号,不然连自己的 GitHub 用户帐号、使用的操作系统、自己项目抄的外部代码都可能被别人搜索出来(暴论)
😂 可以说是非常倒霉了... 本来我们有想重命名复制来的函数的,可是因为忘记 strip 掉调试符号被别人搜索出抄的源码了... 藏叶于林直接被无效化 emmm,,,
GitHub
base64url/src/base64.c at master · mllg/base64url
Fast and url-safe base64 encoder and decoder for R - mllg/base64url
duangsuse::Echo
OBJ pr2six 这个有意思的东西的意思找到了 — liba.so 使用了这里的源代码 这说明 b6 这个 OBJ 也有可能是 static const char basis_64[] BD 和 BE 分别可能是 Base64decode / Base64encode bd 和 be 分别可能是 b64d / b64e BDL 和 BEL 也分别可能是 Base64decode_len / Base64encode_len 如果真的是这样,那么逆向工程的工作量就大大减小了 🤔 — 事实真的就是这样!…
顺便一提,编译的那位大佬是个果粉,开始我以为是 Linux 用户(跑
拿 radare2 liba.so 看一下
这个 liba.so 是从
典型的 Android Studio / Mac OSX 开发环境,项目的名字叫做 CoolLibrary,大概是说 CoolApk 的 API Client Library 吧。
INCLUDEDIR
a.c / b.c / m.c / m.h 这几个文件
大家开发 Native 程序的时候,千万要注意 strip 掉不必要的调试符号,像这种 debug build 带额外调试符号 production 里还不 strip 掉的... 属于... 🤔
老实说,以前我和很多人一样,以为 Java 容易『破解』、但是加上混淆甚至 Proguard repackage 『藏叶于林』就已经无法让人做什么的、C 写『黑盒』『保密算法』更是会让人完全无法看透其中奥妙,现在看来这些二进制层面的东西还蛮好玩的。
为什么藏叶于林会让程序很难被『破解』『修改』呢?因为那么多名字几乎完全没有意义的类存于一个地方,手工找要的代码路径很困难,而找到自己『破解』的地方,是很多『破解者』的第一步操作。
因为被混淆的代码里,那些不必要的,对开发者有意义的东西,都没了,虽然这些信息并没有因此消失,但想再高效的拿到他们,很难。
就像是被录制成波形形式的 TTS 人声,其中可能包含各种无谓的杂音,可能有损失的信息(比如原文本),可能有各种各样的缺失、增添、变动... 本质的东西往往不会那么轻易地被发现,所以现在看到的很多机器学习、人工神经网络的算法,真的是很难,很不容易说是四个字的事情。
自以为自己学会了和真正懂了是有很大差别的。
但是一个人的信息处理手段开始多起来的时候就会发现:远远不止(反向)搜索甚至人工过滤这一种手段,人们甚至可以利用动态分析窥进程序运行时的环境,给业务逻辑所依赖的子逻辑“下套”,打上断点,这样分析所需要消耗的时间就大大减少了。
其实就是个视角问题 #GitHub 而已。我以前知道要『点开』一个 『exe 文件』,我只知道用『修改器』可以搜索我在程序『界面』里看到的『数值』然后可以做各种操作...
我根本不知道我是怎么『点开』那个『EXE 文件』的,我甚至不知道,什么是『EXE 文件』,只知道在 explorer 里它的文件系统『后缀名』叫 .exe,我甚至看不到它后面的那一长串 binary 形式储存的内容,连文件头都看不到。
但现在我看到了 CRT 环境,看到了虚拟地址空间、控制流、中断、抽象数据类型、数据转送、Ring、内存、线程调度、寄存器、动态链接,知道了各种各样在现实世界里存在的微处理器,了解了编译原理和编译优化,看到了各种语言背后更高级或者更低级的结构... 我认识了各种各样的工具,我开始学习理解和编写 X86 汇编... 就差一个实践来深化我的能力了。
所以虽然逆向工程不是多么了不起的能力,也挺好玩的,推荐大家有空学习一下。可以从读写二进制格式(比起『文本』格式)学起。
拿 radare2 liba.so 看一下
id (Debug information 部分)这个 liba.so 是从
/Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/a.c 这个 C 源文件编译的(r2 iX)典型的 Android Studio / Mac OSX 开发环境,项目的名字叫做 CoolLibrary,大概是说 CoolApk 的 API Client Library 吧。
INCLUDEDIR
/Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni下面有
a.c / b.c / m.c / m.h 这几个文件
/Users/kjsolo/Library/Android/ndk/toolchains/x86-4.9/prebuilt/darwin-x86_64/lib/gcc/i686-linux-android/4.9/include
stdarg.h
stddef.h
/Users/kjsolo/Library/Android/ndk/platforms/android-21/arch-x86/usr/include
jni.h
所以说 #dev #security #Backend #reveng #Learn大家开发 Native 程序的时候,千万要注意 strip 掉不必要的调试符号,像这种 debug build 带额外调试符号 production 里还不 strip 掉的... 属于... 🤔
老实说,以前我和很多人一样,以为 Java 容易『破解』、但是加上混淆甚至 Proguard repackage 『藏叶于林』就已经无法让人做什么的、C 写『黑盒』『保密算法』更是会让人完全无法看透其中奥妙,现在看来这些二进制层面的东西还蛮好玩的。
为什么藏叶于林会让程序很难被『破解』『修改』呢?因为那么多名字几乎完全没有意义的类存于一个地方,手工找要的代码路径很困难,而找到自己『破解』的地方,是很多『破解者』的第一步操作。
因为被混淆的代码里,那些不必要的,对开发者有意义的东西,都没了,虽然这些信息并没有因此消失,但想再高效的拿到他们,很难。
就像是被录制成波形形式的 TTS 人声,其中可能包含各种无谓的杂音,可能有损失的信息(比如原文本),可能有各种各样的缺失、增添、变动... 本质的东西往往不会那么轻易地被发现,所以现在看到的很多机器学习、人工神经网络的算法,真的是很难,很不容易说是四个字的事情。
自以为自己学会了和真正懂了是有很大差别的。
但是一个人的信息处理手段开始多起来的时候就会发现:远远不止(反向)搜索甚至人工过滤这一种手段,人们甚至可以利用动态分析窥进程序运行时的环境,给业务逻辑所依赖的子逻辑“下套”,打上断点,这样分析所需要消耗的时间就大大减少了。
其实就是个视角问题 #GitHub 而已。我以前知道要『点开』一个 『exe 文件』,我只知道用『修改器』可以搜索我在程序『界面』里看到的『数值』然后可以做各种操作...
我根本不知道我是怎么『点开』那个『EXE 文件』的,我甚至不知道,什么是『EXE 文件』,只知道在 explorer 里它的文件系统『后缀名』叫 .exe,我甚至看不到它后面的那一长串 binary 形式储存的内容,连文件头都看不到。
但现在我看到了 CRT 环境,看到了虚拟地址空间、控制流、中断、抽象数据类型、数据转送、Ring、内存、线程调度、寄存器、动态链接,知道了各种各样在现实世界里存在的微处理器,了解了编译原理和编译优化,看到了各种语言背后更高级或者更低级的结构... 我认识了各种各样的工具,我开始学习理解和编写 X86 汇编... 就差一个实践来深化我的能力了。
所以虽然逆向工程不是多么了不起的能力,也挺好玩的,推荐大家有空学习一下。可以从读写二进制格式(比起『文本』格式)学起。
GitHub
GitHub - skyline75489/what-happens-when-zh_CN: What-happens-when 的中文翻译,原仓库 https://github.com/alex/what-happens-when
What-happens-when 的中文翻译,原仓库 https://github.com/alex/what-happens-when - skyline75489/what-happens-when-zh_CN
duangsuse::Echo
OBJ pr2six 这个有意思的东西的意思找到了 — liba.so 使用了这里的源代码 这说明 b6 这个 OBJ 也有可能是 static const char basis_64[] BD 和 BE 分别可能是 Base64decode / Base64encode bd 和 be 分别可能是 b64d / b64e BDL 和 BEL 也分别可能是 Base64decode_len / Base64encode_len 如果真的是这样,那么逆向工程的工作量就大大减小了 🤔 — 事实真的就是这样!…
马上会确认一下,这就等于,不得不说,如果没有这个 pr2six 名字的提示,我意识不到这是 Base64 codec 算法... 不如那些逆向工程大佬们,熟悉这些算法是基本素质。
duangsuse::Echo
OBJ pr2six 这个有意思的东西的意思找到了 — liba.so 使用了这里的源代码 这说明 b6 这个 OBJ 也有可能是 static const char basis_64[] BD 和 BE 分别可能是 Base64decode / Base64encode bd 和 be 分别可能是 b64d / b64e BDL 和 BEL 也分别可能是 Base64decode_len / Base64encode_len 如果真的是这样,那么逆向工程的工作量就大大减小了 🤔 — 事实真的就是这样!…
经过一点时间的努力和学习,把 base64.c 里的都对比一下酷安的生成算法里的符号
它就是 map 一个 char 所有可能的值到另一个 char,其实每次使用它
具体的原理用途我不清楚,这里也没时间弄清楚。
radare2
应该没有任何修改,要不然逻辑就错了,这是 base64 不是酷安自己的算法。
在 liba.so 里,它叫 b6,不过有一些修改(radare2
static const unsigned char pr2six[256];这是 一个类似 Threaded Interpretation 的优化表吧(
它就是 map 一个 char 所有可能的值到另一个 char,其实每次使用它
while (pr2six[*(bufin++)] <= 63); 这种,是用于优化编解码过程具体的原理用途我不清楚,这里也没时间弄清楚。
radare2
is; 0x00002500; pc 256
const uint8_t pr2six[256] = {
0x40, 0x40,... , 0x40
};
在酷安的 liba.so 里,它的名字叫做 pr2six,如果没有这个符号,一般可以在 .rodata 段找到,第一个一般就是它,它的开头是 0x40 0x40 0x40... (一共有 45 个 64)应该没有任何修改,要不然逻辑就错了,这是 base64 不是酷安自己的算法。
static const char basis_64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
这是 Base64 索引表,任何手工编码过 base64 的人都不会不知道(((在 liba.so 里,它叫 b6,不过有一些修改(radare2
is; 0x00002600; pr 64)。static const char b6[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";GitHub
base64url/src/base64.c at master · mllg/base64url
Fast and url-safe base64 encoder and decoder for R - mllg/base64url
duangsuse::Echo
经过一点时间的努力和学习,把 base64.c 里的都对比一下酷安的生成算法里的符号 static const unsigned char pr2six[256]; 这是 一个类似 Threaded Interpretation 的优化表吧( 它就是 map 一个 char 所有可能的值到另一个 char,其实每次使用它 while (pr2six[*(bufin++)] <= 63); 这种,是用于优化编解码过程 具体的原理用途我不清楚,这里也没时间弄清楚。 radare2 is; 0x00002500;…
咱不说这些没用的,看上面那个吧,尽可能快开始逆向实际生成逻辑或者 porting
Telegram
duangsuse::Echo
所有可以从 readelf -a liba.so 里找出有意思的信息:
== ELF Header
CLASS: ELF32
DATA: 2*, le
VER: 1
OS/ABI: UNIX - SYSV (v0)
TYPE: DYN
ARCH: Intel 80386 (v1)
== misc
BuildID[sha1] = aa1eac616f2abab80d5b9268e955c0d37de0df26
GNU Gold: v1.1
== .symtab / .rel.plt / .dynsym…
== ELF Header
CLASS: ELF32
DATA: 2*, le
VER: 1
OS/ABI: UNIX - SYSV (v0)
TYPE: DYN
ARCH: Intel 80386 (v1)
== misc
BuildID[sha1] = aa1eac616f2abab80d5b9268e955c0d37de0df26
GNU Gold: v1.1
== .symtab / .rel.plt / .dynsym…
这是我当时还原出的公式:
这是实际的
emmmm
#Zhihu 顺推问题 https://www.zhihu.com/question/38206659
不过我就个人观点,不要迷信位运算,有些东西实践还是很重要的,尤其是不要把普通算式优化成(线性)等价的位运算算式,机械化的东西就交给 GCC/LLVM 这种东西去做,为什么自己要写『位运算』来『优化』
int BEL(int n) { return (4 * (n - 1) / 3 | 3) + 2; }
(4 * (n - 1) / 3 | 3) + 2这是实际的
static int Base64encode_len(int len) { return ((len + 2) / 3 * 4) + 1; }
((n + 2) / 3 * 4) + 1emmmm
#Zhihu 顺推问题 https://www.zhihu.com/question/38206659
不过我就个人观点,不要迷信位运算,有些东西实践还是很重要的,尤其是不要把普通算式优化成(线性)等价的位运算算式,机械化的东西就交给 GCC/LLVM 这种东西去做,为什么自己要写『位运算』来『优化』
a * a 这种东西呢?((n - 1) / 3 * 4) | 3 + 2🤔 这个位运算对比好神奇.... #CS
((n + 2) / 3 * 4) + 1
-1 和 +2 差了 3,而 | 3 这个位运算可以把
a / 3 * 4 之后又加回来? 🤔... 好奇妙的东西
duangsuse::Echo
((n - 1) / 3 * 4) | 3 + 2 ((n + 2) / 3 * 4) + 1 🤔 这个位运算对比好神奇.... #CS -1 和 +2 差了 3,而 | 3 这个位运算可以把 a / 3 * 4 之后又加回来? 🤔 ... 好奇妙的东西
#life #statement 🤔 🥕🐔 duangsuse
每次看到这些自己还远远无法理解和掌握的东西,就会感觉,自己好菜啊... 然而菜也还是有自己能做到的事情吧。
突然想到,为什么说中国人 #China 喜欢『谦称』自己『敬称』别人呢?
为什么老是说别人大佬自己菜呢?
对大家我不知道,对 duangsuse 来说,他的解释是,
因为别人是你无法改变的,别人已经足够努力了,而他们的努力需要得到肯定,所以说他们大佬,即使的确有什么可以更好的地方,你能强迫他们怎样呢?
因为自己是有主观能动性的,对自己来说,不够好,意味着还有进步的空间,总是说自己菜,是希望能坚持着不断前行,向着更好的自己迈进。
因为无法改变别人,所以要夸赞别人,因为希望自己能一直进步,所以对自己谦虚。
每次看到这些自己还远远无法理解和掌握的东西,就会感觉,自己好菜啊... 然而菜也还是有自己能做到的事情吧。
突然想到,为什么说中国人 #China 喜欢『谦称』自己『敬称』别人呢?
为什么老是说别人大佬自己菜呢?
对大家我不知道,对 duangsuse 来说,他的解释是,
因为别人是你无法改变的,别人已经足够努力了,而他们的努力需要得到肯定,所以说他们大佬,即使的确有什么可以更好的地方,你能强迫他们怎样呢?
因为自己是有主观能动性的,对自己来说,不够好,意味着还有进步的空间,总是说自己菜,是希望能坚持着不断前行,向着更好的自己迈进。
因为无法改变别人,所以要夸赞别人,因为希望自己能一直进步,所以对自己谦虚。
duangsuse::Echo
所有可以从 readelf -a liba.so 里找出有意思的信息: == ELF Header CLASS: ELF32 DATA: 2*, le VER: 1 OS/ABI: UNIX - SYSV (v0) TYPE: DYN ARCH: Intel 80386 (v1) == misc BuildID[sha1] = aa1eac616f2abab80d5b9268e955c0d37de0df26 GNU Gold: v1.1 == .symtab / .rel.plt / .dynsym…
那么我们就可以总结一下,逆向工程的进度:
== 这是一个 32 位 ELF 80386 共享对象文件,OS/ABI = UNIX/SysV、小端字序、包含调试信息
== BuildID(sha1): aa1eac616f2abab80d5b9268e955c0d37de0df26
== linker: GNU Gold v1.1
== 使用了 memcpy / memset / strlen / sprintf / strcmp / time / strcat 这些外部函数
== 导出了 BDL / BD / BEL / BE / bd / be | MI / MU / MF / me | r / Java_com_coolapk_market_util_AuthUtils_getAS 这些函数
== 需要 libdl.so / libstdc++.so / libc.so / libm.so / liblog.so 这些动态链接库
== 它的库名 (SONAME) 是 liba.so
== 我们真正想要的逻辑是调试符号中文件
== FILE a.c (
- 这是 liba.so 的源文件名
- 经过调试符号检查,在 a.c 源文件里的函数:
r / Java_com_coolapk_market_util_AuthUtils_getAS
== FILE b.c (
- 这个文件应该是 Base64 算法的存放文件(从这里抄来的)
- 经过逻辑检查和调试符号信息排除,在 b.c 文件里的函数:
BDL / BD / BEL / BE / bd / be
- 还应该包含这两个对象:
- 经过猜测,在预处理过程后 m.c 里的函数:
MI / MU / MF / me
- 还应该包含其他隐藏未导出的函数符号:
body
== 由 Android NDK Toolchain 自动生成的代码包括不限于以下符号,和逻辑没有一个半字的关系:
__x86.get_pc_thunk.bx =
== 编译工具链(Android GCC 4.9 Darwin)
== 这是一个 32 位 ELF 80386 共享对象文件,OS/ABI = UNIX/SysV、小端字序、包含调试信息
== BuildID(sha1): aa1eac616f2abab80d5b9268e955c0d37de0df26
== linker: GNU Gold v1.1
== 使用了 memcpy / memset / strlen / sprintf / strcmp / time / strcat 这些外部函数
== 导出了 BDL / BD / BEL / BE / bd / be | MI / MU / MF / me | r / Java_com_coolapk_market_util_AuthUtils_getAS 这些函数
== 需要 libdl.so / libstdc++.so / libc.so / libm.so / liblog.so 这些动态链接库
== 它的库名 (SONAME) 是 liba.so
== 我们真正想要的逻辑是调试符号中文件
a.c 里的 Java_com_coolapk_market_util_AuthUtils_getAS 函数== FILE a.c (
/Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/a.c)- 这是 liba.so 的源文件名
- 经过调试符号检查,在 a.c 源文件里的函数:
r / Java_com_coolapk_market_util_AuthUtils_getAS
== FILE b.c (
/jni/b.c)- 这个文件应该是 Base64 算法的存放文件(从这里抄来的)
- 经过逻辑检查和调试符号信息排除,在 b.c 文件里的函数:
BDL / BD / BEL / BE / bd / be
- 还应该包含这两个对象:
static const char pr2six[256];
static const char b6[65
]; /* Radix64 + "\x00" */
== FILE m.c (/jni/c.c), /jni/m.h
- 这个文件应该存放了一些子程序定义- 经过猜测,在预处理过程后 m.c 里的函数:
MI / MU / MF / me
- 还应该包含其他隐藏未导出的函数符号:
body
== 由 Android NDK Toolchain 自动生成的代码包括不限于以下符号,和逻辑没有一个半字的关系:
__x86.get_pc_thunk.bx =
mov ebx, dword [esp]; ret
__atexit_handler_wrapper / __on_dlclose / atexit / __stack_chk_fail_local== 编译工具链(Android GCC 4.9 Darwin)
/Users/kjsolo/Library/Android/ndk/toolchains/x86-4.9/prebuilt/darwin-x86_64/lib/gcc/i686-linux-android/4.9/
== 平台(Android 21 x86) /ndk/platforms/android-21/arch-x86/usr/include/jni.h
现在找到在某 GitHub repo 里有主的函数(和对象)有这些:extern static const unsigned char pr2six[256];
extern static const char b6[64 + /* C EOS */ 1];
extern static int BDL(const char *bufcoded); // Base64decode_len
extern static void BD(char *bufplain, const char *bufcoded); // Base64decode
extern static int BEL(int len); // Base64encode_len
extern static void BE(char *encoded, const char *string, int len); // Base64encode
extern static void bd(char *out, const char *in); // b64d after DCE (dead code elimination)
extern static void be(char *out, const char *str); // b64e after DCEGitHub
base64url/src/base64.c at master · mllg/base64url
Fast and url-safe base64 encoder and decoder for R - mllg/base64url
duangsuse::Echo
那么我们就可以总结一下,逆向工程的进度: == 这是一个 32 位 ELF 80386 共享对象文件,OS/ABI = UNIX/SysV、小端字序、包含调试信息 == BuildID(sha1): aa1eac616f2abab80d5b9268e955c0d37de0df26 == linker: GNU Gold v1.1 == 使用了 memcpy / memset / strlen / sprintf / strcmp / time / strcat 这些外部函数 == 导出了 BDL / BD…
顺带一提,有了被那位酷安大佬粗心忘记 strip 掉的调试符号的帮助,Avast 的 RetDec 反编译器瞬间黔驴变猛虎,生成了一大堆新的有用信息(正如我也菜 🐔 变病猫///)
// From module: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/a.c
// Address range: 0x1bc4 - 0x1bf0
// Line range: 27 - 30
// Address range: 0x1ce9 - 0x1d2a
// Line range: 47 - 51
// Address range: 0x704 - 0x8e1
// Line range: 128 - 171
// Address range: 0x6a4 - 0x704
// Line range: 112 - 125
// Address range: 0x904 - 0xb26
// Line range: 184 - 214
// Address range: 0x8e1 - 0x904
// Line range: 178 - 181
// Address range: 0xb2a - 0xc0e
// Line range: 93 - 116
// Address range: 0x1d2a - 0x24ee
// Line range: 53 - 145
// Address range: 0x1bf0 - 0x1ce9
// Line range: 33 - 44
// Address range: 0x18be - 0x1b47
// Line range: 248 - 297
// Address range: 0x173a - 0x177a
// Line range: 201 - 210
// Address range: 0x177a - 0x18be
// Line range: 213 - 245
// Address range: 0x1b47 - 0x1bc4
// Line range: 13 - 24
// From module: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/a.c
// Address range: 0x1bc4 - 0x1bf0
// Line range: 27 - 30
void bd(char * dst, char * src);
// From module: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/a.c// Address range: 0x1ce9 - 0x1d2a
// Line range: 47 - 51
void be(char * dst, char * src);
// From module: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/b.c// Address range: 0x704 - 0x8e1
// Line range: 128 - 171
int32_t BD(char * bufplain, char * bufcoded);
// From module: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/b.c// Address range: 0x6a4 - 0x704
// Line range: 112 - 125
int32_t BDL(char * bufcoded);
// From module: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/b.c// Address range: 0x904 - 0xb26
// Line range: 184 - 214
int32_t BE(char * e, char * string, int32_t len);
// From module: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/b.c// Address range: 0x8e1 - 0x904
// Line range: 178 - 181
int32_t BEL(int32_t len);
// From module: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/m.c// Address range: 0xb2a - 0xc0e
// Line range: 93 - 116
char * body(int32_t ctx, char * data, int32_t size);
// From module: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/a.c// Address range: 0x1d2a - 0x24ee
// Line range: 53 - 145
char * Java_com_coolapk_market_util_AuthUtils_getAS(int32_t env, char * obj, char * entryObject, char * jstr);
// From module: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/a.c// Address range: 0x1bf0 - 0x1ce9
// Line range: 33 - 44
void me(char * dst, char * src);
// From module: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/m.c// Address range: 0x18be - 0x1b47
// Line range: 248 - 297
void MF(char * result, int32_t ctx);
// From module: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/m.c// Address range: 0x173a - 0x177a
// Line range: 201 - 210
void MI(int32_t ctx);
// From module: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/m.c// Address range: 0x177a - 0x18be
// Line range: 213 - 245
void MU(int32_t ctx, char * data, uint32_t size);
// From module: /Users/kjsolo/StudioProjects/CoolLibrary/app/src/main/jni/a.c// Address range: 0x1b47 - 0x1bc4
// Line range: 13 - 24
void r(char * s);GitHub
GitHub - avast/retdec: RetDec is a retargetable machine-code decompiler based on LLVM.
RetDec is a retargetable machine-code decompiler based on LLVM. - avast/retdec
duangsuse::Echo
那么我们就可以总结一下,逆向工程的进度: == 这是一个 32 位 ELF 80386 共享对象文件,OS/ABI = UNIX/SysV、小端字序、包含调试信息 == BuildID(sha1): aa1eac616f2abab80d5b9268e955c0d37de0df26 == linker: GNU Gold v1.1 == 使用了 memcpy / memset / strlen / sprintf / strcmp / time / strcat 这些外部函数 == 导出了 BDL / BD…
那么,我会删掉 liba.so 里的 liblog 依赖,然后 multilib x86 动态链接执行它的一些逻辑(
就这么愉快的在这个可靠的静态分析基础上开始动态分析喽? 😆
就这么愉快的在这个可靠的静态分析基础上开始动态分析喽? 😆
duangsuse::Echo
经过一点时间的努力和学习,把 base64.c 里的都对比一下酷安的生成算法里的符号 static const unsigned char pr2six[256]; 这是 一个类似 Threaded Interpretation 的优化表吧( 它就是 map 一个 char 所有可能的值到另一个 char,其实每次使用它 while (pr2six[*(bufin++)] <= 63); 这种,是用于优化编解码过程 具体的原理用途我不清楚,这里也没时间弄清楚。 radare2 is; 0x00002500;…
🤔 酷安 liba.so 添加了无用的依赖关系 liblog.so 使得在 GNU/Linux 电脑上动态链接失败无法正常工作,怎么办?
==
(骗你们的)
实际上,这个项目在
[DuangSUSE@duangsuse]~/Projects/HackingCoolApk/liba% readelf -d liba.so
0x00000015 (DEBUG) 0xd7
0x00000001 (NEEDED) 共享库:[libstdc++.so]
0x00000001 (NEEDED) 共享库:[libm.so]
0x00000001 (NEEDED) 共享库:[libc.so]
0x00000001 (NEEDED) 共享库:[libdl.so]
🤔 酷安 liba.so 的 JNI 导出添加了包名校验逻辑,怎么办?
== 删掉就好。(滑稽)
🤔我该如何使用这个库文件?
动态链接加载使用。
🤔既然已经知道了这么多,为什么不完全重写 liba.so 呢?
没时间、技能不够熟练,壮士可以去(
我也想有源代码的直接复制粘贴,没源代码的上反汇编,可是这相当麻烦(要考虑一些重定位的问题和一些窥孔优化寄存器分配冲突的问题,所以必须要 inline assembly,我不想管那些也不想把汇编再拿 GCC 那蹩脚的汇编模板重写一遍),不如直接用成品,反正也不太差,符合工程规范(滑稽)。
就目前而言,反汇编主要用于软件分析,至于完全逆向工程然后重写算法什么的... 基本都是一些高级 native 编译器工程师的工作吧。
想直接拿 C 什么的重写(甚至只是一部分),没那么容易
反正我的目的就是让 x86-compat Linux 机也可以生成酷安的令牌,不是 Android 的专利
==
readelf -d liba.so
== radare2 iS; seek 到 .dynamic 段,第一个依赖关系项 liblog 在 obj._DYNAMIC+69
从 CTF wiki 查到 DT_DEBUG 21(十六进制 0x15)一般工具不会管这种项目,而 NEEDED (0x1) 就会被 linker 视为必须的依赖库o+ liba.so; v 模式,按一下 i 键输入 15,或者 w \x15 也行(骗你们的)
实际上,这个项目在
obj._DYNAMIC+104
seek 到这个地址,然后 w \x15 就可以让 dynamic linker 直接忽视掉这个死依赖了。[DuangSUSE@duangsuse]~/Projects/HackingCoolApk/liba% readelf -d liba.so
0x00000015 (DEBUG) 0xd7
0x00000001 (NEEDED) 共享库:[libstdc++.so]
0x00000001 (NEEDED) 共享库:[libm.so]
0x00000001 (NEEDED) 共享库:[libc.so]
0x00000001 (NEEDED) 共享库:[libdl.so]
🤔 酷安 liba.so 的 JNI 导出添加了包名校验逻辑,怎么办?
== 删掉就好。(滑稽)
🤔我该如何使用这个库文件?
动态链接加载使用。
🤔既然已经知道了这么多,为什么不完全重写 liba.so 呢?
没时间、技能不够熟练,壮士可以去(
我也想有源代码的直接复制粘贴,没源代码的上反汇编,可是这相当麻烦(要考虑一些重定位的问题和一些窥孔优化寄存器分配冲突的问题,所以必须要 inline assembly,我不想管那些也不想把汇编再拿 GCC 那蹩脚的汇编模板重写一遍),不如直接用成品,反正也不太差,符合工程规范(滑稽)。
就目前而言,反汇编主要用于软件分析,至于完全逆向工程然后重写算法什么的... 基本都是一些高级 native 编译器工程师的工作吧。
想直接拿 C 什么的重写(甚至只是一部分),没那么容易
反正我的目的就是让 x86-compat Linux 机也可以生成酷安的令牌,不是 Android 的专利
ctf-wiki.github.io
ELF文件基本结构 - CTF Wiki