标签:8V,lz77, nisi

最后排名第四,大哥们tql。

赛题质量还不错。第一天跟X-NUCA冲突了,第二天匆忙做了几道题。时间不够有几题题目都没看到

比赛时间:8月24-8月25日

题目下载地址

Reverse

8V

V8的汇编代码。

简单的异或加密,每次异或密钥的值都会改变

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
res = "\xd2\"\xf1\x8d\xb7\xe0\xd0MF\x87T?\x1fI\x1c\xe7\xcb\x07\xc3\x95z\xb3z\x0b\xbb\xdb\xa1I\xc5;"
res = map(ord,list(res))
a3 = 88
for i in range(30):
    res[i] ^= a3
    a3 *= 65
    a3 += 66
    a3 %=256
for i in range(30)[::-1]:
    res[i]^=a3
    a3 *=35
    a3 -=16
    a3 %=256
print("".join(map(chr,res)))

babyre

lz77压缩算法,写出解压算法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
f = open("output.file","rb")
g = open("input.file","wb")
bits = ""
i = 0
tmp = f.read(1)
tmpbuf = [0]*0x11
buf = [0]*0x1000
s = ""
k = 1
count = 1
debug = 0
while(True):

    bit = ord(tmp)
    if bit & 1<<(7-i):
        tmp1 = f.read(1)
        bit1 = ord(tmp1)

        bit<<=(i+1)
        bit &= 0xff
        bit |= (bit1>>(7-i))
        #print(hex(bit),i,hex(ord(tmp)),hex(ord(tmp1)))
        g.write(chr(bit))
        buf[count] = bit
        count+=1
        count %= 0x1000
        k+=1
        k%=0x1000
        s+=chr(bit)
        i = (i+1)%8
        if i == 0:
            tmp = f.read(1)
        else:
            tmp = tmp1
    else:
        tmp1 = f.read(1)
        tmp2 = f.read(1)
        bit1 = ord(tmp1)
        bit2 = ord(tmp2)
        m = (bit&((1<<(7-i))-1))<<(5+i)
        m&=0xFFF
        if i <=3:
            m |= (bit1 >>(3-i))
        else:
            m |= (bit1 << (i-3))
            m |= bit2 >> (8-(i-3))
        if i<=3:
            l = bit1 & ((1<<(3-i))-1)
            l <<= (1+i)
            l |= (bit2 >> (8-(1+i)))
        else:
            l = bit2 >> ((8-(i-3))-4)
        l &= 0b1111
        i = (i+1)%8
        if i == 0:
            tmp = f.read(1)
        else:
            tmp = tmp2
#         if debug<10:
#             print(l,m,hex(bit),i)
#             if debug == 6:
#                 print(buf)
#             debug+=1
#         else:
#             break
        l+=2
        for j in range(l):
            buf[(count+j)%0x1000] = buf[(m+j)%0x1000]
            #print(buf[(m+j)%0x1000])
            g.write(chr(buf[(m+j)%0x1000]))
        count+=l
        count%=0x1000
        k = 1

解压后打开bmp得到flag

King of KOF

一个KOF的安装包。一开始不知道安装包是nisi,硬调的。

注意到40B5A840BDA8是用来存放临时字符串的。

sub_401434是nisi的解释器。

安装后需要输入注册码。查找字符串查不到提示语。

用CE查找UTF16字符串,很快能找到提示语。在附近观察字符串,注意到一些比较有用的信息:这是注册码,注册码校验失败后的提示信息,fnCheckfnKoF97Base64Encode,一些提示输入flag的提示语,还有一串base64字符串:

1
cYZ1KIjhiR7Ol4RN9c0Xh7XryYfUD7A0m96h0/MQMI45mVhgTAnAtENpnzVKhfDZpVzfuiCBx5+BctkWo0GfU5qIYQV1bnFbNsQ3RVtc2Kg2xDdFW1zYqDbEN0VbXNioNsQ3RVtc2Kg2xDdFW1zYqDbEN0VbXNioNsQ3RVtc2Kg2xDdFW1zYqDbEN0VbXNioNsQ3RVtc2Kg2xDdFW1zYqDbEN0VbXNioNsQ3RVtc2Kg2xDdFW1zYqDbEN0VbXNioNsQ3RVtc2Kg2xDdFW1zYqDbEN0VbXNioNsQ3RVtc2Kg2xDdFW1zYqDbEN0VbXNioNsQ3RVtc2Kg2xDdFW1w=

b64decode再hexencoe得到:

1
7186752888e1891ece97844df5cd1787b5ebc987d40fb0349bdea1d3f310308e399958604c09c0b443699f354a85f0d9a55cdfba2081c79f8172d916a3419f539a886105756e715b36c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5cd8a836c437455b5c

可以看到,后面有一长串8个字符重复,估计都是同样的字符加密,padding长度是8,估计都是由'\x00'*8加密得到的密文。分组长度为8的加密一开始想到的就是des,但是没有密钥。

跟踪调试几步后,发现在40B5A840BDA8对比了输入和常量,常量是用UTF-16存储的,之前的字符串这是注册码就是注册码

之后校验flag的部分比较复杂,观察这两个地方的数据,注意到在user\AppData\Local\Temp\nsiF181.tmp目录创建了一些临时dll。同时从0-255遍历每个字符,可能是获取字符的ascii的utf-16值?遍历完所有后跟踪不久可以发现多了一个奇怪的KOF91.dll

复制出来ida打开,发现加了壳,不过是比较简单的壳。

重新开始调试,这次在输入flag之后,x86dbg选项中打开加载dll后中断,可以看到KOF97.dll被加载了,入口点在8000,81C4壳的代码执行完成。这时候就可以用插件把dlldump下来了。

dump后发现fnCheck与fnKoF97两个函数。这两个就是校验与加密函数,加密算法是TEA,密钥在算法内。用它来解密之前的密文,得到

1
dj_eyucjamkc]rm]rfc]emmb+mjb+b_wq{

发现前四个字符和flag字符偏移固定为2,是个凯撒密码,加密可能在之前的nisi里面。修正一下得到flag

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include "stdio.h"
#include "stdlib.h"
#include "stdint.h"

void decrypt (uint32_t v[2], uint32_t k[4]) {
    uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i;  /* set up; sum is 32*delta */
    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 key[4] = {0x20776F77,0x20756F79,0x646E6966,0x21656D20};
    uint8_t cipher[] = {113, 134, 117, 40, 136, 225, 137, 30, 206, 151, 132, 77, 245, 205, 23, 135, 181, 235, 201, 135, 212, 15, 176, 52, 155, 222, 161, 211, 243, 16, 48, 142, 57, 153, 88, 96, 76, 9, 192, 180, 67, 105, 159, 53, 74, 133, 240, 217, 165, 92, 223, 186, 32, 129, 199, 159, 129, 114, 217, 22, 163, 65, 159, 83, 154, 136, 97, 5, 117, 110, 113, 91, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168, 54, 196, 55, 69, 91, 92, 216, 168,0};
    int i;
    uint32_t *p = (uint32_t*)cipher;
    for(i = 0;i<30;i++)
    {
        decrypt(p,key);
        p+=2;
    }
    for(i = 0;i<240;i++)
    {
        if(cipher[i]!=0)
            printf("%c",cipher[i]+2);
    }
    printf("\n");
}

Mobile

mblockchain

jadx打开后看java代码。

需要输入key和flag。key算一次md5后,取其中的三个字符再md5,md5值作为aes的key加密flag,之后重复md5之前的哈希作为key继续加密9次,一共十次。爆破这三个字符即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
from Crypto.Cipher import AES
from hashlib import md5
foundpad = "\x10"*16

cipher = "74f0b165db8a628716b53a9d4f6405980db2f833afa1ed5eeb4304c5220bdc0b541f857a7348074b2a7775d691e71b490402621e8a53bad4cf7ad4fcc15f20a8066e087fc1b2ffb21c27463b5737e34738a6244e1630d8fa1bf4f38b7e71d707425c8225f240f4bd2b03d6c2471e900b75154eb6f9dfbdf5a4eca9de5163f9b3ee82959f166924e8ad5f1d744c51416a1db89638bb4d1411aa1b1307d88c1fb5".decode("hex")
"""
cipherpad = cipher[-16:]
for i in range(240,256):
    print(i)
    for j in range(256):
        for k in range(256):
            key = chr(i) + chr(j) + chr(k)
            ck = key
            for _ in range(10):
                ck = md5(ck).digest()
            aes = AES.new(ck,mode = AES.MODE_ECB)
            cp = aes.decrypt(cipherpad)
            if cp == foundpad:
                print(cp.encode("hex"))
                print(ck.encode("hex"))
                print(i,j,k)
                exit()
"""
key = chr(241) + chr(183) + chr(36)
subkey = []
ck = key
for i  in range(10):
    subkey.append(md5(ck).digest())
    ck = md5(ck).digest()
for i in range(10):
    ck = subkey[9-i]
    aes = AES.new(ck)
    cipher = aes.decrypt(cipher)
print(cipher)

评论