版权归作者所有,如有转发,请注明文章出处:cyrus-studio.github.io/blog/
目标 app 简介
可以看到 app 中共有 4 种 Base64 算法。选中一个随机字符串,可以选择其中一种算法进行编码解码。
app地址:github.com/CYRUS-STUDI...
标准 Java Base64 算法还原
使用 jadx 反编译 apk,搜索找到按钮的代码
因为 app 中 ui 是用 Compose 实现的,所有 ui 都是 Function,所以这里看到 Text 是一个 Function
在 Function f57lambda2 上按 X 找到调用该 Function 的地方
再继续找 m6044getLambda2$app_release 的上层调用是 rememberedValue6
可以看到 rememberedValue6 中实际是调用了Base64ActivityKt.Base64App... <math xmlns="http://www.w3.org/1998/Math/MathML"> l a m b d a lambda </math>lambda16 方法
swift
rememberedValue6 = new Function0() { // from class: com.cyrus.example.base64.Base64ActivityKt$$ExternalSyntheticLambda6
@Override // kotlin.jvm.functions.Function0
public final Object invoke() {
Unit Base64App$lambda$37$lambda$36$lambda$20$lambda$17$lambda$16;
Base64App$lambda$37$lambda$36$lambda$20$lambda$17$lambda$16 = Base64ActivityKt.Base64App$lambda$37$lambda$36$lambda$20$lambda$17$lambda$16(Base64App$lambda$4, mutableState2);
return Base64App$lambda$37$lambda$36$lambda$20$lambda$17$lambda$16;
}
};
<math xmlns="http://www.w3.org/1998/Math/MathML"> l a m b d a lambda </math>lambda16 方法实现如下:
swift
public static final Unit Base64App$lambda$37$lambda$36$lambda$20$lambda$17$lambda$16(String selected, MutableState selectedString$delegate) {
Intrinsics.checkNotNullParameter(selected, "$selected");
Intrinsics.checkNotNullParameter(selectedString$delegate, "$selectedString$delegate");
byte[] bytes = selected.getBytes(Charsets.UTF_8);
Intrinsics.checkNotNullExpressionValue(bytes, "getBytes(...)");
String encodeToString = Base64.encodeToString(bytes, 0);
Log.d("Base64", "标准 Base64 编码(Java): " + encodeToString);
selectedString$delegate.setValue(encodeToString);
return Unit.INSTANCE;
}
-
这里使用的是 Java 库中的 Base64.encodeToString 编码
-
日志标签 Base64
使用 adb logcat 过滤出 Base64 相关日志
adb logcat -s Base64
- 清空当前缓存日志:adb logcat -c
得到日志如下:
diff
adb logcat -s Base64
--------- beginning of system
--------- beginning of main
03-10 02:03:10.277 21281 21281 D Base64 : 标准 Base64 编码(Java): ZVpvV3NBZndXd0k2ejU=
03-10 02:04:13.786 21281 21281 D Base64 : 标准 Base64 解码(Java): eZoWsAfwWwI6z5
使用 CyberChef 解密加密串,结果和原文是一样的,确定是标准 Base64 算法。
标准 C++ Base64 算法还原
加密串特征看起来就很像 Base64,因为都有 = 填充符号
ini
adb logcat -s Base64
--------- beginning of system
--------- beginning of main
03-10 15:10:42.840 25364 25364 D Base64 : 标准 Base64 编码(C++): UWhENDdtSU10V1picA==
03-10 15:10:47.062 25364 25364 D Base64 : 标准 Base64 解码(C++): QhD47mIMtWZbp
03-10 15:10:55.533 25364 25364 D Base64 : 标准 Base64 编码(C++): VFRtYVFiN2Y2dA==
03-10 15:10:56.215 25364 25364 D Base64 : 标准 Base64 解码(C++): TTmaQb7f6t
使用 CyberChef 对比加密串发现就是标准的 Base64
自定义码表版 Base64 算法还原
1. 算法逆向分析
yaml
adb logcat -s Base64
--------- beginning of main
--------- beginning of system
03-10 15:19:46.455 25364 25364 D Base64 : 自定义Base64 编码后的字符串: ovjsvg4ZCwu1qw9dtu1h
03-10 15:19:54.629 25364 25364 D Base64 : 自定义Base64 解码后的字符串: 9RRTn3qe5AoCMMG
03-10 15:19:57.772 25364 25364 D Base64 : 自定义Base64 编码后的字符串: m2zttLfNnW
03-10 15:19:58.273 25364 25364 D Base64 : 自定义Base64 解码后的字符串: 3fSNQg7
03-10 15:19:59.839 25364 25364 D Base64 : 自定义Base64 编码后的字符串: D29eANfhrwHJA2u
03-10 15:20:00.223 25364 25364 D Base64 : 自定义Base64 解码后的字符串: woDjqGEhcke
使用 CyberChef 对比加密串发现解密出来的参数是不一样的
通过分析反编译的代码知道,C++ Base64 编码按钮实际调用的是下面的方法
swift
public static final Unit Base64App$lambda$37$lambda$36$lambda$30$lambda$27$lambda$26(Function1 customEncode, String selected, MutableState selectedString$delegate) {
Intrinsics.checkNotNullParameter(customEncode, "$customEncode");
Intrinsics.checkNotNullParameter(selected, "$selected");
Intrinsics.checkNotNullParameter(selectedString$delegate, "$selectedString$delegate");
byte[] bytes = selected.getBytes(Charsets.UTF_8);
Intrinsics.checkNotNullExpressionValue(bytes, "getBytes(...)");
String str = (String) customEncode.invoke(bytes);
Log.d("Base64", "自定义Base64 编码后的字符串: " + str);
selectedString$delegate.setValue(str);
return Unit.INSTANCE;
}
-
customEncode 是一个 Function1<byte[], String> 类型的 函数接口,表示它是一个接受 byte[] 并返回 String 的函数。
-
customEncode.invoke(bytes) 调用了这个函数,将 selected 字符串转换为 byte[] 后进行编码。
这里 Base64ActivityKt.Base64App...lambda <math xmlns="http://www.w3.org/1998/Math/MathML"> 27 27 </math>27lambda$26 被包装成一个 Function0,然后在 invoke() 时调用。
swift
if (changed5 || rememberedValue10 == Composer.INSTANCE.getEmpty()) {
mutableState6 = mutableState17;
rememberedValue10 = new Function0() { // from class: com.cyrus.example.base64.Base64ActivityKt$$ExternalSyntheticLambda10
@Override // kotlin.jvm.functions.Function0
public final Object invoke() {
Unit Base64App$lambda$37$lambda$36$lambda$30$lambda$27$lambda$26;
Base64App$lambda$37$lambda$36$lambda$30$lambda$27$lambda$26 = Base64ActivityKt.Base64App$lambda$37$lambda$36$lambda$30$lambda$27$lambda$26(Function1.this, str2, mutableState6);
return Base64App$lambda$37$lambda$36$lambda$30$lambda$27$lambda$26;
}
};
startRestartGroup.updateRememberedValue(rememberedValue10);
} else {
mutableState6 = mutableState17;
}
-
Function1.this 代表外层 Function1 实例,但 Jadx 反编译时可能有误。
-
实际上,这里的 Function1.this 就是 Base64App 传进来的 customEncode 变量。
在 Base64ActivityKt 类中,Base64App 方法定义如下:
java
public static final void Base64App(
final Function1<? super byte[], String> encode,
final Function1<? super String, byte[]> decode,
final Function1<? super byte[], String> customEncode,
final Function1<? super String, byte[]> customDecode,
final Function1<? super byte[], String> dynamicBase64Encode,
final Function2<? super String, ? super Integer, byte[]> dynamicBase64Decode,
Composer composer,
final int i
) {
上层调用传参在这里
实际上传参类型是 Base64Activity <math xmlns="http://www.w3.org/1998/Math/MathML"> o n C r e a t e onCreate </math>onCreate1 <math xmlns="http://www.w3.org/1998/Math/MathML"> 3 3 </math>31
是 FunctionReferenceImpl 的实现类,最后实际调用的是 customBase64Encode
customBase64Encode 方法是一个 native 方法,在 base64 动态库中
解压 apk 中的 libbase64.so ,使用 ida 反汇编 so 找到 native 方法
F5 并 修正参数类型 JNIEnv *env, jobject obj, jbyteArray data
返回值为 v17,而 v17 的值来自 v16,v16 的值来自 v19 或 v20;
而 v20 = 0LL; ,所有返回值 来自 v19
编码的字符被 push_back() 添加到 v19 这个 std::string 对象,其中频繁访问了 xmmword_54C10 和 qword_54C20 这两个全局常量
xmmword_54C10 和 qword_54C20 并未初始化
- .bss 段(Block Started by Symbol)存储的是未初始化的全局或静态变量。
按 X 找到引用这两个常量的函数,找到初始化的地方在这里
这个字符串跟 Base64 索引表很像
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_
对比标准 Base64 码表,结构上是很像的
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_
通过 CyberChef 修改 Base64 码表 解码加密串进行对比,但码表识别失败,因为字符串中包含特殊字符 "-"
在特殊字符 "-" 前面加上转义字符 "" 就可以了,再对比解密串发现结果是一样的
2. 通过 python 还原 base64 算法
python
import base64
# 标准 Base64 字符集
std_b64chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
# 自定义 Base64 字符集
custom_b64chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
# 创建编码和解码映射表
encode_table = str.maketrans(std_b64chars, custom_b64chars)
decode_table = str.maketrans(custom_b64chars, std_b64chars)
def custom_base64_encode(data: bytes) -> str:
"""使用自定义 Base64 字符表进行编码"""
standard_b64 = base64.b64encode(data).decode() # 先进行标准 Base64 编码
return standard_b64.translate(encode_table) # 替换为自定义字符集
def custom_base64_decode(data: str) -> bytes:
"""使用自定义 Base64 字符表进行解码"""
standard_b64 = data.translate(decode_table) # 还原回标准 Base64
return base64.b64decode(standard_b64) # 进行解码
# 测试数据
original_text = "9RRTn3qe5AoCMMG"
encoded_text = custom_base64_encode(original_text.encode()) # 编码
decoded_text = custom_base64_decode(encoded_text).decode() # 解码
print("原始文本:", original_text)
print("自定义 Base64 编码:", encoded_text)
print("自定义 Base64 解码:", decoded_text)
输出如下:
makefile
原始文本: 9RRTn3qe5AoCMMG
自定义 Base64 编码: ovjsvg4ZCwu1qw9dtu1h
自定义 Base64 解码: 9RRTn3qe5AoCMMG
魔改版 Base64 算法还原
1. 算法逆向分析
yaml
adb logcat -s Base64
--------- beginning of system
--------- beginning of main
03-10 18:13:14.539 25364 25364 D Base64 : 动态码表编码后的字符串: BaGdaJ38TZb6fZyMcb6I,字符串长度:15
03-10 18:13:26.655 25364 25364 D Base64 : 动态码表解码后的字符串: 9RRTn3qe5AoCMMG,字符串长度:15
03-10 18:13:49.574 25364 25364 D Base64 : 动态码表编码后的字符串: LxeUUiCgK3,字符串长度:7
03-10 18:13:50.314 25364 25364 D Base64 : 动态码表解码后的字符串: 3fSNQg7,字符串长度:7
jadx 中找到目标方法 dynamicBase64Encode 和 dynamicBase64Decode
ida 反汇编找到目标方法
dynamicBase64Encode 只是一个跳转函数(thunk),PLT/GOT 表 中的一个间接跳转,实际执行的是 _Z19dynamicBase64EncodePKhm。
arduino
// attributes: thunk
void __usercall dynamicBase64Encode(const unsigned __int8 *a1@<X0>, __int64 a2@<X1>, _QWORD *a3@<X8>)
{
_Z19dynamicBase64EncodePKhm(a1, a2, a3);
}
点击 _Z19dynamicBase64EncodePKhm ,找到算法实现,其中访问了全局变量 xmmword_54C30
找到 xmmword_54C30 初始化的地方,找到码表
csharp
__int64 sub_23F30()
{
char *v0; // x0
v0 = (char *)operator new(0x50uLL);
xmmword_54C30 = xmmword_14B00;
qword_54C40 = (__int64)v0;
strcpy(v0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
return __cxa_atexit((void (__fastcall *)(void *))std::string::~string, &xmmword_54C30, &off_4FA00);
}
通过 Cyberchef 解码发现并不能解码成功
使用 Frida Hook dynamicBase64Encode 函数并打印参数、返回值
ini
Java.perform(function () {
var base = Module.findBaseAddress("libbase64.so");
if (base === null) {
console.log("[-] libbase64.so not found!");
return;
}
var targetAddr = base.add(0x23650); // 目标函数地址
console.log("[*] Target function address: " + targetAddr);
Interceptor.attach(targetAddr, {
onEnter: function (args) {
console.log("\n[+] Hooked function at: " + targetAddr);
// 读取参数
var arg0 = args[0]; // X0 寄存器
var arg1 = args[1].toInt32(); // X1 (整数)
var arg2 = args[2]; // X8
// 打印参数值
console.log(" [X0] arg0 (pointer) : " + arg0);
console.log(" [X1] arg1 (int) : " + arg1);
console.log(" [X8] arg2 (pointer) : " + arg2);
// 如果 X0 是字符串或数组,可以尝试打印
if (arg0.readUtf8String) {
console.log(" -> arg0 String: " + arg0.readUtf8String());
} else {
console.log(" -> arg0 (Hex Dump):\n" + hexdump(arg0, {length: 64}));
}
},
onLeave: function (retval) {
console.log("[-] Function returned: " + retval);
console.log(" -> retval (Hex Dump):\n" + hexdump(retval, {length: 64}));
}
});
console.log("[*] Hook installed at " + targetAddr);
});
- 关于 Frida 的使用可以参考这篇文章:使用 Frida Hook Android App
执行脚本
r
frida -H 127.0.0.1:1234 -F -l dynamicBase64Encode.js
输出如下:
less
[*] Target function address: 0x6fa03a4650
[*] Hook installed at 0x6fa03a4650
[Remote::AndroidExample]->
[+] Hooked function at: 0x6fa03a4650
[X0] arg0 (pointer) : 0x7034576d20
[X1] arg1 (int) : 15
[X8] arg2 (pointer) : 0x0
-> arg0 String: 2NJDtPDd5avPu12
[-] Function returned: 0x6fa36f9b00
-> retval (Hex Dump):
0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
6fa36f9b00 c1 f4 a2 af 6f 00 05 00 41 e7 a2 af 6f 00 1c 00 ....o...A...o...
6fa36f9b10 41 4e a3 af 6f 00 1c 00 c1 4e a3 af 6f 00 0c 00 AN..o....N..o...
6fa36f9b20 41 ec a2 af 6f 00 16 00 41 ec a2 af 6f 00 16 00 A...o...A...o...
6fa36f9b30 41 ec a2 af 6f 00 16 00 c1 ed a2 af 6f 00 1c 00 A...o.......o...
日志如下:
diff
adb logcat -s Base64
--------- beginning of system
--------- beginning of crash
--------- beginning of main
03-11 15:09:01.912 9195 9195 D Base64 : 动态码表编码后的字符串: Dr2FeIefeJf6XYWfScL9,字符串长度:15
03-11 15:10:12.186 9195 9195 D Base64 : 动态码表解码后的字符串: 2NJDtPDd5avPu12,字符串长度:15
由输出可以知道:
-
参数 a1 是原文,类型是 字符串
-
参数 a2 是原文的长度,类型是 int
-
参数 a3 看值应该不是传参,实际上是 X8 寄存器,可能用于传递返回值的指针地址。
2. 算法还原
F5 反汇编代码
IDA F5 反汇编 dynamicBase64Encode 方法代码如下:
ini
void __usercall dynamicBase64Encode(const unsigned __int8 *a1@<X0>, __int64 a2@<X1>, _QWORD *a3@<X8>)
{
_OWORD *v6; // x0
__int128 v7; // q0
__int64 v8; // x8
char *v9; // x12
__int64 v10; // x14
char v11; // w12
char *v12; // x11
__int64 v13; // x26
int v14; // w25
int v15; // w8
char *v16; // x27
unsigned int v17; // w28
int v18; // w8
char *v19; // x9
__int128 v20; // [xsp+0h] [xbp-20h] BYREF
void *v21; // [xsp+10h] [xbp-10h]
__int64 v22; // [xsp+18h] [xbp-8h]
v22 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
v6 = (_OWORD *)operator new(0x50uLL);
*(_QWORD *)&v7 = 0x2020202020202020LL;
*((_QWORD *)&v7 + 1) = 0x2020202020202020LL;
v8 = 0LL;
v21 = v6;
*((_BYTE *)v6 + 64) = 0;
*v6 = v7;
v6[1] = v7;
v6[2] = v7;
v6[3] = v7;
v20 = xmmword_14B00;
do
{
v9 = (char *)qword_54C40;
v10 = a2 & 0x3F ^ v8;
if ( (xmmword_54C30 & 1) == 0 )
v9 = (char *)&xmmword_54C30 + 1;
v11 = v9[v8++];
if ( (v20 & 1) != 0 )
v12 = (char *)v21;
else
v12 = (char *)&v20 + 1;
v12[v10] = v11;
}
while ( v8 != 64 );
*a3 = 0LL;
a3[1] = 0LL;
a3[2] = 0LL;
if ( a2 )
{
v13 = 0LL;
v14 = 0;
v15 = -6;
if ( (v20 & 1) != 0 )
v16 = (char *)v21;
else
v16 = (char *)&v20 + 1;
do
{
v14 = a1[v13] | (v14 << 8);
if ( v15 < -8 )
{
v15 += 8;
}
else
{
v17 = v15 + 14;
do
{
v17 -= 6;
std::string::push_back(a3, (unsigned __int8)v16[(v14 >> v17) & 0x3FLL]);
}
while ( v17 > 5 );
v15 = v17 - 6;
}
++v13;
}
while ( v13 != a2 );
if ( (unsigned int)v15 > 0xFFFFFFFA )
{
v18 = v14 << 8 >> (v15 + 8);
if ( (v20 & 1) != 0 )
v19 = (char *)v21;
else
v19 = (char *)&v20 + 1;
std::string::push_back(a3, (unsigned __int8)v19[v18 & 0x3E]);
}
}
if ( (v20 & 1) != 0 )
operator delete(v21);
}
_QWORD 与 _OWORD
_QWORD 和 _OWORD都是 IDA Pro 反编译器使用的类型,它们不是标准的 C/C++ 关键字,而是 IDA 用于表示不同大小的数据类型。
类型 | 大小(字节) | 等效 C/C++ 类型 | 对应寄存器(ARM64) |
---|---|---|---|
_QWORD | 8 字节(64-bit) | __int64 | X0-X30 |
_OWORD | 16 字节(128-bit) | __int128 | Q0-Q31 |
_QWORD *a3@
在 IDA 反编译代码中,_QWORD *a3@ 表示:
-
a3 是一个 _QWORD(64-bit )的指针。
-
@ 说明 a3 这个指针的值存储在 ARM64 寄存器 X8。
在 ARM64 反汇编代码中,_QWORD *a3@ 代表 X8 存储的指针 a3,它指向一个 uint64_t 数据或 std::string。
在这里 a3 就是一个 std::string 存放编码后的字符串。所以并不是一个传参。
ReadStatusReg
ini
v22 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
这段代码的作用是读取 ARM64 处理器的系统寄存器,用于访问某些 CPU 状态信息。
这里可以忽略。
xmmword_54C30、xmmword_14B00、qword_54C40
通过引用查找发现 xmmword_54C30、qword_54C40 是在 sub_23F30 方法中初始化的,sub_23F30 方法在 init_array 时被调用
- __cxa_atexit(...) 用于在程序退出时清理占用的内存资源
xmmword_14B00 是一个全局常量值如下:
用 C 代码还原 sub_23F30 方法,初始化全局变量
arduino
#include <cstdint>
#include <cstdlib>
#include <string>
#include <cstring>
alignas(16) const __int128 xmmword_14B00 = (__int128) 0x4000000000000000LL << 64 | 0x51; // 全局 128-bit 常量 xmmword_14B00
alignas(16) __int128 xmmword_54C30; // / 全局 128-bit 变量 xmmword_54C30
char *qword_54C40 = nullptr; // 存储 Base64 码表指针
void sub_23F30() {
// 分配 80 字节(0x50)内存
char *v0 = new char[0x50];
// 复制 xmmword_14B00 的内容到 xmmword_54C30
xmmword_54C30 = xmmword_14B00;
// 保存 Base64 码表地址
qword_54C40 = v0;
// 复制标准 Base64 码表到 v0
strcpy(v0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
}
复制反汇编代码到 CLion,用 C 代码还原 dynamicBase64Encode 方法
ini
std::string dynamicBase64Encode(const unsigned __int8 *a1, __int64 a2) {
// 输出
std::string a3;
__int128 *v6; // x0
__int128 v7; // q0
__int64 v8; // x8
char *v9; // x12
__int64 v10; // x14
char v11; // w12
char *v12; // x11
__int64 v13; // x26
int v14; // w25
int v15; // w8
char *v16; // x27
unsigned int v17; // w28
int v18; // w8
char *v19; // x9
__int128 v20; // [xsp+0h] [xbp-20h] BYREF
void *v21; // [xsp+10h] [xbp-10h]
__int64 v22; // [xsp+18h] [xbp-8h]
// 访问 CPU 状态信息
// v22 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
v6 = (__int128 *) operator new(0x50uLL);
*(__int64 *) &v7 = 0x2020202020202020LL;
*((__int64 *) &v7 + 1) = 0x2020202020202020LL;
v8 = 0LL;
v21 = v6;
*((uint8_t *) v6 + 64) = 0;
*v6 = v7;
v6[1] = v7;
v6[2] = v7;
v6[3] = v7;
v20 = xmmword_14B00;
do {
v9 = (char *) qword_54C40;
v10 = a2 & 0x3F ^ v8;
if ((xmmword_54C30 & 1) == 0)
v9 = (char *) &xmmword_54C30 + 1;
v11 = v9[v8++];
if ((v20 & 1) != 0)
v12 = (char *) v21;
else
v12 = (char *) &v20 + 1;
v12[v10] = v11;
} while (v8 != 64);
// *a3 = 0LL;
// a3[1] = 0LL;
// a3[2] = 0LL;
a3.clear(); // 清空字符串
if (a2) {
v13 = 0LL;
v14 = 0;
v15 = -6;
if ((v20 & 1) != 0)
v16 = (char *) v21;
else
v16 = (char *) &v20 + 1;
do {
v14 = a1[v13] | (v14 << 8);
if (v15 < -8) {
v15 += 8;
} else {
v17 = v15 + 14;
do {
v17 -= 6;
a3.push_back((unsigned __int8) v16[(v14 >> v17) & 0x3FLL]);
} while (v17 > 5);
v15 = v17 - 6;
}
++v13;
} while (v13 != a2);
if ((unsigned int) v15 > 0xFFFFFFFA) {
v18 = v14 << 8 >> (v15 + 8);
if ((v20 & 1) != 0)
v19 = (char *) v21;
else
v19 = (char *) &v20 + 1;
a3.push_back((unsigned __int8) v19[v18 & 0x3E]);
}
}
if ((v20 & 1) != 0)
operator delete(v21);
return a3;
}
测试调用 dynamicBase64Encode
c
int main() {
// 初始化全局变量
sub_23F30();
// 要编码的原始数据
std::string input = "2NJDtPDd5avPu12";
// 调用 Base64 编码函数
std::string result = dynamicBase64Encode(reinterpret_cast<const uint8_t*>(input.c_str()), input.size());
printf("input: %s\n", input.c_str());
printf("result: %s", result.c_str());
return 0;
}
断点看看,可以看到 a3 成功编码了字符串而且结果和 app 的是一样的
输出如下:
makefile
input: 2NJDtPDd5avPu12
result: Dr2FeIefeJf6XYWfScL9
可以看到和 app 中的加密结果是一样的。
3. 完整还原的 C 代码
ini
#include <cstdint>
#include <cstdlib>
#include <string>
#include <cstring>
alignas(16) const __int128 xmmword_14B00 = (__int128) 0x4000000000000000LL << 64 | 0x51; // 全局 128-bit 常量 xmmword_14B00
alignas(16) __int128 xmmword_54C30; // / 全局 128-bit 变量 xmmword_54C30
char *qword_54C40 = nullptr; // 存储 Base64 码表指针
void sub_23F30() {
// 分配 80 字节(0x50)内存
char *v0 = new char[0x50];
// 复制 xmmword_14B00 的内容到 xmmword_54C30
xmmword_54C30 = xmmword_14B00;
// 保存 Base64 码表地址
qword_54C40 = v0;
// 复制标准 Base64 码表到 v0
strcpy(v0, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
}
std::string dynamicBase64Encode(const unsigned __int8 *a1, __int64 a2) {
// 输出
std::string a3;
__int128 *v6; // x0
__int128 v7; // q0
__int64 v8; // x8
char *v9; // x12
__int64 v10; // x14
char v11; // w12
char *v12; // x11
__int64 v13; // x26
int v14; // w25
int v15; // w8
char *v16; // x27
unsigned int v17; // w28
int v18; // w8
char *v19; // x9
__int128 v20; // [xsp+0h] [xbp-20h] BYREF
void *v21; // [xsp+10h] [xbp-10h]
__int64 v22; // [xsp+18h] [xbp-8h]
// 访问 CPU 状态信息
// v22 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
v6 = (__int128 *) operator new(0x50uLL);
*(__int64 *) &v7 = 0x2020202020202020LL;
*((__int64 *) &v7 + 1) = 0x2020202020202020LL;
v8 = 0LL;
v21 = v6;
*((uint8_t *) v6 + 64) = 0;
*v6 = v7;
v6[1] = v7;
v6[2] = v7;
v6[3] = v7;
v20 = xmmword_14B00;
do {
v9 = (char *) qword_54C40;
v10 = a2 & 0x3F ^ v8;
if ((xmmword_54C30 & 1) == 0)
v9 = (char *) &xmmword_54C30 + 1;
v11 = v9[v8++];
if ((v20 & 1) != 0)
v12 = (char *) v21;
else
v12 = (char *) &v20 + 1;
v12[v10] = v11;
} while (v8 != 64);
// *a3 = 0LL;
// a3[1] = 0LL;
// a3[2] = 0LL;
a3.clear(); // 清空字符串
if (a2) {
v13 = 0LL;
v14 = 0;
v15 = -6;
if ((v20 & 1) != 0)
v16 = (char *) v21;
else
v16 = (char *) &v20 + 1;
do {
v14 = a1[v13] | (v14 << 8);
if (v15 < -8) {
v15 += 8;
} else {
v17 = v15 + 14;
do {
v17 -= 6;
a3.push_back((unsigned __int8) v16[(v14 >> v17) & 0x3FLL]);
} while (v17 > 5);
v15 = v17 - 6;
}
++v13;
} while (v13 != a2);
if ((unsigned int) v15 > 0xFFFFFFFA) {
v18 = v14 << 8 >> (v15 + 8);
if ((v20 & 1) != 0)
v19 = (char *) v21;
else
v19 = (char *) &v20 + 1;
a3.push_back((unsigned __int8) v19[v18 & 0x3E]);
}
}
if ((v20 & 1) != 0)
operator delete(v21);
return a3;
}
int main() {
// 初始化全局变量
sub_23F30();
// 要编码的原始数据
std::string input = "2NJDtPDd5avPu12";
// 调用 Base64 编码函数
std::string result = dynamicBase64Encode(reinterpret_cast<const uint8_t*>(input.c_str()), input.size());
printf("input: %s\n", input.c_str());
printf("result: %s", result.c_str());
return 0;
}