初音未来の消失

hgame_2019 write up

Reverse

wakarimasu

wakarimasu

本质是解两个矩阵,一个是乘法矩阵求逆,另外一个仅仅是矩阵的加减

由于矩阵求逆的时候存在小数精度问题,故用计算机算出来的值不会精确到整数,但是一旦你看到那个结果就一定会知道你算对了

算矩阵的网站:
https://zh.numberempire.com/matrixcalculator.php

https://zh.numberempire.com/matrixbinarycalculator.php

A =

8 1 7 1 1 0
4 8 1 2 3 9
3 8 6 6 4 8
3 5 7 8 8 7
0 9 0 2 3 4
2 3 2 5 4 0

B =

122 207 140 149 142 168
95 201 122 145 136 167
112 192 127 137 134 147
95 207 110 134 133 173
136 212 160 162 152 179
121 193 126 126 119 147

c =

16 8 8 14 6 11
5 23 5 10 12 23
14 23 19 7 8 10
4 13 22 17 11 22
6 14 2 11 18 9
5 8 8 10 16 13

X1 * B = A

X2 + B = C

X1=AB^-1

=

5.999951 5.999888 6.000136 5.999828 5.999879 7.000097
2.999953 4.999902 7.000126 5.999842 5.999886 6.000086
5.999953 4.999893 4.000127 5.99984 6.99989 7.000089
2.999956 6.999908 5.000125 5.999846 6.999877 5.000089
6.999948 5.999879 7.000144 6.999816 4.999875 7.000104
6.999956 5.999896 6.000123 2.999842 5.999887 7.000091

x2 = C - B

=

8 7 1 13 5 11
1 15 4 8 9 14
11 15 13 1 4 2
1 8 15 9 3 15
6 5 2 9 15 5
3 5 6 5 12 13

再将X1与X2结合(X1为高四位,X2为低四位)

#include<cstdio>
int a[]={6,6,6,6,6,7,3,5,7,6,6,6,6,5,4,6,7,7,3,7,5,6,7,5,7,6,7,7,5,7,7,6,6,3,6,7};
int b[]={8,7,1,13,5,11,1,15,4,8,9,14,11,15,13,1,4,2,1,8,15,9,3,15,6,5,2,9,15,5,3,5,6,5,12,13};

int main(){
    for(int i = 0; i <= 35; i++)
        printf("%c", (a[i]<<4) + b[i]);

} 

xor

xor

题目本身难度不大,就是异或即可,但是这个地方IDA有点问题,本来程序定义了一个数组,但是IDA里面的显示为一个变量,通过计算偏移值可以算出它是数组,这个地方以后要多加注意。

#include<cstdio>
char a[50]="hgame{Y0u_mayb3_need_th1s_0ne!!!!!}";
int b[50]={0,0,0,0,0,0,1,0,7,0,92,18,38,11,93,43,11,23,0,23,43,69,6,86,44,54,67,0,66,85,126,72,85,30};
int main(){
    for(int i = 0; i < 35; i++)
        printf("%c", a[i] ^ b[i]);
    }

Pro的Python教室(二)

secend 很简单的一道题,python入门 使用如下网站进行反编译: https://tool.lu/pyc/ 得到

print "Welcome to Processor's Python Classroom Part 2!\n"
print "Now let's start the origin of Python!\n"
print 'Plz Input Your Flag:\n'
enc = raw_input()
len = len(enc)
enc1 = []
enc2 = ''
aaa = 'ioOavquaDb}x2ha4[~ifqZaujQ#'
for i in range(len):
    if i % 2 == 0:
        enc1.append(chr(ord(enc[i]) + 1))
        continue
    enc1.append(chr(ord(enc[i]) + 2))

s1 = []
for x in range(3):
    for i in range(len):
        if (i + x) % 3 == 0:
            s1.append(enc1[i])
            continue

enc2 = enc2.join(s1)
if enc2 in aaa:
    print "You 're Right!"
else:
    print "You're Wrong!"
    exit(0)

解密代码:

aaa = 'ioOavquaDb}x2ha4[~ifqZaujQ#'
list = list(aaa)
len = len(aaa)

dec1 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
dec2 = ''
cnt = 0
for x in range(3):
    for i in range(len):
        if (i + x) % 3 == 0:
            dec1[i] = list[cnt]
            cnt += 1
            continue

