Back
Featured image of post qwb 2022 rev writeups

qwb 2022 rev writeups

还是太菜了呜呜呜

find_basic

没保存过程数据,以后找时间再补吧

用 IDA 查看,能够发现反编译结果很奇怪,看看反汇编就能够发现存在很多 pusha 和 call 之类的指令,不如对着汇编动调

动调后因为不好找核心逻辑的位置,所以在输入处下个硬件断点,发现取出了输入的第一位内容,然后进行乘法和累加,类似下面这段代码:

ADD        EAX,0x11
MOVZX      EAX,byte ptr [EAX]
MOVZX      EAX,AL
IMUL       EAX,EAX,-0x35cf6

主逻辑在0x750a9的位置,继续跟踪一下逻辑,整体相当于n个虚拟机,结构大概如下:

father_func@0x750a9:
    MOV CL, 0x??
    CALL son_func
son_func:
    CMP CL, 0x??
    JMP ???
    CMP CL, 0x??
    JMP ???

每个子函数的开头和结尾都有一些类似话指令的逻辑,但动调发现好像不影响程序的逻辑,所以就忽略了

跟踪了几个验证用的逻辑后,发现主要是乘法和累加,说明应该是一个矩阵运算,考虑到工作量很大,那就需要想办法进行自动提取了

使用了 idapython 进行自动调试,并记录过程中运行的指令:

from binascii import hexlify

ea = get_reg_value('eip')
end = ea + 0x1806
main_func = get_func_name(ea)

fp = open('all_asm.txt','w')

while True:
    ea = get_reg_value('eip')
    next_ea = next_head(ea)
    fp.write(hexlify(get_bytes(ea, next_ea - ea)).decode())
    fp.write('\t' + GetDisasm(ea)+'\n')
    if ea >= end:
        break
    if main_func == get_func_name(ea):
        step_into()
    else:
        step_over()
    wait_for_next_event(WFNE_SUSP, -1)

需要先动调到要提取的函数的第一条指令

这段代码把所有执行的指令都提取了出来,这样也能够验证一下之前的分析结果是否正确

观察提取结果发现,可以根据核心逻辑的上下指令进行定位,位于 jnz 之后,jmp 之前:

codes = open('all_asm.txt', 'r').read().split('\n')
c = open('asm.txt', 'w')
for i in range(len(codes)):
    if codes[i].find('jnz') != -1 and codes[i+2].find('jnz') == -1:
        j = i+3
        while j < len(codes) and codes[j].find('jmp') == -1:
            c.write(codes[j]+'\n')
            j += 1

codes = open('asm.txt', 'r').read().split('\n')
codes = [i[:i.find('\t')] for i in codes]

datas = b''
for i in codes:
    datas += unhexlify(i)
datas += '\xc3'
print(datas)
open('mch1', 'wb').write(datas)

之前提取的时候同时打印了指令的二进制代码,主要原因是提取的指令无法使用 nasm 编译,用 pwntools 速度又太慢,不如直接提取

提取到的内容直接反编译就好了,得到一个大长串的式子

提示说是需要对每个式子取绝对值,那就按照每次减常数来切割就行,最后的代码如下:

from z3 import *
a1 = [BitVec('x%d' % i, 32)for i in range(28)]

con1 = 40085 * a1[3] - 222506 * a1[2] + 54507 * a1[4] + 88056 * a1[1] + 212571 * a1[5] - 160722 * a1[0] - 434973
con2 = 49300 * a1[3] + 259229 * a1[0] + 278066 * a1[2] - 127937 * a1[1] - 295169 * a1[4] - 8368677
con3 = 42214 * a1[1] - 108025 * a1[3] + 205972 * a1[0] + 27559 * a1[2] - 17114904
con4 = - 151496 * a1[1] + 204740 * a1[0] + 80143 * a1[2] - 12295783
con5 = 241935 * a1[1] + 124128 * a1[0] - 38790036
con6 = 273221 * a1[0] - 27868542
con7 = -279656 * a1[2] - 199574 * a1[1] - 258130 * a1[8] - 200399 * a1[3] - 173903 * a1[7] + 175816 * a1[0] - 234569 * a1[6] - 108273 * a1[4] - 222957 * a1[5] + 128244179
con8 = - 81541 * a1[1] - 268763 * a1[0] + 219073 * a1[3] + 34782 * a1[6] + 21153 * a1[5] + 173005 * a1[7] + 76285 * a1[4] + 32825 * a1[2] - 13874925
con9 = 85214 * a1[2] - 268299 * a1[3] - 230981 * a1[1] + 290772 * a1[5] - 74394 * a1[4] + 28044 * a1[6] - 242995 * a1[0] + 50871139
con10 = -279656 * a1[2] - 199574 * a1[1] - 258130 * a1[8] - 200399 * a1[3] - 173903 * a1[7] + 175816 * a1[0] - 234569 * a1[6] - 108273 * a1[4] - 222957 * a1[5] + 128244179
con11 = - 81541 * a1[1] - 268763 * a1[0] + 219073 * a1[3] + 34782 * a1[6] + 21153 * a1[5] + 173005 * a1[7] + 76285 * a1[4] + 32825 * a1[2] - 13874925
con12 = 85214 * a1[2] - 268299 * a1[3] - 230981 * a1[1] + 290772 * a1[5] - 74394 * a1[4] + 28044 * a1[6] - 242995 * a1[0] + 50871139
con13 = -208564 * a1[0] + 81934 * a1[9] - 106641 * a1[7] + 198477 * a1[2] + 154505 * a1[1] + 48440 * a1[5] - 149004 * a1[3] - 108909 * a1[4] - 51714 * a1[10] - 296420 * a1[8] + 263021 * a1[6] + 688726
con14 = - 131130 * a1[2] + 224265 * a1[3] + 230702 * a1[0] - 176285 * a1[7] - 274778 * a1[4] + 103848 * a1[8] - 136039 * a1[9] - 241151 * a1[5] + 15542 * a1[6] - 17521 * a1[1] + 41644083
con15 = 195056 * a1[4] - 15717 * a1[9] - 180214 * a1[6] - 114427 * a1[5] + 277782 * a1[7] + 261379 * a1[8] - 225266 * a1[2] + 107609 * a1[0] + 259792 * a1[3] + 270563 * a1[11] + 205124 * a1[1] + 138334 * a1[10] + 103474 * a1[12] - 117027475
con16 = 189573 * a1[8] + 64393 * a1[6] + 231137 * a1[1] + 145315 * a1[4] - 53938 * a1[10] - 291345 * a1[5] + 216413 * a1[3] - 204681 * a1[0] - 65519 * a1[9] - 262826 * a1[2] + 187002 * a1[7] + 271732 * a1[11] - 38663722
con17 = 15645 * a1[13] + 276267 * a1[12] + 31190 * a1[5] - 244002 * a1[2] + 81415 * a1[3] - 22940 * a1[10] - 126076 * a1[7] + 8932 * a1[8] + 112153 * a1[4] + 194218 * a1[11] + 197656 * a1[9] - 204463 * a1[0] - 219500 * a1[1] + 19777 * a1[6] - 24531260
con18 = 279969 * a1[8] - 123977 * a1[4] + 162094 * a1[0] - 215769 * a1[1] - 18878 * a1[14] - 80292 * a1[11] - 237675 * a1[5] - 222121 * a1[6] + 269381 * a1[12] + 153934 * a1[13] - 165380 * a1[10] - 157137 * a1[2] - 186748 * a1[3] + 170756 * a1[7] - 186932 * a1[9] + 87264470
con19 = -87190 * a1[2] - 74836 * a1[1] + 16892 * a1[9] - 185781 * a1[8] - 12726 * a1[7] + 85022 * a1[12] + 232989 * a1[10] + 68516 * a1[0] - 120254 * a1[6] - 204892 * a1[5] - 65901 * a1[4] - 201087 * a1[13] + 158612 * a1[11] - 49445 * a1[3] - 181860 * a1[14] - 111015 * a1[15] + 43646834
con20 = -170184 * a1[3] - 137671 * a1[4] - 85374 * a1[9] - 73658 * a1[11] + 230891 * a1[13] + 54346 * a1[15] - 280694 * a1[0] + 60411 * a1[2] + 27171 * a1[7] - 50618 * a1[6] + 11843 * a1[10] + 131778 * a1[5] + 13956 * a1[8] - 42562 * a1[12] - 19972 * a1[1] - 145797 * a1[14] - 58717 * a1[16] + 74613584
con21 = 242475 * a1[16] - 234385 * a1[0] + 124653 * a1[2] - 287929 * a1[13] - 190916 * a1[12] - 277578 * a1[11] + 39 * a1[8] - 41625 * a1[6] + 67262 * a1[5] - 250144 * a1[9] - 70886 * a1[10] - 223492 * a1[15] - 179651 * a1[7] + 206538 * a1[17] + 161965 * a1[3] - 146258 * a1[4] + 167068 * a1[1] + 196330 * a1[14] + 76353817
con22 = 29700 * a1[18] - 60542 * a1[5] + 274107 * a1[11] + 154914 * a1[13] - 143185 * a1[12] + 167424 * a1[2] + 137439 * a1[8] - 186151 * a1[10] - 77157 * a1[9] - 233090 * a1[6] - 27400 * a1[7] - 76557 * a1[15] - 108002 * a1[17] + 103161 * a1[14] - 133956 * a1[1] - 219502 * a1[4] - 202897 * a1[0] - 250957 * a1[3] - 119297 * a1[16] + 100812197
con23 = -171971 * a1[9] + 38740 * a1[4] - 31661 * a1[10] - 194653 * a1[18] - 295910 * a1[16] + 136489 * a1[12] + 212619 * a1[17] + 165592 * a1[11] + 211791 * a1[1] + 156909 * a1[2] - 232187 * a1[8] - 73709 * a1[7] + 79735 * a1[14] + 184882 * a1[13] + 111105 * a1[6] + 148840 * a1[3] - 35774 * a1[19] - 275711 * a1[0] + 135265 * a1[5] - 141221 * a1[15] - 39117122
con24 = -186514 * a1[17] - 7791 * a1[2] + 276755 * a1[11] - 294815 * a1[14] - 238763 * a1[15] - 146099 * a1[5] + 184977 * a1[16] + 178413 * a1[1] + 287303 * a1[3] - 71946 * a1[10] - 73771 * a1[9] - 129032 * a1[18] + 200202 * a1[20] - 150509 * a1[6] - 156625 * a1[13] + 14093 * a1[7] + 192584 * a1[12] - 122770 * a1[0] - 255494 * a1[8] + 65 * a1[4] - 108479 * a1[19] + 13521895
con25 = 210978 * a1[7] + 300336 * a1[10] + 207254 * a1[15] + 216206 * a1[5] - 63529 * a1[0] - 274903 * a1[11] - 10750 * a1[14] + 25008 * a1[4] - 100942 * a1[19] - 104857 * a1[2] + 266501 * a1[8] + 229070 * a1[17] - 234559 * a1[16] + 298459 * a1[3] - 172052 * a1[6] - 98938 * a1[12] + 66155 * a1[13] - 84761 * a1[1] - 283508 * a1[18] + 288577 * a1[21] - 75407 * a1[20] - 204447 * a1[9] + 4351595
con26 = -201846 * a1[14] + 272550 * a1[20] + 60398 * a1[6] + 45580 * a1[7] + 195108 * a1[11] + 38596 * a1[0] + 220445 * a1[18] - 190873 * a1[15] + 103477 * a1[9] + 118842 * a1[19] + 206336 * a1[10] - 249940 * a1[17] - 48084 * a1[21] + 104901 * a1[5] - 48576 * a1[4] + 287104 * a1[16] - 286686 * a1[1] - 30253 * a1[22] + 121183 * a1[3] + 90967 * a1[2] - 195519 * a1[12] - 129304 * a1[8] + 141188 * a1[13] - 56642147
con27 = 110609 * a1[4] + 5913 * a1[21] - 197578 * a1[7] + 45127 * a1[18] + 282426 * a1[13] - 71019 * a1[16] - 6980 * a1[11] + 208216 * a1[15] - 13544 * a1[20] + 17852 * a1[8] + 167833 * a1[12] + 145568 * a1[17] + 3610 * a1[19] + 91985 * a1[1] - 267402 * a1[5] - 32355 * a1[14] - 197823 * a1[23] + 135525 * a1[2] - 229424 * a1[22] + 38093 * a1[10] + 50167 * a1[6] + 118713 * a1[9] + 123874 * a1[0] - 89499 * a1[3] - 43090537
con28 = -164755 * a1[9] + 175470 * a1[8] - 28660 * a1[1] + 7217 * a1[11] - 295102 * a1[4] - 28531 * a1[19] - 106265 * a1[25] - 92750 * a1[10] + 16738 * a1[21] - 231714 * a1[6] + 172042 * a1[24] - 215890 * a1[17] + 199697 * a1[12] - 84235 * a1[7] + 44614 * a1[13] + 75104 * a1[5] - 195843 * a1[0] - 15784 * a1[14] - 131950 * a1[15] - 268167 * a1[16] - 197565 * a1[20] + 24340 * a1[23] + 105130 * a1[2] - 79750 * a1[22] - 264668 * a1[3] + 50329 * a1[18] + 137774797
con28 = 62119 * a1[17] - 17215 * a1[24] + 289621 * a1[18] + 53006 * a1[20] + 95969 * a1[11] + 202404 * a1[0] + 247060 * a1[21] + 144211 * a1[19] + 280106 * a1[7] - 126431 * a1[10] - 226837 * a1[12] + 10463 * a1[23] + 121257 * a1[13] - 84190 * a1[9] + 88917 * a1[1] + 15453 * a1[14] + 271442 * a1[4] + 110851 * a1[3] - 231422 * a1[5] + 176741 * a1[22] + 266134 * a1[2] - 197327 * a1[6] - 55225 * a1[8] - 265465 * a1[15] + 119612 * a1[16] - 98514358
con29 = 62119 * a1[17] - 17215 * a1[24] + 289621 * a1[18] + 53006 * a1[20] + 95969 * a1[11] + 202404 * a1[0] + 247060 * a1[21] + 144211 * a1[19] + 280106 * a1[7] - 126431 * a1[10] - 226837 * a1[12] + 10463 * a1[23] + 121257 * a1[13] - 84190 * a1[9] + 88917 * a1[1] + 15453 * a1[14] + 271442 * a1[4] + 110851 * a1[3] - 231422 * a1[5] + 176741 * a1[22] + 266134 * a1[2] - 197327 * a1[6] - 55225 * a1[8] - 265465 * a1[15] + 119612 * a1[16] - 98514358
con30 = 151924 * a1[25] - 265311 * a1[6] + 107604 * a1[11] - 47851 * a1[24] + 227178 * a1[13] - 162699 * a1[2] + 2171 * a1[20] + 211070 * a1[23] + 94815 * a1[22] + 124760 * a1[16] + 41462 * a1[19] - 277022 * a1[15] - 62501 * a1[26] - 17727 * a1[14] - 257908 * a1[4] - 175112 * a1[21] + 8972 * a1[10] - 71801 * a1[8] - 114724 * a1[5] - 252898 * a1[9] + 161457 * a1[1] - 64461 * a1[0] - 111493 * a1[18] + 200145 * a1[17] - 290075 * a1[3] + 158466 * a1[12] - 275262 * a1[7] + 86899519
con31 = 142850 * a1[18] - 166704 * a1[1] + 284852 * a1[22] + 248972 * a1[7] - 76200 * a1[17] + 261708 * a1[19] + 91911 * a1[24] + 22347 * a1[3] + 76006 * a1[21] + 256511 * a1[6] - 100052 * a1[14] - 115830 * a1[2] - 93202 * a1[23] + 248858 * a1[12] - 262669 * a1[10] + 67895 * a1[5] - 111771 * a1[8] - 132193 * a1[11] - 141512 * a1[13] + 139406 * a1[27] + 109646 * a1[16] - 286309 * a1[9] + 175476 * a1[15] + 138067 * a1[20] + 192825 * a1[25] + 199577 * a1[0] - 63091 * a1[4] - 285207 * a1[26] - 58820340

