京麒CTF2024-HarmonyOS移动端Native层逆向
2024-6-18 17:53:46 Author: mp.weixin.qq.com(查看原文) 阅读量:10 收藏

鸿蒙HarmonyOS逆向

找到的工具:HapViewer 发行版(https://gitee.com/westinyang/hap-viewer/releases)

鸿蒙逆向目前没有完整的逆向工具所以手动逆向。


先将.hap文件后缀更改为.zip解压后就可以看见.hap的文件结构了。


.abc文件类似于安卓的.dex文件,直接用txt文本打开发现并没有进行加密或者混淆源码直接在里面。




发现需要用utf-8可以解决部分中文乱码。




提取出主要的代码:

    

constructor(parent, params, __localStorage, elmtId = -1) {
super(parent, __localStorage, elmtId);
this.context = getContext(this);
this.__message = new ObservedPropertySimplePU('请输入flag', this, "message");
this.__button_name = new ObservedPropertySimplePU('提交', this, "button_name");
this.__flag = new ObservedPropertySimplePU('', this, "flag");
this.__result = new ObservedPropertySimplePU('', this, "result");
this.dialogController = new CustomDialogController({
builder: () => {
let jsDialog = new CustomDialogExample(this, {
textValue: this.__result,
});
jsDialog.setController(this.dialogController);
ViewPU.create(jsDialog);
}
}, this);
this.setInitiallyProvidedValue(params);
}

这段代码初始化了一个flag提交框。

        

this.observeComponentCreation((elmtId, isInitialRender) => {
ViewStackProcessor.StartGetAccessRecordingFor(elmtId);
Button.createWithLabel(this.button_name);
Button.onClick(() => {
this.context.resourceManager.getRawFileContent("bin").then((value) => {
var c = testNapi.check(this.flag, value);
if ((c & 0b100) === 0b100) {
this.result = '系统环境异常';
}
else if ((c & 0b10) === 0b10) {
this.result = 'flag格式错误';
}
else if ((c & 0b1) === 0b1) {
this.result = 'flag错误或系统环境异常';
}
else {
this.result = 'flag正确';
}
this.dialogController.open();
});
});
if (!isInitialRender) {
Button.pop();
}
ViewStackProcessor.StopGetAccessRecording();
});

在txt里面找到了判断flag的按钮。var c = testNapi.check(this.flag, value);
发现这里就有check函数可以判断flag。

发现testNapi那么就可以对标安卓的Native层方法了:16.7:NAPI 加载原理(上) | 《ArkUI实战》(https://www.arkui.club/chapter16/16_7_source.html#_16-7-1-hap-%E5%B7%A5%E7%A8%8B%E7%BB%93%E6%9E%84)


testNapi方法是写在libentry.so文件里。


拖入ida直接开始查找!


先了解鸿蒙的Native层方法注册流程(鸿蒙用的是魔改后的Node.js的原生库ffi-napi):
注册方法:
RegisterEntryModule-》napi_module_register-》要注册方法的结构体napi_module

typedef struct napi_module {
int nm_version;
unsigned int nm_flags;
void* nm_filename;
napi_addon_register_func nm_register_func;
void* nm_modname;
void* nm_priv;
void* reserved[4];
} napi_module;



发现这个check方法是在这里。




找到check方法。


这里有很多api特别重要先提前了解一波:这些api的来源都是js的Node-API(https://nodejs.cn/api/n-api.html),华为用ArkTS又封装了一遍。

问chatgpt就可以知道他们的作用:

napi_get_cb_info
napi_get_value_string_utf8
napi_get_typedarray_info
napi_get_reference_value
napi_call_function
napi_create_int32
napi_coerce_to_bool

napi_call_function函数和TS的testNapi.register回调函数

在之前找到的源码区:

aboutToAppear() {
// 注册 testNapi 处理程序,针对不同的 batteryInfo 属性进行比较和返回结果
// 电池剩余电量差值判断
testNapi.register(0, (a) => {
var t = batteryInfo.batterySOC - a;
var f;
if (t > 0)
f = 1;
else if (t == 0)
f = 0;
else
f = -1;
return f === 0;
});
...
// 直接返回电池温度
testNapi.register(262, () => {
return batteryInfo.batteryTemperature;
});
// 直接返回电池是否存在
testNapi.register(263, () => {
return batteryInfo.isBatteryPresent;
});
// 直接返回电池容量等级
testNapi.register(264, () => {
return batteryInfo.batteryCapacityLevel;
});
}

这些代码就是在ArkTS源码区注册的回调函数,Native层的napi_call_function函数可以通过序号调用这些ArkTs层的代码。

先进行部分分析,最后再汇总分析所有伪代码。

重点关注一下SO文件里的API函数

Native层的获取回调函数的函数:

// 通过bin_i的值获取注册在TS的代码,将注册的方法存放于reg_method_0
napi_get_reference_value(env, *(v29 + 40), ®_method_0);
// 通过bin_i_or_100的值获取注册在TS的代码,将函数存放在reg_method_1
napi_get_reference_value(env, *(v36 + 40), ®_method_1);

下面分析一下逻辑伪代码。

根据这个两个获取回调函数地址的函数我们可以猜测出check方法分为三个部分:

1. 根据bin[i]获取reg_method_0地址和reg_method_1地址的查找代码

2. 找到函数后根据bin[i+1]获取VM执行流程

bin[i+1]== 0

bin[i+1]== 1

bin[i+1]== 2

3. 在检查完环境问题后进行加密的重量级部分

根据bin[i+1]的值分析不同控制流所执行的逻辑

reg_method_1方法在被找到的那一刻就被执行了,返回值是method_1_ret:


1.当bin[i+1]== 0

else if ( !keyvalue )// keyvalue==0,method_1_ret被转为int32,传入一个参数
{
//VM整体操作流程序号加4
v40 = targetidx + 4;
//获取bin[i+3]作为整数
napi_create_int32(env, *(bin + targetidx + 3), &int_3_arg);
//调用reg_method_0(bin[i+3])得到返回值——》methodfun_0_ret
napi_call_function(env,this,reg_method_0,1,&int_3_arg, &methodfun_0_ret);
//将reg_method_1函数的返回值作为switch_case_key
napi_get_value_int32(env, method_1_ret, &method_1_ret_bool);// 转为int32
LOBYTE(switch_case_key) = method_1_ret_bool;
}

1. VM整体操作流程序号加4
2. 获取bin[i+3]作为整数
3. 调用reg_method_0(bin[i+3])得到返回值——》methodfun_0_ret
4. 将reg_method_1函数的返回值作为switch_case_key
注:switch_case_key用来加密

2.当bin[i+1]== 1

v40 = targetidx + 3;//VM整体操作流程序号加上3
// keyvalue == 1,method_1_ret返回值被转为utf8,传入一个参数
if ( keyvalue == 1 )
{
//获取bin[i+2]的值作为size
size = *(bin + targetidx + 2);
//获取bin + v40中的字符串,其实就是bin[i+3]
napi_create_string_utf8(env, bin + v40, size, &int_3_arg);
v40 += size;//VM整体操作流程序号加上字符串的长度
env_1 = env;
//调用reg_method_0函数,将字符串int_3_arg传入reg_method_0函数
napi_call_function(env, this, reg_method_0, 1LL, &int_3_arg, &methodfun_0_ret);
//得到返回值reg_method_0函数的返回值
napi_get_value_string_utf8(env, method_1_ret, buf, 128LL, &stringlen1);
if ( stringlen1 )
{
....
}
1. VM整体操作流程序号加上3
2. 获取bin[i+2]的值作为size
3. 获取bin + v40中的字符串,其实就是bin[i+3]
4. VM整体操作流程序号加上字符串的长度
5. 调用reg_method_0函数,将字符串int_3_arg传入reg_method_0函数
6. 得到返回值reg_method_0函数的返回值

3.当bin[i+1]== 2

if ( keyvalue == 2 )// keyvalue == 2,method_1_ret的返回值被转为bool,传入一个参数
{
//获取bin[i+3]的值作为int_3_arg
napi_create_int32(env, *(bin + targetidx + 3), &int_3_arg);
//转换类型int32-》bool
napi_coerce_to_bool(env, int_3_arg, &int_3_arg);
//调用reg_method_0(int_3_arg)->返回值methodfun_0_ret
napi_call_function(env, this, reg_method_0, 1LL, &int_3_arg, &methodfun_0_ret);
//将reg_method_1函数的返回值作为switch_case_key
napi_get_value_bool(env, method_1_ret, &method_1_ret_bool);
LOBYTE(switch_case_key) = method_1_ret_bool;
v40 = targetidx + 4;
}
1. 获取bin[i+3]的值作为int_3_arg
2. 转换类型int32-》bool
3. 调用reg_method_0(int_3_arg)
4. 将reg_method_1函数的返回值作为switch_case_key
注:switch_case_key用来加密

分析完执行流程通过Python来简化一下

def dump_bin(bin):
d = []
pc = 0
while pc < len(bin):
op = bin[pc]
print('####################', pc, op)

# 获取函数地址reg_method_0,通过bin[pc]查找
# print('reg_method_0 = func[%d]' % (op))
# 获取函数地址reg_method_1,通过bin[pc] | 0x100 查找
#调用reg_method_1获得返回值
print('method_1_ret = call func[%d]' % (op | 0x100))
#获取操作类型
type = bin[pc + 1]
if type == 0 :
print('method_0_ret = call func[%d](%d)' % (op, bin[pc + 3]))
key = bin[pc + 3]
pc += 4
elif type == 1:
#获取bin中字符串的长度
size = bin[pc + 2]
s = bin[pc + 3: pc + 3 + size]
print('method_0_ret = call func[%d](%s)' % (op, repr(s)))
pc += 3 + size
elif type == 2 :
print('method_0_ret = call func[%d](%d)' % (op, bin[pc + 3]))
key = bin[pc + 3]
pc += 4
else:
pc += 3
assert False
d.append((op, key))
return d
with open(r'.\bin', 'rb') as file:
encrypted_data = file.read()

print(dump_bin(encrypted_data))

得到执行流程:

#################### 0 3
method_1_ret = call func[259]
method_0_ret = call func[3](1)
#################### 4 0
method_1_ret = call func[256]
method_0_ret = call func[0](100)
#################### 8 4
method_1_ret = call func[260]
method_0_ret = call func[4](10)
#################### 12 7
method_1_ret = call func[263]
method_0_ret = call func[7](0)
#################### 16 5
method_1_ret = call func[261]
method_0_ret = call func[5](b'hackers')
#################### 26 8
method_1_ret = call func[264]
method_0_ret = call func[8](1)
#################### 30 1
method_1_ret = call func[257]
method_0_ret = call func[1](3)
#################### 34 6
method_1_ret = call func[262]
method_0_ret = call func[6](50)
#################### 38 2
method_1_ret = call func[258]
method_0_ret = call func[2](2)

绕过ArkTS层的环境异常检测

ArkTS层注册的回调函数有很多我就截取了部分:

aboutToAppear() {
// 电池插入类型差值判断
testNapi.register(3, (a) => {
var t = batteryInfo.pluggedType - a;
var f;
if (t > 0)
f = 1;
else if (t == 0)
f = 0;
else
f = -1;
return f === 0;
});
....
// 直接返回电池插入类型
testNapi.register(259, () => {
return batteryInfo.pluggedType;
});
}

这里是写在ArkTS层的回调函数代码!


发现这些回调函数的id:3,259;0,256;等等...

这里监测环境异常主要是通过检查电池信息是否和预定的数据一样不一样则判定环境异常。

根据跑出来的回调函数的注册码在ArkTS层找函数

method_1_ret = call func[259]
// 直接返回电池插入类型
testNapi.register(259, () => {
return batteryInfo.pluggedType;
});

method_0_ret = call func[3](1)
// 电池插入类型差值判断
testNapi.register(3, (a) => {
var t = batteryInfo.pluggedType - a;
var f;
if (t > 0)
f = 1;
else if (t == 0)
f = 0;
else
f = -1;
return f === 0;
});

再根据so层的代码分析可以知道:

method_0_ret的返回值必须是非0才可以通过环境监测,这里也就是要求:

batteryInfo.pluggedType 的值等于 1 就可以通过环境检查!!

依次类推也就可以知道如何绕过环境监测了!


知识连接:
OpenHarmony4.0源码解析之电源管理子系统 - 文章 OpenHarmony开发者论坛(https://forums.openharmony.cn/forum.php?mod=viewthread&tid=616)

batteryInfo.batterySOC 256
batteryInfo.chargingStatus 257
batteryInfo.healthStatus 258
batteryInfo.pluggedType 259
batteryInfo.voltage 260
batteryInfo.technology 261
batteryInfo.batteryTemperature 262
batteryInfo.isBatteryPresent 263
batteryInfo.batteryCapacityLevel 264

写个脚本来看看需要的设置的电池数据,来绕过环境异常检测:

def dump_bin(bin):
d = []
pc = 0
while pc < len(bin):
op = bin[pc]
print('------------->', pc, op)

# 获取函数地址reg_method_0,通过bin[pc]查找
# print('reg_method_0 = func[%d]' % (op))
# 获取函数地址reg_method_1,通过bin[pc] | 0x100 查找
#调用reg_method_1获得返回值
print('method_1_ret = call func[%d]' % (op | 0x100))
if (op | 0x100) == 256 :
print("batteryInfo.batterySOC:",end="")
elif (op | 0x100) == 257 :
print("batteryInfo.chargingStatus:",end="")
elif (op | 0x100) == 258 :
print("batteryInfo.healthStatus:",end="")
elif (op | 0x100) == 259 :
print("batteryInfo.pluggedType:",end="")
elif (op | 0x100) == 260 :
print("batteryInfo.voltage:",end="")
elif (op | 0x100) == 261 :
print("batteryInfo.technology:",end="")
elif (op | 0x100) == 262 :
print("batteryInfo.batteryTemperature:",end="")
elif (op | 0x100) == 263 :
print("batteryInfo.isBatteryPresent:",end="")
elif (op | 0x100) == 264 :
print("batteryInfo.batteryCapacityLevel:",end="")
#获取操作类型
type = bin[pc + 1]
if type == 0 :
print(bin[pc + 3])
print('method_0_ret = call func[%d](%d)' % (op, bin[pc + 3]))
key = bin[pc + 3]
pc += 4
elif type == 1:
#获取bin中字符串的长度
size = bin[pc + 2]
s = bin[pc + 3: pc + 3 + size]
print(repr(s))
print('method_0_ret = call func[%d](%s)' % (op, repr(s)))
pc += 3 + size
elif type == 2 :
print(bin[pc + 3])
print('method_0_ret = call func[%d](%d)' % (op, bin[pc + 3]))
key = bin[pc + 3]
pc += 4
else:
pc += 3
assert False
d.append((op, key))
return d
with open(r'.\bin', 'rb') as file:
encrypted_data = file.read()

print(dump_bin(encrypted_data))

要求的电池环境,成功绕过:

-------------> 0 3
batteryInfo.pluggedType:1
-------------> 4 0
batteryInfo.batterySOC:100
-------------> 8 4
batteryInfo.voltage:10
-------------> 12 7
batteryInfo.isBatteryPresent:0
-------------> 16 5
batteryInfo.technology:b'hackers'
-------------> 26 8
batteryInfo.batteryCapacityLevel:1
-------------> 30 1
batteryInfo.chargingStatus:3
-------------> 34 6
batteryInfo.batteryTemperature:50
-------------> 38 2
batteryInfo.healthStatus:2

分析Check函数的加密逻辑

do
{
if valuekey == 0:
switch_case_key...
if valuekey == 1:
switch_case_key...
if valuekey == 2:
switch_case_key...
....
switch ( bin_i )
{
case 0:
....
v54 = switch_case_key & 0x3F | (switch_case_key >> 1) & 0x40 | (2 * switch_case_key) & 0x80;
....
case 1:
.....
case 8:
....
}
}
while ( v40 < bin_len );

继续分析发现switch里面的加密和switch_case_key的关系特别大,根据交叉引用可以找到相关逻辑:

根据bin[i+1]的值不同导致switch_case_key的值也不同

r

eg_method_1方法在被找到的那一刻就被执行了,返回值是method_1_ret

1.当bin[i+1]== 0

        

else if ( !keyvalue ) // keyvalue==0,method_1_ret被转为int32,传入一个参数
{
v40 = targetidx + 4;
napi_create_int32(env, *(bin + targetidx + 3), &int_3_arg);
napi_call_function(env, this, reg_method_0, 1LL, &int_3_arg, &methodfun_0_ret);
napi_get_value_int32(env, method_1_ret, &method_1_ret_bool);// 转为int32
LOBYTE(switch_case_key) = method_1_ret_bool;
}

根据前面的ArkTS层函数的调用规则可以知道:

(1)reg_method_1函数就是用来返回当前电池状态信息的!

(2)而reg_method_0函数就是用来检查电池信息是否符合要求的!

(3)从而也就可以确定switch_case_key的值了!*(bin + targetidx + 3)
用python脚本就是:

op = bin[pc]
type = bin[pc + 1]
if type == 0:
key = bin[pc + 3]
pc += 4

2.当bin[i+1]== 1

        

if ( keyvalue == 1 ) // keyvalue == 1,method_1_ret返回值被转为utf8,传入一个参数
{
size = *(bin + targetidx + 2);
napi_create_string_utf8(env, bin + v40, size, &int_3_arg);
v40 += size;
env_1 = env;
napi_call_function(env, this, reg_method_0, 1LL, &int_3_arg, &methodfun_0_ret);
napi_get_value_string_utf8(env, method_1_ret, buf, 128LL, &stringlen1);
if ( stringlen1 )
{
if ( stringlen1 < 0x20 )
{
idx1 = 0LL;
switch_case_key = 0;
goto LABEL_56;
}
idx1 = stringlen1 & 0xFFFFFFFFFFFFFFE0LL;
if ( (stringlen1 & 0xFFFFFFFFFFFFFFE0LL) - 32 >= 0x60 )
{
....
while ( stringlen1 != idx1 )
LABEL_56:
LOBYTE(switch_case_key) = buf[idx1++] ^ switch_case_key;
}
else
{
switch_case_key = 0;
}
}

(1)通过前面的python脚本可知:bin[i+1]== 1的值会去检测:batteryInfo.technology:b'hackers'

(2)*(bin + targetidx + 2)是字符串长度

(3)知道正确的电池信息后就可以知道 b'hackers'.len 等于 7 ,所以 stringlen1 < 0x20

(4)switch_case_key的计算流程就会进入LABEL_56

(5)也就是下面的算法:

while ( stringlen1 != idx1 )
LOBYTE(switch_case_key) = buf[idx1++] ^ switch_case_key;

用python脚本就是:

op = bin[pc]
type = bin[pc + 1]
elif type == 1:
size = bin[pc + 2]
s = bin[pc + 3: pc + 3 + size]
key = 0
for i in s: key ^= i
pc += 3 + size

3.当bin[i+1]== 2

if ( keyvalue == 2 ) // keyvalue == 2,method_1_ret的返回值被转为bool,传入一个参数
{
napi_create_int32(env, *(bin + targetidx + 3), &int_3_arg);
napi_coerce_to_bool(env, int_3_arg, &int_3_arg);
napi_call_function(env, this, reg_method_0, 1LL, &int_3_arg, &methodfun_0_ret);
napi_get_value_bool(env, method_1_ret, &method_1_ret_bool);
LOBYTE(switch_case_key) = method_1_ret_bool;
v40 = targetidx + 4;
}

根据前面的ArkTS层函数的调用规则可以知道:

(1)reg_method_1函数就是用来返回当前电池状态信息的!

(2)而reg_method_0函数就是用来检查电池信息是否符合要求的!

(3)从而也就可以确定switch_case_key的值了!*(bin + targetidx + 3)
用python脚本就是:

op = bin[pc]
type = bin[pc + 1]
if type == 2:
key = bin[pc + 3]
pc += 4

成功得到VMopcode的加密流程和加密密钥switch_case_key

def dump_bin(bin):
d = []
pc = 0
while pc < len(bin):
op = bin[pc]
type = bin[pc + 1]
if type == 2 or type == 0:
key = bin[pc + 3]
pc += 4
elif type == 1:
size = bin[pc + 2]
s = bin[pc + 3: pc + 3 + size]
key = 0
for i in s: key ^= i
pc += 3 + size
else:
pc += 3
assert False
d.append((op, key))
return d

with open(r'.\bin', 'rb') as file:
encrypted_data = file.read()

print(dump_bin(encrypted_data))
# [(3, 1), (0, 100), (4, 10), (7, 0), (5, 101), (8, 1), (1, 3), (6, 50), (2, 2)]

第一个值是opcode,第二个值是key。
加密流程和密钥;
[(3, 1), (0, 100), (4, 10), (7, 0), (5, 101), (8, 1), (1, 3), (6, 50), (2, 2)]

加密算法部分解析(已解决)

拜读大佬wp,但是奈何大佬的代码跑不起来,而且最终加密后的flag数据对比的位置和加密后的数据也不知道怎么来的,最后无奈放弃QAQ


拜读大佬wp:
‌‬‌‍⁠⁠‍‬‬‍‌⁠‌‬⁠‍‍⁠‍2024 05.27 jqctf 初赛 wp - LaoGong - 飞书云文档 (https://ycznkvrmzo.feishu.cn/docx/ZqU0dU0h2oW3eFxZtZMctShFnyh)

最后经过大佬的指点终于成功了!!剩下的就是算法学习了!

安装环境:

┌──(kali㉿kali)-[~/tools]
└─$ git clone https://github.com/IchildYu/load-elf.git
正克隆到 'load-elf'...
remote: Enumerating objects: 40, done.
remote: Counting objects: 100% (40/40), done.
remote: Compressing objects: 100% (37/37), done.
remote: Total 40 (delta 18), reused 0 (delta 0), pack-reused 0
接收对象中: 100% (40/40), 13.93 KiB | 6.97 MiB/s, 完成.
处理 delta 中: 100% (18/18), 完成.

┌──(kali㉿kali)-[~/tools/load-elf]
└─$ gcc ./x64_main.c -o lib -g -ldl -masm=intel -shared -fPIC

成功跑出flag,源码在下面:


python:

def dump_bin(bin):
d = []
pc = 0
while pc < len(bin):
op = bin[pc]
# print('#', pc, op)
# print('b = func[%d]()' % (op | 0x100))
type = bin[pc + 1]
if type == 2 or type == 0:
# missing bin[pc + 2]
# print('a = func[%d](%d)' % (op, bin[pc + 3]))
key = bin[pc + 3]
pc += 4
elif type == 1:
size = bin[pc + 2]
s = bin[pc + 3: pc + 3 + size]
# print('a = func[%d](%s)' % (op, repr(s)))
# print('b = xor(b)')
key = 0
for i in s: key ^= i
pc += 3 + size
else:
pc += 3
assert False
d.append((op, key))
return d

def g(x, n):
return (x >> n) & 1

def s(x, n):
return (x & 1) << n

def swapbit(x, m, n):
if m == n: return x
return s(g(x, m), n) | s(g(x, n), m) | (x & ~(s(1, n) | s(1, m)))

def swapkeep(x, mask):
swapbits = ~mask & 0xff
m = swapbits.bit_length() - 1
assert 0 <= m < 8
swapbits ^= 1 << m
n = swapbits.bit_length() - 1
assert 0 <= n < 8
swapbits ^= 1 << n
assert swapbits == 0
return swapbit(x, m, n)

def ror1(x, n):
n &= 7
if isinstance(x, int):
x &= 0xff
return (x >> n) | (x << (8 - n)) & 0xff
else:
return LShR(x, n) | (x << (8 - n)) & 0xff

entries = [
0x2efa,
0x42e9,
0x3428,
0x38fd,
0x2522,
0x480d,
0x4cc6,
0x3df6,
0x51df
]

bin = open('./bin', 'rb').read()
_seq = dump_bin(bin)
# print(_seq)

import ctypes

lib = ctypes.cdll.LoadLibrary('./lib')
# extern void setup();
lib.setup()

def encrypt(array, seq):
for op, key in seq:
for i in range(38):
v = lib.bf_round(key, entries[op], i)
type, val0, val1 = v >> 16, (v >> 8) & 0xff, v & 0xff
if type == 0:
assert val0 == 0
# print(i, 'c ^= 0x%x' % val1)
array[i] ^= val1
elif type == 1:
# print(i, 'c = ror1(c, %d) ^ 0x%x' % (val0, val1))
array[i] = ror1(array[i], val0) ^ val1
elif type == 2:
# print(i, 'c = swapkeep(c, 0x%x) ^ 0x%x' % (val0, val1))
array[i] = swapkeep(array[i], val0) ^ val1
else:
assert False, type

from z3 import *
_array = [BitVec('x%d' % i, 8) for i in range(38)]
array = _array[:]
encrypt(array, _seq)

result = [226, 125, 77, 72, 55, 231, 235, 154, 118, 5, 125, 135, 49, 162, 160, 77, 248, 159, 61, 164, 56, 139, 225, 229, 136, 139, 89, 191, 4, 222, 40, 234, 126, 202, 215, 252, 133, 165]
# print(len(result))

s = Solver()
for i in range(38):
s.add(array[i] == result[i])

assert s.check() == sat
model = s.model()
# print(model)
flag = []
for i in _array:
flag.append(model[i].as_long())

print(bytes(flag))
# flag{3da8767cfb9424b9bbcc09008e36642d}

lib的源码:

#include <stdio.h>
#include <dlfcn.h>
#include <string.h>
#include <assert.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdarg.h>

#define ERROR 0
#define WARNING 1
#define INFO 2
#define DEBUG 3
#define VERBOSE 4

const char* LOG_LEVEL_CHARS = "EWIDV";
const char* LOG_LEVEL_COLORS[] = {
"\x1b[31m",
"\x1b[33m",
"\x1b[32m",
"\x1b[0m",
"\x1b[34m",
};
int _log_level = INFO;
int _log_color = 1;

void set_log_level(int log_level) {
if (log_level < 0) log_level = 0;
if (log_level > 4) log_level = 4;
_log_level = log_level;
}

void set_log_color(int log_color) {
_log_color = log_color;
}

void Log(int log_level, const char* format, ...) {
if (log_level < 0) log_level = 0;
if (log_level > 4) log_level = 4;
if (log_level > _log_level) return;
if (_log_color) printf("%s", LOG_LEVEL_COLORS[log_level]);
printf("[%c] ", LOG_LEVEL_CHARS[log_level]);
va_list args;
va_start(args, format);
vprintf(format, args);
va_end(args);
if (_log_color) printf("\x1b[0m");
}

#define LOGE(format, ...) Log(ERROR, format, ##__VA_ARGS__)
#define LOGW(format, ...) Log(WARNING, format, ##__VA_ARGS__)
#define LOGI(format, ...) Log(INFO, format, ##__VA_ARGS__)
#define LOGD(format, ...) Log(DEBUG, format, ##__VA_ARGS__)
#define LOGV(format, ...) Log(VERBOSE, format, ##__VA_ARGS__)

// default info
#define SET_LOGE() set_log_level(ERROR)
#define SET_LOGW() set_log_level(WARNING)
#define SET_LOGI() set_log_level(INFO)
#define SET_LOGD() set_log_level(DEBUG)
#define SET_LOGV() set_log_level(VERBOSE)

// default on
#define SET_LOGCOLOR_OFF() set_log_color(0)
#define SET_LOGCOLOR_ON() set_log_color(1)

#define R_NONE 0
#define R_COPY 5
#define R_GLOB_DAT 6
#define R_JUMP_SLOT 7
#define R_RELATIVE 8
#define R_IRELATIVE 37

typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
typedef unsigned long long ullong;

typedef struct {
uchar e_ident[16];
ushort e_type;
ushort e_machine;
uint e_version;
size_t e_entry;
size_t e_phoff;
size_t e_shoff;
uint e_flags;
ushort e_ehsize;
ushort e_phentsize;
ushort e_phnum;
ushort e_shentsize;
ushort e_shnum;
ushort e_shtrndx;
} elf_header;

typedef struct {
size_t d_tag;
size_t d_un;
} elf_dyn;

typedef struct {
size_t r_offset;
size_t r_info;
} elf_rel;

typedef struct {
size_t r_offset;
size_t r_info;
size_t r_addend;
} elf_rela;

// elf_sym.st_info
#define elf_st_bind(info) ((info) >> 4)
#define elf_st_type(info) ((info) & 0xf)

typedef struct {
uint p_type;
uint p_flags;
size_t p_offset;
size_t p_vaddr;
size_t p_paddr;
size_t p_filesz;
size_t p_memsz;
size_t p_align;
} elf_program_header;

typedef struct {
uint st_name;
uchar st_info;
uchar st_other;
ushort shndx;
size_t st_value;
size_t st_size;
} elf_sym;

// elf_rel[a].r_info
#define elf_r_sym(info) ((info) >> 32)
#define elf_r_type(info) ((uint) (info))

int do_reloc(void* base, size_t offset, size_t info, size_t addend, const elf_sym* symtab, const char* strtab) {
#define sym (elf_r_sym(info))
#define type (elf_r_type(info))
#define value (symtab[sym].st_value)
#define size (symtab[sym].st_size)
#define name (strtab + symtab[sym].st_name)
switch (type) {
case R_NONE:
break;
case R_COPY:
if (value) {
memcpy((void*) ((size_t) base + offset), (const void*) ((size_t) base + value), size);
} else {
const void* sym_value = dlsym((void*) -1, name); // RTLD_DEFAULT
if (!sym_value) {
LOGW("failed to resolve symbol `%s'.\n", name);
break;
}
memcpy((void*) ((size_t) base + offset), sym_value, size);
}
break;
case R_GLOB_DAT:
case R_JUMP_SLOT:
if (value) {
*(size_t*) ((size_t) base + offset) = (size_t) base + value;
} else {
const void* sym_value = dlsym((void*) -1, name); // RTLD_DEFAULT
if (!sym_value) {
LOGW("failed to resolve symbol `%s'.\n", name);
break;
}
*(size_t*) ((size_t) base + offset) = (size_t) sym_value;
}
break;
case R_RELATIVE:
*(size_t*) ((size_t) base + offset) = (size_t) base + addend;
break;
case R_IRELATIVE:
*(size_t*) ((size_t) base + offset) = ((size_t (*)()) ((size_t) base + addend))();
break;
case 1: // R_X86_64_64
if (value) {
*(size_t*) ((size_t) base + offset) = (size_t) base + value + addend;
} else {
const void* sym_value = dlsym((void*) -1, name); // RTLD_DEFAULT
if (!sym_value) {
LOGW("failed to resolve symbol `%s'.\n", name);
break;
}
*(size_t*) ((size_t) base + offset) = (size_t) sym_value + addend;
}
break;
default:
LOGW("unimplemented reloc type: %d.\n", type);
break;
}
#undef sym
#undef type
#undef value
#undef size
#undef name
return 1;
}

#define SKIP_LOAD_WITH_DL

void* load_with_dl(const char* path) {
#ifdef SKIP_LOAD_WITH_DL
LOGD("SKIP_LOAD_WITH_DL defined, load_with_dl returns NULL.\n");
return NULL;
#endif
LOGI("loading %s with dlopen...\n", path);
void* handle = dlopen(path, RTLD_LAZY);
if (handle == NULL) {
LOGE("load_with_dl failed: %s.\n", dlerror());
return NULL;
}
void* base = *(void**) handle;
LOGI("done, loaded at %p.\n", base);
return base;
}

int check_header(elf_header* header) {
if (*(uint*) header->e_ident != 0x464c457f) {
LOGE("elf magic header not detected.\n");
return 0;
}
if (header->e_ident[4] != (sizeof(void*) / 4)) { // ei_class, 1: ELFCLASS32, 2: ELFCLASS64
LOGE("elf class mismatch.\n");
return 0;
}
if (header->e_ident[5] != 1) {
LOGE("LSB expected.\n");
return 0;
}
if (header->e_type != 2 && header->e_type != 3) {
LOGE("Dynamic library or executable expected.\n");
return 0;
}
if (header->e_ehsize != sizeof(elf_header)) {
LOGE("Unexpected header size.\n");
return 0;
}
return 1;
}

const elf_dyn* find_dyn_entry(const elf_dyn* dyn, int type) {
for (; dyn->d_tag != 0; dyn++) { // DT_NULL
if (dyn->d_tag == type) return dyn;
}
return NULL;
}

int do_rel(void* base, const elf_rel* rel, int count, const elf_sym* symtab, const char* strtab) {
for (int i = 0; i < count; i++) {
if (!do_reloc(base, rel[i].r_offset, rel[i].r_info, *(size_t*) ((size_t) base + rel[i].r_offset), symtab, strtab))
return 0;
}
return 1;
}

int do_rela(void* base, const elf_rela* rela, int count, const elf_sym* symtab, const char* strtab) {
for (int i = 0; i < count; i++) {
if (!do_reloc(base, rela[i].r_offset, rela[i].r_info, rela[i].r_addend, symtab, strtab))
return 0;
}
return 1;
}

int check_and_do_rel(void* base, const elf_dyn* dyn, const elf_rel* rel, const elf_sym* symtab, const char* strtab) {
if (find_dyn_entry(dyn, 0x13)->d_un != sizeof(elf_rel)) { // DT_RELENT
LOGE("unexpected rel table entry size.\n");
return 0;
}
LOGD("do rel.\n");
int rel_count = find_dyn_entry(dyn, 0x12)->d_un / sizeof(elf_rel); // DT_RELSZ
if (!do_rel(base, rel, rel_count, symtab, strtab)) return 0;
return 1;
}

int check_and_do_rela(void* base, const elf_dyn* dyn, const elf_rela* rela, const elf_sym* symtab, const char* strtab) {
if (find_dyn_entry(dyn, 0x9)->d_un != sizeof(elf_rela)) { // DT_RELAENT
LOGE("unexpected rela table entry size.\n");
return 0;
}
LOGD("do rela.\n");
int rela_count = find_dyn_entry(dyn, 0x8)->d_un / sizeof(elf_rela); // DT_RELASZ
if (!do_rela(base, rela, rela_count, symtab, strtab)) return 0;
return 1;
}

int load_dynamic(void* base, const elf_dyn* dyn) {
const elf_dyn* res = find_dyn_entry(dyn, 5); // DT_STRTAB
if (res == NULL) {
LOGE("string table not found.\n");
return 0;
}
const char* strtab = (const char*) ((size_t) base + res->d_un);

const elf_sym* symtab = NULL;
res = find_dyn_entry(dyn, 0x6); // DT_SYMTAB
if (res != NULL) {
symtab = (const elf_sym*) ((size_t) base + res->d_un);
if (find_dyn_entry(dyn, 0xB)->d_un != sizeof(elf_sym)) { // DT_SYMENT
LOGE("unexpected symbol table entry size.\n");
return 0;
}
}

for (const elf_dyn* it = dyn; it->d_tag != 0; it++) {
if (it->d_tag != 1) continue; // DT_NEEDED: name of needed library
LOGD("loading needed library `%s'.\n", strtab + it->d_un);
if (!dlopen(strtab + it->d_un, RTLD_NOW | RTLD_GLOBAL))
LOGW("failed to load needed library `%s': %s.\n", strtab + it->d_un, dlerror());
}

int rel_done = 0;
for (const elf_dyn* it = dyn; it->d_tag != 0; it++) { // DT_NULL
switch (it->d_tag) {
case 7: // DT_RELA
if (rel_done) break;
if (!check_and_do_rela(base, dyn, (const elf_rela*) ((size_t) base + it->d_un), symtab, strtab))
return 0;
rel_done = 1;
break;
case 0x11: // DT_REL
if (rel_done) break;
if (!check_and_do_rel(base, dyn, (const elf_rel*) ((size_t) base + it->d_un), symtab, strtab))
return 0;
rel_done = 1;
break;
case 0x17: // DT_JMPREL
;
size_t plt_rel_size = find_dyn_entry(dyn, 0x2)->d_un; // DT_PLTRELSZ
int plt_rel = find_dyn_entry(dyn, 0x14)->d_un; // DT_PLTREL
if (plt_rel == 0x11) { // DT_REL
if (!rel_done) {
res = find_dyn_entry(dyn, 0x11); // DT_REL
if (res != NULL) {
if (!check_and_do_rel(base, dyn, (const elf_rel*) ((size_t) base + res->d_un), symtab, strtab))
return 0;
rel_done = 1;
}
}
plt_rel_size /= sizeof(elf_rel);
LOGD("do jmprel with rel.\n");
if (!do_rel(base, (elf_rel*) ((size_t) base + it->d_un), plt_rel_size, symtab, strtab)) return 0;
} else if (plt_rel == 7) { // DT_RELA
if (!rel_done) {
res = find_dyn_entry(dyn, 7); // DT_RELA
if (res != NULL) {
if (!check_and_do_rela(base, dyn, (const elf_rela*) ((size_t) base + res->d_un), symtab, strtab))
return 0;
rel_done = 1;
}
}
plt_rel_size /= sizeof(elf_rela);
LOGD("do jmprel with rela.\n");
if (!do_rela(base, (elf_rela*) ((size_t) base + it->d_un), plt_rel_size, symtab, strtab)) return 0;
} else {
LOGE("unexpected plt rel type: %d.\n", plt_rel);
return 0;
}
break;
}
}

res = find_dyn_entry(dyn, 0xC); // DT_INIT
if (res != NULL) {
void (*init)() = (void (*)()) ((size_t) base + res->d_un);
LOGI("init proc detected: %p.\n", init);
int choice = 'y';
do {
LOGI("Execute init proc? [(y)es/(n)o] ");
choice = getchar();
if (choice != '\n') while (getchar() != '\n') ;
if (choice >= 'A' && choice <= 'Z') choice += 0x20;
} while (choice != 'y' && choice != 'n');
if (choice == 'y') init();
}

res = find_dyn_entry(dyn, 0x19); // DT_INIT_ARRAY
if (res != NULL) {
void (**init_array)() = (void (**)()) ((size_t) base + res->d_un);
int count = find_dyn_entry(dyn, 0x1B)->d_un / sizeof(size_t); // DT_INIT_ARRAYSZ
while (*init_array == NULL && count) {
init_array++;
count--;
}
if (count) {
LOGI("init array detected:\n");
int choice = '?';
for (int i = 0; i < count; i++) {
if (!init_array[i]) continue;
while (choice != 'y' && choice != 'n' && choice != 'a' && choice != 'o') {
LOGI("\texecute function %p? [(y)es/(n)o/(a)ll items left/n(o)ne items left] ", init_array[i]);
choice = getchar();
if (choice != '\n') while (getchar() != '\n') ; // skip line
if (choice >= 'A' && choice <= 'Z') choice += 0x20; // convert to lower case
}
if ((uchar) (choice - 'n') > 2) { // 'y' or 'a'
LOGI("\texecuting function at %p...\n", init_array[i]);
init_array[i]();
if (choice == 'y') choice = '?';
} else if (choice == 'n') choice = '?';
}
}
}

res = find_dyn_entry(dyn, 0xD); // DT_FINI
if (res != NULL) {
void (*fini)() = (void (*)()) ((size_t) base + res->d_un);
LOGI("fini proc detected: %p.\n", fini);
}

res = find_dyn_entry(dyn, 0x1A); // DT_FINI_ARRAY
if (res != NULL) {
void (**fini_array)() = (void (**)()) ((size_t) base + res->d_un);
int count = find_dyn_entry(dyn, 0x1C)->d_un / sizeof(size_t); // DT_FINI_ARRAYSZ
while (*fini_array == NULL && count) {
fini_array++;
count--;
}
if (count) {
LOGI("fini array detected:\n");
for (int i = 0; i < count; i++) {
if (fini_array[i]) {
LOGI("\t%p\n", fini_array[i]);
}
}
}
}
LOGI("load_dynamic done.\n");
return 1;
}

#define MMAP_LOAD_BASE ((void*) 0xC0000000)
void* load_with_mmap(const char* path) {
LOGI("loading %s with mmap...\n", path);
int fd = open(path, O_RDONLY);
LOGV("open(path, O_RDONLY) returns %d\n", fd);

elf_header header;
LOGV("reading elf header from file...\n");
if (read(fd, &header, sizeof(header)) != sizeof(header)) {
LOGE("read header error\n");
close(fd);
return NULL;
}
LOGV("checking elf header...\n");
if (!check_header(&header)) {
close(fd);
return NULL;
}

elf_program_header pheader;
elf_dyn* dyn = NULL;

int e_phentsize = header.e_phentsize;
int e_phnum = header.e_phnum;

if (e_phentsize != sizeof(pheader)) {
LOGE("unexpected program header size.\n");
close(fd);
return NULL;
}

LOGV("determine LOAD_BASE...\n");
void* base = MMAP_LOAD_BASE;
while (base != mmap(base, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)) {
base = (void*) ((size_t) base + 0x1000000);
}
munmap(base, 0x1000);
LOGD("trying loading at %p\n", base);

lseek(fd, header.e_phoff, SEEK_SET);
for (int i = 0; i < e_phnum; i++) {
LOGV("processing phdr %d...\n", i);
if (read(fd, &pheader, sizeof(pheader)) != sizeof(pheader)) {
LOGE("read pheader error\n");
close(fd);
return NULL;
}
if (pheader.p_type != 1 || pheader.p_memsz == 0) { // not PT_LOAD or nothing to load
if (pheader.p_type == 2) { // DYNAMIC
if (dyn != NULL) {
LOGE("duplicated DYNAMIC PHT detected.\n");
close(fd);
return NULL;
} else {
dyn = (elf_dyn*) ((size_t) base + pheader.p_vaddr);
}
}
continue;
}
void* addr = (void*) (((size_t) base + pheader.p_vaddr) & ~0xfff);
int offset = pheader.p_vaddr & 0xfff;
size_t size = (offset + pheader.p_filesz + 0xfff) & ~0xfff;
if (addr != mmap(addr, size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, pheader.p_offset - offset)) {
// if (addr != mmap(addr, pheader.p_memsz + offset, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE, fd, pheader.p_offset - offset)) {
// if ((uchar*) addr != (uchar*) base + pheader.p_vaddr) {
LOGE("failed to mmap 0x%lx to 0x%lx.\n", pheader.p_offset, pheader.p_vaddr + (size_t) base);
close(fd);
return NULL;
}
if (offset) {
memset(addr, 0, offset);
}
if (pheader.p_memsz != pheader.p_filesz) {
if (pheader.p_memsz < pheader.p_filesz) {
LOGE("unexpected: filesz bigger than memsz.\n");
close(fd);
return NULL;
}
if (pheader.p_memsz + offset > size) {
LOGV("mmap extra pages in memory\n");
addr = (void*) ((size_t) addr + size);
if (addr != mmap(addr, pheader.p_memsz + offset - size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_ANON | MAP_SHARED, -1, 0)) {
LOGE("failed to mmap 0x%lx to 0x%lx.\n", pheader.p_offset, pheader.p_vaddr + (size_t) base);
close(fd);
return NULL;
}
}
}

{
LOGV("testing memory...\n");
char c = *(unsigned char*) (pheader.p_vaddr + (size_t) base);
c = *(unsigned char*) (pheader.p_vaddr + (size_t) base + pheader.p_filesz - 1);
c = *(unsigned char*) (pheader.p_vaddr + (size_t) base + pheader.p_memsz - 1);
}
LOGD("mmaped 0x%lx to 0x%lx, filesz 0x%lx, memsz 0x%lx\n", pheader.p_offset, pheader.p_vaddr + (size_t) base, pheader.p_filesz, pheader.p_memsz);
}
LOGI("done, loaded at %p\n", base);
close(fd);

if (!dyn) return base;

LOGI("DYNAMIC detected, loading...\n");
if (!load_dynamic(base, dyn)) return NULL;
return base;
}

const elf_dyn* get_dyn(void* base) {
elf_header* header = (elf_header*) base;
int e_phnum = header->e_phnum;
elf_program_header* pheader = (elf_program_header*) ((size_t) base + header->e_phoff);
for (int i = 0; i < e_phnum; i++, pheader++) {
if (pheader->p_type == 2) {
return (elf_dyn*) ((size_t) base + pheader->p_vaddr);
}
}
}

void* get_symbol_by_name(void* base, const char* symbol) {
const elf_dyn* dyn = get_dyn(base);
const char* strtab = (const char*) (find_dyn_entry(dyn, 5)->d_un); // DT_STRTAB

if (strtab < (const char*) base)
strtab = (const char*) strtab + (size_t) base;
size_t strsz = find_dyn_entry(dyn, 0xa)->d_un; // DT_STRSZ
const elf_sym* symtab = (const elf_sym*) (find_dyn_entry(dyn, 6)->d_un); // DT_SYMTAB
if ((const char*) symtab < (const char*) base)
symtab = (const elf_sym*) ((const char*) symtab + (size_t) base);

for (; ; symtab++) {
if (symtab->st_name == 0) continue;
if (symtab->st_name >= strsz) {
LOGE("failed to resolve symbol `%s' from library (%p): not found.\n", symbol, base);
return NULL;
}
if (strcmp(strtab + symtab->st_name, symbol) == 0) {
if (symtab->st_value == 0) {
LOGE("failed to resolve symbol `%s' from library (%p): value is NULL.\n", symbol, base);
return NULL;
}
if (elf_st_type(symtab->st_info) != 10) { // STT_GNU_IFUNC
return (void*) ((size_t) base + symtab->st_value);
}
return ((void* (*)()) ((size_t) base + symtab->st_value))();
}
}
}

void* get_symbol_by_offset(void* base, size_t offset) {
return (void*) ((size_t) base + offset);
}

void* load_elf(const char* elf_path) {
void* base = load_with_dl(elf_path);
if (base == NULL) {
base = load_with_mmap(elf_path);
}
//assert(base != NULL && *(unsigned int*) base == 0x464c457f);
return base;
}

// gcc ./x64_main.c -o main -g -ldl
__asm__(
"__round:\n"
"sub rsp, 0x10\n"
"mov [rsp+0x8], rdi\n"
"mov r12, rsi\n"
"call rdx\n"
"add rsp, 0x10\n"
"ret\n"
);

void __round(unsigned char* array, int key, void* entry);

extern int bf_round(int key, int offset, int index);
extern void setup();
extern void one_round(unsigned char* array, int key, int offset);

static char* base;

void setup() {
// SET_LOGE();
const char* path = "./libentry.so";
base = load_elf(path);
*(base + 0x2a07) = 0xc3; // ret
}

void one_round(unsigned char* array, int key, int offset) {
if (base == NULL) setup();
__round(array, key, base + offset);
}

unsigned char g(unsigned char x, unsigned char n) {
return (x >> n) & 1;
}

unsigned char s(unsigned char x, unsigned char n) {
return (x & 1) << n;
}

unsigned char swapbit(unsigned char x, unsigned char m, unsigned char n) {
if (m == n) return x;
return s(g(x, m), n) | s(g(x, n), m) | (x & ~(s(1, n) | s(1, m)));
}

unsigned char bit_length(unsigned char x) {
if (x == 0) return 0;
for (int i = 8; i > 0; i--) {
if (x & (1 << (i - 1))) return i;
}
}

unsigned char swapkeep(unsigned char x, unsigned char mask) {
unsigned char swapbits = ~mask & 0xff;
unsigned char m = bit_length(swapbits) - 1;
assert(0 <= m && m < 7);
swapbits ^= 1 << m;
unsigned char n = bit_length(swapbits) - 1;
assert(0 <= n && n < 7);
swapbits ^= 1 << n;
assert(swapbits == 0);
return swapbit(x, m, n);
}

unsigned char ror1(unsigned char x, unsigned char n) {
n &= 7;
x &= 0xff;
return (x >> n) | (x << (8 - n)) & 0xff;
}

unsigned char rol1(unsigned char x, unsigned char n) {
return ror1(x, 8 - n);
}

#define XOR 0 // c ^ val0 ^ val1
#define ROT 1 // ror1(c, val0) ^ val1
#define SWP 2 // swapkeep(c, val0) ^ val1
#define MAKE_RET_VAL(type, val0, val1) (((type) << 16) | ((val0) << 8) | (val1))

int bf_round(int key, int offset, int index) {
if (base == NULL) setup();
unsigned char array[38];
array[index] = 0;
__round(array, key, base + offset);
unsigned char val1 = array[index];

int flag = 0;
// test xor
for (int i = 0; i < 7; i++) {
array[index] = 1 << i;
__round(array, key, base + offset);
array[index] ^= val1;
if (array[index] != (1 << i)) {
flag = 1;
break;
}
}
if (flag == 0) { // XOR
return MAKE_RET_VAL(XOR, 0, val1);
}

// test rol1
array[index] = 1;
__round(array, key, base + offset);
array[index] ^= val1;
unsigned char val0 = bit_length(array[index]);
assert(val0 != 0);
val0--;
if (val0 != 0) {
assert(array[index] == (1 << val0));
for (int i = 1; i < 7; i++) {
array[index] = 1 << i;
__round(array, key, base + offset);
array[index] ^= val1;
if (array[index] != (1 << ((i + val0) % 8))) {
flag = 0;
break;
}
}
if (flag == 1) {
return MAKE_RET_VAL(ROT, 8 - val0, val1);
}
}

// swapkeep
for (int i = 0; i < 7; i++) {
array[index] = 1 << i;
__round(array, key, base + offset);
array[index] ^= val1;
if (array[index] != (1 << i)) {
assert(bit_length(array[index]));
assert(array[index] == (1 << (bit_length(array[index]) - 1)));
val0 = ~((1 << i) | array[index]);
return MAKE_RET_VAL(SWP, val0, val1);
}
}
assert(0);
}

// gcc ./x64_main.c -o lib -g -ldl -masm=intel -shared
int main() {
{
const char* path = "/lib/x86_64-linux-gnu/libm.so.6";
void* base = load_elf(path);

double (*pow)(double, double) = get_symbol_by_name(base, "pow");
double a = 3.14159;
double b = a;
printf("%g ** %g == %g\n", a, b, pow(a, b));
}
/**/

const char* path = "/lib/x86_64-linux-gnu/libc++.so.1";
void* base = load_elf(path);
void* std_cout = get_symbol_by_name(base, "_ZNSt3__14coutE");
// offset may be different
// std::ostream::operator<<(int)
void* (*print_int)(void*, int) = get_symbol_by_offset(base, 0x5e380);
// std::ostream::put(char)
void* (*print_char)(void*, char) = get_symbol_by_offset(base, 0x5f510);
print_char(print_int(std_cout, 114514), '\n');
/**/

puts("done.");
return 0;
}

最终报错:


解决方案:

arm架构下的伪代码可以发现数据对比!


发现一个ida小技巧:
点击Collapse declarations可以把超长的变量声明缩减成一行!


看雪ID:Loserme

https://bbs.kanxue.com/user-home-970470.htm

*本文为看雪论坛优秀文章,由 Loserme 原创,转载请注明来自看雪社区

# 往期推荐

1、小米路由器固件仿真模拟方案

2、CTF php .so pwn 题型分析

3、免杀动态对抗之syscall[源码分析]

4、Windows主机入侵检测与防御内核技术深入解析

5、基于 Frida 对单字节加密验证程序侧信道爆破

球分享

球点赞

球在看

点击阅读原文查看更多


文章来源: https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458559236&idx=1&sn=7ace846f9344e7375769662cecd84799&chksm=b18d938e86fa1a98bf0e9ddb610a682e44b32feec419e12eb1812d7ae4c70a69a37c99004de5&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh