2023 强网杯逆向 Writeup

发布时间 2023-12-19 21:48:29作者: gaoyucan

总体感觉,难度还好,就是题量太大了。做不完根本做不完。第一天上午没起床,最后一天下午看队伍 60 多名了而且自己也累了就摆烂了,最后是出了 4 个逆向,这两天又做了剩下的三个中的两个,sol那个看见合约俩字,感觉不是我能做的,也懒得看了。反正,后面的题也不难,但是解的人少,所以分贼高,感觉做前面的好亏。

dotdot

AAA 为 白盒 AES,把代码复制出来,随机修改第九轮的输入,改动的部分如下:

public static void AAA(byte[] aaa, byte[] bbb, int randIndx, byte randByte)
{
    for (int index1 = 0; index1 < 9; ++index1)
    {
        if (index1 == 8) {
            if (randIndx != -1)
                aaa[randIndx] = randByte;
        }
        Program.GGG(aaa);
        for (int index2 = 0; index2 < 4; ++index2)
        {
            uint num1 = Program.v11[index1, 4 * index2, (int)aaa[4 * index2]];
            uint num2 = Program.v11[index1, 4 * index2 + 1, (int)aaa[4 * index2 + 1]];
            uint num3 = Program.v11[index1, 4 * index2 + 2, (int)aaa[4 * index2 + 2]];
            uint num4 = Program.v11[index1, 4 * index2 + 3, (int)aaa[4 * index2 + 3]];
            uint index3 = (uint)Program.v12[index1, 24 * index2, (int)(num1 >> 28) & 15, (int)(num2 >> 28) & 15];
            uint index4 = (uint)Program.v12[index1, 24 * index2 + 1, (int)(num3 >> 28) & 15, (int)(num4 >> 28) & 15];
            uint index5 = (uint)Program.v12[index1, 24 * index2 + 2, (int)(num1 >> 24) & 15, (int)(num2 >> 24) & 15];
            uint index6 = (uint)Program.v12[index1, 24 * index2 + 3, (int)(num3 >> 24) & 15, (int)(num4 >> 24) & 15];
            aaa[4 * index2] = (byte)((uint)Program.v12[index1, 24 * index2 + 4, (int)index3, (int)index4] << 4 | (uint)Program.v12[index1, 24 * index2 + 5, (int)index5, (int)index6]);
            uint index7 = (uint)Program.v12[index1, 24 * index2 + 6, (int)(num1 >> 20) & 15, (int)(num2 >> 20) & 15];
            uint index8 = (uint)Program.v12[index1, 24 * index2 + 7, (int)(num3 >> 20) & 15, (int)(num4 >> 20) & 15];
            uint index9 = (uint)Program.v12[index1, 24 * index2 + 8, (int)(num1 >> 16) & 15, (int)(num2 >> 16) & 15];
            uint index10 = (uint)Program.v12[index1, 24 * index2 + 9, (int)(num3 >> 16) & 15, (int)(num4 >> 16) & 15];
            aaa[4 * index2 + 1] = (byte)((uint)Program.v12[index1, 24 * index2 + 10, (int)index7, (int)index8] << 4 | (uint)Program.v12[index1, 24 * index2 + 11, (int)index9, (int)index10]);
            uint index11 = (uint)Program.v12[index1, 24 * index2 + 12, (int)(num1 >> 12) & 15, (int)(num2 >> 12) & 15];
            uint index12 = (uint)Program.v12[index1, 24 * index2 + 13, (int)(num3 >> 12) & 15, (int)(num4 >> 12) & 15];
            uint index13 = (uint)Program.v12[index1, 24 * index2 + 14, (int)(num1 >> 8) & 15, (int)(num2 >> 8) & 15];
            uint index14 = (uint)Program.v12[index1, 24 * index2 + 15, (int)(num3 >> 8) & 15, (int)(num4 >> 8) & 15];
            aaa[4 * index2 + 2] = (byte)((uint)Program.v12[index1, 24 * index2 + 16, (int)index11, (int)index12] << 4 | (uint)Program.v12[index1, 24 * index2 + 17, (int)index13, (int)index14]);
            uint index15 = (uint)Program.v12[index1, 24 * index2 + 18, (int)(num1 >> 4) & 15, (int)(num2 >> 4) & 15];
            uint index16 = (uint)Program.v12[index1, 24 * index2 + 19, (int)(num3 >> 4) & 15, (int)(num4 >> 4) & 15];
            uint index17 = (uint)Program.v12[index1, 24 * index2 + 20, (int)num1 & 15, (int)num2 & 15];
            uint index18 = (uint)Program.v12[index1, 24 * index2 + 21, (int)num3 & 15, (int)num4 & 15];
            aaa[4 * index2 + 3] = (byte)((uint)Program.v12[index1, 24 * index2 + 22, (int)index15, (int)index16] << 4 | (uint)Program.v12[index1, 24 * index2 + 23, (int)index17, (int)index18]);
            uint num5 = Program.v13[index1, 4 * index2, (int)aaa[4 * index2]];
            uint num6 = Program.v13[index1, 4 * index2 + 1, (int)aaa[4 * index2 + 1]];
            uint num7 = Program.v13[index1, 4 * index2 + 2, (int)aaa[4 * index2 + 2]];
            uint num8 = Program.v13[index1, 4 * index2 + 3, (int)aaa[4 * index2 + 3]];
            uint index19 = (uint)Program.v12[index1, 24 * index2, (int)(num5 >> 28) & 15, (int)(num6 >> 28) & 15];
            uint index20 = (uint)Program.v12[index1, 24 * index2 + 1, (int)(num7 >> 28) & 15, (int)(num8 >> 28) & 15];
            uint index21 = (uint)Program.v12[index1, 24 * index2 + 2, (int)(num5 >> 24) & 15, (int)(num6 >> 24) & 15];
            uint index22 = (uint)Program.v12[index1, 24 * index2 + 3, (int)(num7 >> 24) & 15, (int)(num8 >> 24) & 15];
            aaa[4 * index2] = (byte)((uint)Program.v12[index1, 24 * index2 + 4, (int)index19, (int)index20] << 4 | (uint)Program.v12[index1, 24 * index2 + 5, (int)index21, (int)index22]);
            uint index23 = (uint)Program.v12[index1, 24 * index2 + 6, (int)(num5 >> 20) & 15, (int)(num6 >> 20) & 15];
            uint index24 = (uint)Program.v12[index1, 24 * index2 + 7, (int)(num7 >> 20) & 15, (int)(num8 >> 20) & 15];
            uint index25 = (uint)Program.v12[index1, 24 * index2 + 8, (int)(num5 >> 16) & 15, (int)(num6 >> 16) & 15];
            uint index26 = (uint)Program.v12[index1, 24 * index2 + 9, (int)(num7 >> 16) & 15, (int)(num8 >> 16) & 15];
            aaa[4 * index2 + 1] = (byte)((uint)Program.v12[index1, 24 * index2 + 10, (int)index23, (int)index24] << 4 | (uint)Program.v12[index1, 24 * index2 + 11, (int)index25, (int)index26]);
            uint index27 = (uint)Program.v12[index1, 24 * index2 + 12, (int)(num5 >> 12) & 15, (int)(num6 >> 12) & 15];
            uint index28 = (uint)Program.v12[index1, 24 * index2 + 13, (int)(num7 >> 12) & 15, (int)(num8 >> 12) & 15];
            uint index29 = (uint)Program.v12[index1, 24 * index2 + 14, (int)(num5 >> 8) & 15, (int)(num6 >> 8) & 15];
            uint index30 = (uint)Program.v12[index1, 24 * index2 + 15, (int)(num7 >> 8) & 15, (int)(num8 >> 8) & 15];
            aaa[4 * index2 + 2] = (byte)((uint)Program.v12[index1, 24 * index2 + 16, (int)index27, (int)index28] << 4 | (uint)Program.v12[index1, 24 * index2 + 17, (int)index29, (int)index30]);
            uint index31 = (uint)Program.v12[index1, 24 * index2 + 18, (int)(num5 >> 4) & 15, (int)(num6 >> 4) & 15];
            uint index32 = (uint)Program.v12[index1, 24 * index2 + 19, (int)(num7 >> 4) & 15, (int)(num8 >> 4) & 15];
            uint index33 = (uint)Program.v12[index1, 24 * index2 + 20, (int)num5 & 15, (int)num6 & 15];
            uint index34 = (uint)Program.v12[index1, 24 * index2 + 21, (int)num7 & 15, (int)num8 & 15];
            aaa[4 * index2 + 3] = (byte)((uint)Program.v12[index1, 24 * index2 + 22, (int)index31, (int)index32] << 4 | (uint)Program.v12[index1, 24 * index2 + 23, (int)index33, (int)index34]);
        }
    }
    Program.GGG(aaa);
    for (int index = 0; index < 16; ++index)
        aaa[index] = Program.v14[9, index, (int)aaa[index]];
    for (int index = 0; index < 16; ++index)
        bbb[index] = aaa[index];
}

private static void Main(string[] args)
{
    HashSet<String> hashset = new HashSet<String>();
    Random rand = new Random();
    for (int i = 0; i < 32; i++)
    {
        byte[] aaa = new byte[16];
        byte[] numArray2 = new byte[16];
        Array.Clear((Array)aaa, 0, 16);
        if (i == 0)
        {
            Program.AAA(aaa, numArray2, -1, (byte)rand.Next(0, 256));

        }
        else
        {
            Program.AAA(aaa, numArray2, rand.Next(0, 16), (byte)rand.Next(0, 256));

        }
        String a = BitConverter.ToString(numArray2).Replace("-", "");
        Console.WriteLine(a);
    }

}

求解最后一轮的密钥

import phoenixAES

data = """
EB95719A7C696EAABE11F4D47360E913
7895719A7C696E2ABE110AD473E8E913
EB955C9A7C686EAA1C11F4D47360E991
EB957A9A7CEA6EAAC611F4D47360E9F2
C995719A7C696E51BE117CD47330E913
EB95E39A7C5E6EAAC511F4D47360E961
EB95A69A7C476EAA6911F4D47360E928
EB95E79A7C5E6EAAAD11F4D47360E975
EB9571777C6946AABE59F4D4A960E913
EBAF719AA8696EAABE11F4EB73603313
EB95869A7C816EAA6411F4D47360E99A
EB9571BF7C698DAABEEDF4D43860E913
EB9571EA7C69CAAABE3DF4D4A260E913
EB7A719AC9696EAABE11F4257360BE13
EB44719ADE696EAABE11F4C07360D113
8595719A7C696E30BE1114D473BBE913
EB9571957C6972AABE97F4D4F660E913
F995719A7C696E0FBE11DED47374E913
EB95B69A7CED6EAACA11F4D47360E922
6A95719A7C696EC7BE119DD47338E913
EB05719A06696EAABE11F47273609513
EB95711F7C698EAABEF4F4D47260E913
EE95719A7C696EFBBE11A6D473A1E913
EBD1719ACE696EAABE11F45B73604313
EB95715A7C69A4AABEBFF4D45E60E913
EBBD719A85696EAABE11F4E27360DB13
EB95717F7C6924AABE4DF4D4B760E913
EB957E9A7CB06EAAB211F4D47360E9AD
EB9571217C69B9AABE22F4D4D760E913
9095719A7C696E71BE1126D47345E913
5995719A7C696E64BE115DD473DBE913
EB95718E7C6933AABEE9F4D44060E913
"""

with open('crackfile', 'w') as fp:
    fp.write(data)

phoenixAES.crack_file('crackfile', [], True, False, verbose=2)

得到最后一轮密钥为 EA9F6BE2DF5C358495648BEAB9FCFF81

img

密钥为:51574232303233486170707947616D65,解个 AES 得到预期输入为 WelcomeToQWB2023

from Crypto.Cipher import AES,ARC4

aes = AES.new(key=bytes.fromhex("51574232303233486170707947616D65"), mode=AES.MODE_ECB)

e = [97, 147, 49, 123, 248, 150, 224, 0, 165, 39, 183, 55, 74, 227, 3, 168]
m = aes.decrypt(bytes(e))
print(m)
rc4 = ARC4.new(key=m)
with open('License.dat', 'rb') as dat:
    aaa = rc4.decrypt(dat.read())
    with open('dat', 'wb') as dd:
        dd.write(aaa)

输入进去发现反序列化报错,结合报错信息

   在 System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
   在 System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, IMethodCallMessage methodCallMessage)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler)
   在 System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)
   在 SortedListTest.Program.Main(String[] args)

img

把调试发现是 0x28e 处开始连续两个 str 内容被抹去了,看填充的 00 的长度可以看出第一个的长度为 21 第二个的长度为 16,长度结合后面还有 FFF 函数的签名信息,可以判断两个就是 FFF 的两个参数。第二个已知求一下第一个,

import struct
import ctypes

def tea_decrypt(e_arr, key):
    e0, e1 = ctypes.c_uint32(e_arr[0]), ctypes.c_uint32(e_arr[1])
    delta = 3735928559
    sum = ctypes.c_uint32(delta * 32)
    for i in range(32):
        e1.value -= ((e0.value << 4) + key[2]) ^ (e0.value + sum.value) ^ ((e0.value >> 5) + key[3])
        e0.value -= ((e1.value << 4) + key[0]) ^ (e1.value + sum.value) ^ ((e1.value >> 5) + key[1])
        sum.value -= delta
    return (e0.value, e1.value)

TEA = bytes(
    [69, 182, 171, 33, 121, 107, 254, 150, 92, 29, 4, 178, 138, 166, 184, 106, 53, 241, 42, 191, 23, 211, 3, 107]
)
t1 = b""
key = struct.unpack("<4I", bytes.fromhex("57656c636f6d65546f51574232303233"))
for i in range(3):
    e_arr = struct.unpack("<2I", TEA[i * 8 : i * 8 + 8])
    a, b = tea_decrypt(e_arr, key)
    t1 += struct.pack("<2I", a, b)
print(t1)

按照长按照长度、内容回填并加密回去,跑一下就出了:flag{d0tN3t_I5_Ea57_2_y09!G00d_Luck}

img

ezre

.init_array fork 出来一个子进程,用 ptrace 调试主进程的假的 check 函数。

img

结合加密算法识别插件结果,发现就一个SM4

img

img

import struct
from sm4 import SM4Key

e_arr = [0x7C88631647197506, 0x4A0D7D3FFF55668B, 0xDEC2E93F384ED2F5, 0x3C1FB1746F7F7CDB]
key = [0xEFCDAB8967452301, 0xEFCDAB8967452301]
e = struct.pack('<4Q', e_arr[0], e_arr[1], e_arr[2], e_arr[3])
key = struct.pack('<2Q', key[0], key[1])

d = SM4Key(key).decrypt(e)
# cipher.set_key(key, mode=SM4_DECRYPT)
# d = cipher.crypt_cbc(e)
print(d)

uname

IDA 反编译结果一坨答辩,Ghidra 就很好

img

直接对着解密就行

from ctypes import *
import struct

flag_arr = [0x6835B4293DD0D39E, 0xE69C68D3BC875A19, 0x1B69DAF30AE1351F, 0xACA0DA795EF62809]
flag_arr = [c_uint64(_) for _ in flag_arr]

flag_arr[0].value -= 0x5474374041455247
flag_arr[1].value -= 0x823ECE10EBF188BE
flag_arr[2].value -= 0xBAD39663B0B3ADD3
flag_arr[3].value -= 0x6523745F644E5642

shift_arr = [0x0E, 0x10, 0x34, 0x39, 0x17, 0x28, 0x05, 0x25, 0x19, 0x21, 0x2E, 0x0C, 0x3A, 0x16, 0x20, 0x20]

add_arr = [0x465F5530595F4E6F, 0xB378E3C5C3A47B89, 0xD3A49492B08792C3, 0x5474374041455247, 0x6523745F644E5630, 0xD3A49492B08792C3, 0x8E9565954947CC84, 0x33E95EAA8C9B6366, 0x5F30535F5933335F, 0x8E9565954947CC84, 0x823ECE10EBF188BE, 0x465F5530595F4E71, 0x5474374041455247, 0x823ECE10EBF188BE, 0xBAD39663B0B3ADD3, 0x6523745F644E5633, 0x33E95EAA8C9B6365, 0xBAD39663B0B3ADD3, 0x9F44A2B46C50D06D, 0x5F30535F59333363, 0x465F5530595F4E6F, 0x9F44A2B46C50D06D, 0xAD85C2C5B88958B8, 0x547437404145524C, 0x6523745F644E5630, 0xAD85C2C5B88958B8, 0xC8E878739899B1AB, 0x33E95EAA8C9B636B, 0x5F30535F5933335F, 0xC8E878739899B1AB, 0x6E0A8CFF949DDDA2, 0x465F5530595F4E76, 0x5474374041455247, 0x6E0A8CFF949DDDA2, 0x94B4C496B8B573C8, 0x6523745F644E5638, 0x33E95EAA8C9B6365, 0x94B4C496B8B573C8, 0xD997B592BBA2B594, 0x5F30535F59333368, 0x465F5530595F4E6F, 0xD997B592BBA2B594, 0x995181B46135AD9C, 0x5474374041455251, 0x6523745F644E5630, 0x995181B46135AD9C, 0xA2C9A6A6A09B77A0, 0x33E95EAA8C9B6370, 0x5F30535F5933335F, 0xA2C9A6A6A09B77A0, 0xA85D9FDDE3EFC2C9, 0x465F5530595F4E7B, 0x5474374041455247, 0xA85D9FDDE3EFC2C9, 0x808083856161C8AC, 0x6523745F644E563D, 0x33E95EAA8C9B6365, 0x808083856161C8AC, 0xB378E3C5C3A47B89, 0x5F30535F5933336D, 0x465F5530595F4E6F, 0xB378E3C5C3A47B89, 0xD3A49492B08792C3, 0x5474374041455256, 0x6523745F644E5630, 0xD3A49492B08792C3, 0x8E9565954947CC84, 0x33E95EAA8C9B6375, 0x5F30535F5933335F, 0x8E9565954947CC84, 0x823ECE10EBF188BE, 0x465F5530595F4E80]

