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

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
duangsuse::Echo
酷安大佬们很聪明,我以为(对『破解者』来说)那么难看的 *char 操作都是酷大佬写的,可其实是 GCC 4.9 写的(跑
[DuangSUSE@duangsuse]~/Projects/HackingCoolApk/liba/RE/dwarview% base64 -d
e7177eb11a13ea5672c543da1690e302dG9rZW46Ly9jb20uY29vbGFway5tYXJrZXQvYzY3ZWY1OTQzNzg0ZDA5NzUwZGNmYmIzMTAyMGYwYWI_7d3bcfe1ca700fdccf5a8595f98f70d8
{�{���խwy�z�g9�wZׯt{}6token://com.coolapk.market/c67ef5943784d09750dcfbb31020f0abbase64: 输入无效

有点意思了 🤔
duangsuse::Echo
[DuangSUSE@duangsuse]~/Projects/HackingCoolApk/liba/RE/dwarview% base64 -d e7177eb11a13ea5672c543da1690e302dG9rZW46Ly9jb20uY29vbGFway5tYXJrZXQvYzY3ZWY1OTQzNzg0ZDA5NzUwZGNmYmIzMTAyMGYwYWI_7d3bcfe1ca700fdccf5a8595f98f70d8 {�{���խwy�z�g9�wZׯt{}6token://com.c…
puts Base64.decode64(Base64.decode64('OGQwN2Y4OWY1OTU4YTVmY2NkZjAwN2FjMWVmY2IzZDcvSVdZd1lHTXlBVE16SW1ZbU5HWndVek41QURaMGd6TnpRVE8xWVdaM1l6WXZRWFpySlhZdDV5YXdGR2J2OTJZdTAyYmo5eUw2NFdacjlHZDIwM2UwOTYxYWQzNDVjMjc2NWFlMzFhMTFiZTc3MTdl').reverse)
酷安大佬貌似是拼接了一大堆包含时间、包名、UUID String 的信息,然后服务端也重复这个算法,比较 Hash 值么?
已经开始无聊了 🤔 全 TM 是 base64 + 拼接字符串
相当无聊的 面向子程序调用分析(COA
有时候可能会很 SSA(Static Single Assignment) 地想,是不是找到了最后要的某个东西,就能根据它推断出它的依赖逻辑呢?这个本地变量就是最后的 Token 结果。
🤔继续往下分析动态字符数组栈上分配...
邪恶的 duangsuse 居然把这个依赖 DAG 收起来了... 🤔
duangsuse::Echo
邪恶的 duangsuse 居然把这个依赖 DAG 收起来了... 🤔
你们这个是个什么口令生成算法啊,你们这是雷普前端开发者们啊你们这个算法。
麻烦你们,真的太过分了你们搞这个『加密算法』干什么,我的项目每一行代码都过不了那个 lint 检查啊,它现在没法连上你们的 API,你叫我项目怎么办啊,它还事个幼儿园基本的弱智应用好不好。

你们这是什么算法啊,哼!哼!啊啊啊啊啊啊啊。你们害死我项目了谁是出要写这种东西主意的快出来你们写代码的,再不出来我 ****** 了啊!

我跟你们说你们这群 Android 开发者啊,一天到晚搞什么这些加密啊代码保护啊会害死你们的(迫真)
你们没有前途我跟你们说你们这400多号人好好写 Jawa 不好么,一天到晚 show C/C++ 有什么意思,有什么意思啊,我偶尔写一下都快被恶心死了。

麻烦你们重视一下你们的 Jack 有点发展目标好不好,一天到晚写写 Rx 写写 Vertx 写写 Databinding 不是人啊,你们一天到晚搞 C++ 不好好写 pattern 没有前途的,NDK 害死人。

#Haha #security #Android #dev 😤
coolapk_a_kdev_f.zip
178.1 KB
#security #share #tools #coolapk 🤔 还没有测试过,或许能用
🤔目前生成的 token 还不能让酷安服务器满意
2a6e2adc2897c8d8133db17c2cd3b1045834ce58-d7d5-38eb-95d5-563167a1983d0x588f16cd

(第一部分是机器代码计算的校验和(用到了 base64 和 MD5),我花了两天用 C 重写了,第二部分是 UUID,第三部分是当前 UNIX 时间的十六进制表示)

7fa737c3bc4e80f1381d3c7e1bfdd40d47b11e0b-97b6-454b-9831-ccaff8f19f020x5c6a5245

🤔 哦... 我记得看汇编的时候的确可能拼接了这个 0x, 不过因为不是用 strcat 所以我没加,加上试试
还是不行,
duangsuse::Echo
2a6e2adc2897c8d8133db17c2cd3b1045834ce58-d7d5-38eb-95d5-563167a1983d0x588f16cd (第一部分是机器代码计算的校验和(用到了 base64 和 MD5),我花了两天用 C 重写了,第二部分是 UUID,第三部分是当前 UNIX 时间的十六进制表示) 7fa737c3bc4e80f1381d3c7e1bfdd40d47b11e0b-97b6-454b-9831-ccaff8f19f020x5c6a5245 🤔 哦... 我记得看汇编的时候的确可能拼接了这个…
我用 UUIDParser 弄了一下,发现给的 UUID 其实都有区别(一个是 name based 一个是 time based)... 最讨厌的就是有时候动态分析很麻烦 🤔

我看这逻辑也挺 plain 的,继续分析,Ruby 重写一下 emmmm...

bd 就是 base64 decode(dst, src)
be 就是 base64 encode(dst, src, srclen)

me 就是 MD5 based message digest(dst, src)

r 就是 reverse string(s)

🤔 好像虽然不是很能完全理解,但是基于调试符号的帮助,重写也不是很困难,我得想想这些名字都会代表什么
像这种反汇编对编译器学习者就很有用(
== /Users/kjsolo/StudioProjects/
== CoolLibrary/app/src/main/jni/a.c

尝试尽可能还原代码原貌,利用从 DWARF 调试符号里以及反汇编分析获得的信息。

void r(char *s) // L13
{ // L14
int length = strlen(s); // L15
int c, i, j; // L16

for (i = 0, j = length - 1; i < j; i++) // L18
{
c = s[i]; // L20
s[i] = s[j]; // L21
s[j] = c; // L22
j--;
} // L24
}

void bd(char *dst, const char *src) // L27
{
BD(dst, src);
}


void me(char *dst, const char *src) // L33
{
unsigned char *digest[16]; // L35
MD5_CTX context; // L36
MI(&context);
MU(&context, src, strlen(src));
MF(digest, &context);

for (int i = 0; i <= 15; i++) {
sprintf(dst[i + i], "%02x", digest[i]);
}
}


void be(char *dst, const char *src) // L47
{
int src_len = strlen(src);

BE(dst, src, src_len);
}

虽然看起来没用(其实也没多大用,大嘘)
不过很好玩的样子(迫真)

我居然彻底还原了原项目的代码风格布局... 细思恐极
现在 duangsuse 在 Radare2 的帮助下已经能模式识别很多 GCC 4.9 汇编模式了

马上,duangsuse 将顺路教大家一些阅读 radare2 自动分析反汇编代码的技巧。 #reveng #Learn
duangsuse::Echo
🤔 Sticker
我们来对照一下这些代码和他们的汇编形式,让我们自己也来当一回 GCC 4.9: 🤔

🐔为什么要手动『编译』一些源代码呢? #Compiler #CS #PL #backend #reveng
为什么要手动分析一些反汇编代码呢?
人不是生来就是 native 编译器,所以我们要写汇编,要了解各种实体机,X86、ARM、MIPS、AVR,看各种反汇编让自己能成为编译器(虽然依然不支持向量化和某些高等编译优化诸如逃逸分析指针别名还有更高级的寄存器分配算法,哭哭)。

有时候或许做梦能梦到自己变成编译器,正在拿着一张 native 代码的草稿图纸后序遍历游走在 AST(Abstract Syntax Tree)的大森林之中... 🤔
或许我今天的任务是为一个叫 CoolLibrary 的项目以 linux-android 的格式生成 i686 机器代码
我的前端们,预处理器、分词器、解析器,他们处理项目结构,他们处理符号定义替换、展开宏、保证了各个编译单元的独立解耦... 然后他们把我需要的东西 — AST 喂给我,而我则会处理 AST,告诉他们翻译生成的程序或者哪里有错误

我走过各种个样的子树 — 有字面量、常量、变量、指针、数组、控制结构、代码块、子程序定义、子程序调用,他们有各种各样自己的属性、flag,有时候我从背包里找出图纸判断他们是否真的应该在这,有时候我会确认他们的属性是否设置得当,有时候我拿出笔记本,记录下他们的存在以便以后查看
K&R C、C89(ANSI C)、C99、C11... 这些冗长生硬的定义通通不是我的实际写照,我只是一个简单的小工具,我负责把你们的一行行容易理解的行为代码用另外一种更低级、更底层,能让实际机器开心的方式表述出来
我所认识并理解的是一门古老的『编程语言』,它的名字叫 C,源自 BCPL,而 BCPL 又源自 CPL,BCPL 已经是 20 世纪 60 年代的东西了,即使这样也没有被 2018 年的你们抛弃?

有时候我会发现森林中暗含的危险路径,打破了抽象类型系统的限制,我就会回溯停止代码生成工作,忠实地汇报错误。
有时候我能走完这个森林,并且完成有时候会一起进行优化和低级化代码的任务,生成那些被你们认为是黑盒的东西,你们认为它牢不可破,可是我知道,即便是使用我的同道 packer / obfuscator 再对我生成的程序进行处理,也永远有人能看透我翻译出的机械逻辑背后藏着的东西,因为我... 我只是个工具
如果你不知道我对你的代码做了些什么,又怎么能看到我留下的『可执行』数据背后的秘密?

如果我是编译器,管它是 JIT 即时编译器还是 AOT 编译器,这真是最吼的。 🤔🐸(尬膜)

一言不合就念诗(划掉)

== 第一个例子,简单到只包含调用的函数
void bd(char *dst, const char *src)
{
BD(dst, src);
}

== 第二个例子,普通一点的包含本地变量的函数
void be(char *dst, const char *src)
{
size_t src_len = strlen(src);

BE(dst, src, src_len);
}

== 第三个例子,一个字符串倒序程序,它包含本地变量和一个循环控制结构
void r(char *s) {
int length = strlen(s);
int c, i, j;

for (i = 0, j = length -1; i < j; i++)
{
c = s[i];
s[i] = s[j];
s[j] = s[i];
j--;
}
}

== 最后一个例子,逻辑稍微长一点的程序,同上

void me(char *dst, const char *src)
{
unsigned char *digest;
MD5_CTX context;
MI(&context);
MU(&context, src, strlen(src));
MF(digest, &context);

for (int i = 0; i <= 15; i++)
sprintf(dst[i * 2], "%02x", digest[i]);
}
duangsuse::Echo
我们来对照一下这些代码和他们的汇编形式,让我们自己也来当一回 GCC 4.9: 🤔 🐔为什么要手动『编译』一些源代码呢? #Compiler #CS #PL #backend #reveng 为什么要手动分析一些反汇编代码呢? 人不是生来就是 native 编译器,所以我们要写汇编,要了解各种实体机,X86、ARM、MIPS、AVR,看各种反汇编让自己能成为编译器(虽然依然不支持向量化和某些高等编译优化诸如逃逸分析指针别名还有更高级的寄存器分配算法,哭哭)。 有时候或许做梦能梦到自己变成编译器,正在拿着一张…
前端的解析(这里是广义的,就是说输入源代码字符流输出 AST)比较机械化,而且和重点无关,不讲。

我们来看看这几个小例子编译之后(在 liba.so 里)是什么样的(反汇编 & 自动化分析由 Radare 2 提供 <3) 🤔

void bd(char *dst, const char *src)

sym.bd (int arg_8h, int arg_ch, int arg_14h);

arg int arg_8h @ ebp+0x8
arg int arg_ch @ ebp+0xc
var int var_4h @ esp+0x4
arg int arg_14h @ esp+0x14

push ebp
mov ebp, esp
push ebx
lea esp, [esp - 0x14]
call sym.__x86.get_pc_thunk.bx
add ebx, 0x23fb
mov eax, dword [arg_ch]
mov dword [var_4h], eax
mov eax, dword [arg_8h]
mov dword [esp], eax
call sym.BD
lea esp, [arg_14h]
pop ebx
pop ebp
ret

首先我们这里是逆向分析练习,所以还是有源代码的,但是那只是为了方便起见,这里我假设你猜到了 bd 是 base64 decode 的意思,然后其他的都不知道

为 r2 自动分析出的东西起个好名字是很重要的,因为很少有人能把那么多 arg_ch / arg_8h 这样基于偏移量的名字记好,分析程序逻辑的时候你需要知道哪是哪(当然 r2 有时候也会根据调用的函数自动推导出一些名字出来,但那很
差)

在这个函数 bd 里,我们可以直接一步一步向下走(其实阅读时有很多的技巧,比如可以由结果往参数从后向前看,但这都是自己积累出来的,别人没法喂给你)

== push ebp

考虑一下你是怎么调用别人的函数的:

+ mov eax, 0xfa ; 15(0xf) * 16(16 ^ 1) + 10(a)
+ mov ebx, dword [ebp+0xc] ; your arg2
+ mov dword [esp+0x4], ebx ; func arg2
+ mov dword [esp], eax ; func arg1
+ call func


1. 啥是 0xfa,啥是 fah
0xfa 就是 16 进制 fa、fah(hex) 也是 16 进制 fa
btw. 0b101010 就是二进制 101010 / 0o01234567 则是八进制 01234567

十六进制有 0123456789abcdef 这 16 个数位、二进制有 01 这两个数位
进制换算是高中数学的基础内容... 🤔 当然,就 RE (逆向工程)来说,好像这也不是非会不可的,建议还是了解一下
不然也可以用 irb 这样的计算器(大雾)帮你换算 #Ruby (不会告诉你们我把 Ruby 当计算器用)

2. mov 是啥?
mov 是 x86 里的数据转送之令,后面你看到的 mov 都是带两个参数的『二元指令』『二地址指令』mov
mov dst, src 它负责把指定数据对象从 src 复制到 dst

= mov ebp, esp 可以理解为 ebp = esp
= mov dword [esp+4], eax 可以理解为把寄存器 eax 的值存到 [esp+4] 这一个双字(64 位)的内存地址里去
= mov eax, dword [ebp+8] 可以理解为把 [ebp+8] 这一个双字的数据转送(放)到 eax 寄存器里

== lea 又是啥
理解成 mov 就行,实际上很多时候使用偏移量的 mov 和 lea 都可以视作一样的
如果你是那种要知道茴香豆的茴字有几种写法的人,这里,page 327

== push 是啥 call 又是啥
push 1 就是让栈指针 esp 递减 sizeof(1),然后 mov [esp], 1
call
就是先 push eip,然后再把 eip(程序计数器)指向某个存储器地址

push 3
push 2
push 1
call addup_3
add esp, 0xc

push eax
push "%s\n"
call printf

ret
则反 call 其道行之,它 poppush 的对应操作)出 return addresss 然后设置 IP(指令指针)
= add / sub 是啥
add eax, 1 <=> eax += 1
sub eax, 1
<=> eax -= 1

{顺便,我们可以分析一下参数到底是怎么传的:}

3. 汇编是啥
一句话:汇编是机器代码的助记符
所以说自动反编译现在一些高大上编译器生成的代码几乎不能看,但是汇编却往往很值得一看(当然之中也存在 false disassembly 这种干扰反汇编器的东西,不过现在逆向工程有了自动化分析辅助已经可以自动检测出很多反逆向工程的『花指令』了,所以说汇编还是很诚实的)
有些简单的汇编甚至可以被类似扫描器的算法线性弄成机器代码,可见他们有多相似

汇编是一种低级语言,你很少能看到有某种汇编存在这高级一点的结构(比如嵌套代码块)
汇编的结构可以认为是线性的,很像基于指令流的虚拟机实际执行时代码的样子

比如(伪代码)

if (jzm !is null) {
println "🐸";
}

可以用一种比较通用的 AST 结构表述

(IfBranch (Not
(Is (Variable "jzm") (null)))) {
(FunCall "println") { StringLiteral("🐸") }
}

翻译成等价的汇编逻辑,就变成这样的了

0 getstatic "jzm"
1 is? null
2 not
3 branchnot 7
4 ldstr "🐸"
5 getstatic "println"
6 call
7 halt

汇编就是现代电子计算机底层一点的通用语言(当然是特指某种汇编,比如 arm 对 arm, x86 对 x86,有时候同一个处理器 family 族还会存在不同的指令支持差异,这也就是 i386 不能安装 i686 的一些软件包的原因)

4. ebx / esp / ebp / eax 又是啥
X86 是个基于寄存器(而不是类似 JVM 的栈)而运作的解释器,比如,假设我们要打印 0 到 101 这 102 个数,那么程序可以这么写:

X86 里一般会用到的寄存器有这些:
eax ebx ecx edx
esi edi
esp ebp
eip

他们真正的名字是去掉前面那个 e 的,也就是说 ax、bx、cx(regiester)、dx(register)...
不过 X86 不是上面那么搞的,因为 80386 之前 Intel 也弄过其他的处理器,所以,AX 在 386 机上面表示某通用寄存器的低位(类似 256 这个十进制数的个位 6),用以和之前的代码兼容....

== bit / byte / halfword / word / doubleword 是什么
电子计算机计算机内部使用二进制(虽然好像再看低一点就成数字电路里的电位高低了)来表示数字
一个 bit 是一个二进制数位。一个 byte 是 8 个 bit、一个 halfword(半字)是 2 个 byte、一个 word (字)是四个 byte、一个 doublebyte(双字)是八个 byte


早些时候,很多电子计算机包括一些嵌入式设备啊/// 游戏机啊 //// 都是 16 位甚至 8 位的
这意味着至少他们计算的精度和容量很不行(比如,如果让 8 个二进制位保存有符号的数字的话,最多能保存 2 ** (8 - 1) 个有效数字,也就才 128 位,不多不多,刚刚好连现在很多聊天软件消息列表的长度都保存不了

80306 机是 32 位的,而 x64 就是扩展到默认 64 位的 386 机而已,感谢上帝吧(迫真)
要不然 AS 这种很吃内存的 IDE 不使用 PAE(物理地址扩展)只有 4G 内存用起来.... 呃