for i in range(len):
    if i % 2 == 0:
        dec2 = dec2 + chr(ord(dec1[i]) - 1)
    else:
        dec2 = dec2 + chr(ord(dec1[i]) - 2)

print(dec2)

maze

maze 迷宫题,用正确的方式走出迷宫即可,wsad分别控制上下左右

aaa = """11111111111111111111111111111111111111111111111111111111111111111111...............111111111111111...............111
    111111111111.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111
    .111111111111111.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111s111111111111111.111111111
    1111s111111111111111.11111111111111111111111111111.11111111111111111111111111111.11111111111111111111111111111.11111
    111111111111111111111111.11111111111111111111111111111.1111.111111111111111111111111.1t............111111111111111.1
    t............111111111111111.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111.1111111111111
    11.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111.111111111
    111111.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111.111111111111111.1111111111111.11111
    1111111111.1111111111111.111111111111111...............111111111111111...............1111111111111111111111111111111
    111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"""

cnt = 0
map = ""

for c in aaa:
    if(c != '\n' and c != ' '):
        map = map + c
        cnt += 1
        if(cnt % 60 == 0):
            map = map + '\n'

print(map)

得到如下地图:

111111111111111111111111111111111111111111111111111111111111
11111111...............111111111111111...............1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111s111111111111111.1111111111111s1111111
11111111.11111111111111111111111111111.111111111111111111111
11111111.11111111111111111111111111111.111111111111111111111
11111111.11111111111111111111111111111.1111.1111111111111111
11111111.1t............111111111111111.1t............1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111.1111111111111.111111111111111.1111111111111.1111111
11111111...............111111111111111...............1111111
111111111111111111111111111111111111111111111111111111111111
111111111111111111111111111111111111111111111111111111111111

走出迷宫即可: wwwwaaaaaaaaaaaaaasssssssssssssssddddddddddddddwwwwwwwaaaaaaaaaaaa

HappyVM

happyVM

第一次做VM题,花了很多时间,最终还是有所收获

首先逆向虚拟机的操作数,操作数无非几个操作:压栈,出栈,寄存器与内存之间的读写,寄存器与寄存器之间的赋值

使用如下代码配合ida python获得所有操作数:

addr = 0x400D40
cnt = 0x78-0x40+1
i = addr
print "********my calc********"
print "********my calc********"
print "********my calc********"
print "********my calc********"
print "********my calc********"
while(i <= addr + cnt -1):
        c = int(Byte(i))
            if(c in [0x0,0x8,0x9,0xA,0xC,0xD,0xE,0x11,0x13,0x14]):
                i += 1
                print "opt:" , hex(c) , "   data:",  hex(Byte(i))
            else:
                print "opt:" , hex(c)
                i += 1

得到如下指令:

0(0x00) : opt: 0x11    data: 0x2d
2(0x01) : opt: 0x0    data: 0x22
4 : opt: 0x5
5 : opt: 0x10
6 : opt: 0x14    data: 0x9
8 : opt: 0x17
9(0x9) : opt: 0x0    data: 0x32
11 : opt: 0x5
12 : opt: 0x3
13 : opt: 0x11    data: 0x16
15 : opt: 0x6
16 : opt: 0x0    data: 0x16
18 : opt: 0x5
19 : opt: 0x11    data: 0x16
21 : opt: 0x17
22(0x16) : opt: 0xe    data: 0x1
24 : opt: 0x15
25 : opt: 0x4
26 : opt: 0xf
27 : opt: 0x1
28 : opt: 0x16
29 : opt: 0x2
30 : opt: 0x0    data: 0x0
32 : opt: 0x4
33 : opt: 0x3
34 : opt: 0x5
35 : opt: 0x10
36 : opt: 0x14    data: 0x2b
38 : opt: 0x5
39 : opt: 0x9    data: 0x3
41 : opt: 0x13    data: 0x16
43(0x2b) : opt: 0x5
44 : opt: 0x12

45(0x2d) : opt: 0x15
46 : opt: 0x4
47 : opt: 0x10
48 : opt: 0x14    data: 0x36
50 : opt: 0xa    data: 0x1
52 : opt: 0x13    data: 0x2d

54(0x36) : opt: 0x3
55 : opt: 0x4
56 : opt: 0x12

逐行翻译,其中部分压栈与出栈操作可以合并为内存与寄存器,寄存器与寄存器之间的数据交换,翻译得到如下代码:

    simplify:

    call 0x2d;//strlen(str)
    B = 0x22;
    if(A != B) return;//字符串长度为0x22
0x9:
    B = 0x32;
    push(C);//此时C = 0x22
    call(0x16);
    C = pop();
    B = 0x16;
    call(0x16);
    return;

0x16:
    C--;//倒序处理整个字符串
    A = str[C];
    A ^= B; //str[C] = str[C] ^ B ,而且B每次加3
    str[C] = A;
    push(B);
    A = 0;
    B = C;
    if(A == B){//如果这个字符串已经处理完成
        goto 0x2b;
    }else{
        B = pop();
        B += 3;
        goto 0x16;
    }

0x2b:
    B = pop();

0x2d:
    do{
        A = str[C];
        if(A == B) break;
        C++;
    }while(A != B)

    A = C;
    ret;

最后获取到s2的数据,solve

str = [132 , 131 , 157 , 145 , 129 , 151 , 215 , 190 , 67 , 114 , 97 , 115 , 115 , 12 , 106 , 112 , 115 , 17 , 72 , 44 , 52 , 51 , 49 , 54 , 35 , 52 , 62 , 92 , 35 , 78 , 23 , 17 , 25 , 89 ]
B = 0x16
for i in range(0x21, -1, -1):
    str[i] = str[i] ^ B
    B += 3

B = 0x32
for i in range(0x21, -1, -1):
    str[i] = str[i] ^ B
    B += 3

for i in range(0x22):
    print(chr(str[i]),end="")

ShinyShot!

utf-8' 'ShinyShot!

这个程序主要是利用了smc(动态代码修改技术)

首先我们向程序里面输入一个数,然后这个程序利用这个数去修改了一些程序的代码

然后输入一个字符串,将字符串base64加密后得到的密文与程序尾所保存的字符串进行比较,如果相同,则flag正确

程序在我们输入了一个数字之后

执行了

*((_BYTE *)TopLevelExceptionFilter + (cout >> 3)) ^= 1 << (cout & 7);

这个指令,TopLevelExceptionFilter的意义暂时不明白,大概就是一个指向某个内存的指针,简记为T, 值为0x401000

程序把*(p + (cout >> 3)) 处的数据异或了一个1 << (cout & 7)

其实1 << (cout & 7)就是将1左移了几位

我们看到后面的

.text:004018DE EB 15             jmp     short loc_4018F5
.text:004018E0                   ; ---------------------------------------------------------------------------
.text:004018E0
.text:004018E0                   loc_4018E0:                   ; CODE XREF: sub_4017AA+126↑j
.text:004018E0 E8 A7 FE FF FF    call    sub_40178C
.text:004018E5                   ; ---------------------------------------------------------------------------
.text:004018E5
.text:004018E5                   loc_4018E5:
.text:004018E5 8D 85 40 FF FF FF lea     eax, [ebp-0C0h]
.text:004018EB 89 44 24 04       mov     [esp+414h+var_410], eax ; char *
.text:004018EF 8D 45 A4          lea     eax, [ebp+Buf]
.text:004018F2 89 04 24          mov     [esp+414h+var_414], eax ; char *
.text:004018F5
.text:004018F5                   loc_4018F5:                   ; CODE XREF: sub_4017AA+134↑j
.text:004018F5 E8 00 FC FF FF    call    sub_4014FA

0x 004018DE jmp 15 的意义是,除开jmp 15,往后数0x15个字节,将程序运行指针设置到此处。

而sub_4014FA是一个需要两个参数的函数,没有参数的传入,函数一定不会有正确答案。

所以我们应当将 jmp 15改为jmp到传入参数的位置

经过计算jmp 5 即可,现在我们的工作是要把0x004018DF处的15修改为5

0x15 = ‭00010101‬

0x5 = 00000101

也就是把第五位的1抹掉就行了,之前那个不明意义的内存修改就是这个用处了。

num = (0x8df<<3) + 0x4 = 18172

后面是一个base64加密,但闲鱼本人并不是密码学选手,太菜了,所以暂时跳过。。

最后将改版的base64加密后的字符串与程序内的进行比较,得到flag

退出移动版