def rol64(a, b):
    a &= 0xFFFFFFFFFFFFFFFF
    k0 = (a << b) & 0xFFFFFFFFFFFFFFFF
    k1 = (a >> (64 - b)) & 0xFFFFFFFFFFFFFFFF
    return k0 | k1

i = 71
while i > -1:
    while i & 3 != 0:
        temp_idx = (i & 7) * 2
        t3 = rol64(flag_arr[1].value ^ flag_arr[2].value, (-shift_arr[temp_idx + 1]) & 0x3F)
        t2 = flag_arr[2].value - t3
        t2 &= 0xFFFFFFFFFFFFFFFF
        t1 = rol64(flag_arr[3].value ^ flag_arr[0].value, (-shift_arr[temp_idx]) & 0x3F)
        t0 = flag_arr[0].value - t1
        t0 &= 0xFFFFFFFFFFFFFFFF
        flag_arr[0].value = t0
        flag_arr[1].value = t1
        flag_arr[2].value = t2
        flag_arr[3].value = t3
        i -= 1
    temp_idx = (i & 7) * 2
    t3 = rol64(flag_arr[1].value ^ flag_arr[2].value, (-shift_arr[temp_idx + 1]) & 0x3F)
    t1 = rol64(flag_arr[3].value ^ flag_arr[0].value, (-shift_arr[temp_idx]) & 0x3F)
    t2 = flag_arr[2].value - t3 - add_arr[(i & 0xFC) + 2]
    t2 &= 0xFFFFFFFFFFFFFFFF
    t0 = flag_arr[0].value - t1 - add_arr[(i & 0xFC)]
    t1 -= add_arr[(i & 0xFC) + 1]
    t1 &= 0xFFFFFFFFFFFFFFFF
    t3 -= add_arr[(i & 0xFC) + 3]
    t3 &= 0xFFFFFFFFFFFFFFFF
    flag_arr[0].value = t0
    flag_arr[1].value = t1
    flag_arr[2].value = t2
    flag_arr[3].value = t3
    i -= 1

flag = struct.pack("<4Q", flag_arr[0].value, flag_arr[1].value, flag_arr[2].value, flag_arr[3].value)
print(flag)

xrtFuze

vm 里套 flat,出题人没活可以咬个打火机。

反编译器,

from xrtFuze_data import *
import struct
import ctypes

