没有去专门绕过壳的反调试,完全使用frida和IDA静态分析,完成了第一个任务成功获取flag。
加密算法共4种,第二个任务注册机,缺一个算法的解密算法,其他三个算法均已写好C实现的解密算法。
frida默认会被检测,经过修改源码中的几个特征(端口号、临时目录、maps中相关字符串等),自行编译后获得一个可以hook除了libsec2023.so之外的所有so的frida。
随后hook系统函数dlopen,当加载libil2cpp.so之后从内存dump下来解密后的so文件,通过比较apk中的so来修复一些结构体信息,此时得到了一个完整的可被IDA正常解析的libil2cpp.so,因此可以用IL2CPPDumper恢复IDA中符号和结构体。
找到CollectCoin函数,使用frida去patch其中金币增加的逻辑,改为每次增加1000,这样我们就可以一次性达到获取flag的分数,从而轻易获取flag。
之后发现有两个被混淆了函数名称的C#类,经过足够的分析后,可以判断其中一个是VM,包含一些字段用来模拟栈、操作码等,总共有21个方法,除了构造方法外均为VM的Handler。随后在xxx函数通过frida分析找到XTEA加密,然后用frida在内存中找到并提取了密钥(第一处加密)。
(PS:这里的加密多少处是指我找的顺序,不是逻辑上的顺序)
Hook SmallKeyboard__iI1Ii_4610736函数后发现函数在开头跳转到了g_sec2023_p_array的函数中,随后在libsec2023.so分析该函数,发现被混淆。模式基本上都是将常规的分支、直接跳转改为用寄存器寻址跳转,寄存器中的跳转地址由一个常数+一个偏移来得到,常数和取偏移的基址在一个函数中是固定的,在跳转前,使用条件选择指令等来获取相应的偏移,从而间接实现分支跳转,使用Unicorn模拟执行+IDAPython修改汇编+遇到特殊情况手动改的方式,我们可以轻易地去除这个混淆。
去除混淆后可以找到两个加密算法(第三、四处加密),其中第三处可以直接分析,第四处加密通过frida hook得知是调用了Sec2023.Encrypt方法,该类由壳加载,JADX / JEB等工具无法直接分析,需要使用frida-dump工具在运行时dump下来,然后分析。随后可以发现一个加了BlackObfuscator的dex中就找到了该方法,由于没有去除混淆的方案,这里选择使用复制所有真实指令,结合第三处加密,通过frida观察加密结果来分析加密算法实现的是否正确了,得到了想要的结果之后就可以写这两处加密的解密函数。
最后回头去分析用VM实现的加密算法(第二处加密),我的解决方案是在frida中hook所有handler,通过输出所有被执行过的指令为C代码,将栈在输出的源码中处理成寄存器变量(register关键字),所有通过opcode来取出的值处理为常量,这样重新编译后就可以得到较为精简的加密算法,该过程需要结合frida多次观察和分析。
分析完这四处算法,逆向编写出这4处加密算法的解密算法,就可以成功实现注册机。
*由于实际解题过程有点复杂和混乱,如果有地方没说清楚请谅解,因为我也忘了我都干了些啥=_=||
下载frida源码,将其中的/server/server.vala中的re.frida_server改为其他字符串,再把src/linux/linux-host-session.vala中的so文件特征字符串更改,然后用一个27042之外的端口启动修改好的frida,随后启动游戏,发现启动成功,并不会被检测。
虽然用修改后的frida去hook libsec2023.so仍然会被检测,但是hook其他库没有出现问题。而so在被dlopen加载之后有可能就解密好了,因此这里选择用frida去尝试hook dlopen去dump libil2cpp.so。
从手机里把pull出来so,发现函数已经解密,IDA能正常解析,但是还有部分函数会爆红。因此用010editor对apk中的so和dump出来的so进行比对,补上尾部的重定位表,重新IDA打开,此时已经可以正常解析。
此时的libil2cpp.so非常完整,尝试使用IL2CPPDumper恢复符号,发现可以成功Dump
在dump.cs中搜索金币相关方法以及字段,发现了CollectCoin这个函数
IDA不断跟进,可以找到一处自增逻辑,那么我们可以利用frida去修改这里的汇编来使我们的金币获取更快
重新启动游戏,随便吃几粒金币,即可获取flag
题目要求实现外挂的注册机,在dnspy中找到SmallKeyboard类,可以分析到输入的小键盘一些相关字段和方法
在IDA中跟进get_input_il1li函数,可以发现是一个判断按键类型进入不同函数的逻辑
结合frida辅助分析可以知道,当KeyType<2时为数字输入,当KeyType=2时为确认,触发关键逻辑
其中SmallKeyboard__iI1Ii_4610736为关键函数,将我们输入的key转为UInt64类型传入,跟进可以发现有一处跳转会跳到g_sec2023_p_array中的一个函数,此处我已经patch(为了IDA能正常反编译该函数)
使用frida hook首部的跳转指令(此处已经是patch成NOP)后一条指令,可以发现我们输入的key发生了变化
该函数中,我们可以找到一个XTEA加密算法,通过frida hook我们可以获取到key
下面是对该加密算法的实现,经过frida对输入输出进行验证,是正确的。
上面的情况说明跳转到的那个地方有对input进行加密的函数,打开libsec2023.so跟进那个函数分析,其中v5函数指针调用的是一个打log的函数,不用理会。主要是sub_3B8CC
跟进可以发现函数被混淆了,因为分支跳转指令利用寄存器寻址
分析可以发现,是将常规的分支、直接跳转改为用寄存器寻址跳转,寄存器中的跳转地址由一个常数+一个偏移来得到,常数和取偏移的基址在一个函数中是固定的,在跳转前,使用条件选择指令等来获取相应的偏移,从而间接实现分支跳转。
现在的情况是,我没有去干掉壳的反调试,因此没办法调试libsec2023.so,而这个壳frida也hook不上。但我又不想费力去手动patch掉所有混淆的汇编指令,此时的最佳选择显然是利用模拟执行相关的工具去帮我们完成。
通过脚本+人工半自动分析,可以很快的去除掉所有需要去除的花指令
去混淆后即可分析函数,经过分析,可以知道是对input以4字节为一组分别进行加密。
其中enc_1逻辑较为明显,显然是可逆的
下面这个函数实现了上面的加密算法,经过frida验证输入输出是正确的。
而enc_2中调用了一个类中的方法,我们只知道这个方法叫什么,但是并不知道是哪个类下的方法,因此我们需要hook GetStaticMethodID这个JNI函数。
[圖片]
通过hook JNI函数找到调用解密函数的类
通过hook,我们可以知道调用的是Sec2023.Encrypt方法,但是JADX中并没有找到,不能直接分析。说明这个类是由壳动态加载的,因此我们需要找办法dump下来dex。
通过这个工具我们可以很轻松地在内存里找到相关的dex,dump下来就可以接着分析了。
打开分析,可以发现加了BlackObfuscator,而我并没有现成的解决方案可以参考和使用,那就只能推理和猜猜看了。
这里我用一种很简单粗暴的办法,就是把每个case中的真实指令直接复制出来,然后可以发现变量之间的运算有明显的逻辑关系,经过推理和结合frida分析验证(上面的frida脚本log了加密完的key),可以推理出正确的加密算法
分析完上面提到的算法之后,我们可以在此处找到一个函数的调用,它改变了input的值,所以应为加密算法
对这两个类进行分析,可以知道上面那个类中的都是opcode dict的key,下面那个类是VM的Handler和一些相关的字段。
VM直接分析不好分析,我们需要侧面去分析。两种常用的方法:一是把指令数组和handler自己实现在程序外部自行分析,二是直接hook来分析。这里选择直接hook通过打log来还原VM逻辑。
首先需要解析VM类的this指针
然后通过手动hook一个个handler来实现VM解释器
随便输入一个字符串,点击确认按钮,我们就可以得到一个输出的形式为C代码的VM逻辑。
输出如下,因为后面我放到直接把他作为加密函数来验证VM逻辑是否dump正确,所以有一些改动
由于我们上面其他加密算法都已经找到了,再加上这个,我们可以对输入输出做完整的验证。经过frida hook输入输出可以知道dump下来的VM逻辑没问题。至此,最后一个问题就是如何去得到这个算法的逆算法。
对于这种VM,我们可以直接重新开优化编译,并把其中的一些变量用register关键字定义为寄存器变量,这样IDA的反编译效果会有极大的提升。
重新编译后,IDA反编译出的算法逻辑如下,总共一百多行,不多也不少。难就难在有指令替换。打算直接动调猜每一条指令的作用(因为变量不会被重复赋值,指令也并没有像原本VM这种难以接受的分析,所以我觉得这是可行的)
题目要求对于任意TOKEN,我们的注册机都能生成一个KEY来注册外挂。那就意味着随机生成的TOKEN经过解密之后就是KEY。那注册机其实就是上述所有算法的解密算法实现。我们知道,首先执行的是壳内两个算法加密,然后是VM内的算法加密,最后是XTEA算法加密,那么注册机就是逆过来实现就完事了。
暂未实现
function WriteMemToFile(addr, size, file_path) {
Java.perform(function() {
var prefix
=
'/data/data/com.com.sec2023.rocketmouse.mouse/files/'
var mkdir
=
Module.findExportByName(
'libc.so'
,
'mkdir'
);
var chmod
=
Module.findExportByName(
'libc.so'
,
'chmod'
);
var fopen
=
Module.findExportByName(
'libc.so'
,
'fopen'
);
var fwrite
=
Module.findExportByName(
'libc.so'
,
'fwrite'
);
var fclose
=
Module.findExportByName(
'libc.so'
,
'fclose'
);
var call_mkdir
=
new NativeFunction(mkdir,
'int'
, [
'pointer'
,
'int'
]);
var call_chmod
=
new NativeFunction(chmod,
'int'
, [
'pointer'
,
'int'
]);
var call_fopen
=
new NativeFunction(fopen,
'pointer'
, [
'pointer'
,
'pointer'
]);
var call_fwrite
=
new NativeFunction(fwrite,
'int'
, [
'pointer'
,
'int'
,
'int'
,
'pointer'
]);
var call_fclose
=
new NativeFunction(fclose,
'int'
, [
'pointer'
]);
call_mkdir(Memory.allocUtf8String(prefix),
0x1FF
);
call_chmod(Memory.allocUtf8String(prefix),
0x1FF
);
var fp
=
call_fopen(
Memory.allocUtf8String(prefix
+
file_path),
Memory.allocUtf8String(
'wb'
));
if
(call_fwrite(addr,
1
, size, fp)) {
console.log(
'[+] Write file success, file path: '
+
prefix
+
file_path);
}
else
{
console.log(
'[x] Write file failed'
);
}
call_fclose(fp);
});
}
function HookLibWithCallback(name, callback) {
var dlopen
=
Module.findExportByName(
'libdl.so'
,
'dlopen'
);
var detach_listener
=
Interceptor.attach(dlopen, {
onEnter: function(args) {
var cur
=
args[
0
].readCString();
console.log(
'[+] dlopen called, name: '
+
cur);
if
(cur.indexOf(name) !
=
-
1
) {
this.hook
=
true;
}
},
onLeave: function() {
if
(this.hook) {
console.log(
'[+] Hook Lib success, name:'
, name);
callback();
detach_listener.detach();
}
}
});
}
function DumpIL2CPP() {
var libil2cpp
=
TraverseModules(
'single'
, {name:
'libil2cpp.so'
});
WriteMemToFile(libil2cpp.base, libil2cpp.size,
'libil2cpp.so'
);
}
function main() {
HookLibWithCallback(
'libil2cpp.so'
, DumpIL2CPP);
}
main();
function WriteMemToFile(addr, size, file_path) {
Java.perform(function() {
var prefix
=
'/data/data/com.com.sec2023.rocketmouse.mouse/files/'
var mkdir
=
Module.findExportByName(
'libc.so'
,
'mkdir'
);
var chmod
=
Module.findExportByName(
'libc.so'
,
'chmod'
);
var fopen
=
Module.findExportByName(
'libc.so'
,
'fopen'
);
var fwrite
=
Module.findExportByName(
'libc.so'
,
'fwrite'
);
var fclose
=
Module.findExportByName(
'libc.so'
,
'fclose'
);
var call_mkdir
=
new NativeFunction(mkdir,
'int'
, [
'pointer'
,
'int'
]);
var call_chmod
=
new NativeFunction(chmod,
'int'
, [
'pointer'
,
'int'
]);
var call_fopen
=
new NativeFunction(fopen,
'pointer'
, [
'pointer'
,
'pointer'
]);
var call_fwrite
=
new NativeFunction(fwrite,
'int'
, [
'pointer'
,
'int'
,
'int'
,
'pointer'
]);
var call_fclose
=
new NativeFunction(fclose,
'int'
, [
'pointer'
]);
call_mkdir(Memory.allocUtf8String(prefix),
0x1FF
);
call_chmod(Memory.allocUtf8String(prefix),
0x1FF
);
var fp
=
call_fopen(
Memory.allocUtf8String(prefix
+
file_path),
Memory.allocUtf8String(
'wb'
));
if
(call_fwrite(addr,
1
, size, fp)) {
console.log(
'[+] Write file success, file path: '
+
prefix
+
file_path);
}
else
{
console.log(
'[x] Write file failed'
);
}
call_fclose(fp);
});
}
function HookLibWithCallback(name, callback) {
var dlopen
=
Module.findExportByName(
'libdl.so'
,
'dlopen'
);
var detach_listener
=
Interceptor.attach(dlopen, {
onEnter: function(args) {
var cur
=
args[
0
].readCString();
console.log(
'[+] dlopen called, name: '
+
cur);
if
(cur.indexOf(name) !
=
-
1
) {
this.hook
=
true;
}
},
onLeave: function() {
if
(this.hook) {
console.log(
'[+] Hook Lib success, name:'
, name);
callback();
detach_listener.detach();
}
}
});
}
function DumpIL2CPP() {
var libil2cpp
=
TraverseModules(
'single'
, {name:
'libil2cpp.so'
});
WriteMemToFile(libil2cpp.base, libil2cpp.size,
'libil2cpp.so'
);
}
function main() {
HookLibWithCallback(
'libil2cpp.so'
, DumpIL2CPP);
}
main();
function PatchIncrease() {
var libil2cpp
=
TraverseModules(
'single'
, {name:
'libil2cpp.so'
});
var insn
=
libil2cpp.base.add(
0x465674
);
console.log(
'[+] Patching..'
);
Memory.protect(insn,
4
,
'rwx'
);
insn.writeByteArray([
0x01
,
0xA0
,
0x0F
,
0x11
]);
}
function PatchIncrease() {
var libil2cpp
=
TraverseModules(
'single'
, {name:
'libil2cpp.so'
});
var insn
=
libil2cpp.base.add(
0x465674
);
console.log(
'[+] Patching..'
);
Memory.protect(insn,
4
,
'rwx'
);
insn.writeByteArray([
0x01
,
0xA0
,
0x0F
,
0x11
]);
}
function HookSmallKeyboard$$parse_input_iI1Ii_4610736() {
var libil2cpp
=
TraverseModules(
'single'
, {name:
'libil2cpp.so'
});
var HookSmallKeyboard$$parse_input_iI1Ii_4610736
=
libil2cpp.base.add(
0x465AB4
);
Interceptor.attach(HookSmallKeyboard$$parse_input_iI1Ii_4610736, {
onEnter: function(args) {
PrintNativeBackTraceFuzzy(this.context, TraverseModules(
'all'
, {}));
console.log(
'[+] HookSmallKeyboard$$parse_input_iI1Ii_4610736 called'
);
console.log(
'input_key: '
, args[
1
]);
Dump_g_sec2023_o_array();
}
});
}
/
/
假设输入了
1111
,则key会变成
0x7d8174410d817fa
/
/
[
+
] HookSmallKeyboard$$parse_input_iI1Ii_4610736 called
/
/
input_key:
0x7d8174410d817fa
function HookSmallKeyboard$$parse_input_iI1Ii_4610736() {
var libil2cpp
=
TraverseModules(
'single'
, {name:
'libil2cpp.so'
});
var HookSmallKeyboard$$parse_input_iI1Ii_4610736
=
libil2cpp.base.add(
0x465AB4
);
Interceptor.attach(HookSmallKeyboard$$parse_input_iI1Ii_4610736, {
onEnter: function(args) {
PrintNativeBackTraceFuzzy(this.context, TraverseModules(
'all'
, {}));
console.log(
'[+] HookSmallKeyboard$$parse_input_iI1Ii_4610736 called'
);
console.log(
'input_key: '
, args[
1
]);
Dump_g_sec2023_o_array();
}
});
}
/
/
假设输入了
1111
,则key会变成
0x7d8174410d817fa
/
/
[
+
] HookSmallKeyboard$$parse_input_iI1Ii_4610736 called
/
/
input_key:
0x7d8174410d817fa
function get_reg_code() {
var libil2cpp
=
TraverseModules(
'single'
, {name:
'libil2cpp.so'
});
var ctor
=
libil2cpp.base.add(
0x4660E8
);
Interceptor.attach(ctor, {
onEnter: function(args) {
console.log(
'[+] vm ctor called'
);
console.log(
'ipt_enc_high: '
, this.context.x22);
},
onLeave: function(retval) {
console.log(
'[+] vm ctor returned'
);
}
});
var loc_465D34
=
libil2cpp.base.add(
0x465D34
);
Interceptor.attach(loc_465D34, {
onEnter: function(args) {
console.log(
'[+] get_reg_code called'
);
console.log(
'key_array: '
, parseSystemArrayObjectU32(this.context.x20));
console.log(
'ipt_enc_high: '
, this.context.x22);
console.log(
'ipt_enc_low: '
, this.context.x21);
console.log(
'reg_code_i32: '
, this.context.x0);
}
});
}
function get_reg_code() {
var libil2cpp
=
TraverseModules(
'single'
, {name:
'libil2cpp.so'
});
var ctor
=
libil2cpp.base.add(
0x4660E8
);
Interceptor.attach(ctor, {
onEnter: function(args) {
console.log(
'[+] vm ctor called'
);
console.log(
'ipt_enc_high: '
, this.context.x22);
},
onLeave: function(retval) {
console.log(
'[+] vm ctor returned'
);
}
});
var loc_465D34
=
libil2cpp.base.add(
0x465D34
);
Interceptor.attach(loc_465D34, {
onEnter: function(args) {
console.log(
'[+] get_reg_code called'
);
console.log(
'key_array: '
, parseSystemArrayObjectU32(this.context.x20));
console.log(
'ipt_enc_high: '
, this.context.x22);
console.log(
'ipt_enc_low: '
, this.context.x21);
console.log(
'reg_code_i32: '
, this.context.x0);
}
});
}
uint32_t key[
4
]
=
{
0x7B777C63
,
0xC56F6BF2
,
0x2B670130
,
0x76ABD7FE
};
void EncryptXTEA(uint32_t
*
msg, uint32_t
*
key, ssize_t
round
) {
uint32_t v0
=
msg[
0
], v1
=
msg[
1
];
uint32_t delta
=
DELTA,
sum
=
0xBEEFBEEF
;
for
(ssize_t idx
=
0
; idx <
round
; idx
+
+
) {
v0
+
=
(((v1 <<
7
) ^ (v1 >>
8
))
+
v1) ^ (
sum
-
key[
sum
&
3
]);
sum
+
=
delta;
v1
+
=
(((v0 <<
8
) ^ (v0 >>
7
))
-
v0) ^ (
sum
+
key[(
sum
>>
13
) &
3
]);
}
msg[
0
]
=
v0;
msg[
1
]
=
v1;
}
uint32_t key[
4
]
=
{
0x7B777C63
,
0xC56F6BF2
,
0x2B670130
,
0x76ABD7FE
};
void EncryptXTEA(uint32_t
*
msg, uint32_t
*
key, ssize_t
round
) {
uint32_t v0
=
msg[
0
], v1
=
msg[
1
];
uint32_t delta
=
DELTA,
sum
=
0xBEEFBEEF
;
for
(ssize_t idx
=
0
; idx <
round
; idx
+
+
) {
v0
+
=
(((v1 <<
7
) ^ (v1 >>
8
))
+
v1) ^ (
sum
-
key[
sum
&
3
]);
sum
+
=
delta;
v1
+
=
(((v0 <<
8
) ^ (v0 >>
7
))
-
v0) ^ (
sum
+
key[(
sum
>>
13
) &
3
]);
}
msg[
0
]
=
v0;
msg[
1
]
=
v1;
}
from
emu_utils
import
*
from
unicorn
import
*
from
unicorn.arm64_const
import
*
def
trace_back_insn_with_target(insn_queue, target_reg):
for
insn
in
insn_queue:
if
(target_reg
in
insn.op_str):
if
(insn.mnemonic
=
=
'add'
):
print
(insn.mnemonic
+
'\t'
+
insn.op_str)
if
(insn.mnemonic
=
=
'ldr'
):
print
(insn.mnemonic
+
'\t'
+
insn.op_str)
if
(insn.mnemonic
=
=
'csel'
):
print
(insn.mnemonic
+
'\t'
+
insn.op_str)
def
log_hook(emu, addr, size, user_data):
disasm
=
get_disasm(emu, addr, size)
print
(
hex
(addr)
+
'\t'
+
disasm.mnemonic
+
'\t'
+
disasm.op_str)
def
step_over_hook(emu, addr, size, none):
disasm
=
get_disasm(emu, addr, size)
if
(disasm.mnemonic
=
=
'bl'
or
disasm.mnemonic
=
=
'blr'
):
emu.reg_write(UC_ARM64_REG_PC, addr
+
size)
if
(disasm.mnemonic
=
=
'ret'
):
print
(
'function returned'
)
emu.emu_stop()
if
(addr
=
=
0x3ac68
):
emu.reg_write(UC_ARM64_REG_W10,
0xEECF7326
)
def
normal_hook(emu, addr, size, insn_queue):
global
const_value, offset_value, cond, cond_value, uncond_value
disasm
=
get_disasm(emu, addr, size)
reg_maps
=
get_reg_maps()
insn_queue.insert(
0
, disasm)
if
(
len
(insn_queue) >
8
):
insn_queue.pop()
if
(disasm.mnemonic
=
=
'csel'
):
cond_value
=
emu.reg_read(reg_maps[disasm.op_str.split(
', '
)[
1
]])
uncond_value
=
emu.reg_read(reg_maps[disasm.op_str.split(
', '
)[
2
]])
cond
=
disasm.op_str.split(
', '
)[
3
]
if
(disasm.mnemonic
=
=
'cset'
):
cond_value
=
1
uncond_value
=
0
cond
=
disasm.op_str.split(
', '
)[
1
]
if
(disasm.mnemonic
=
=
'ldr'
):
if
(
len
(disasm.op_str.split(
', '
))
=
=
3
):
offset_value
=
emu.reg_read(
reg_maps[disasm.op_str.split(
', '
)[
1
].split(
'['
)[
1
]])
elif
(
len
(disasm.op_str.split(
', '
))
=
=
4
):
offset_value
=
emu.reg_read(
reg_maps[disasm.op_str.split(
', '
)[
1
].split(
'['
)[
1
]])
cond_value
*
=
8
if
(disasm.mnemonic
=
=
'add'
and
'#'
not
in
disasm.op_str
and
'w'
not
in
disasm.op_str):
const_value
=
emu.reg_read(reg_maps[disasm.op_str.split(
', '
)[
2
]])
if
(disasm.mnemonic
=
=
'br'
):
print
(
'on br insn'
)
target_reg
=
disasm.op_str
trace_back_insn_with_target(insn_queue, target_reg)
print
(
hex
(const_value),
hex
(offset_value),
cond, cond_value, uncond_value)
cond_addr
=
emu.mem_read(offset_value
+
cond_value,
4
)
cond_addr
=
(
int
.from_bytes(
cond_addr, byteorder
=
'little'
)
+
const_value) &
0xffffffff
uncond_addr
=
emu.mem_read(offset_value
+
uncond_value,
4
)
uncond_addr
=
(
int
.from_bytes(
uncond_addr, byteorder
=
'little'
)
+
const_value) &
0xffffffff
patch_asm
=
b''
patch_asm
+
=
get_asm(
'b'
+
cond
+
' '
+
hex
(cond_addr), addr
-
4
)
patch_asm
+
=
get_asm(
'b '
+
hex
(uncond_addr), addr)
emu.reg_write(UC_ARM64_REG_PC, addr
+
size)
def
emulate_execution(filename, start_addr, hook_func, user_data):
emu
=
Uc(UC_ARCH_ARM64, UC_MODE_LITTLE_ENDIAN)
textSec
=
get_section(filename,
'.text'
)
dataSec
=
get_section(filename,
'.data'
)
textSec_entry
=
textSec.header[
'sh_addr'
]
textSec_size
=
textSec.header[
'sh_size'
]
textSec_raw
=
textSec.header[
'sh_offset'
]
TEXT_BASE
=
textSec_entry >>
12
<<
12
TEXT_SIZE
=
(textSec_size
+
0x1000
) >>
12
<<
12
TEXT_RBASE
=
textSec_raw >>
12
<<
12
dataSec_entry
=
dataSec.header[
'sh_addr'
]
dataSec_size
=
dataSec.header[
'sh_size'
]
dataSec_raw
=
dataSec.header[
'sh_offset'
]
DATA_BASE
=
dataSec_entry >>
12
<<
12
DATA_SIZE
=
(dataSec_size
+
0x1000
) >>
12
<<
12
DATA_RBASE
=
dataSec_raw >>
12
<<
12
VOID_1_BASE
=
0x00000000
VOID_1_SIZE
=
TEXT_BASE
VOID_2_BASE
=
TEXT_BASE
+
TEXT_SIZE
VOID_2_SIZE
=
DATA_BASE
-
VOID_2_BASE
STACK_BASE
=
DATA_BASE
+
DATA_SIZE
STACK_SIZE
=
0xFFFFFFFF
-
STACK_BASE >>
12
<<
12
emu.mem_map(VOID_1_BASE, VOID_1_SIZE)
emu.mem_map(TEXT_BASE, TEXT_SIZE)
emu.mem_map(DATA_BASE, DATA_SIZE)
emu.mem_map(VOID_2_BASE, VOID_2_SIZE)
emu.mem_map(STACK_BASE, STACK_SIZE)
emu.mem_write(TEXT_BASE, read(filename)[TEXT_RBASE:TEXT_RBASE
+
TEXT_SIZE])
emu.mem_write(DATA_BASE, read(filename)[DATA_RBASE:DATA_RBASE
+
DATA_SIZE])
emu.reg_write(UC_ARM64_REG_FP, STACK_BASE
+
0x1000
)
emu.reg_write(UC_ARM64_REG_SP, STACK_BASE
+
STACK_SIZE
/
/
2
)
emu.hook_add(UC_HOOK_CODE, log_hook)
emu.hook_add(UC_HOOK_CODE, step_over_hook, user_data)
emu.hook_add(UC_HOOK_CODE, hook_func, user_data)
emu.emu_start(start_addr,
0x0
)
if
__name__
=
=
'__main__'
:
filename
=
'./libsec2023.so'
start_addr
=
0x3ac68
insn_queue
=
[]
emulate_execution(filename, start_addr, normal_hook, insn_queue)
from
emu_utils
import
*
from
unicorn
import
*
from
unicorn.arm64_const
import
*
def
trace_back_insn_with_target(insn_queue, target_reg):
for
insn
in
insn_queue:
if
(target_reg
in
insn.op_str):
if
(insn.mnemonic
=
=
'add'
):
print
(insn.mnemonic
+
'\t'
+
insn.op_str)
if
(insn.mnemonic
=
=
'ldr'
):
print
(insn.mnemonic
+
'\t'
+
insn.op_str)
if
(insn.mnemonic
=
=
'csel'
):
print
(insn.mnemonic
+
'\t'
+
insn.op_str)
def
log_hook(emu, addr, size, user_data):
disasm
=
get_disasm(emu, addr, size)
print
(
hex
(addr)
+
'\t'
+
disasm.mnemonic
+
'\t'
+
disasm.op_str)
def
step_over_hook(emu, addr, size, none):
disasm
=
get_disasm(emu, addr, size)
if
(disasm.mnemonic
=
=
'bl'
or
disasm.mnemonic
=
=
'blr'
):
emu.reg_write(UC_ARM64_REG_PC, addr
+
size)
if
(disasm.mnemonic
=
=
'ret'
):
print
(
'function returned'
)
emu.emu_stop()
if
(addr
=
=
0x3ac68
):
emu.reg_write(UC_ARM64_REG_W10,
0xEECF7326
)
def
normal_hook(emu, addr, size, insn_queue):
global
const_value, offset_value, cond, cond_value, uncond_value
disasm
=
get_disasm(emu, addr, size)
reg_maps
=
get_reg_maps()
insn_queue.insert(
0
, disasm)
if
(
len
(insn_queue) >
8
):
insn_queue.pop()
if
(disasm.mnemonic
=
=
'csel'
):
cond_value
=
emu.reg_read(reg_maps[disasm.op_str.split(
', '
)[
1
]])
uncond_value
=
emu.reg_read(reg_maps[disasm.op_str.split(
', '
)[
2
]])
cond
=
disasm.op_str.split(
', '
)[
3
]
if
(disasm.mnemonic
=
=
'cset'
):
cond_value
=
1
uncond_value
=
0
cond
=
disasm.op_str.split(
', '
)[
1
]
if
(disasm.mnemonic
=
=
'ldr'
):
if
(
len
(disasm.op_str.split(
', '
))
=
=
3
):
offset_value
=
emu.reg_read(
reg_maps[disasm.op_str.split(
', '
)[
1
].split(
'['
)[
1
]])
elif
(
len
(disasm.op_str.split(
', '
))
=
=
4
):
offset_value
=
emu.reg_read(
reg_maps[disasm.op_str.split(
', '
)[
1
].split(
'['
)[
1
]])
cond_value
*
=
8
if
(disasm.mnemonic
=
=
'add'
and
'#'
not
in
disasm.op_str
and
'w'
not
in
disasm.op_str):
const_value
=
emu.reg_read(reg_maps[disasm.op_str.split(
', '
)[
2
]])
if
(disasm.mnemonic
=
=
'br'
):
print
(
'on br insn'
)
target_reg
=
disasm.op_str
trace_back_insn_with_target(insn_queue, target_reg)
print
(
hex
(const_value),
hex
(offset_value),
cond, cond_value, uncond_value)
cond_addr
=
emu.mem_read(offset_value
+
cond_value,
4
)
cond_addr
=
(
int
.from_bytes(
cond_addr, byteorder
=
'little'
)
+
const_value) &
0xffffffff
uncond_addr
=
emu.mem_read(offset_value
+
uncond_value,
4
)
uncond_addr
=
(
int
.from_bytes(
uncond_addr, byteorder
=
'little'
)
+
const_value) &
0xffffffff
patch_asm
=
b''
patch_asm
+
=
get_asm(
'b'
+
cond
+
' '
+
hex
(cond_addr), addr
-
4
)
patch_asm
+
=
get_asm(
'b '
+
hex
(uncond_addr), addr)
emu.reg_write(UC_ARM64_REG_PC, addr
+
size)
def
emulate_execution(filename, start_addr, hook_func, user_data):
emu
=
Uc(UC_ARCH_ARM64, UC_MODE_LITTLE_ENDIAN)
textSec
=
get_section(filename,
'.text'
)
dataSec
=
get_section(filename,
'.data'
)
textSec_entry
=
textSec.header[
'sh_addr'
]
textSec_size
=
textSec.header[
'sh_size'
]
textSec_raw
=
textSec.header[
'sh_offset'
]
TEXT_BASE
=
textSec_entry >>
12
<<
12
TEXT_SIZE
=
(textSec_size
+
0x1000
) >>
12
<<
12
TEXT_RBASE
=
textSec_raw >>
12
<<
12
dataSec_entry
=
dataSec.header[
'sh_addr'
]
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2023-4-17 19:07
被fallw1nd编辑
,原因: