腾讯游戏安全2023安卓初赛题解
腾讯游戏安全2023安卓初赛题解
1、绕过检测:
这里有着对frida和ida的检测,我搜索到了对frida和ida常用端口的检测,所以最简单的就是更换端口
./fr -l 0.0.0.0:1234
adb forward tcp:1234 tcp:1234
./android_server64 -p 23947
adb forward tcp:23947 tcp:23947
frida -H 127.0.0.1:27045 -f com.com.sec2023.rocketmouse.mouse -l .\hook_vm.js
2、il2cppdumper
因为是unity游戏,所以使用il2cppdumper进行恢复符号表是第一步的操作,但是使用dunper工具发现dump失败,显示让我输入,所以两个原因,不是metadata.dat文件被加密就是il2cpp.so文件被加密,但是经过查看metadata.dat他的开头符合正常的文件开头,所以大概率是正确的,所以关键就出在so文件那了,我选择在内存中dump这个so文件。
要内存dump首选的肯定还是frida进行内存dump,但是我没有选择在内存中搜索elf文件开头,然后解析文件结构的方式进行dump,而是选择在内存中对so文件所有的段进行dump,脚本如下:
let dump_so_name = "libil2cpp.so"
function hook_so() {
try {
var module = Process.getModuleByName(dump_so_name);
var base_addr = module.base
var base_size = module.size
var all_size = 0
if (module) {
var file = new File("/data/data/com.com.sec2023.rocketmouse.mouse/files/libil2cpp.so", "wb");
var ranges = Process.enumerateRanges("r");
ranges.forEach(function (range) {
if ((parseInt(range.base, 16) >= parseInt(base_addr)) && (parseInt(range.base, 16) <= parseInt(base_addr) + base_size)) {
all_size += range.size
var buffer = ptr(range.base).readByteArray(range.size);
file.write(buffer);
file.flush();
console.log(`[+] ${range.base}-${"0x" + range.size.toString(16)}-${range.protection}`)
}
}
);
file.close();
console.log("[+] dump successful")
console.log("[+] dump size:", "0x" + all_size.toString(16))
}
} catch (e) {
console.log("dump so error \t" + e)
}
}
function hook_dlopen() {
var dlopen = Module.findExportByName(null, "dlopen");
Interceptor.attach(dlopen, {
onEnter: function (args) {
this.call_hook = false;
var so_name = ptr(args[0]).readCString();
if (so_name.indexOf(dump_so_name) >= 0) {
console.log("dlopen:", ptr(args[0]).readCString());
this.call_hook = true;
}
}, onLeave: function (retval) {
if (this.call_hook) {
hook_so();
}
}
});
}
hook_dlopen()
这里有一个关键点就是
var ranges = Process.enumerateRanges("r");
这里最好使用的就是Process,因为我最开始使用Module进行搜索,怎么都会漏掉一个段,所以还是使用Process比较好,问了一下Module的范围是比较小的
还有一个关键点就是
if ((parseInt(range.base, 16) >= parseInt(base_addr)) && (parseInt(range.base, 16) <= parseInt(base_addr) + base_size))
如果不使用这个的话可能会导致报错,显示越界,所以使用这个进行约束一下
dump下来就可以进行恢复了
tips:
这里学到一个,将dump出来未分段的文件patch到原来加密的so里面,然后重构一下代码,借鉴的一个师哥的代码(tql)

将这两个段的代码重新删除原来的定义同时重新生成函数
import idc
import ida_bytes
begin = 0x0000000002B6850
end = 0x0000000013CB778
fp = open(r"C:\Users\waaa\Desktop\安卓实战\腾讯2023游戏\2023腾讯游戏安全技术竞赛-安卓客户端安全-初赛题目\mouse_pre.aligned.signed\libil2cpp(1).so","rb")
data = fp.read()
# for i in range(begin,end):
# idc.patch_byte(i,data[i-begin])
ida_bytes.patch_bytes(begin,data)
print("finish")
import idc
import idaapi
def upc(begin,end):
for i in range(begin,end):
idc.del_items(i)
for i in range(begin,end):
idc.create_insn(i)
for i in range(begin,end):
idaapi.add_func(i)
print("Finish!!!")
begin = 0x0000000002B6850
end = 0x000000000C4E42C
upc(begin,end)
神奇的妙妙小脚本
恢复符号:
分别使用两个文件进行导入:运行Il2CppDumper提供的脚本ida_with_struct_py3.py,运行后分别选择script.json与il2cpp.h,耐心等待解析完毕
运行Il2CppDumper提供的脚本ida_py3.py,运行后选择script.json

3、il2cpp.so分析:
外挂编写--flag获取:
从dump.cs中搜索coin可以看到一些函数:

通过偏移找一下函数

显而易见判断函数,我这里采用的时hook里面的getvalue的函数,然后会得到所有的值包括血量和刷金币都是1000,成功拿到flag

代码如下:
var tar = "TssSdtInt__GetValue"
function hook_coin() {
const baseAddr = Module.findBaseAddress('libil2cpp.so')
if (baseAddr) {
const funcAddr = baseAddr.add(0x466E34)
Interceptor.attach(funcAddr, {
onEnter: function (args) {
console.log("参数:",args[0].toString(16))
},
onLeave: function (retval) {
retval.replace(0)
console.log('返回值:', retval.toString(16))
},
})
}
注册机分析:
总体加密流程解释:
在上面分析coin的时候往下翻可以看到有一个Smallkeyboard函数,“小键盘”听起来有点像我们在输入时的操作,可以全部hook一下看看什么功能

竟然函数还有混淆,这不就是说明等着我手动还原你吗

我hook1得到了我输入的东西,hook2得到了加密,所以2肯定是一个加密操作

但是得不到有用的东西,据说是动态回填的,只有动态执行到这个的时候才会出现代码
hook混淆smallkeyboard类代码
tips:
下面这个时c#中字符串的结构体,我们在对字符串进行打印hook时,要特别处理一下这些,需要解析一下结构体
/*
struct System_String_o
{
System_String_c *klass;
void *monitor;
System_String_Fields fields;
};
struct System_String_Fields
{
int32_t _stringLength;
uint16_t _firstChar;
};
*/
hook代码如下:
let dump_so_name = "libil2cpp.so";
var baseAddr = Module.findBaseAddress("libil2cpp.so");
console.log("libil2cpp_base_addr: " + baseAddr);
/*
struct System_String_o
{
System_String_c *klass;
void *monitor;
System_String_Fields fields;
};
struct System_String_Fields
{
int32_t _stringLength;
uint16_t _firstChar;
};
*/
//解析字符串形式的结构体
function parseSystemString(addr) {
try {
var _stringLength = ptr(addr).add(0x10).readU32();
var string_real_addr = ptr(addr).add(0x10 + 4);
var string_val = string_real_addr.readUtf16String(_stringLength);
return string_val;
} catch (e) {
console.log("parseSystemString throw:\t" + e);
}
}
function parse_iII1i(addr) {
var KeyType = ptr(addr).add(0x18).readPointer();
var SValue = ptr(addr).add(0x20).readPointer();
var RSvalue = parseSystemString(SValue);
return RSvalue;
}
function hook_SmallKeyboard() {
try {
// hook_list[0] 处理 up、[1] 处理 press、[2] 处理 ok
var hook_list = [0x465880, 0x465fdc, 0x465e90]; // 0 => up, 1 => press, 2 => ok
for (var i = 0; i < hook_list.length; i++) {
var addr = hook_list[i];
const idx = i;
Interceptor.attach(ptr(addr).add(baseAddr), {
onEnter: function (args) {
if (idx === 0) { // key board
console.log("###############################");
var SmallKeyboard_this = args[0];
var strInput = SmallKeyboard_this.add(0x18).readPointer();
var oO0o0o0 = SmallKeyboard_this.add(0x38).readPointer();
var iIIIi = SmallKeyboard_this.add(0x40).readPointer();
var real_strInput = parseSystemString(strInput);
var real_oO0o0o0 = parseSystemString(oO0o0o0);
console.log(iIIIi);
var real_iIIIi = parseSystemString(iIIIi);
console.log("strInput:\t" + real_strInput);
console.log("oO0o0o0:\t" + real_oO0o0o0); // 实际上就是生成的 token 值
console.log("iIIIi:\t" + real_iIIIi); // 实际上是我们已经输入的值
} else if (idx === 2) { // 当键入 ok 时调用
var SmallKeyboard_this = args[0];
var uint64_t_i1I = args[1];
console.log(uint64_t_i1I);
}
},
onLeave: function (retval) {
}
});
}
} catch (e) {
console.log("hook_SmallKeyboard throw:\t" + e);
}
}
function parse_Int32(addr) {
var m_value = ptr(addr).add(0x10).readU32();
return m_value;
}
function hook_generate_token() {
try {
/*
var offset = 0x000000000831950; // 挂钩 System_String__Concat 函数
Interceptor.attach(ptr(baseAddr).add(offset), {
onEnter: function (args) {
this.token = args[0];
this.str1 = args[1];
},
onLeave: function (retval) {
console.log(`${parseSystemString(this.token)} +Connect+ ${parseSystemString(this.str1)} ==> ${parseSystemString(retval)}`);
}
});
*/
} catch (e) {
console.log(e);
}
}
hook_SmallKeyboard();
hook_generate_token();
可以得到一些提示就是根据OO0OoOOo这个混淆嘞找到一些加密函数,类似于opcode,这个是比较容易逆向的,就是指令混淆罢了,下面再细说

hookOO0类代码
再hook完这个之后,我去找了一下ooo这个混淆类
找一下这个混淆类,我可以搜到一些东西

看这个函数三个参数,像不像输入,key,输出
进去看一下

交叉引用一下看看有什么东西,往下翻到xtea的魔改,这个也好逆向

原本我接下来的操作是通过hook上面那个输入下面加密的代码的栈回溯来追踪他的调用栈,发现这个是徒劳,得不到什么有用的东西现在有点懂了,hook那个只能得到调用栈,
所以最终我对tea这个函数进行栈回溯,得到了一些东西:
function call_xtea() {
const baseAddr = Module.findBaseAddress('libil2cpp.so')
var offest = 0x465AB4
const func_inp = baseAddr.add(offest)
Interceptor.attach(func_inp, {
onEnter: function (args) {
console.log('RegisterNatives called from:\\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\\n') + '\\n');
},
onLeave: function (retval) {
//console.log('RegisterNatives called from:\\n' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\\n') + '\\n');
//console.log('返回值:', retval.toString(16))
},
})
}
得到了一些东西
RegisterNatives called from:\n
0x776ec8499c libil2cpp.so!0x46599c\n
0x77f1abc18c libsec2023.so!0x3118c\n
发现有一个libsec2023的调用,所以肯定是调用完libsec.so之后的加密在调用libil2cpp.so的加密
libil2cpp.so逆向分析:
vm分析:
接下来就是逆向这样的代码,其实就是一些指令混淆,逆向起来还是比较好玩的

我通过将这些所有的类似于混淆的函数进行hook得到了他的加密流程:

先来分析一下他的vm指令是什么意思,他的指令混淆背后是什么东西
通过具体的数据进行猜测
def op1(a,b): #a+b
return a + b + (a | ~b) + (b ^ a) - (a & ~b) + 1
def op2(a,b): #b-a
return b - a + (b ^ a) + (b & a) - (b | a)
def op3(a,b): #b*a
return (b + 0x64 + (~(b + 0x64) & 0xFFFFFF9B) - (((b + 0x64) | 0x64) ^ 0xFFFFFF9B)) * a
def op4(a,b): #b<<a
return (b + (~(b + 0x80) | 0xFFFFFF7F) + ((b + 0x80) | 0xFFFFFF7F) + 0x82) << a
def op5(a,b): #b>>a
return ((~(b + 0x100) | 0xFFFFFEFF) + 2 * b + (~(b + 0x100) & 0xFFFFFEFF) + 0x202) >> a
def op6(a,b): #a&b
return (a + (a ^ ~b) + (~b & ~a) + (b & ~a) + 1) & b
def op7(a,b): #a^b
return ((b & ~a) + a + (a & ~b) - (b | a)) | b & (a + 2 * ~a + 1)
hook代码如下:
function opcode() {
const baseaddr = Module.findBaseAddress('libil2cpp.so')
var op_addr = [0x46AEA4, 0x46AF20, 0x46AF94, 0x46B00C, 0x46B088, 0x46B110, 0x46B188, 0x46B210, 0x46B270]
for (var i = 0; i < op_addr.length; i++) {
const addr = op_addr[i]
const idx = i
Interceptor.attach(((baseaddr).add(addr)), {
onEnter: function (args) {
if (addr == 0x46AEA4) {
var a = this.context.x11.and(0xffffffff)
var b = this.context.x10.and(0xffffffff)
console.log("data\t" + b)
console.log("opc\t" + a)
console.log("new_data=data+opc")
//console.log("op1("+this.context.x11+"+"+this.context.x10+")")
}
if (addr == 0x46AF20) {
var a = this.context.x11.and(0xffffffff)
var b = this.context.x10.and(0xffffffff)
console.log("data\t" + b)
console.log("opc\t" + a)
console.log("new_data=data - opc")
//console.log("op2("+this.context.x11+"-"+this.context.x10+")")
}
if (addr == 0x46AF94) {
var a = this.context.x10.and(0xffffffff)
var b = this.context.x11.and(0xffffffff)
console.log("data\t" + b)
console.log("opc\t" + a)
console.log("new_data=data * opc")
//console.log("op3("+this.context.x10.toInt32()+"*"+this.context.x11.toInt32()+")")
}
if (addr == 0x46B00C) {
var a = this.context.x11.and(0xffffffff)
var b = this.context.x10.and(0xffffffff)
console.log("data\t" + b)
console.log("opc\t" + a)
console.log("new_data=data << opc")
//console.log("op4("+this.context.x11.toInt32()+"<<"+this.context.x10.toInt32()+")")
}
if (addr == 0x46B088) {
var a = this.context.x11.and(0xffffffff)
var b = this.context.x10.and(0xffffffff)
console.log("data\t" + b)
console.log("opc\t" + a)
console.log("new_data=data >> opc")
//console.log("op5("+this.context.x11.toInt32()+">>"+this.context.x10.toInt32()+")")
}
if (addr == 0x46B110) {
var a = this.context.x11.and(0xffffffff)
var b = this.context.x10.and(0xffffffff)
console.log("data\t" + b)
console.log("opc\t" + a)
console.log("new_data=data & opc")
//console.log("op6("+this.context.x11.toInt32()+"&"+this.context.x10.toInt32()+")")
}
if (addr == 0x46B188) {
var a = this.context.x10.and(0xffffffff)
var b = this.context.x12.and(0xffffffff)
console.log("data\t" + b)
console.log("opc\t" + a)
console.log("new_data=data ^ opc")
//console.log("op7("+this.context.x10.toInt32()+"^"+this.context.x12.toInt32()+")")
}
if (addr == 0x46B210) {
var a = this.context.x11.and(0xffffffff)
var b = this.context.x10.and(0xffffffff)
console.log("data\t" + b)
console.log("opc\t" + a)
console.log("new_data=data < opc")
//console.log("op8("+this.context.x11.toInt32()+"<"+this.context.x10.toInt32()+")")
}
if (addr == 0x46B270) {
var a = this.context.x11.and(0xffffffff)
var b = this.context.x10.and(0xffffffff)
console.log("data\t" + b)
console.log("opc\t" + a)
console.log("new_data=data == opc")
//console.log("op9("+this.context.x11.toInt32()+"=="+this.context.x10.toInt32()+")")
}
},
onLeave: function (retval) {
},
})
}
}
得到流程:
data 0x10d817fa
opc 0x18
new_data=data >> opc
data 0x10
opc 0xff
new_data=data & opc
data 0x18
opc 0x8
new_data=data - opc
data 0x10
opc 0x0
new_data=data < opc
data 0x10d817fa
opc 0x10
new_data=data >> opc
data 0x10d8
opc 0xff
new_data=data & opc
data 0x10
opc 0x8
new_data=data - opc
data 0x8
opc 0x0
new_data=data < opc
data 0x10d817fa
opc 0x8
new_data=data >> opc
data 0x10d817
opc 0xff
new_data=data & opc
data 0x8
opc 0x8
new_data=data - opc
data 0x0
opc 0x0
new_data=data < opc
data 0x10d817fa
opc 0x0
new_data=data >> opc
data 0x10d817fa
opc 0xff
new_data=data & opc
data 0x0
opc 0x8
new_data=data - opc
data 0xfffffff8
opc 0x0
new_data=data < opc
data 0xfa
opc 0x1b
new_data=data - opc
data 0x17
opc 0xc2
new_data=data ^ opc
data 0xd8
opc 0xa8
new_data=data+opc
data 0x10
opc 0x36
new_data=data ^ opc
data 0xdf
opc 0x0
new_data=data ^ opc
data 0xdf
opc 0x0
new_data=data << opc
data 0xff
opc 0x0
new_data=data << opc
data 0xdf
opc 0xff
new_data=data & opc
data 0xdf
opc 0x0
new_data=data+opc
data 0x4
opc 0x1
new_data=data+opc
data 0x0
opc 0x8
new_data=data+opc
data 0x8
opc 0x19
new_data=data < opc
data 0xd5
opc 0x8
new_data=data ^ opc
data 0xdd
opc 0x8
new_data=data << opc
data 0xff
opc 0x8
new_data=data << opc
data 0xdd00
opc 0xff00
new_data=data & opc
data 0xdd00
opc 0xdf
new_data=data+opc
data 0x5
opc 0x1
new_data=data+opc
data 0x8
opc 0x8
new_data=data+opc
data 0x10
opc 0x19
new_data=data < opc
data 0x180
opc 0x10
new_data=data ^ opc
data 0x190
opc 0x10
new_data=data << opc
data 0xff
opc 0x10
new_data=data << opc
data 0x1900000
opc 0xff0000
new_data=data & opc
data 0x900000
opc 0xdddf
new_data=data+opc
data 0x6
opc 0x1
new_data=data+opc
data 0x10
opc 0x8
new_data=data+opc
data 0x18
opc 0x19
new_data=data < opc
data 0x26
opc 0x18
new_data=data ^ opc
data 0x3e
opc 0x18
new_data=data << opc
data 0xff
opc 0x18
new_data=data << opc
data 0x3e000000
opc 0xff000000
new_data=data & opc
data 0x3e000000
opc 0x90dddf
new_data=data+opc
data 0x7
opc 0x1
new_data=data+opc
data 0x18
opc 0x8
new_data=data+opc
data 0x20
opc 0x19
new_data=data < opc
data 0xd1601856
opc 0x18
new_data=data >> opc
data 0xd1
opc 0xff
new_data=data & opc
data 0x18
opc 0x8
new_data=data - opc
data 0x10
opc 0x0
new_data=data < opc
data 0xd1601856
opc 0x10
new_data=data >> opc
data 0xd160
opc 0xff
new_data=data & opc
data 0x10
opc 0x8
new_data=data - opc
data 0x8
opc 0x0
new_data=data < opc
data 0xd1601856
opc 0x8
new_data=data >> opc
data 0xd16018
opc 0xff
new_data=data & opc
data 0x8
opc 0x8
new_data=data - opc
data 0x0
opc 0x0
new_data=data < opc
data 0xd1601856
opc 0x0
new_data=data >> opc
data 0xd1601856
opc 0xff
new_data=data & opc
data 0x0
opc 0x8
new_data=data - opc
data 0xfffffff8
opc 0x0
new_data=data < opc
data 0x56
opc 0x2f
new_data=data - opc
data 0x18
opc 0xb6
new_data=data ^ opc
data 0x60
opc 0x37
new_data=data+opc
data 0xd1
opc 0x98
new_data=data ^ opc
data 0x27
opc 0x0
new_data=data+opc
data 0x27
opc 0x0
new_data=data << opc
data 0xff
opc 0x0
new_data=data << opc
data 0x27
opc 0xff
new_data=data & opc
data 0x27
opc 0x0
new_data=data+opc
data 0x4
opc 0x1
new_data=data+opc
data 0x0
opc 0x8
new_data=data+opc
data 0x8
opc 0x19
new_data=data < opc
data 0xae
opc 0x8
new_data=data+opc
data 0xb6
opc 0x8
new_data=data << opc
data 0xff
opc 0x8
new_data=data << opc
data 0xb600
opc 0xff00
new_data=data & opc
data 0xb600
opc 0x27
new_data=data+opc
data 0x5
opc 0x1
new_data=data+opc
data 0x8
opc 0x8
new_data=data+opc
data 0x10
opc 0x19
new_data=data < opc
data 0x97
opc 0x10
new_data=data+opc
data 0xa7
opc 0x10
new_data=data << opc
data 0xff
opc 0x10
new_data=data << opc
data 0xa70000
opc 0xff0000
new_data=data & opc
data 0xa70000
opc 0xb627
new_data=data+opc
data 0x6
opc 0x1
new_data=data+opc
data 0x10
opc 0x8
new_data=data+opc
data 0x18
opc 0x19
new_data=data < opc
data 0x49
opc 0x18
new_data=data+opc
data 0x61
opc 0x18
new_data=data << opc
data 0xff
opc 0x18
new_data=data << opc
data 0x61000000
opc 0xff000000
new_data=data & opc
data 0x61000000
opc 0xa7b627
new_data=data+opc
data 0x7
opc 0x1
new_data=data+opc
data 0x18
opc 0x8
new_data=data+opc
data 0x20
opc 0x19
new_data=data < opc
我输入的是12345678
可见他前面肯定还是有其他操作的
他的加密算法就是这样,很简单
def vmenc(inp):
part1 = inp & 0xffffffff
part2 = (inp >> 32) & 0xffffffff
# part1=0x10d817c8
# part2=0xf01549a0
p1_inp1 = (part1 >> 0) & 0xff
p1_inp2 = (part1 >> 8) & 0xff
p1_inp3 = (part1 >> 16) & 0xff
p1_inp4 = (part1 >> 24) & 0xff
p2_inp1 = (part2 >> 0) & 0xff
p2_inp2 = (part2 >> 8) & 0xff
p2_inp3 = (part2 >> 16) & 0xff
p2_inp4 = (part2 >> 24) & 0xff
p1_inp1-=0x1b
p1_inp2^=0xc2
p1_inp3+=0xa8
p1_inp4^=0x36
p2_inp1 -= 0x2f
p2_inp2 ^= 0xb6
p2_inp3 += 0x37
p2_inp4 ^= 0x98
p1_inp1=((p1_inp1^0)<<0)&0xff
p1_inp2=((p1_inp2^0x8)<<8)&0xff00
p1_inp3 = ((p1_inp3 ^ 0x10) << 0x10) & 0xff0000
p1_inp4 = ((p1_inp4 ^ 0x18) << 0x18) & 0xff000000
p1_sum=p1_inp1+p1_inp2+p1_inp3+p1_inp4
#print(hex(p1_inp1),hex(p1_inp2),hex(p1_inp3),hex(p1_inp4))
print(hex(p1_sum))
p2_inp1 = ((p2_inp1 + 0) << 0) & 0xff
p2_inp2 = ((p2_inp2 + 0x8) << 8) & 0xff00
p2_inp3 = ((p2_inp3 + 0x10) << 0x10) & 0xff0000
p2_inp4 = ((p2_inp4 + 0x18) << 0x18) & 0xff000000
#print(hex(p2_inp1), hex(p2_inp2), hex(p2_inp3), hex(p2_inp4))
p2_sum = p2_inp1 + p2_inp2 + p2_inp3 + p2_inp4
print(hex(p2_sum))
return p1_sum,p2_sum
# vmenc()
def vmdec(part1,part2):
# part1=0x3e90ddad
# part2=0x805c0771
p1_inp1 = (part1 >> 0) & 0xff
p1_inp2 = (part1 >> 8) & 0xff
p1_inp3 = (part1 >> 16) & 0xff
p1_inp4 = (part1 >> 24) & 0xff
p2_inp1 = (part2 >> 0) & 0xff
p2_inp2 = (part2 >> 8) & 0xff
p2_inp3 = (part2 >> 16) & 0xff
p2_inp4 = (part2 >> 24) & 0xff
p1_inp1 = ((p1_inp1 ^ 0))
p1_inp2 = ((p1_inp2 ^ 0x8))
p1_inp3 = ((p1_inp3 ^ 0x10) )
p1_inp4 = ((p1_inp4 ^ 0x18))
p1_inp1 += 0x1b
p1_inp2 ^= 0xc2
p1_inp3 -= 0xa8
p1_inp4 ^= 0x36
p1_inp1 = ((p1_inp1) << 0) & 0xff
p1_inp2 = ((p1_inp2) << 8) & 0xff00
p1_inp3 = ((p1_inp3) << 0x10) & 0xff0000
p1_inp4 = ((p1_inp4) << 0x18) & 0xff000000
p1_sum = p1_inp1 + p1_inp2 + p1_inp3 + p1_inp4
print(hex(p1_sum))
p2_inp1 = ((p2_inp1 - 0) )
p2_inp2 = ((p2_inp2 - 0x8) )
p2_inp3 = ((p2_inp3 - 0x10) )
p2_inp4 = ((p2_inp4 - 0x18) )
p2_inp1 += 0x2f
p2_inp2 ^= 0xb6
p2_inp3 -= 0x37
p2_inp4 ^= 0x98
p2_inp1 = ((p2_inp1 ) << 0) & 0xff
p2_inp2 = ((p2_inp2) << 8) & 0xff00
p2_inp3 = ((p2_inp3 ) << 0x10) & 0xff0000
p2_inp4 = ((p2_inp4 ) << 0x18) & 0xff000000
p2_sum = p2_inp1 + p2_inp2 + p2_inp3 + p2_inp4
print(hex(p2_sum))
return p1_sum + (p2_sum << 32)
xtea分析:
下面我只给了解密代码,加密代码也是类似,我是用的c写的
先hook一下key的值
function xtea() {
const baseAddr = Module.findBaseAddress("libil2cpp.so")
var offest = 0x465CA4
const xteas = baseAddr.add(offest)
Interceptor.attach(xteas, {
onEnter: function (args) {
console.log(hexdump(this.context.x20, {
offset: 0,// 相对偏移
length: 64,//dump 的大小
header: true,
ansi: true
}));
},
onLeave: function (retval) {
},
})
}
#include <iostream>
#include <stdio.h>
using namespace std;
void __cdecl xtea(uint32_t* a1, uint32_t* key)
{
uint32_t sum1 = (0xBEEFBEEF- 0x21524111*64)&0xffffffff;
uint32_t sum2 = (0x9D9D7DDE - 0x21524111 * 64) & 0xffffffff;
uint32_t v24 = 64;
uint32_t idx;
uint32_t enc1 = a1[0];
uint32_t enc2 = a1[1];
printf("sum1:%d\n",sum1);
printf("sum2:%d\n", sum2);
do
{
idx = (sum2 >> 13) & 3;
sum2 += 0x21524111;
sum1 += 0x21524111;
enc2 -= (sum2 + (key[(sum2 >> 13) & 3])) ^ (((enc1 << 8) ^ (enc1 >> 7)) - enc1);
enc1 -= (sum1 - (key[(sum1)&3])) ^ (((enc2 << 7) ^ (enc2 >> 8)) + enc2);
--v24;
//printf("=====================");
/*
idx = (sum2 >> 13) & 3;
enc1 += (sum1 - (key[(sum1)&3])) ^ (((enc2 << 7) ^ (enc2 >> 8)) + enc2);
--v24;
sum1 -= 0x21524111;
enc2 += (sum2 + (key[(sum2 >> 13) & 3])) ^ (((enc1 << 8) ^ (enc1 >> 7)) - enc1);
sum2 -= 0x21524111;
*/
} while (v24);
a1[0]= enc1 ;
a1[1]= enc2 ;
}
int main() {
unsigned int v7, v8;
int i;
uint32_t enc[2] = { 7290969,0};
uint32_t key[4] = { 0x7b777c63, 0xc56f6bf2, 0x2b670130, 0x76abd7fe
};
xtea(enc, key);
for (int i = 0; i < 2; i++)
cout << enc[i]<<endl;
}
python的也有,毕竟都要写在一块
from ctypes import *
from struct import pack, unpack
# Encryption function for XTEA
def encrypt(v, key):
v0, v1 = c_uint32(v[0]), c_uint32(v[1])
delta = 0x21524111
total1 = c_uint32(0xBEEFBEEF)
total2 = c_uint32(0x9D9D7DDE)
for i in range(64):
v0.value += (((v1.value << 7) ^ (v1.value >> 8)) + v1.value) ^ (total1.value - key[total1.value & 3])
total1.value -= delta
v1.value += (((v0.value << 8) ^ (v0.value >> 7)) - v0.value) ^ (total2.value + key[(total2.value >> 13) & 3])
total2.value -= delta
return v0.value, v1.value
# Decryption function for XTEA
def decrypt(v, key):
v0, v1 = c_uint32(v[0]), c_uint32(v[1])
delta = 0x21524111
total1 = c_uint32(0xBEEFBEEF - 64 * delta)
total2 = c_uint32(0x9D9D7DDE - 64 * delta)
for i in range(64):
total2.value += delta
v1.value -= (((v0.value << 8) ^ (v0.value >> 7)) - v0.value) ^ (total2.value + key[(total2.value >> 13) & 3])
total1.value += delta
v0.value -= (((v1.value << 7) ^ (v1.value >> 8)) + v1.value) ^ (total1.value - key[total1.value & 3])
return v0.value, v1.value
# XTEA encryption wrapper
def enxtea(inp: bytes, key: bytes):
k = unpack("<4I", key)
inp_len = len(inp) // 4
value = unpack(f"<{inp_len}I", inp)
res = b""
for i in range(0, inp_len, 2):
v = [value[i], value[i + 1]]
x = encrypt(v, k)
res += pack("<2I", *x)
return res
# XTEA decryption wrapper
def dextea(inp: bytes, key: bytes):
k = unpack("<4I", key)
inp_len = len(inp) // 4
value = unpack(f"<{inp_len}I", inp)
res = b""
for i in range(0, inp_len, 2):
v = [value[i], value[i + 1]]
x = decrypt(v, k)
res += pack("<2I", *x)
return res
# XTEA encryption example
def xtea_enc(low_4val=0x3e90dddf, high_4val=0x23f6a9b5):
# Prepare input and key
inp = pack("<I", low_4val) + pack("<I", high_4val)
key = [2071428195, 3312413682, 728170800, 1990973438]
key = pack("<I", key[0]) + pack("<I", key[1]) + pack("<I", key[2]) + pack("<I", key[3])
# Encrypt
ret = enxtea(inp, key)
xtea_enc1 = unpack("<I", ret[0:4])[0]
xtea_enc2 = unpack("<I", ret[4:8])[0]
return xtea_enc1, xtea_enc2
# XTEA decryption example
def xtea_dec(xtea_enc1=0x7dd89904, xtea_enc2=0xc6ff408e):
# Prepare input and key
key = [2071428195, 3312413682, 728170800, 1990973438]
key = pack("<I", key[0]) + pack("<I", key[1]) + pack("<I", key[2]) + pack("<I", key[3])
# Decrypt
inp = pack("<I", xtea_enc1) + pack("<I", xtea_enc2)
ret = dextea(inp, key)
xtea_dec1 = unpack("<I", ret[0:4])[0]
xtea_dec2 = unpack("<I", ret[4:8])[0]
return xtea_dec1, xtea_dec2
libsec2023.so逆向分析:
我们跳转过去看一下是什么加密

看看上面的函数里面是什么
这个是我手动改的

对于encde1,里面全是一个个小片段,根本看不出来逻辑
BR跳转分析:
全是一个个BR跳转,还是老方法分析呗,hook住所有的地址,进行查看

function return_BR() {
const baseAddr = Module.findBaseAddress("libsec2023.so")
const offests = [0x3b8cc,
0x3BA00, 0x3BA30, 0x3BA70, 0x3BADC, 0x3BB28, 0x3BB4C,0x3bb54,
0x3A08C, 0x3A0C8,0x3a0f8,
0x3B950,0x3B990,0x3B9CC ,0x3B9B0 ]
// , 0x3A08C, 0x3A0C8
console.log("base地址",baseAddr)
for (var i = 0; i < offests.length; i++) {
const offest = offests[i]
Interceptor.attach(baseAddr.add(offest), {
onEnter: function (args) {
if (offest == 0x3b8cc) {
console.log("addr:0x3b8cc ==>", (args[0]))
}
if (offest == 0x3BA00) {
console.log("addr:0x3BA00 ==>","X10:", (this.context.x10 - baseAddr).toString(16))
}
if (offest == 0x3BA30) {
console.log("addr:0x3BA30 ==>","X12:", (this.context.x12 - baseAddr).toString(16))
}
if (offest == 0x3BA70) {
console.log("addr:0x3BA70 ==>","X12:", (this.context.x12 - baseAddr).toString(16))
}
if (offest == 0x3BADC) {
console.log("addr:0x3BADC ==>","X13:", (this.context.x13 - baseAddr).toString(16))
}
if (offest == 0x3BB28) {
console.log("addr:0x3BB28 ==>","X13:", (this.context.x13 - baseAddr).toString(16))
}
if (offest == 0x3BB4C) {
console.log("addr:0x3BB4C ==>","X10:", (this.context.x10 - baseAddr).toString(16))
}
if (offest == 0x3BB54) {
console.log("addr:0x3BB54 ==>","ret:", (this.context.lr ).toString(16))
}
if (offest == 0x3A08C) {
console.log("addr:0x3A08C ==>","X8:", (this.context.x8 - baseAddr).toString(16))
}
if (offest == 0x3A0C8) {
console.log("addr:0x3A0C8 ==>","X8:", (this.context.x8 - baseAddr).toString(16))
}
if (offest == 0x3a0f8) {
console.log("addr:0x3a0f8 ==>","ret:", (this.context.lr - baseAddr).toString(16))
}
if (offest == 0x3B950) {
console.log("addr:0x3B950 ==>","x8:", (this.context.x8 - baseAddr).toString(16))
}
if (offest == 0x3B990) {
console.log("addr:0x3B990 ==>","x8:", (this.context.x8 - baseAddr).toString(16))
}
if (offest == 0x3B9b0) {
console.log("addr:0x3B9b0 ==>","x19:", (this.context.x19).toString(16),"x8:",(this.context.x8).toString(16))
}
if (offest == 0x3B9CC) {
console.log("addr:0x3B9CC ==>","ret:", (this.context.lr ).toString(16))
}
}, onLeave: function (retval) {
}
})
}
}

我得到了三个加密,其中一个四字节的,还有一个小函数还是有加密
unidbg调用
使用unidbg进行调用分析一下,unidbg真好用
package com.tengxun2023;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.debugger.DebuggerType;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.DalvikModule;
import com.github.unidbg.linux.android.dvm.DvmClass;
import com.github.unidbg.linux.android.dvm.VM;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.virtualmodule.android.AndroidModule;
import com.github.unidbg.Emulator;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.debugger.BreakPointCallback;
import com.github.unidbg.debugger.DebuggerType;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.virtualmodule.android.AndroidModule;
import java.io.File;
import com.github.unidbg.arm.context.Arm64RegisterContext;
import java.io.File;
public class libsec2023 {
public final AndroidEmulator emulator;
public final VM vm;
public final Memory memory;
public final Module module;
DvmClass cNative;
public int hitCount = 0;
public libsec2023(){
emulator = AndroidEmulatorBuilder.for64Bit().build();
memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
emulator.getSyscallHandler().setEnableThreadDispatcher(true);
vm = emulator.createDalvikVM();
// vm.setVerbose(true);
new AndroidModule(emulator,vm).register(memory);
DalvikModule dalvikModule = vm.loadLibrary(new File("I:\\libsec2023.so"), true);
module = dalvikModule.getModule();
vm.callJNI_OnLoad(emulator,module);
}
public static void main(String[] args){
libsec2023 mainActivity=new libsec2023();
mainActivity.debugger();
}
private void debugger(){
emulator.attach(DebuggerType.CONSOLE).addBreakPoint(module.base+0x3b8cc);
module.callFunction(emulator,0x3b8cc,0x12D687);
}
}
正好学习使用一些unidbg
加解密代码:
可以得到一个加密:
def sec2023enc(inp):
part1=inp&0xffffffff
part2=(inp>>32)&0xffffffff
# 测试使用
# part1=0x12D687
# part2=0x123456
p1_inp1 = ((part1 >> 0) ^0)&0xff
p1_inp2 = ((part1 >> 8) ^1)&0xff
p1_inp3 = ((part1 >> 16) ^2)&0xff
p1_inp4 = ((part1 >> 24) ^3)&0xff
#print(hex(p1_inp1),hex(p1_inp2),hex(p1_inp3),hex(p1_inp4))
p1_inp1-=0x1c
p1_inp2^=0xd3
p1_inp3-=0x5e
p1_inp4^=0x86
p1_inp1 -= 0
p1_inp2 -= 0x8
p1_inp3 -= 0x10
p1_inp4 -= 0x18
p1_inp1 = ((p1_inp1) << 0) & 0xff
p1_inp2 = ((p1_inp2) << 8) & 0xff00
p1_inp3 = ((p1_inp3) << 0x10) & 0xff0000
p1_inp4 = ((p1_inp4) << 0x18) & 0xff000000
p1_sum = p1_inp1 + p1_inp2 + p1_inp3 + p1_inp4
print(hex(p1_sum))
p2_inp1 = ((part2 >> 0) ^ 0) & 0xff
p2_inp2 = ((part2 >> 8) ^ 1) & 0xff
p2_inp3 = ((part2 >> 16) ^ 2) & 0xff
p2_inp4 = ((part2 >> 24) ^ 3) & 0xff
#print(hex(p1_inp1), hex(p1_inp2), hex(p1_inp3), hex(p1_inp4))
p2_inp1 -= 0x1c
p2_inp2 ^= 0xd3
p2_inp3 -= 0x5e
p2_inp4 ^= 0x86
p2_inp1 -= 0
p2_inp2 -= 0x8
p2_inp3 -= 0x10
p2_inp4 -= 0x18
p2_inp1 = ((p2_inp1) << 0) & 0xff
p2_inp2 = ((p2_inp2) << 8) & 0xff00
p2_inp3 = ((p2_inp3) << 0x10) & 0xff0000
p2_inp4 = ((p2_inp4) << 0x18) & 0xff000000
p2_sum = p2_inp1 + p2_inp2 + p2_inp3 + p2_inp4
print(hex(p2_sum))
return p1_sum,p2_sum
def sec2023dec(part1,part2):
# part1 = inp & 0xffffffff
# part2 = (inp >> 32) & 0xffffffff
# 测试使用
# part1 = 0x85b2046b
# part2 = 0x85b2e63a
p1_inp1 = (part1 >> 0) & 0xff
p1_inp2 = (part1 >> 8) & 0xff
p1_inp3 = (part1 >> 16) & 0xff
p1_inp4 = (part1 >> 24) & 0xff
p2_inp1 = (part2 >> 0) & 0xff
p2_inp2 = (part2 >> 8) & 0xff
p2_inp3 = (part2 >> 16) & 0xff
p2_inp4 = (part2 >> 24) & 0xff
p1_inp1 += 0
p1_inp2 += 0x8
p1_inp3 += 0x10
p1_inp4 += 0x18
p1_inp1 += 0x1c
p1_inp2 ^= 0xd3
p1_inp3 += 0x5e
p1_inp4 ^= 0x86
p1_inp1 ^= 0
p1_inp2 ^= 1
p1_inp3 ^= 2
p1_inp4 ^= 3
p1_inp1 = ((p1_inp1) << 0) & 0xff
p1_inp2 = ((p1_inp2) << 8) & 0xff00
p1_inp3 = ((p1_inp3) << 0x10) & 0xff0000
p1_inp4 = ((p1_inp4) << 0x18) & 0xff000000
p1_sum = p1_inp1 + p1_inp2 + p1_inp3 + p1_inp4
print(hex(p1_sum))
p2_inp1 += 0
p2_inp2 += 0x8
p2_inp3 += 0x10
p2_inp4 += 0x18
p2_inp1 += 0x1c
p2_inp2 ^= 0xd3
p2_inp3 += 0x5e
p2_inp4 ^= 0x86
p2_inp1 ^= 0
p2_inp2 ^= 1
p2_inp3 ^= 2
p2_inp4 ^= 3
p2_inp1 = ((p2_inp1) << 0) & 0xff
p2_inp2 = ((p2_inp2) << 8) & 0xff00
p2_inp3 = ((p2_inp3) << 0x10) & 0xff0000
p2_inp4 = ((p2_inp4) << 0x18) & 0xff000000
p2_sum = p2_inp1 + p2_inp2 + p2_inp3 + p2_inp4
print(hex(p2_sum))
return p1_sum + (p2_sum << 32)
encode2分析:

修改一下指针,改一个JNIEnv就行
可以看到这是调用java层代码,我们使用frida-dex-dump进行dump
得到一些代码,使用脚本给他归结到一起
import os
import zipfile
import argparse
def rename_class(path):
files = os.listdir(path)
dex_index = 0
if path.endswith('/'):
path = path[:-1]
print(path)
for i in range(len(files)):
if files[i].endswith('.dex'):
old_name = path + '/' + files[i]
if dex_index == 0:
new_name = path + '/' + 'classes.dex'
else:
new_name = path + '/' + 'classes%d.dex' % dex_index
dex_index += 1
if os.path.exists(new_name):
continue
os.rename(old_name, new_name)
print('[*] 重命名完毕')
def extract_META_INF_from_apk(apk_path, target_path):
r = zipfile.is_zipfile(apk_path)
if r:
fz = zipfile.ZipFile(apk_path, 'r')
for file in fz.namelist():
if file.startswith('META-INF'):
fz.extract(file, target_path)
else:
print('[-] %s 不是一个APK文件' % apk_path)
def zip_dir(dirname, zipfilename):
filelist = []
if os.path.isfile(dirname):
if dirname.endswith('.dex'):
filelist.append(dirname)
else:
for root, dirs, files in os.walk(dirname):
for dir in dirs:
# if dir == 'META-INF':
# print('dir:', os.path.join(root, dir))
filelist.append(os.path.join(root, dir))
for name in files:
# print('file:', os.path.join(root, name))
filelist.append(os.path.join(root, name))
z = zipfile.ZipFile(zipfilename, 'w', zipfile.ZIP_DEFLATED)
for tar in filelist:
arcname = tar[len(dirname):]
if ('META-INF' in arcname or arcname.endswith('.dex')) and '.DS_Store' not in arcname:
# print(tar + " -->rar: " + arcname)
z.write(tar, arcname)
print('[*] APK打包成功,你可以拖入APK进行分析啦!')
z.close()
if __name__ == '__main__':
args = {
'dex_path': 'G:\\fida-hexdump\\frida-dexdump-2.0.1\\com.android.vending-quick_launch',
'apk_path': '"C:\\Users\\waaa\\Desktop\\安卓实战\\腾讯2023游戏\\2023腾讯游戏安全技术竞赛-安卓客户端安全-初赛题目\\mouse_pre.aligned.signed.apk"',
'output': 'I:\\test\\1.apk'
}
rename_class(args['dex_path'])
extract_META_INF_from_apk(args['apk_path'], args['dex_path'])
zip_dir(args['dex_path'], args['output'])
然后放大到dex里面分析:
java分析:

因为jadx有混淆,但是jeb可以去混淆,但是还是有点乱,不过不影响看
直接提取关键代码进行分析:
int v = (inp1[3] & 0xFF) + ((inp1[0] << 24 & 0xFF000000) + (inp1[1] << 16 & 0xFF0000) + (inp1[2] << 8 & 0xFF00));
int v1 = v >> 7 | (v & 0x7F) << 25;
byte[] inp2 = {((byte)(v1 >> 24 & 0xFF)), ((byte)(v1 >> 16 & 0xFF)), ((byte)(v1 >> 8 & 0xFF)), ((byte)(v1 & 0xFF))};
int v2 = 0;
inp2[v2] = (byte)(inp2[v2] ^ new byte[]{50, -51, -1, -104, 25, -78, 0x7C, -102}[v2 % 8]);
inp2[v2] = (byte)(inp2[v2] + v2);
++v2;
break;
最后代码如下:
# java层加密解密
def javaenc(inp1):
key = [0x32, 0xCD, 0xFF, 0x98, 0x19, 0xB2, 0x7C, 0x9A]
#inp1 = [0x6d, 0x94, 0xca, 0xe4]
inp2 = [0] * 4
v = 0
v = (inp1[0] << 24) + (inp1[1] << 16) + (inp1[2] << 8) + inp1[3]
# 位移操作
v1 = (v >> 7) | (v << 25)
# 拆分 v1 为四个字节,存入 inp2
for i in range(4):
inp2[i] = (v1 >> (8 * (3-i))) & 0xFF
# 加密操作
for i in range(4):
inp2[i] ^= key[i % len(key)] & 0xFF # 按位异或
inp2[i] += i # 加上索引
inp2[i] &= 0xFF # 保证结果是一个字节
# 打印结果
for i in range(4):
print(hex(inp2[i]), end=',')
print()
return inp2
def javadec(inp2):
key = [0x32, 0xCD, 0xFF, 0x98, 0x19, 0xB2, 0x7C, 0x9A]
inp1 = [0] * 4
#inp2 = [0x1a,0x59,0x38,0x46] # 输入加密后的数据
v = 0
# 解密操作
for i in range(4):
inp2[i] = (inp2[i] - i) & 0xFF # 减去索引并保持字节范围
inp2[i] ^= key[i % 8] & 0xFF # 按位异或
# 合并 inp2 的四个字节到一个整数 v
v=(inp2[0] << 24) + (inp2[1] << 16) + (inp2[2] << 8) +inp2[3]
# 逆向位移操作
v1 = (v << 7 ) | (v >> 25)
# 拆分 v1 为四个字节,存入 inp1
for i in range(4):
inp1[i] = (v1 >> (8 * (3-i))) & 0xFF
# 打印结果
for i in range(4):
print(hex(inp1[i]), end=',')
print()
return inp1
最终加解密代码:
我们现在就可以简单分析一下他的加密了
1、首先就是so层的一个个BR跳转加密
2、然后调用java层代码进行加密
3、接下来跳转到libil2cpp.so文件,就是那个vm虚拟机加密
4、最终就是那个魔改xtea加密了
解密代码我们反过来就行
def vmenc(inp):
part1 = inp & 0xffffffff
part2 = (inp >> 32) & 0xffffffff
# part1=0x10d817c8
# part2=0xf01549a0
p1_inp1 = (part1 >> 0) & 0xff
p1_inp2 = (part1 >> 8) & 0xff
p1_inp3 = (part1 >> 16) & 0xff
p1_inp4 = (part1 >> 24) & 0xff
p2_inp1 = (part2 >> 0) & 0xff
p2_inp2 = (part2 >> 8) & 0xff
p2_inp3 = (part2 >> 16) & 0xff
p2_inp4 = (part2 >> 24) & 0xff
p1_inp1-=0x1b
p1_inp2^=0xc2
p1_inp3+=0xa8
p1_inp4^=0x36
p2_inp1 -= 0x2f
p2_inp2 ^= 0xb6
p2_inp3 += 0x37
p2_inp4 ^= 0x98
p1_inp1=((p1_inp1^0)<<0)&0xff
p1_inp2=((p1_inp2^0x8)<<8)&0xff00
p1_inp3 = ((p1_inp3 ^ 0x10) << 0x10) & 0xff0000
p1_inp4 = ((p1_inp4 ^ 0x18) << 0x18) & 0xff000000
p1_sum=p1_inp1+p1_inp2+p1_inp3+p1_inp4
#print(hex(p1_inp1),hex(p1_inp2),hex(p1_inp3),hex(p1_inp4))
print(hex(p1_sum))
p2_inp1 = ((p2_inp1 + 0) << 0) & 0xff
p2_inp2 = ((p2_inp2 + 0x8) << 8) & 0xff00
p2_inp3 = ((p2_inp3 + 0x10) << 0x10) & 0xff0000
p2_inp4 = ((p2_inp4 + 0x18) << 0x18) & 0xff000000
#print(hex(p2_inp1), hex(p2_inp2), hex(p2_inp3), hex(p2_inp4))
p2_sum = p2_inp1 + p2_inp2 + p2_inp3 + p2_inp4
print(hex(p2_sum))
return p1_sum,p2_sum
# vmenc()
def vmdec(part1,part2):
# part1=0x3e90ddad
# part2=0x805c0771
p1_inp1 = (part1 >> 0) & 0xff
p1_inp2 = (part1 >> 8) & 0xff
p1_inp3 = (part1 >> 16) & 0xff
p1_inp4 = (part1 >> 24) & 0xff
p2_inp1 = (part2 >> 0) & 0xff
p2_inp2 = (part2 >> 8) & 0xff
p2_inp3 = (part2 >> 16) & 0xff
p2_inp4 = (part2 >> 24) & 0xff
p1_inp1 = ((p1_inp1 ^ 0))
p1_inp2 = ((p1_inp2 ^ 0x8))
p1_inp3 = ((p1_inp3 ^ 0x10) )
p1_inp4 = ((p1_inp4 ^ 0x18))
p1_inp1 += 0x1b
p1_inp2 ^= 0xc2
p1_inp3 -= 0xa8
p1_inp4 ^= 0x36
p1_inp1 = ((p1_inp1) << 0) & 0xff
p1_inp2 = ((p1_inp2) << 8) & 0xff00
p1_inp3 = ((p1_inp3) << 0x10) & 0xff0000
p1_inp4 = ((p1_inp4) << 0x18) & 0xff000000
p1_sum = p1_inp1 + p1_inp2 + p1_inp3 + p1_inp4
print(hex(p1_sum))
p2_inp1 = ((p2_inp1 - 0) )
p2_inp2 = ((p2_inp2 - 0x8) )
p2_inp3 = ((p2_inp3 - 0x10) )
p2_inp4 = ((p2_inp4 - 0x18) )
p2_inp1 += 0x2f
p2_inp2 ^= 0xb6
p2_inp3 -= 0x37
p2_inp4 ^= 0x98
p2_inp1 = ((p2_inp1 ) << 0) & 0xff
p2_inp2 = ((p2_inp2) << 8) & 0xff00
p2_inp3 = ((p2_inp3 ) << 0x10) & 0xff0000
p2_inp4 = ((p2_inp4 ) << 0x18) & 0xff000000
p2_sum = p2_inp1 + p2_inp2 + p2_inp3 + p2_inp4
print(hex(p2_sum))
return p1_sum + (p2_sum << 32)
# x1,x2=vmdec(0x3e90dddf,0x23f6a9b5)
# ans=x1+(x2<<32)
# print("x1,x2=",hex(ans))
# vmdec()
#sec2023加密解密
def sec2023enc(inp):
part1=inp&0xffffffff
part2=(inp>>32)&0xffffffff
# 测试使用
# part1=0x12D687
# part2=0x123456
p1_inp1 = ((part1 >> 0) ^0)&0xff
p1_inp2 = ((part1 >> 8) ^1)&0xff
p1_inp3 = ((part1 >> 16) ^2)&0xff
p1_inp4 = ((part1 >> 24) ^3)&0xff
#print(hex(p1_inp1),hex(p1_inp2),hex(p1_inp3),hex(p1_inp4))
p1_inp1-=0x1c
p1_inp2^=0xd3
p1_inp3-=0x5e
p1_inp4^=0x86
p1_inp1 -= 0
p1_inp2 -= 0x8
p1_inp3 -= 0x10
p1_inp4 -= 0x18
p1_inp1 = ((p1_inp1) << 0) & 0xff
p1_inp2 = ((p1_inp2) << 8) & 0xff00
p1_inp3 = ((p1_inp3) << 0x10) & 0xff0000
p1_inp4 = ((p1_inp4) << 0x18) & 0xff000000
p1_sum = p1_inp1 + p1_inp2 + p1_inp3 + p1_inp4
print(hex(p1_sum))
p2_inp1 = ((part2 >> 0) ^ 0) & 0xff
p2_inp2 = ((part2 >> 8) ^ 1) & 0xff
p2_inp3 = ((part2 >> 16) ^ 2) & 0xff
p2_inp4 = ((part2 >> 24) ^ 3) & 0xff
#print(hex(p1_inp1), hex(p1_inp2), hex(p1_inp3), hex(p1_inp4))
p2_inp1 -= 0x1c
p2_inp2 ^= 0xd3
p2_inp3 -= 0x5e
p2_inp4 ^= 0x86
p2_inp1 -= 0
p2_inp2 -= 0x8
p2_inp3 -= 0x10
p2_inp4 -= 0x18
p2_inp1 = ((p2_inp1) << 0) & 0xff
p2_inp2 = ((p2_inp2) << 8) & 0xff00
p2_inp3 = ((p2_inp3) << 0x10) & 0xff0000
p2_inp4 = ((p2_inp4) << 0x18) & 0xff000000
p2_sum = p2_inp1 + p2_inp2 + p2_inp3 + p2_inp4
print(hex(p2_sum))
return p1_sum,p2_sum
def sec2023dec(part1,part2):
# part1 = inp & 0xffffffff
# part2 = (inp >> 32) & 0xffffffff
# 测试使用
# part1 = 0x85b2046b
# part2 = 0x85b2e63a
p1_inp1 = (part1 >> 0) & 0xff
p1_inp2 = (part1 >> 8) & 0xff
p1_inp3 = (part1 >> 16) & 0xff
p1_inp4 = (part1 >> 24) & 0xff
p2_inp1 = (part2 >> 0) & 0xff
p2_inp2 = (part2 >> 8) & 0xff
p2_inp3 = (part2 >> 16) & 0xff
p2_inp4 = (part2 >> 24) & 0xff
p1_inp1 += 0
p1_inp2 += 0x8
p1_inp3 += 0x10
p1_inp4 += 0x18
p1_inp1 += 0x1c
p1_inp2 ^= 0xd3
p1_inp3 += 0x5e
p1_inp4 ^= 0x86
p1_inp1 ^= 0
p1_inp2 ^= 1
p1_inp3 ^= 2
p1_inp4 ^= 3
p1_inp1 = ((p1_inp1) << 0) & 0xff
p1_inp2 = ((p1_inp2) << 8) & 0xff00
p1_inp3 = ((p1_inp3) << 0x10) & 0xff0000
p1_inp4 = ((p1_inp4) << 0x18) & 0xff000000
p1_sum = p1_inp1 + p1_inp2 + p1_inp3 + p1_inp4
print(hex(p1_sum))
p2_inp1 += 0
p2_inp2 += 0x8
p2_inp3 += 0x10
p2_inp4 += 0x18
p2_inp1 += 0x1c
p2_inp2 ^= 0xd3
p2_inp3 += 0x5e
p2_inp4 ^= 0x86
p2_inp1 ^= 0
p2_inp2 ^= 1
p2_inp3 ^= 2
p2_inp4 ^= 3
p2_inp1 = ((p2_inp1) << 0) & 0xff
p2_inp2 = ((p2_inp2) << 8) & 0xff00
p2_inp3 = ((p2_inp3) << 0x10) & 0xff0000
p2_inp4 = ((p2_inp4) << 0x18) & 0xff000000
p2_sum = p2_inp1 + p2_inp2 + p2_inp3 + p2_inp4
print(hex(p2_sum))
return p1_sum + (p2_sum << 32)
# java层加密解密
def javaenc(inp1):
key = [0x32, 0xCD, 0xFF, 0x98, 0x19, 0xB2, 0x7C, 0x9A]
#inp1 = [0x6d, 0x94, 0xca, 0xe4]
inp2 = [0] * 4
v = 0
v = (inp1[0] << 24) + (inp1[1] << 16) + (inp1[2] << 8) + inp1[3]
# 位移操作
v1 = (v >> 7) | (v << 25)
# 拆分 v1 为四个字节,存入 inp2
for i in range(4):
inp2[i] = (v1 >> (8 * (3-i))) & 0xFF
# 加密操作
for i in range(4):
inp2[i] ^= key[i % len(key)] & 0xFF # 按位异或
inp2[i] += i # 加上索引
inp2[i] &= 0xFF # 保证结果是一个字节
# 打印结果
for i in range(4):
print(hex(inp2[i]), end=',')
print()
return inp2
def javadec(inp2):
key = [0x32, 0xCD, 0xFF, 0x98, 0x19, 0xB2, 0x7C, 0x9A]
inp1 = [0] * 4
#inp2 = [0x1a,0x59,0x38,0x46] # 输入加密后的数据
v = 0
# 解密操作
for i in range(4):
inp2[i] = (inp2[i] - i) & 0xFF # 减去索引并保持字节范围
inp2[i] ^= key[i % 8] & 0xFF # 按位异或
# 合并 inp2 的四个字节到一个整数 v
v=(inp2[0] << 24) + (inp2[1] << 16) + (inp2[2] << 8) +inp2[3]
# 逆向位移操作
v1 = (v << 7 ) | (v >> 25)
# 拆分 v1 为四个字节,存入 inp1
for i in range(4):
inp1[i] = (v1 >> (8 * (3-i))) & 0xFF
# 打印结果
for i in range(4):
print(hex(inp1[i]), end=',')
print()
return inp1
def xtea(enc1, enc2):
key = [0x7b777c63, 0xc56f6bf2, 0x2b670130, 0x76abd7fe]
sum1 = 0x6A726A6F # 1784642223
sum2 = 0x490C49D2 # 1225603486
delta = 0x21524111
v24 = 64
while v24 > 0:
sum2 = (sum2 + delta) & 0xFFFFFFFF
sum1 = (sum1 + delta) & 0xFFFFFFFF
idx2 = ((sum2 >> 13)& 0xFFFFFFFF & 3)& 0xFFFFFFFF
idx1 = sum1 & 3
enc2 = (enc2 - (((sum2 + key[idx2]) & 0xFFFFFFFF) ^
(((enc1 << 8) & 0xFFFFFFFF ^ (enc1 >> 7)& 0xFFFFFFFF)& 0xFFFFFFFF - enc1) & 0xFFFFFFFF)) & 0xFFFFFFFF
enc1 = (enc1 - (((sum1 - key[idx1]) & 0xFFFFFFFF) ^
(((enc2 << 7) & 0xFFFFFFFF ^ (enc2 >> 8)& 0xFFFFFFFF) + enc2) & 0xFFFFFFFF)) & 0xFFFFFFFF
v24 -= 1
return enc1, enc2
# inp[0] = enc1
# inp[1] = enc2
from ctypes import *
from struct import pack, unpack
# Encryption function for XTEA
def encrypt(v, key):
v0, v1 = c_uint32(v[0]), c_uint32(v[1])
delta = 0x21524111
total1 = c_uint32(0xBEEFBEEF)
total2 = c_uint32(0x9D9D7DDE)
for i in range(64):
v0.value += (((v1.value << 7) ^ (v1.value >> 8)) + v1.value) ^ (total1.value - key[total1.value & 3])
total1.value -= delta
v1.value += (((v0.value << 8) ^ (v0.value >> 7)) - v0.value) ^ (total2.value + key[(total2.value >> 13) & 3])
total2.value -= delta
return v0.value, v1.value
# Decryption function for XTEA
def decrypt(v, key):
v0, v1 = c_uint32(v[0]), c_uint32(v[1])
delta = 0x21524111
total1 = c_uint32(0xBEEFBEEF - 64 * delta)
total2 = c_uint32(0x9D9D7DDE - 64 * delta)
for i in range(64):
total2.value += delta
v1.value -= (((v0.value << 8) ^ (v0.value >> 7)) - v0.value) ^ (total2.value + key[(total2.value >> 13) & 3])
total1.value += delta
v0.value -= (((v1.value << 7) ^ (v1.value >> 8)) + v1.value) ^ (total1.value - key[total1.value & 3])
return v0.value, v1.value
# XTEA encryption wrapper
def enxtea(inp: bytes, key: bytes):
k = unpack("<4I", key)
inp_len = len(inp) // 4
value = unpack(f"<{inp_len}I", inp)
res = b""
for i in range(0, inp_len, 2):
v = [value[i], value[i + 1]]
x = encrypt(v, k)
res += pack("<2I", *x)
return res
# XTEA decryption wrapper
def dextea(inp: bytes, key: bytes):
k = unpack("<4I", key)
inp_len = len(inp) // 4
value = unpack(f"<{inp_len}I", inp)
res = b""
for i in range(0, inp_len, 2):
v = [value[i], value[i + 1]]
x = decrypt(v, k)
res += pack("<2I", *x)
return res
# XTEA encryption example
def xtea_enc(low_4val=0x3e90dddf, high_4val=0x23f6a9b5):
# Prepare input and key
inp = pack("<I", low_4val) + pack("<I", high_4val)
key = [2071428195, 3312413682, 728170800, 1990973438]
key = pack("<I", key[0]) + pack("<I", key[1]) + pack("<I", key[2]) + pack("<I", key[3])
# Encrypt
ret = enxtea(inp, key)
xtea_enc1 = unpack("<I", ret[0:4])[0]
xtea_enc2 = unpack("<I", ret[4:8])[0]
return xtea_enc1, xtea_enc2
# XTEA decryption example
def xtea_dec(xtea_enc1=0x7dd89904, xtea_enc2=0xc6ff408e):
# Prepare input and key
key = [2071428195, 3312413682, 728170800, 1990973438]
key = pack("<I", key[0]) + pack("<I", key[1]) + pack("<I", key[2]) + pack("<I", key[3])
# Decrypt
inp = pack("<I", xtea_enc1) + pack("<I", xtea_enc2)
ret = dextea(inp, key)
xtea_dec1 = unpack("<I", ret[0:4])[0]
xtea_dec2 = unpack("<I", ret[4:8])[0]
return xtea_dec1, xtea_dec2
def all_encode(inp):
#libsec2023的加密
enc1_part1,enc1_part2=sec2023enc(inp)
#enc转换为数组
enc1_part1_list=[(enc1_part1 >> 0x18) & 0xff,(enc1_part1 >> 0x10) & 0xff,
(enc1_part1 >> 8) & 0xff,enc1_part1 & 0xff]
enc1_part2_list=[(enc1_part2 >> 0x18) & 0xff,(enc1_part2 >> 0x10) & 0xff,
(enc1_part2 >> 8) & 0xff,enc1_part2 & 0xff]
#java层的加密
enc2_part1_list=javaenc(enc1_part1_list)
enc2_part2_list=javaenc(enc1_part2_list)
#将数组转换为大数
enc2_part1 = enc2_part1_list[0] + (enc2_part1_list[1] << 8) +(enc2_part1_list[2] << 16) + (enc2_part1_list[3] << 24)
enc2_part2 = enc2_part2_list[0] + (enc2_part2_list[1] << 8) +(enc2_part2_list[2] << 16) + (enc2_part2_list[3] << 24)
#enc2 = enc2_part2 + (enc2_part1 << 32)
#vm虚拟机的加密
enc3_part1,enc3_part2=vmenc(enc2_part1,enc2_part2)
#tea魔改加密
enc4_part1,enc4_part2=xtea(enc3_part1,enc3_part2)
ans = enc4_part1 + (enc4_part2 << 32)
return ans
def all_decode(enc3_part1,enc3_part2=0):
# tea魔改加密
enc4_part1, enc4_part2 = xtea_dec(enc3_part1, enc3_part2)
print("11111")
print(enc4_part1, enc4_part2)
# vm虚拟机的解密
# enc4_part1=3993548948
# enc4_part2=1346781900
enc3 = vmdec(enc4_part1, enc4_part2)
print("2222")
print(enc3)
enc3_part2 = enc3 & 0xffffffff
enc3_part1 = (enc3 >> 32) & 0xffffffff
#将数字转换为数组
enc1_part1_list = [enc3_part1 & 0xff,(enc3_part1 >> 8) & 0xff , (enc3_part1>> 0x10) & 0xff,(enc3_part1 >> 0x18) & 0xff]
enc1_part2_list = [enc3_part2 & 0xff,(enc3_part2 >> 8) & 0xff , (enc3_part2>> 0x10) & 0xff,(enc3_part2 >> 0x18) & 0xff]
#java层解密
enc2_part1_list = javadec(enc1_part1_list)
enc2_part2_list = javadec(enc1_part2_list)
print("3333333")
print(enc2_part1_list)
print(enc2_part2_list)
#将数组转换为大数
enc2_part1 = enc2_part1_list[3] + (enc2_part1_list[2] << 8) + (enc2_part1_list[1] << 16) + (
enc2_part1_list[0] << 24)
enc2_part2 = enc2_part2_list[3] + (enc2_part2_list[2] << 8) + (enc2_part2_list[1] << 16) + (
enc2_part2_list[0] << 24)
#libsec2023的解密
ans=sec2023dec(enc2_part1,enc2_part2)
return ans
x=all_decode(93523648)
print("x=",x)

浙公网安备 33010602011771号