class Dis(object):
    def __init__(self, filename) -> None:
        with open(filename, "rb") as f:
            self.code = f.read()
        self.inner_func = {0x26: "read_flag", 0x29: "puts", 0x1A8: "str_hashcode"}

    def get_addr_by_id(self, func_id):
        if func_id not in func_id_arr:
            return func_id
        return func_addr_arr[func_id_arr.index(func_id)]

    def get_func_name(self, func_addr):
        if func_addr in self.inner_func.keys():
            return self.inner_func[func_addr]
        return f"fun_{func_addr:08x}"

    def dis_func(self, func_addr, j=0):
        locals_num = self.read_i16(func_addr) & 0xffff
        params_num = self.read_i16(func_addr + 2) & 0xffff
        for i in range(params_num):
            self.log_asm(func_addr, f"local_{locals_num - params_num + i} = arg_{i}")

        ins_addr = func_addr + 16
        while True:
            opcode = self.read_i8(ins_addr + j) & 0xff
            match opcode:
                case 5 | 0xD2:
                    t = self.read_i8(ins_addr + j + 1)
                    op_dst = t & 0xF
                    op_src = (t >> 4) & 0xF
                    if opcode == 5:
                        self.log_asm(ins_addr + j, f"local_{op_dst}= local_{op_src}")
                    else:
                        self.log_asm(ins_addr + j, f"local_{op_dst} = *local_{op_src}")
                    j += 2
                case 0xB | 0x25 | 0x3C | 0x49 | 0x90 | 0x95 | 0x9F | 0xDC:
                    op1 = self.read_i8(ins_addr + j + 1)
                    op2 = self.read_i8(ins_addr + j + 2)
                    op3 = self.read_i8(ins_addr + j + 3)
                    match opcode:
                        case 0x25:
                            self.log_asm(ins_addr + j, f"(u32_arr)local_{op2}[local_{op3}] = local_{op1}")
                        case 0x3c:
                            self.log_asm(ins_addr + j, f"local_{op1} = (u32_arr)local_{op2}[local_{op3}]")
                        case 0x90:
                            self.log_asm(ins_addr + j, f"local_{op1} = (u8_arr)local_{op2}[local_{op3}]")
                        case 0x49:
                            self.log_asm(ins_addr + j, f"local_{op1} = (8_arr)local_{op2}[local_{op3}]")
                        case 0xb:
                            self.log_asm(ins_addr + j, f"(u8_arr)local_{op2}[local_{op3}] = local_{op1}")
                        case 0x95:
                            self.log_asm(ins_addr + j, f"local_{op1} = locals[op2] % local_{op3}")
                        case 0x9F:
                            self.log_asm(ins_addr + j, f"local_{op1} = local_{op2} ^ local_{op3}")
                        case 0xDC:
                            self.log_asm(ins_addr + j, f"local_{op1}= local_{op2} + local_{op3}")
                    j += 4
                case 0x10:
                    op1 = self.read_i8(ins_addr + j + 1)
                    op2 = self.read_i32(ins_addr + j + 2)
                    len = self.read_i32(ins_addr + j + 2 * op2 + 4)
                    self.log_asm(
                        ins_addr + j, f"local_{op1} = load_arr(0x{op2:08x}, {len}) # type unk decided by local_{op1}"
                    )
                    j += 6
                case 0x17:
                    op1 = self.read_i8(ins_addr + j + 1)
                    op2 = self.read_i8(ins_addr + j + 2)
                    op3 = self.read_i8(ins_addr + j + 3)
                    self.log_asm(ins_addr + j, f"local_{op1} = local_{op2} * local_{op3}")
                    j += 4
                case 0x1E | 0xD0 | 0xD3:
                    op1 = self.read_i8(ins_addr + j + 1)
                    op2 = self.read_i16(ins_addr + j + 2)
                    if 0xD3 == opcode:
                        self.log_asm(ins_addr + j, f"if (!local_{op1}) goto:{2 * op2: 08x}")
                    elif 0x1E == opcode:
                        self.log_asm(ins_addr + j, f"if (local_{op1}) goto:{2 * op2: 08x}")
                    else:
                        self.log_asm(ins_addr + j, f"if (local_{op1} >= 0) goto:{2 * op2: 08x}")
                    j += 4
                case 0x21:
                    op1 = self.read_i8(ins_addr + j + 1)
                    op2 = self.read_i32(ins_addr + j + 2)
                    addr = self.get_addr_by_id(op2)
                    self.log_asm(ins_addr + j, f"local_{op1} = 0x{addr:08x}")
                    j += 6
                case 0x24 | 0x57 | 0x6C | 0xF0:
                    size = self.read_i8(ins_addr + j + 1) >> 4
                    op1 = self.read_i8(ins_addr + j + 1) & 0xF
                    op2 = self.read_i8(ins_addr + j + 4) >> 4
                    op3 = self.read_i8(ins_addr + j + 4) & 0xF
                    op4 = self.read_i8(ins_addr + j + 5) >> 4
                    op5 = self.read_i8(ins_addr + j + 5) & 0xF
                    op6 = self.read_i16(ins_addr + j + 2)
                    match opcode:
                        case 0x6C | 0x57:
                            func_addr = self.get_addr_by_id(op6)
                            match size:
                                case 5:
                                    self.log_asm(
                                        ins_addr + j,
                                        f"{self.get_func_name(func_addr)}(local_{op3},local_{op2},local_{op5},local_{op4},local_{op1})",
                                    )
                                case 4:
                                    self.log_asm(
                                        ins_addr + j,
                                        f"{self.get_func_name(func_addr)}(local_{op3},local_{op2},local_{op5},local_{op4})",
                                    )
                                case 3:
                                    self.log_asm(
                                        ins_addr + j,
                                        f"{self.get_func_name(func_addr)}(local_{op3},local_{op2},local_{op5})",
                                    )
                                case 2:
                                    self.log_asm(
                                        ins_addr + j, f"{self.get_func_name(func_addr)}(local_{op3},local_{op2})"
                                    )
                                case 1:
                                    self.log_asm(ins_addr + j, f"{self.get_func_name(func_addr)}(local_{op3})")
                                case 0:
                                    self.log_asm(ins_addr + j, f"{self.get_func_name(func_addr)}()")
                        case 0xF0:
                            _op0 = self.read_i8(ins_addr + j + 1)
                            _op1 = self.read_i8(ins_addr + j + 4)
                            func_addr = self.get_addr_by_id(op6)
                            match _op0:
                                case 7:
                                    self.log_asm(
                                        ins_addr + j,
                                        f"{self.get_func_name(func_addr)}(local_{_op1},local_{_op1 + 1},local_{_op1 + 2},local_{_op1 + 3},local_{_op1 + 4},local_{_op1 + 5},local_{_op1 + 6})",
                                    )
                                case 6:
                                    self.log_asm(
                                        ins_addr + j,
                                        f"{self.get_func_name(func_addr)}(local_{_op1},local_{_op1 + 1},local_{_op1 + 2},local_{_op1 + 3},local_{_op1 + 4},local_{_op1 + 5})",
                                    )
                                case 5:
                                    self.log_asm(
                                        ins_addr + j,
                                        f"{self.get_func_name(func_addr)}(local_{_op1},local_{_op1 + 1},local_{_op1 + 2},local_{_op1 + 3},local_{_op1 + 4})",
                                    )
                                case 4:
                                    self.log_asm(
                                        ins_addr + j,
                                        f"{self.get_func_name(func_addr)}(local_{_op1},local_{_op1 + 1},local_{_op1 + 2},local_{_op1 + 3})",
                                    )
                                case 3:
                                    self.log_asm(
                                        ins_addr + j,
                                        f"{self.get_func_name(func_addr)}(local_{_op1},local_{_op1 + 1},local_{_op1 + 2})",
                                    )
                                case 2:
                                    self.log_asm(
                                        ins_addr + j, f"{self.get_func_name(func_addr)}(local_{_op1},local_{_op1 + 1})"
                                    )
                                case 1:
                                    self.log_asm(ins_addr + j, f"{self.get_func_name(func_addr)}(local_{_op1})")
                                case 0:
                                    self.log_asm(ins_addr + j, f"{self.get_func_name(func_addr)}()")
                        case 0x24:
                            self.log_asm(
                                ins_addr + j,
                                f"retval = [local_{op3}, local_{op2}, local_{op5}, local_{op4}, local_{op1}][0:{size}]",
                            )
                    j += 6
                case 0x2E:
                    if opcode == 0x2E:
                        self.log_asm(
                            ins_addr + j,
                            f"local_{self.read_i8(ins_addr + j + 1)} = local_{self.read_i8(ins_addr + j + 2)}",
                        )
                    j += 4
                case 0x32 | 0x3B | 0x54 | 0x64 | 0xA5:
                    op1 = self.read_i8(ins_addr + j + 1)
                    op2 = self.read_i8(ins_addr + j + 2)
                    op3 = self.read_i8(ins_addr + j + 3)
                    match opcode:
                        case 0x32:
                            self.log_asm(ins_addr + j, f"local_{op1} = local_{op2} + {op3}")
                        case 0x3B:
                            self.log_asm(ins_addr + j, f"local_{op1} = (int)local_{op2} >> {op3}")
                        case 0xA5:
                            self.log_asm(ins_addr + j, f"local_{op1} = local_{op2} << {op3}")
                        case 0x65:
                            self.log_asm(ins_addr + j, f"local_{op1} = {op3} * local_{op2}")
                        case 0x54:
                            self.log_asm(ins_addr + j, f"local_{op1} = {op3} ^ local_{op2}")
                    j += 4
                case 0x3A | 0xDB:
                    self.log_asm(ins_addr + j, f"local_{self.read_i8(ins_addr + j + 1)} = retval")
                    j += 2
                case 0x41:
                    op1 = self.read_i8(ins_addr + j + 1) & 0xF
                    self.log_asm(ins_addr + j, f"local_{op1} -= local_[{self.read_i8(ins_addr + j + 1) >> 4}]")
                    j += 2
                case 0x42 | 0x8E:
                    op1 = self.read_i8(ins_addr + j + 1)
                    op2 = self.read_i32(ins_addr + j + 2)
                    if opcode == 0x42:
                        self.log_asm(ins_addr + j, f"local_{op1} = {op2}")
                    else:
                        len = self.read_i16(ins_addr + j + 2 + 2 * op2)
                        jmp_tab = dict()
                        for idx in range(len):
                            key = self.read_i32(ins_addr + j + 2 * op2 + 4 + 4 * idx)
                            value = self.read_i32(ins_addr + j + 2 * op2 + 4 + 4 * idx + 4 * len)
                            jmp_tab[key] = hex(ins_addr + j + value)
                        self.log_asm(ins_addr + j, f"goto: tab[local_{op1}] # tab = {jmp_tab}")
                    j += 6
                case 0x44 | 0xA3 | 0xF2:
                    op1 = self.read_i8(ins_addr + j + 1) & 0xF
                    op2 = (self.read_i8(ins_addr + j + 1) >> 4) & 0xF
                    match opcode:
                        case 0xF2:
                            self.log_asm(ins_addr + j, f"local_{op1} %= local_{op2}")
                        case 0x44:
                            self.log_asm(ins_addr + j, f"local_{op1} ^= local_{op2}")
                        case 0xA3:
                            self.log_asm(ins_addr + j, f"local_{op1} |= local_{op2}")
                    j += 2
                case 0x46:
                    op1 = self.read_i8(ins_addr + j + 1)
                    op2 = self.read_i8(ins_addr + j + 2)
                    op3 = self.read_i8(ins_addr + j + 3)
                    self.log_asm(ins_addr + j, f"local_{op1} = local_{op2} / local_{op3}")
                    j += 4

                case 0x4B | 0xBA | 0xD8:
                    op1 = (self.read_i8(ins_addr + j + 1) >> 4) & 0xF
                    op2 = self.read_i8(ins_addr + j + 1) & 0xF
                    op3 = self.read_i16(ins_addr + j + 2)
                    match opcode:
                        case 0x4B:
                            self.log_asm(
                                ins_addr + j, f"if local_{op2} >= local_{op1} goto:{(2 * op3) + j + ins_addr: 08x}"
                            )
                        case 0xD8:
                            self.log_asm(
                                ins_addr + j, f"if local_{op2} != local_{op1} goto:{(2 * op3) + j + ins_addr: 08x}"
                            )
                        case 0xBA:
                            self.log_asm(
                                ins_addr + j, f"if local_{op2} == local_{op1} goto:{(2 * op3) + j + ins_addr: 08x}"
                            )
                    j += 4

                case 0x58:
                    self.log_asm(ins_addr + j, f"return 0")
                    j += 1
                case 0x5A | 0x5D | 0x78 | 0x80:
                    op1 = self.read_i8(ins_addr + j + 1) & 0xF
                    op2 = (self.read_i8(ins_addr + j + 1) >> 4) & 0xF
                    op3 = self.read_i16(ins_addr + j + 2)
                    match opcode:
                        case 90:
                            self.log_asm(ins_addr + j, f"local_{op1} = local_{op2} % (u32){op3}")
                        case 93:
                            self.log_asm(ins_addr + j, f"local_{op1} = local_{op2} + (u32){op3}")
                        case 160:
                            self.log_asm(ins_addr + j, f"local_{op1} = local_{op2} & (u32){op3}")
                        case 120:
                            self.log_asm(ins_addr + j, f"local_{op1} = local_{op2} ^ (u32){op3}")

                    j += 4
                case 0x5E:
                    op1 = self.read_i8(ins_addr + j + 1)
                    op2 = self.read_i16(ins_addr + j + 2)
                    addr = self.get_addr_by_id(op2)
                    self.log_asm(ins_addr + j, f"local_{op1} = 0x{addr:08x}")
                    j += 4
                case 0x6E:
                    op1 = self.read_i8(ins_addr + j + 1)
                    op2 = self.read_i8(ins_addr + j + 2)
                    self.log_asm(ins_addr + j, f"local_{op1} = local_{op2}")
                    j += 4
                case 0x7E:
                    op1 = self.read_i8(ins_addr + j + 1)
                    op2 = self.read_i16(ins_addr + j + 2)
                    self.log_asm(ins_addr + j, f"local_{op1} = (u16){op2}")
                    j += 4
                case 0x8D | 0xAC:
                    op1 = self.read_i8(ins_addr + j + 1) & 0xF
                    op2 = (self.read_i8(ins_addr + j + 1) >> 4) & 0xF
                    if opcode == 0x8D:
                        self.log_asm(ins_addr + j, f"local_{op1} = (u16)local_{op2}")
                    elif opcode == 0xAC:
                        self.log_asm(ins_addr + j, f"local_{op1} = (u8)local_{op2}")
                    j += 2
                case 0x99:
                    op1 = self.read_i8(ins_addr + j + 1)
                    self.log_asm(ins_addr + j, f"goto:{op1 * 2 + j + ins_addr:08x}")
                    j += 2
                case 0xBC:
                    op1 = self.read_i8(ins_addr + j + 1) & 0xF
                    op2 = self.read_i8(ins_addr + j + 1) >> 4
                    self.log_asm(ins_addr + j, f"local_{op1} += local_{op2}")
                    j += 2
                case 0xC1 | 0xFC:
                    op1 = self.read_i8(ins_addr + j + 1) & 0xF
                    self.log_asm(ins_addr + j, f"return local_{op1}")
                    j += 1
                case 0xC7:
                    op1 = self.read_i8(ins_addr + j + 1) & 0xF
                    op2 = (self.read_i8(ins_addr + j + 1) >> 4) & 0xF
                    self.log_asm(ins_addr + j, f"local_{op1} = local_{op2}")
                    j += 2

                case 0xCC:
                    op1 = 2 * (self.read_i16(ins_addr + j + 2) + j)
                    self.log_asm(ins_addr + j, f"goto:{op1 + j + ins_addr: 08x}")
                    j += 4
                case 0xD5:
                    tmp = self.read_i8(ins_addr + j + 1)
                    op1 = tmp & 0xF
                    op2 = (tmp >> 4) & 0xF
                    op3 = self.read_i16(ins_addr + j + 2)
                    con = 4 if 0xB9 == op3 else 1
                    if con == 4:
                        self.log_asm(ins_addr + j, f"local_{op1} = u32_arr(local_{op2})")
                    else:
                        self.log_asm(ins_addr + j, f"local_{op1} = u8_arr(local_{op2})")
                    j += 4
                case 0xF7:
                    op1 = self.read_i8(ins_addr + j + 1) & 0xF
                    op2 = self.read_i8(ins_addr + j + 1) >> 4
                    self.log_asm(ins_addr + j, f"local_{op1} = {op2}")
                    j += 2
                case _:
                    break

    def log_asm(self, addr, ins):
        print(f"0x{addr:08x}  {ins}")

    
    def read_i8(self, offset):
        return ctypes.c_uint8(self.code[offset]).value

    def read_i16(self, offset):
        return struct.unpack("<h", self.code[offset : offset + 2])[0]
    
    def read_i32(self, offset):
        return struct.unpack("<i", self.code[offset : offset + 4])[0]

