腾讯游戏安全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)

image-20241223103723522

将这两个段的代码重新删除原来的定义同时重新生成函数

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

image-20241223104635871

3、il2cpp.so分析:

外挂编写--flag获取:

从dump.cs中搜索coin可以看到一些函数:

image-20241223130445868

通过偏移找一下函数

image-20241225161201041

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

image-20241225161323578

代码如下:

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一下看看什么功能

image-20241225163208986

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

image-20241225170110283

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

image-20241225170532509

但是得不到有用的东西,据说是动态回填的,只有动态执行到这个的时候才会出现代码

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,这个是比较容易逆向的,就是指令混淆罢了,下面再细说

image-20241225165812255

hookOO0类代码

再hook完这个之后,我去找了一下ooo这个混淆类

找一下这个混淆类,我可以搜到一些东西

image-20241225165322416

看这个函数三个参数,像不像输入,key,输出

进去看一下

image-20241225165511842

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

image-20241225165545333

原本我接下来的操作是通过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分析:

接下来就是逆向这样的代码,其实就是一些指令混淆,逆向起来还是比较好玩的

image-20241225171259866

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

image-20241225171338068

先来分析一下他的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逆向分析:

我们跳转过去看一下是什么加密

image-20241225173057124

看看上面的函数里面是什么

这个是我手动改的

image-20241225173120005

对于encde1,里面全是一个个小片段,根本看不出来逻辑

BR跳转分析:

全是一个个BR跳转,还是老方法分析呗,hook住所有的地址,进行查看

image-20241225173214208

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) {

            }
        })
    }
}

image-20241225173610632

我得到了三个加密,其中一个四字节的,还有一个小函数还是有加密

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分析:

image-20241225174330896

修改一下指针,改一个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分析:

image-20241225174837940

因为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)

posted @ 2025-08-06 20:21  xiaowaaa  阅读(40)  评论(0)    收藏  举报