2024年8月

Destination

挂上TitanHide,注意到NtSetInformationThread, NtGetContextThread这两个反调试
x64dbg断下后在线程开始ret即可。
NtSetInformationThread会使当前线程从调试器隐藏,调试器跑飞
NtGetContextThread可以获取到当前线程的寄存器状况,用来检测DR寄存器来反硬件断点
还有一个反调试是我进入后发现的,主线程卡住,但是线程窗口里面有一个线程一直在Sleep(1);,于是干掉就行了。

于是我们就得到了第一个patch的附件,我们以这个附件为基础继续调。
注意到我们输入完flag之后给我们来了个int3,导致进入SEH流程,如下图所示。
2024-08-20T18:01:48.png
图中框起来的是SEH处理函数,我们进去后,显然是似了。
2024-08-20T18:02:35.png
我们观察到,这些花指令由2种构成
1.call $+5; add [esp], 5; retn
2.jnz addr; jz addr
显然第一种可以化简为nop,第二种我们可以化简为jmp addr
处理脚本如下

from pwn import u32, u8
binfile = open("Destination_patched_2.exe","rb").read()
 
founds = []
# find all junkcode
index = 0
while True:
    index = binfile.find(b'\xE8\x00\x00\x00\x00\x83\x04\x24\x05\xC3',index+1) #! index+1 ! 不然死循环了...
    if index == -1:
        break
    founds.append(index)
    print(index)
binfile = bytearray(binfile)
for i in founds:
    print(binfile[i:i+10])
    for j in range(i,i+10):
        binfile[j] = 0x90
    print(binfile[i:i+10])
# find all junkcode
index = 0
while True:
    i = binfile.find(b'\x0f\x84',index+1)
    index = i
    if i == -1:
        break
    if binfile[index+6:index+8] == b'\x0f\x85' and u32(binfile[index+2:index+6]) - u32(binfile[index+8:index+12]) == 6:
        binfile[i] = 0x90
        binfile[i+1] = 0xe9
        binfile[i+7] = 0x66
        binfile[i+8] = 0x0f
        binfile[i+9] = 0x18
        binfile[i+10] = 0x20
        binfile[i+11] = 0x90

index = 0
while True:
    i = binfile.find(b'\x74',index+1)
    index = i
    if i == -1:
        break
    if binfile[i+2] == 0x75 and binfile[i+1] - binfile[i+3] == 2:
        binfile[i] = 0xeb
        binfile[i+2] = 0x90
        binfile[i+3] = 0x90
        binfile[i+4] = 0x90

# replace all matches
print(open("Destination_p3.exe","wb").write(binfile))

处理完花指令后,我们进入Ghidra,直接当成Func,然后手动处理一点东西,就得到了:
2024-08-20T18:05:47.png


void xxtea(void)

{
  uint e;
  uint sum;
  uint z;
  uint i;
  int round;
  uint y;
  
  round = 50;
  sum = 0;
  z = src[0xb];
  do {
    sum += 0xa4b46062;
    e = sum >> 2 & 3;
    src[0xb] = z;
    for (i = 0; i < 11; i += 1) {
      y = src[i + 1];
      z = src[i] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (key[i & 3 ^ e] ^ z));
      src[i] = z;
    }
    z = src[0xb] +
        ((z >> 5 ^ src[0] << 2) + (src[0] >> 3 ^ z << 4) ^ (sum ^ src[0]) + (key[i & 3 ^ e] ^ z));
    src[0xb] = z;
    round += -1;
  } while (round != 0);
  return;
}

显然的xxtea。
分析完这个后,我们下硬件断点,发现没断下来而且值还被改了,我们回到主函数,找到这个地址
2024-08-20T18:09:13.png
有一个jmp far
2024-08-20T18:09:26.png
ida的反汇编这里有点问题,我们x32dbg看一看,发现是jmp far 0x33:xxxxx
2024-08-20T18:10:09.png
这是windows下的天堂之门,0x23就是转32位,0x33就是转64位运行,这个是CS寄存器的值。
我们把jmp far的目标地址函数dump下来,放到一个二进制文件,拖入ida,选中64 bit mode,就可以看到真正的源码了。
2024-08-20T18:11:37.png
可以注意到,这是一个已经玩烂的不安全CRC,它曾出现在:北邮去年招新赛的五子棋程序逆向、D^3CTF,moectf2024中。
提供两种解法,第一种就是z3-Solver

from z3 import *
from copy import deepcopy
s = Solver()
x = [BitVec(f"x{i}", 32) for i in range(12)]
y = deepcopy(x)
ans = [0xA790FAD6, 0xE8C8A277, 0xCF0384FA, 0x2E6C7FD7, 0x6D33968B, 0x5B57C227, 0x653CA65E, 0x85C6F1FC, 0xE1F32577, 0xD4D7AE76, 0x3FAF6DC4, 0x0D599D8C]
print(len(ans))
for i in range(12):
    for j in range(32):
        x[i] = If(LShR(x[i], 31) == 1, 2*x[i] ^ 0x84A6972F, 2*x[i])
    s.add(x[i] == ans[i])
print(s.check())
print(s.model())
'''
12
sat
[x1 = 1585230664,
 x10 = 950802794,
 x0 = 2656343363,
 x3 = 2246810078,
 x7 = 688747158,
 x11 = 422273590,
 x9 = 2522199337,
 x4 = 3061995652,
 x5 = 3304541857,
 x6 = 582058634,
 x8 = 428794995,
 x2 = 3651684911]
'''

也是最方便的,下面的解法是按照原理来的:
由于由于左移会变偶数,而xor后又会变成奇数,所以有唯一解。
我们先获取最低位是否为1,如果是1,说明是奇数,如果为奇数,说明它刚刚走的是最高位是1的那条分支,也说明它异或了立即数,我们直接给它异或回来,然后右移1,由于我们已经确定最高位是1,我们给它补上1即可。
如果最低位是0,我们就直接右移1,然后确定最高位是0即可。

解密脚本

#include <iostream>
#include <Windows.h>
// n = 12
typedef DWORD uint;
void xxtea(uint32_t* src, int n, uint32_t const key[4])
{

    if (n > 0) /* Coding Part */
    {
        uint e;
        uint sum;
        uint z;
        uint i;
        int round;
        uint y;

        round = 50;
        sum = 0;
        z = src[0xb];
        do {
            sum += 0xa4b46062;
            e = sum >> 2 & 3;
            src[n - 1] = z;
            for (i = 0; i < n - 1; i += 1) {
                y = src[i + 1];
                z = src[i] + ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (key[i & 3 ^ e] ^ z));
                src[i] = z;
            }
            z = src[n - 1] +
                ((z >> 5 ^ src[0] << 2) + (src[0] >> 3 ^ z << 4) ^ (sum ^ src[0]) + (key[i & 3 ^ e] ^ z));
            src[n - 1] = z;
            round += -1;
        } while (round != 0);
    }
    else if (n <= 0)
    {
        uint32_t y, z, sum;
        unsigned p, rounds, e;

        n = -n;
        rounds = 50;
        sum = rounds * 0xa4b46062;
        y = src[0];
        do
        {
            e = (sum >> 2) & 3;
            for (p = n - 1; p > 0; p--)
            {
                z = src[p - 1];
                y = src[p] -= ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (key[p & 3 ^ e] ^ z));
            }
            z = src[n - 1];
            y = src[0] -= ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4) ^ (sum ^ y) + (key[p & 3 ^ e] ^ z));
            sum -= 0xa4b46062;
        } while (--rounds);

    }
}