diser = Dis("input.bin")
diser.dis_func(0x00035445, 0)

第一步 xor:好像是 RC4,不过没细看,直接调试提取的密钥流

img

TEA 加密KEY:

img

TEA 算法实现

img

最后对比的密文

img

import ctypes
import struct

def tea_decrypt(e_arr, key):
    e0, e1 = ctypes.c_int32(e_arr[0]), ctypes.c_int32(e_arr[1])
    delta = 0x57429F48  # 0x57429F48
    sum = ctypes.c_int32(delta * 32)
    for i in range(32):
        e1.value -= ((e0.value << 4) + key[2]) ^ (e0.value + sum.value) ^ ((e0.value >> 5) + key[3])
        e0.value -= ((e1.value << 4) + key[0]) ^ (e1.value + sum.value) ^ ((e1.value >> 5) + key[1])
        sum.value -= delta
    return (e0.value & 0xFFFFFFFF, e1.value & 0xFFFFFFFF)

def tea_encrypt(m_arr, key):
    m0, m1 = ctypes.c_int32(m_arr[0]), ctypes.c_int32(m_arr[1])
    delta = 0x57429F48  # 0x57429F48
    sum = ctypes.c_int32(0)
    for i in range(32):
        sum.value += delta
        m0.value += ((m1.value << 4) + key[0]) ^ (m1.value + sum.value) ^ ((m1.value >> 5) + key[1])
        m1.value += ((m0.value << 4) + key[2]) ^ (m0.value + sum.value) ^ ((m0.value >> 5) + key[3])
    return (m0.value & 0xFFFFFFFF, m1.value & 0xFFFFFFFF)