s = Solver()

for i in range(28):
    s.add(a1[i] > 0x20)
    s.add(a1[i] < 0x7f)

s.add(a1[0] == ord('f'))
s.add(a1[1] == ord('l'))
s.add(a1[2] == ord('a'))
s.add(a1[3] == ord('g'))
s.add(a1[4] == ord('{'))
s.add(a1[27] == ord('}'))

s.add(con1 == 0)
s.add(con2 == 0)
s.add(con3 == 0)
s.add(con4 == 0)
s.add(con5 == 0)
s.add(con6 == 0)
s.add(con7 == 0)
s.add(con8 == 0)
s.add(con9 == 0)
s.add(con10 == 0)
s.add(con11 == 0)
s.add(con12 == 0)
s.add(con13 == 0)
s.add(con14 == 0)
s.add(con15 == 0)
s.add(con16 == 0)
s.add(con17 == 0)
s.add(con18 == 0)
s.add(con19 == 0)
s.add(con20 == 0)
s.add(con21 == 0)
s.add(con22 == 0)
s.add(con23 == 0)
s.add(con24 == 0)
s.add(con25 == 0)
s.add(con26 == 0)
s.add(con27 == 0)
s.add(con28 == 0)
s.add(con29 == 0)
s.add(con30 == 0)
s.add(con31 == 0)