unsigned char a[] = {0xd6, 0xfa, 0x90, 0xa7, 0x77, 0xa2, 0xc8, 0xe8, 0xfa, 0x84, 0x03, 0xcf, 0xd7, 0x7f, 0x6c, 0x2e, 0x8b, 0x96, 0x33, 0x6d, 0x27, 0xc2, 0x57, 0x5b, 0x5e, 0xa6, 0x3c, 0x65, 0xfc, 0xf1, 0xc6, 0x85, 0x77, 0x25, 0xf3, 0xe1, 0x76, 0xae, 0xd7, 0xd4, 0xc4, 0x6d, 0xaf, 0x3f, 0x8c, 0x9d, 0x59, 0x0d};
//unsigned char a[] = "flag{111111111111111111111111111111111111111}";
unsigned char k[] = {0x6b, 0x7a, 0x0e, 0x6b, 0xee, 0x11, 0x30, 0xd1, 0x6d, 0x2c, 0xe1, 0xa7, 0xa6, 0xac, 0x99, 0xc1 };
int main()
{
    for (int i = 0; i < 12; i++)
    {
        uint32_t n = *(i + (uint32_t*)a);
        for (int j = 0; j < 32; j++)
        {
            if (n & 1)
            {
                n ^= 0x84A6972F;
                n >>= 1;
                n |= 0x80000000;
            }
            else
            {
                n >>= 1;
                n &= 0x7fffffff;
            }
        }
        *(i + (uint32_t*)a) = n;
    }

    xxtea((uint32_t*)a, -12, (uint32_t*)k);
    xxtea((uint32_t*)a, -12, (uint32_t*)k);
    std::cout << a; //DubheCTF{82e1e3f8-85fe469f-8499dd48-466a9d60}
    return 0;
}

[ACTF新生赛2020] Oruga

抄出来爆破

#include <iostream>

unsigned char byte_201020[256] = {
    0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x23, 0x23, 0x23,
    0x00, 0x00, 0x00, 0x23, 0x23, 0x00, 0x00, 0x00, 0x4F, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x4F, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x4C, 0x00, 0x4F, 0x4F, 0x00, 0x4F, 0x4F, 0x00, 0x50, 0x50, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x4C, 0x00, 0x4F, 0x4F, 0x00, 0x4F, 0x4F, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x4C, 0x4C, 0x00, 0x4F, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00,
    0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x4D, 0x4D, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x4D, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x45, 0x45,
    0x00, 0x00, 0x00, 0x30, 0x00, 0x4D, 0x00, 0x4D, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x45, 0x45,
    0x54, 0x54, 0x54, 0x49, 0x00, 0x4D, 0x00, 0x4D, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00,
    0x00, 0x54, 0x00, 0x49, 0x00, 0x4D, 0x00, 0x4D, 0x00, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00,
    0x00, 0x54, 0x00, 0x49, 0x00, 0x4D, 0x00, 0x4D, 0x00, 0x4D, 0x21, 0x00, 0x00, 0x00, 0x45, 0x45
};

int __fastcall sub_78A(const char* src)
{
    int v2; // [rsp+Ch] [rbp-Ch]
    int v3; // [rsp+10h] [rbp-8h]
    int v4; // [rsp+14h] [rbp-4h]

    v2 = 0;
    v3 = 5;
    v4 = 0;
    while (byte_201020[v2] != 0x21)
    {
        v2 -= v4;
        if (src[v3] != 'W' || v4 == -16)
        {
            if (src[v3] != 'E' || v4 == 1)
            {
                if (src[v3] != 'M' || v4 == 0x10)
                {
                    if (src[v3] != 'J' || v4 == -1)
                        return v3 + 1;
                    v4 = -1;
                }
                else
                {
                    v4 = 16;
                }
            }
            else
            {
                v4 = 1;
            }
        }
        else
        {
            v4 = -16;
        }
        ++v3;
        while (!byte_201020[v2])
        {
            if (v4 == -1 && (v2 & 0xF) == 0)
                return v3;
            if (v4 == 1 && v2 % 16 == 15)
                return v3;
            if (v4 == 16 && (unsigned int)(v2 - 240) <= 0xF)
                return v3;
            if (v4 == -16 && (unsigned int)(v2 + 15) <= 0x1E)
                return v3;
            v2 += v4;
        }
    }
    return v3 + 1;
}