e = [0xFF & -100, 0xFF & 108, 0xFF & 72, 0xFF & 22, 0xFF & 112, 0xFF & 18, 0xFF & 90, 0xFF & 45, 0xFF & -31, 0xFF & -41, 0xFF & -11, 0xFF & 124, 0xFF & -59, 0xFF & 70, 0xFF & 50, 0xFF & 104]
e = bytes(e)

# e = bytes.fromhex("92A29D26ADF6CB417A798D79CCB4EF8C")

key = [0x23575896, 0x89654528, 0x12582548, 0x45897856]

t1 = b''
for i in range(2):
    e_arr = struct.unpack('>2I', e[i * 8: i * 8 + 8])
    print(hex(e_arr[0]), hex(e_arr[1]))
    a, b = tea_decrypt(e_arr, key)
    print(hex(a), hex(b))
    t1 += struct.pack('>2I', a, b)

enc1_inp = b'0123456789abcdef'
enc1_ret = bytes.fromhex('2B601DC09DFBCA337A798D7919B4EF8C')
flag = []
for i in range(16):
    xor_key = enc1_inp[i] ^ enc1_ret[i]
    flag.append(xor_key ^ t1[i])

print(bytes(flag))

后面是比赛完做的,懒得写思路了,直接贴一下脚本吧

ZZtransfer

智障转移?好端端的一个 x86_64 小端程序,你为啥非要改成 32位大端?还是那句话,出题人没活可以咬个打火机。

先修一下文件头,虽然修完还是跑不了,但是可以分析了。题目就是把 bmp 置乱、加密后压缩了一手,压缩算法可以用 BinaryAI 识别库看出来,直接上代码。

#include <stdio.h>
#include "fastlz.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>