if sat == s.check():
    m = s.model()
    flag = ''
    for i in a1:
        flag += chr(m[i].as_long())

import hashlib

hl = hashlib.md5()
hl.update(flag.encode())
print (hl.hexdigest() == '042905954c2c27f21bd74489ea0d151f')
print (flag)

GameMaster

.net逆向

一个21点游戏,逆向发现存在作弊码

if (memcmp1(input, "MF3K", 4)) {
    try {
        game.Player.Bet -= 22m;
        for (int i = 0; i < memory.Length; i++) {
            memory[i] ^= 34;
        }
        Environment.SetEnvironmentVariable("AchivePoint1", game.Player.Balance.ToString());
        return;
     }
}
if (memcmp1(input, "EEPW", 4)) {
    try {
        game.Player.Balance += 175m;
        byte[] key = new byte[16]
        {
            66, 114, 97, 105, 110, 115, 116, 111, 114, 109,
            105, 110, 103, 33, 33, 33
        };
        RijndaelManaged rijndaelManaged = new RijndaelManaged();
        rijndaelManaged.Key = key;
        rijndaelManaged.Mode = CipherMode.ECB;
        rijndaelManaged.Padding = PaddingMode.Zeros;
        ICryptoTransform cryptoTransform = rijndaelManaged.CreateDecryptor();
        m = cryptoTransform.TransformFinalBlock(memory, 0, memory.Length);
        Environment.SetEnvironmentVariable("AchivePoint2", game.Player.Balance.ToString());
        return;
    }
}
if (memcmp1(input, "6VD6", 4)) {
    try {
        game.Player.Balance -= 27m;
        Environment.SetEnvironmentVariable("AchivePoint3", game.Player.Balance.ToString());
        BinaryFormatter binaryFormatter = new BinaryFormatter();
        MemoryStream serializationStream = new MemoryStream(m);
        binaryFormatter.Deserialize(serializationStream);
        return;
    }
}