int main()
{
    int i;
    char* src = (char*)malloc(40);
    if (!src) return -1;
    memcpy_s(src, 40, "actf{", 5);
    int last = 5;
    for (i = 0; i < 40; i++)
    {
        src[6 + i] = 0;
        src[5 + i] = 'W';
        if (sub_78A(src) == i + 6)
        {
            src[5 + i] = 'J';
            if (sub_78A(src) == i + 6)
            {
                src[5 + i] = 'M';
                if (sub_78A(src) == i + 6)
                {
                    src[5 + i] = 'E';
                    if (sub_78A(src) == i + 6)
                    {
                        src[5 + i] = '}';
                        if (sub_78A(src) == i + 6)
                        {
                            break;
                        }
                    }
                }
            }
        }
    }

    printf("%s", src);
    free(src);
}

actf{MEWEMEWJMEWJM}

[WUSTCTF2020] level4

2024-08-01T10:33:50.png
题目给了中序和后序排列

Practice my Data Structure code.....
Typing....Struct.....char....*left....*right............emmmmm...OK!
Traversal!
Traversal type 1:2f0t02T{hcsiI_SwA__r7Ee}
Traversal type 2:20f0Th{2tsIS_icArE}e7__w
Traversal type 3:    //type3(&x[22]);   No way!

显然缺了个前序排列,应该就是结果
我们用Python解

def ToPreOrder(Postorder,Inorder):
    length = len(Postorder)
    if length == 0:
        return 0
    root = Postorder[length-1]
    for i in range(length):
        if root == Inorder[i]:
            break
    print(root,end="")
    ToPreOrder(Postorder[0:i],Inorder[0:i])
    ToPreOrder(Postorder[i:length-1],Inorder[i+1:length])
    
ToPreOrder("20f0Th{2tsIS_icArE}e7__w","2f0t02T{hcsiI_SwA__r7Ee}")
# wctf2020{This_IS_A_7reE}

[GUET-CTF2019] number_game

树中序和后序排列,动调一下可以拿到替换表。
2024-08-02T02:39:34.png
然后再分析一下,可以得知,相邻行/列不能相同,且输入只能是0 1 2 3 4这五个字符。
2024-08-02T02:39:47.png
写出z3-solver脚本即可。

from z3 import *
x = [BitVec(f"x{i}", 8) for i in range(25)]
s = Solver()

raw = """
1 4 # 2 3
3 0 # 1 #
0 # 2 3 #
# 3 # # 0
4 2 # # 1
"""

raw = "".join(raw.replace(' ', '').splitlines())
for i, v in enumerate(raw):
    if v != '#':
        s.add(x[i] == ord(v))
    else:
        s.add(x[i] >= ord('0'), x[i] <= ord('4'))

for i in range(5):
    for j in range(5):
        for k in range(j + 1, 5):
            s.add(x[5 * i + j] != x[5 * i + k])
            s.add(x[5 * j + i] != x[5 * k + i])


print(s)
print(s.check())
m = s.model()
ans = ""

for i in x:
    ans += chr(m[i].as_long())

for i in range(5):
    for j in range(5):
        print(ans[5*i+j], end=' ')
    print("")


# Tree transform
d_before = "0123456789"
d_after = "7381940526"
d_table = [d_before.index(d_after[i]) for i in range(len(d_before))]
print("table", d_table)

answers = [2, 7, 9, 11, 14, 15, 17, 18, 22, 23]
flag_ans = [i for i in range(10)]
for i in range(len(answers)):
    flag_ans[d_table[i]] = ans[answers[i]]

flag = "".join(flag_ans)
print(flag)
sat
1 4 0 2 3 
3 0 4 1 2
0 1 2 3 4
2 3 1 4 0
4 2 3 0 1
table [7, 3, 8, 1, 9, 4, 0, 5, 2, 6]
1134240024

flag{1134240024}