int main() {
    FILE *fd = fopen("C:\\Users\\gaoyucan\\Downloads\\ZZtransfer_fdbc8ca1ba60f847449e3eee8ed38a5e\\rawdata", "rb");
    uint8_t *buff = malloc(0x100000);
    int len = 0;
    while (1) {
        uint16_t l;
        fread(&l, sizeof(uint16_t), 1, fd);
        l = ((l << 8) & 0xffff) | (l >> 8);
        fread(buff + len, 1, l, fd);
        if (!strcmp("ENDDN3", (char *) buff + len)) {
            memset(buff + len, 0, l);
            fclose(fd);
            break;
        }
        len += l;
    }
    uint8_t *bmp_buff = malloc(0x100000);
    fastlz_decompress(buff, len, bmp_buff, 0x100000);
    free(buff);
    uint32_t offset = bmp_buff[10] | (bmp_buff[11] << 8);
    uint32_t w = bmp_buff[18] | (bmp_buff[19] << 8);
    uint32_t h = bmp_buff[22] | (bmp_buff[23] << 8);
    uint8_t *content = malloc(3 * w * h);
    memcpy(content, bmp_buff + offset, w * h * 3);
    uint64_t *box = malloc(8 * w * h * 2);
    uint64_t v9 = 0x7B;
    for (int i = 0; i < w * h * 2; ++i) {
        v9 ^= ((v9 ^ (v9 << 13)) >> 7) ^ (v9 << 13) ^ ((((v9 ^ (v9 << 13)) >> 7) ^ v9 ^ (v9 << 13)) << 17);
        box[i] = v9;
    }
    uint32_t *order_arr = malloc(4 * w * h);
    for (int i = 0; i < w * h; ++i) {
        order_arr[i] = i;
    }
    for (int i = w * h; i > 1; --i) {
        uint32_t t = order_arr[box[i - 1] % i];
        order_arr[box[i - 1] % i] = order_arr[i - 1];
        order_arr[i - 1] = t;
    }

    uint64_t *box_2 = &box[w * h];
    uint8_t *temp_content = malloc(3 * w * h);
    memcpy(temp_content, content, 3 * w * h);
    temp_content[0] ^= ((*box_2) >> 16);
    temp_content[1] ^= ((*box_2) >> 8) ^ 1;
    temp_content[2] ^= ((*box_2) >> 0) ^ 0x41;
    for (int i = 1; i < w * h; ++i) {
        uint64_t k = box_2[i];
        temp_content[i * 3] ^= content[(i - 1) * 3 + 0] ^ (k >> 16);
        temp_content[i * 3 + 1] ^= content[(i - 1) * 3 + 1] ^ (k >> 8);
        temp_content[i * 3 + 2] ^= content[(i - 1) * 3 + 2] ^ (k >> 0);
    }
    memcpy(content, temp_content, 3 * w * h);
    for (int i = 0; i < w * h; ++i) {
        memcpy(content + order_arr[order_arr[i]] * 3, temp_content + i * 3, 3);
    }
    free(temp_content);
    free(order_arr);
    free(box);
    FILE *fd2 = fopen("./flag.bmp", "wb");
    fwrite(bmp_buff, 1, offset, fd2);
    fwrite(content, 1, w * h * 3, fd2);
    free(content);
    fclose(fd2);
    return 0;
}

fancy

逻辑很简单,调了一下大概是用时间作为随机数种子,置乱 sbox ,然后输入每个字节拆成高低4位各一个字节,后面就是一个简单的代换。大概思路就是 hook 拿指定时间戳成的 sbox,然后解密。 文件创建时间是 2023‎年‎12‎月‎14‎日,‏‎22:46:26 对应时间戳是 1702565186,但是获取的加密的时间戳不是这个,试了一下是前面一秒 1702565185

Hook 脚本:



const fn_clock_gettime = Module.getExportByName(null, "clock_gettime")
const mod_fancy = Process.getModuleByName("fancy")

Interceptor.attach(fn_clock_gettime, {
    onEnter: (params) => {
        this.timespec = params[1];
    }, 
    onLeave: (retval) => {
        console.log(`clock_gettime() tv_sec => ${this.timespec.readU64()}`)
        this.timespec.writeU64(1702565185);
        console.log(`clock_gettime() tv_sec => ${this.timespec.readU64()}`)

    }
})
var i = 0
Interceptor.attach(mod_fancy.base.add(0xFA35),function (args) {
    if (i == 0) {
        i++
        let sbox =  this.context.rcx.readByteArray(256);
        var sboxArr = new Uint8Array(sbox);
        console.log(JSON.stringify(sboxArr));

    }
})

解密脚本:

sbox_arr = [ 136, 224, 9, 190, 66, 164, 131, 52, 193, 234, 33, 80, 75, 192, 210, 48, 105, 21, 176, 24, 1, 63, 107, 0, 238, 151, 245, 120, 31, 133, 104, 94, 160, 86, 180, 112, 72, 102, 110, 242, 150, 142, 22, 29, 209, 129, 135, 169, 25, 148, 202, 183, 74, 128, 251, 232, 175, 20, 4, 155, 69, 219, 96, 109, 68, 216, 206, 5, 253, 122, 247, 61, 231, 23, 185, 78, 118, 196, 218, 84, 153, 88, 108, 125, 182, 42, 95, 162, 205, 161, 89, 145, 179, 207, 38, 172, 54, 154, 37, 47, 166, 106, 223, 6, 92, 214, 177, 3, 208, 26, 230, 32, 211, 171, 213, 225, 188, 36, 65, 158, 55, 45, 40, 159, 126, 30, 229, 252, 197, 99, 139, 181, 17, 191, 49, 168, 43, 98, 124, 140, 77, 147, 178, 149, 157, 249, 226, 97, 53, 156, 70, 174, 19, 152, 101, 15, 239, 34, 165, 187, 0, 240, 73, 100, 222, 143, 103, 58, 113, 13, 71, 119, 215, 189, 199, 203, 246, 121, 115, 81, 201, 67, 28, 163, 173, 60, 137, 114, 41, 170, 241, 127, 132, 237, 198, 254, 167, 27, 144, 228, 195, 184, 186, 56, 44, 10, 2, 233, 134, 123, 116, 227, 244, 200, 212, 221, 220, 194, 50, 248, 35, 117, 51, 11, 39, 76, 236, 146, 111, 79, 217, 14, 46, 87, 93, 18, 83, 235, 204, 243, 7, 91, 12, 8, 64, 85, 16, 130, 59, 82, 57, 141, 250, 90, 138, 62]

key = b"C0de_is_fancy"
e = b""
flag = []
with open("cipher", "rb") as cipher:
    e = cipher.read()
for i in range(len(e)):
    flag.append((sbox_arr.index(e[i]) + 256 - key[i % len(key)]) % 256)
flag = "".join(chr((flag[i] << 4) | flag[i + 1]) for i in range(0, len(flag), 2))
print(flag)