依次触发三个作弊码,就会对文件内容进行解密并反序列化

解密后发现序列化内容中包含了一个dll文件:

private static void Check1(ulong x, ulong y, ulong z, byte[] KeyStream)
{
    int num = -1;
    for (int i = 0; i < 320; i++)
    {
        x = (((x >> 29 ^ x >> 28 ^ x >> 25 ^ x >> 23) & 1UL) | x << 1);
        y = (((y >> 30 ^ y >> 27) & 1UL) | y << 1);
        z = (((z >> 31 ^ z >> 30 ^ z >> 29 ^ z >> 28 ^ z >> 26 ^ z >> 24) & 1UL) | z << 1);
        bool flag = i % 8 == 0;
        if (flag)
        {
            num++;
        }
        KeyStream[num] = (byte)((long)((long)KeyStream[num] << 1) | (long)((ulong)((uint)((z >> 32 & 1UL & (x >> 30 & 1UL)) ^ (((z >> 32 & 1UL) ^ 1UL) & (y >> 31 & 1UL))))));
    }
}

private static void ParseKey(ulong[] L, byte[] Key)
{
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 4; j++)
        {
            Key[i * 4 + j] = (byte)(L[i] >> j * 8 & 255UL);
        }
    }
}

public T1()
{
    try
    {
        string environmentVariable = Environment.GetEnvironmentVariable("AchivePoint1");
        string environmentVariable2 = Environment.GetEnvironmentVariable("AchivePoint2");
        string environmentVariable3 = Environment.GetEnvironmentVariable("AchivePoint3");
        bool flag = environmentVariable == null || environmentVariable2 == null || environmentVariable3 == null;
        if (!flag)
        {
            ulong num = ulong.Parse(environmentVariable);
            ulong num2 = ulong.Parse(environmentVariable2);
            ulong num3 = ulong.Parse(environmentVariable3);
            ulong[] array = new ulong[3];
            byte[] array2 = new byte[40];
            byte[] array3 = new byte[40];
            byte[] array4 = new byte[12];
            byte[] first = new byte[] { 
                101, 5, 80, 213, 163, 26, 59, 38, 
                19, 6, 173, 189, 198, 166, 140, 183, 
                42, 247, 223, 24, 106, 20, 145, 37, 
                24, 7, 22, 191, 110, 179, 227, 5, 
                62, 9, 13, 17, 65, 22, 37, 5
            };
            byte[] array5 = new byte[] {
                60, 100, 36, 86, 51, 251, 167, 108, 
                116, 245, 207, 223, 40, 103, 34, 62, 
                22, 251, 227
            };
            array[0] = num;
            array[1] = num2;
            array[2] = num3;
            T1.Check1(array[0], array[1], array[2], array2);
            bool flag2 = first.SequenceEqual(array2);
            if (flag2)
            {
                T1.ParseKey(array, array4);
                for (int i = 0; i < array5.Length; i++)
                {
                    array5[i] ^= array4[i % array4.Length];
                }
                MessageBox.Show("flag{" + Encoding.Default.GetString(array5) + "}", "Congratulations!", MessageBoxButtons.OK);
            }
        }
    }
    catch (Exception)
    {
    }
}

