duangsuse::Echo
719 subscribers
4.28K photos
130 videos
583 files
6.49K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
Forwarded from 湘江一桥
直接用java写相对要小巧一些或者说是兼容更好?
Forwarded from dnaugsuz
Kotlin + Proguard / R8 也是很小的,你 Disable Kotlin Reflect 用 Kotlin JVM 的 Reflect 就可以
Forwarded from dnaugsuz
小体积的原则就是尽可能自己写(

比如 parser,很多时候自己写 recursive descent method 的 parser 都会比 Bison/Flex Style 的高大上 LL(1) LALR 算法(比如说,Java 里的 Jay、Cup、Beaver 甚至 JavaCC 这些 Parser generator) Parser 小很多。

而且又优雅。
#DEV #Android 🤔 我的第一个 MinBase64 是两年前 17.1 的,那时候才初三呢...
Forwarded from 羽毛的小白板
其实这是我做的第二个小工具。第一个是好几年前做的,就单纯专门接收 GCM 然后发个通知。之后就没碰了。而且那时候是用家里的老爷机弄,开个 AS 就卡到爆炸了😶
是手机上 AIDE 开发的,那时候不知道什么设计模式,就是单纯的模式识别抄代码(我甚至抄到了 KK 沉浸状态栏的代码,利用一个四边形的彩色 View... 还有一个渐变动画 bling bling 效果的代码),花了一周左右,能用是从完全不懂 Jawa 开始四天后。

然后我发上了酷安 #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 没有很大区别,编译的时候少加了一个不需要的 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 反溢出机制
我去,这也太鬼畜梦幻了,他们居然忘了 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
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
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

#Native #Build 难记... 所以我每次都从某些模板创建这种文件,然后去查 NDK 的文档,这么多宏、变量根本记不住。
然而能写出来依然觉得好大佬啊(迫真

下面是 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)
duangsuse::Echo
(先熟悉一下分析环境,CoolApk 的 API Spec 待会再写) #reveng #GeekApk #CoolAPK #PL Spectrum 我觉得非常需要重构,我会给 DSL 制定新的语法规范,因为实际使用中,我能看到很多东西并不是开始我设计 DSL 的时候想的那样... DSL 目前有点太混沌了,新的 GeekSpec 1.0 将能兼容 GeekApk v1b 和 CoolApk v6 两款实际应用的 API,一样简洁富有表现力的语法,但会有更清晰的描述语义和更多的特性(比如返回状态 matching)。…
所有可以从 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.c
FUN 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 Clang

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 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.h
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]
0x00000730      3c3f           cmp al, 0x3f
0x00000732 76e9 jbe 0x71d

(63 的 16 进制表示形式是 0x3f
对应上了这里的 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,,,
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 看一下 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 汇编... 就差一个实践来深化我的能力了。

所以虽然逆向工程不是多么了不起的能力,也挺好玩的,推荐大家有空学习一下。可以从读写二进制格式(比起『文本』格式)学起。
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 如果真的是这样,那么逆向工程的工作量就大大减小了 🤔 — 事实真的就是这样!…
hhh 其实我 #reveng 根本没有变么, @drakeet 经过那事会称破解纯纯写作者为耗子,我也开始用『黑盒』『藏叶于林』这两个名词了 🤔
(注意:我虽然『破解』过纯纯写作,但我根本不是那种所谓的破解者,真的只是因为 @drakeet 在频道上说了一下自己使用的技术,我想体验一下这些技术,仅此而已,没有任何恶意)

少说点术语,少找点 URI,多看点干货,多看点实际的知识,少弄一大堆少了实际引用的名词,深拷贝学到的知识,新年加油哦。
马上会确认一下,这就等于,不得不说,如果没有这个 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 里的都对比一下酷安的生成算法里的符号

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+/";
这是我当时还原出的公式:

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) + 1

emmmm


#Zhihu 顺推问题 https://www.zhihu.com/question/38206659
不过我就个人观点,不要迷信位运算,有些东西实践还是很重要的,尤其是不要把普通算式优化成(线性)等价的位运算算式,机械化的东西就交给 GCC/LLVM 这种东西去做,为什么自己要写『位运算』来『优化』 a * a 这种东西呢?
((n - 1) / 3 * 4) | 3 + 2
((n + 2) / 3 * 4) + 1

🤔 这个位运算对比好神奇.... #CS

-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 来说,他的解释是,

因为别人是你无法改变的,别人已经足够努力了,而他们的努力需要得到肯定,所以说他们大佬,即使的确有什么可以更好的地方,你能强迫他们怎样呢?
因为自己是有主观能动性的,对自己来说,不够好,意味着还有进步的空间,总是说自己菜,是希望能坚持着不断前行,向着更好的自己迈进。

因为无法改变别人,所以要夸赞别人,因为希望自己能一直进步,所以对自己谦虚。