使用 z3 求解三个环境变量

from z3 import *

x = BitVec('x', 64)
y = BitVec('y', 64)
z = BitVec('z', 64)

flag1 = [
    101, 5, 80, 213, 163, 26, 59, 38, 19, 6, 173, 
    189, 198, 166, 140, 183, 42, 247, 223, 24, 106, 
    20, 145, 37, 24, 7, 22, 191, 110, 179, 227, 5, 
    62, 9, 13, 17, 65, 22, 37, 5
]

s = Solver()

for i in range(320):
    x = (((x >> 29 ^ x >> 28 ^ x >> 25 ^ x >> 23) & 1) | x << 1)
    y = (((y >> 30 ^ y >> 27) & 1) | y << 1)
    z = (((z >> 31 ^ z >> 30 ^ z >> 29 ^ z >> 28 ^ z >> 26 ^ z >> 24) & 1) | z << 1)
    cur = ((z >> 32) & 1 & (x >> 30 & 1)) ^ ((((z >> 32) & 1) ^ 1) & ((y >> 31) & 1))
    s.add(cur == (flag1[i // 8] >> (7 - i % 8)) & 1)

if s.check() == sat:
    m = s.model()
    print (m)
else:
    print ('no res')

求解,或动调改内存

array = [156324965, 868387187, 3131229747]
Key = [0] * 12
for i in range(3):
    for j in range(4):
        Key[i * 4 + j] = (array[i] >> (j * 8)) & 0xff

print (Key)
cipher = [60, 100, 36, 86, 51, 251, 167, 108, 116, 245, 207, 223, 40, 103, 34, 62, 22, 251, 227]

for i in range(len(cipher)):
    print (chr(cipher[i] ^ Key[i % len(Key)]), end='')

Easyapk

逻辑在 so 层,有大量的垃圾指令,实际逻辑是先进行 rot13,再进行 tea 加密 解密脚本如下:

#include <stdio.h>  
#include <stdint.h>  

//解密函数  
void decrypt (uint32_t* v, uint32_t* k) {  
    uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i;  /* set up */  
    uint32_t delta=0x9e3779b9;                     /* a key schedule constant */  
    uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];   /* cache key */  
    for (i=0; i<32; i++) {                         /* basic cycle start */  
        v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);  
        v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);  
        sum -= delta;  
    }                                              /* end cycle */  
    v[0]=v0; v[1]=v1;  
}  
  
int main()  
{  
    uint32_t v[]={1570024068u, 351937696u, 727056912u, 3063668041u, 2867849940u, 1267528902u, 159365321u, 3052163538u},k[4]={0x33323130, 0x37363534, 0x62613938, 0x66656463};  
    // v为要加密的数据是两个32位无符号整数  
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位  
    printf("加密后的数据:%u %u\n",v[0],v[1]);  
    decrypt(v, k);  
    decrypt(v + 2, k);  
    decrypt(v + 4, k);  
    decrypt(v + 6, k);
    unsigned char *x = (unsigned char *)v;
    for (int i = 0; i < 32; i++) {
        printf("%x", x[i]);
    }  
    return 0;  
}  
// synt{Vg_Vf_A0g_guNg_zHpu_unEqre}

结果进行rot13即可

flag{It_Is_N0t_thAt_mUch_haRder}

deeprev

这道题一开始找不到逻辑,后来在 check 下断点,断在了 ld 中,看一下各个段的大小,发现relo部分很大,说明逻辑应该藏在了链接过程中

后来发现是仿照 google ctf eldar 那题的,直接借用别人的脚本

import lief
from collections import namedtuple
from dataclasses import dataclass

b = lief.ELF.parse('./deeprev')

def to_sym(name):
    assert len(name) == 1
    return ord(name[0])

Rel = namedtuple('REL', ['dst', 'val', 'ridx'])
Copy = namedtuple('CPY', ['dst', 'symbol', 'ridx'])
R64 = namedtuple('R64', ['dst', 'symbol', 'addend', 'ridx'])
R32 = namedtuple('R32', ['dst', 'symbol', 'addend', 'ridx'])

@dataclass
class Symbol(object):
    idx: int

    def __repr__(self):
        return f's{self.idx}'

@dataclass
class Reloc(object):
    idx: int

    def __repr__(self):
        return f'r{self.idx}'

@dataclass
class Ref(object):

    def __repr__(self):
        return f'&{self.val}'

@dataclass
class SymAddr(object):
    sym: Symbol
    field: str

    def __repr__(self):
        return f'{self.sym}.{self.field}'

@dataclass
class RelocAddr(object):
    reloc: Reloc
    field: str

    def __repr__(self):
        return f'{self.reloc}.{self.field}'

    def vaddr(self):
        off = 0
        print(self.field)
        # match self.field:
        #    case 'r_address': off = 0
        #    case 'r_info': off = 8
        #    case 'r_addend': off = 16

        return (self.reloc.idx * 24) + off + rela.virtual_address

@dataclass
class FlagAddr(object):
    idx: int

    def __repr__(self):
        return f'flag[{self.idx}]'

BaseAddr = namedtuple('baseaddr', [])
CheckAddr = namedtuple('check', [])

rela = [x for x in b.sections if x.name == '.rela.dyn'][0]
dynsym = [x for x in b.sections if x.name == '.dynsym'][0]

def format_addr(addr: int):
    if (addr >= rela.virtual_address and addr < rela.virtual_address + rela.size):
        offset = addr - rela.virtual_address
        r_offset = (offset // 24)
        r_rem = offset % 24

        if r_rem == 0:
            return RelocAddr(Reloc(r_offset), 'r_address')
        elif r_rem == 8:
            return RelocAddr(Reloc(r_offset), 'r_info')
        elif r_rem == 16:
            return RelocAddr(Reloc(r_offset), 'r_addend')
        else:
            return RelocAddr(Reloc(r_offset), r_rem)

    elif (addr > dynsym.virtual_address
          and addr < dynsym.virtual_address + dynsym.size
          ):
        offset = addr - dynsym.virtual_address
        r_offset = (offset // 24)
        r_rem = offset % 24

        if r_rem == 0:
            return SymAddr(Symbol(r_offset), 'st_name')
        elif r_rem == 8:
            return Symbol(r_offset)
        elif r_rem == 16:
            return SymAddr(Symbol(r_offset), 'st_size')
        else:
            return SymAddr(Symbol(r_offset), r_rem)

    elif addr >= 0x404040 and addr < 0x404040+0x21:
        off = addr-0x404040
        return FlagAddr(off)
    elif addr == 0x804000:
        return BaseAddr()
    elif addr == 0x404064:
        return CheckAddr()
    else:
        return addr

def parse(b) -> list:
    print('[*] Loading relocations...')
    relocs = list(b.relocations)

    print('[*] Parsing...')
    instructions = []
    for i in range(3, len(relocs)):
        r = relocs[i]
        if r.type == 1:
            instructions.append(
                R64(format_addr(r.address), to_sym(r.symbol.name), format_addr(r.addend), i))
        elif r.type == 5:  # CPY
            instructions.append(
                Copy(format_addr(r.address), to_sym(r.symbol.name), i))
        elif r.type == 8:  # REL
            instructions.append(
                Rel(format_addr(r.address), format_addr(r.addend), i))
        elif r.type == 7:  # R32
            # instructions.append(
            #    R32(r.address, to_sym(r.symbol.name), r.addend, i))
            instructions.append(R32(1, 1, 1, 1))

    return instructions

def dump(instructions):
    for op in instructions:
        if type(op).__name__ == 'REL':
            print(f'[{op.ridx:04d}] :: rel {op.dst}, {op.val}')
        elif type(op).__name__ == 'CPY':
            print(f'[{op.ridx:04d}] :: copy {op.dst}, s{op.symbol}')
        elif type(op).__name__ == 'R64':
            print(f'[{op.ridx:04d}] :: r64 {op.dst}, {op.symbol} + {op.addend}') # 其实是 s{symbol} + {addend}
        elif type(op).__name__ == 'R32':
            print('7')
        if(op.ridx == 1266):
            break

instructions = parse(b)
dump(instructions)

提取后发现主要逻辑为加载 shellcode 并执行

具体过程是使用了 rel 指令将 shellcode 移动到某个地址,随后使用 r64 指令调用该地址

[0005] :: rel s2, flag[0]
[0006] :: rel s2.st_size, 1
[0007] :: copy s4, s2
[0009] :: rel r8.r_address, 0x16008040cc253480
[0010] :: rel r8.r_info, 0xc3
[0011] :: rel s3, r8.r_address
[0012] :: rel s3.st_name, 0x1000a0000001a
[0013] :: r64 r8.r_address, 3 + 0        ; 这里相当于 call s3
[0014] :: rel s2, r101002.r_address
[0015] :: rel s2.st_size, 0x18
[0016] :: copy r8.r_address, s2
[0018] :: rel r17.r_address, 0x8040cc250480
[0019] :: rel r17.r_info, 0xc3
[0020] :: rel s3, r17.r_address
[0021] :: rel s3.st_name, 0x1000a0000001a
[0022] :: r64 r17.r_address, 3 + 0
[0023] :: copy r17.r_address, s2
[0024] :: rel s2, s4
[0025] :: rel s2.st_size, 1
[0026] :: copy r3.r_address, s2

[0597] :: rel s2, r3.r_address
[0598] :: rel s2.st_size, 8
[0599] :: copy s6, s2
[0601] :: rel r600.r_address, 0x70008040fc253480
[0602] :: rel r600.r_info, 0xc3
[0603] :: rel s3, r600.r_address
[0604] :: rel s3.st_name, 0x1000a0000001a
[0605] :: r64 r600.r_address, 3 + 0
[0606] :: rel s2, r101002.r_address
[0607] :: rel s2.st_size, 0x18
[0608] :: copy r600.r_address, s2
[0609] :: rel s2, s6
[0610] :: rel s2.st_size, 8
[0611] :: copy r612.r_addend, s2
[0612] :: r64 s5, 5 + 0
[0613] :: rel r612.r_addend, 0
[0614] :: rel s6, 0

上述代码对 flag 第一位执行了两处 shellcode,使用 Capstone 分析发现 shellcode 中的逻辑为 f ^ 0x16 + 0 随后对该值 xor 0x70,最后值为 0 表示通过

前28位flag均是该逻辑的重复

xor = [0x16, 0x17, 0x10, 0x12, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x24, 0x2c, 0x26, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x23, 0x27, 0x24, 0x25, 0x26, 0x27]
add = [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b]
xor2 = [0x70, 0x7c, 0x73, 0x78, 0x6f, 0x27, 0x2a, 0x2c, 0x7f, 0x35, 0x2d, 0x32, 0x37, 0x3b, 0x22, 0x59, 0x53, 0x8e, 0x3d, 0x2a, 0x59, 0x27, 0x2d, 0x29, 0x34, 0x2d, 0x61, 0x32]

for x1, a, x2 in zip(xor, add, xor2):
    print (chr((x2 - a) ^ x), end='')
# flag{366c950370fec47e34581a0

对最后几位 flag 进行的计算有个小 smc 的操作:

[1074] :: rel s2, flag[28]
[1075] :: copy s6, s2
[1076] :: rel s2, flag[29]
[1077] :: copy s7, s2
[1078] :: rel s8, 0
[1079] :: rel s2, s8
[1080] :: copy r1081.r_addend, s2
[1081] :: r64 s8, 8 + 0           ; 上一个指令将此处修改为 r64 s8, s8 + s2, 相当于 add s8, s2
[1082] :: rel r1081.r_addend, 0
[1083] :: rel s2, s6
[1084] :: copy r1085.r_addend, s2
[1085] :: r64 s8, 8 + 0
[1086] :: rel r1085.r_addend, 0
[1087] :: rel s9, 0
[1088] :: rel s2, s9
[1089] :: copy r1090.r_addend, s2
[1090] :: r64 s9, 9 + 0
[1091] :: rel r1090.r_addend, 0
[1092] :: rel s2, s7
[1093] :: copy r1094.r_addend, s2
[1094] :: r64 s9, 9 + 0
[1095] :: rel r1094.r_addend, 0
[1096] :: rel s2, s9
[1097] :: copy r1098.r_addend, s2
[1098] :: r64 s10, 8 + 0
[1099] :: rel r1098.r_addend, 0
[1101] :: rel r1100.r_address, 0x6c0080415c253480
[1102] :: rel r1100.r_info, 0xc3
[1103] :: rel s3, r1100.r_address
[1104] :: rel s3.st_name, 0x1000a0000001a
[1105] :: r64 r1100.r_address, 3 + 0
[1106] :: rel s2, r101002.r_address
[1107] :: rel s2.st_size, 0x18
[1108] :: copy r1100.r_address, s2
[1109] :: rel s2, s10
[1110] :: rel s2.st_size, 8
[1111] :: copy r1112.r_addend, s2
[1112] :: r64 s5, 5 + 0
[1113] :: rel r1112.r_addend, 0
[1114] :: rel s6, 0
[1115] :: rel s7, 0
[1116] :: rel s8, 0
[1117] :: rel s9, 0
[1118] :: rel s10, 0

总结一下:

(flag[28] + flag[29]) ^ 0x6c = 0
(flag[28] * 2 + flag[29]) ^ 0xa1 = 0
(flag[30] + flag[31]) ^ 0xb1 = 0
(flag[30] * 2 + flag[31]) ^ 0xe5 = 0

计算得到flag最后四位

Built with Hugo
Theme Stack designed by Jimmy