Week1

Web

Classic Childhood Game

纯前端实现 -> 代码审计
发现Events.js里有mota()加密函数,输入控制台得Flaghgame{fUnnyJavascript&FunnyM0taG4me}

Guess Who I Am

手撸。

Reverse

test your IDA

签到题。

easyasm

汇编白学,我估计后面的Week会有hardasm之类的题目。

题面:

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
; void __cdecl enc(char *p)
.text:00401160 _enc proc near ; CODE XREF: _main+1B↑p
.text:00401160
.text:00401160 i = dword ptr -4
.text:00401160 Str = dword ptr 8
.text:00401160
.text:00401160 push ebp
.text:00401161 mov ebp, esp
.text:00401163 push ecx
.text:00401164 mov [ebp+i], 0
.text:0040116B jmp short loc_401176
.text:0040116D ; ---------------------------------------------------------------------------
.text:0040116D
.text:0040116D loc_40116D: ; CODE XREF: _enc+3B↓j
.text:0040116D mov eax, [ebp+i]
.text:00401170 add eax, 1
.text:00401173 mov [ebp+i], eax
.text:00401176
.text:00401176 loc_401176: ; CODE XREF: _enc+B↑j
.text:00401176 mov ecx, [ebp+Str]
.text:00401179 push ecx ; Str
.text:0040117A call _strlen
.text:0040117F add esp, 4
.text:00401182 cmp [ebp+i], eax
.text:00401185 jge short loc_40119D
.text:00401187 mov edx, [ebp+Str]
.text:0040118A add edx, [ebp+i]
.text:0040118D movsx eax, byte ptr [edx]
.text:00401190 xor eax, 33h
.text:00401193 mov ecx, [ebp+Str]
.text:00401196 add ecx, [ebp+i]
.text:00401199 mov [ecx], al
.text:0040119B jmp short loc_40116D
.text:0040119D ; ---------------------------------------------------------------------------
.text:0040119D
.text:0040119D loc_40119D: ; CODE XREF: _enc+25↑j
.text:0040119D mov esp, ebp
.text:0040119F pop ebp
.text:004011A0 retn
.text:004011A0 _enc endp
Input: your flag
Encrypted result: 0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e

exp:

1
2
3
4
5
enc = [0x5b,0x54,0x52,0x5e,0x56,0x48,0x44,0x56,0x5f,0x50,0x3,0x5e,0x56,0x6c,0x47,0x3,0x6c,0x41,0x56,0x6c,0x44,0x5c,0x41,0x2,0x57,0x12,0x4e]

for i in enc:
print(chr(i^0x33),end = '')
# hgame{welc0me_t0_re_wor1d!}

easyenc

exp:

1
2
3
4
5
6
7
8
a = [0x04,0xFF,0xFD,0x9,0x01,0xF3,0xb0,0x0,0x0,0x05,0xf0,0xad,0x07,0x06,0x17,0x5,0xeb,0x17,0xfd,0x17,0xea,0x01,0xee,0x1,0xea,0xb1,0x05,0xfa,0x08,0x01,0x17,0xac,0xec,0x01,0xea,0xfd,0xf0,0x05,0x07,0x6]
ans = [0 for _ in range(48)]
len1 = 41

for i in range(40):
ans[i] = ((a[i]+86)&0xff) ^ 0x32
print(chr(ans[i]),end='')
# hgame{4ddit1on_is_a_rever5ible_0peration}

encode

exp:

1
2
3
4
5
enc = [8,6,7,6,1,6,13,6,5,6,11,7,5,6,14,6,3,6,15,6,4,6,5,6,15,5,9,6,3,7,15,5,5,6,1,6,3,7,9,7,15,5,6,6,15,6,2,7,15,5,1,6,15,5,2,7,5,6,6,7,5,6,2,7,3,7,5,6,15,5,5,6,14,6,7,6,9,6,14,6,5,6,5,6,2,7,13,7]

for i in range(len(enc)//2):
print(chr(enc[2*i+1]*16+enc[2*i]),end='')
# hgame{encode_is_easy_for_a_reverse_engineer}

a_cup_of_tea

TEA算法的逆向。

exp:

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
from ctypes import *
from Crypto.Util.number import *

k = [0x12345678,0x23456789,0x34567890,0x45678901]
enc = [0x2E63829D,0xC14E400F,0x9B39BFB9,0x5A1F8B14,0x61886DDE,0x6565C6CF,0x9F064F64,0x236A43F6]
v = [0,0]

def decrypt(v,k):
v0=c_uint32(v[0])
v1=c_uint32(v[1])
delta= -0x543210dd
sum1=c_uint32(delta*32)
for i in range(32):
v1.value-=((v0.value<<4)+k[2])^(v0.value+sum1.value)^((v0.value>>5)+k[3])
v0.value-=((v1.value<<4)+k[0])^(v1.value+sum1.value)^((v1.value>>5)+k[1])
sum1.value-=delta
return v0.value,v1.value

flag = []
for i in range(4):
v[0] = enc[i*2]
v[1] = enc[i*2+1]
m,n=decrypt(v,k)
flag.append(m)
flag.append(n)

f = ''
for i in flag:
ftmp = long_to_bytes(i).decode()
f1 = list(ftmp)
f1.reverse()
f += ''.join(f1)

print(f)
# hgame{Tea_15_4_v3ry_h3a1thy_drlnk}

还是搞不清大小端序。

Misc

e99p1ant_want_girlfriend

改长宽。

神秘的海报

LSB+音频隐写。
谷歌云盘文件的下载可以参考这篇文章
音频隐写考点是弱密码123456(我一开始尝试114514的)。

Week2

Reverse

before_main

变表base64,有啥东西在main函数前面?

在init里面注册了一个地址数组,点进去发现了sub_1228这个函数。

可以确定是变表了。

exp:

1
2
3
4
5
import base64
origin='AMHo7dLxUEabf6Z3PdWr6cOy75i4fdfeUzL17kaV7rG='
base64_table=str.maketrans('qaCpwYM2tO/RP0XeSZv8kLd6nfA7UHJ1No4gF5zr3VsBQbl9juhEGymc+WTxIiDK','ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/')
print(base64.b64decode(origin.translate(base64_table)))
# b'hgame{s0meth1ng_run_befOre_m@in}'

不仔细看/不动调的话会以为是原base64加密用的那个表(也是变表),主动防御了属于是。

stream

题面:兔兔假期前学习了编程,你能看出来他学的是什么语言吗

一眼Python。

使用pyinstxtractor工具解包,再反编译.pyc文件即可。

反编译后的.py代码如下:

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
import base64

def gen(key):
s = list(range(256))
j = 0
for i in range(256):
j = (j + s[i] + ord(key[i % len(key)])) % 256
tmp = s[i]
s[i] = s[j]
s[j] = tmp
i = j = 0
data = []
for _ in range(50):
i = (i + 1) % 256
j = (j + s[i]) % 256
tmp = s[i]
s[i] = s[j]
s[j] = tmp
data.append(s[(s[i] + s[j]) % 256])
return data


def encrypt(text, key):
result = ''
for c, k in zip(text, gen(key)):
result += chr(ord(c) ^ k)
result = base64.b64encode(result.encode()).decode()
return result

text = input('Flag: ')
key = 'As_we_do_as_you_know'
enc = encrypt(text, key)
if enc == 'wr3ClVcSw7nCmMOcHcKgacOtMkvDjxZ6asKWw4nChMK8IsK7KMOOasOrdgbDlx3DqcKqwr0hw701Ly57w63CtcOl':
print('yes!')
return None
None('try again...')

exp:

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
import base64

def gen(key):
s = list(range(256))
j = 0
for i in range(256):
j = (j + s[i] + ord(key[i % len(key)])) % 256
tmp = s[i]
s[i] = s[j]
s[j] = tmp
i = j = 0
data = []
for _ in range(50):
i = (i + 1) % 256
j = (j + s[i]) % 256
tmp = s[i]
s[i] = s[j]
s[j] = tmp
data.append(s[(s[i] + s[j]) % 256])
return data

key = 'As_we_do_as_you_know'
genkey = gen(key)
enc = 'wr3ClVcSw7nCmMOcHcKgacOtMkvDjxZ6asKWw4nChMK8IsK7KMOOasOrdgbDlx3DqcKqwr0hw701Ly57w63CtcOl'

dec = base64.b64decode(enc.encode()).decode()
flag = ''

for i in range(len(dec)):
flag += chr(ord(dec[i]) ^ genkey[i])

print(flag)
# hgame{python_reverse_is_easy_with_internet}

math

五阶矩阵乘法逆运算,回忆起了被线性代数支配的恐惧。

还好我有Python。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import numpy as np

x = np.array([[126,225,62,40,216],[253,20,124,232,122],[62,23,100,161,36],[118,21,184,26,142],[59,31,186,82,79]])
y = np.array([[63998,33111,67762,54789,61979],[69619,37190,70162,53110,68678],[63339,30687,66494,50936,60810],[48784,30188,60104,44599,52265],[43048,23660,43850,33646,44270]])

z = np.linalg.inv(x)

f = np.dot(y,z)
flag = ''

for i in np.nditer(f):
flag += chr(int(i+0.5))

print(flag)
# hgame{y0ur_m@th_1s_gO0d}

VidarCamera

安卓逆向,jadx打开,CameraActivity里发现伪Tea加密。

出题人保留Tea的大部分特征,但是也去除了一部分。

“是故意的还是不小心”
“是故意的”

exp:

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
from ctypes import *
from Crypto.Util.number import *

def decrypt(v, key):
v0, v1 = c_uint32(v[0]), c_uint32(v[1])
delta = 878077251

total = c_uint32(delta * 33)
for i in range(33): # 循环33次而非32次
total.value -= delta
v1.value -= (((v0.value << 4) ^ (v0.value >> 5)) + v0.value) ^ (total.value + key[(total.value>>11) & 3])
v0.value -= (((v1.value << 4) ^ (v1.value >> 5)) + v1.value) ^ (total.value + key[total.value & 3]) ^ total.value # 这里多了一点东西

return v0.value, v1.value

key1 = [2233,4455,6677,8899]

enc = [637666042,457511012,-2038734351,578827205,-245529892,-1652281167,435335655,733644188,705177885,-596608744]

i = 8
while i>=0:
k = [enc[i],enc[i+1]]
enc[i],enc[i+1] = decrypt(k,key1)
i -= 1

flag = ''

for i in enc:
ftmp = long_to_bytes(i).decode()
f1 = list(ftmp)
f1.reverse()
flag += ''.join(f1)

print(flag)
# hgame{d8c1d7d34573434ea8dfe5db40fbb25c0}

Crypto

Rabin

Rabin加密,参考这里

原理是中国剩余定理,可能有多解,选出适合的解即可。

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import gmpy2

def rabin_decrypt(c, p, q, e=2):
n = p * q
mp = pow(c, (p + 1) // 4, p)
mq = pow(c, (q + 1) // 4, q)
yp = gmpy2.invert(p, q)
yq = gmpy2.invert(q, p)
r = (yp * p * mq + yq * q * mp) % n
rr = n - r
s = (yp * p * mq - yq * q * mp) % n
ss = n - s
return (r, rr, s, ss)

p=65428327184555679690730137432886407240184329534772421373193521144693375074983
q=98570810268705084987524975482323456006480531917292601799256241458681800554123
c=0x4e072f435cbffbd3520a283b3944ac988b98fb19e723d1bd02ad7e58d9f01b26d622edea5ee538b2f603d5bf785b0427de27ad5c76c656dbd9435d3a4a7cf556
m = rabin_decrypt(c,p,q)
for i in range(4):
try:
print(bytes.fromhex(hex(m[i])[2:]))
except:
pass
# hgame{That'5_s0_3asy_to_s@lve_r@bin}

Misc

Tetris Master

手打俄罗斯方块。

crazy_qrcode

手算二维码(手挖比特币还会远吗)

题目给了一个压缩包和一个扫不了的二维码,尝试修改其纠错码和掩码,发现纠错码为H,掩码为4时可以扫描,得压缩包密码QDjkXkpM0BHNXujs。进压缩包一看好家伙,一个二维码被等分成25份,每一份随机进行0/90/180/270度旋转。按二维码特征(参照我写的这篇文章)可以恢复成下图。

然后根据“二维码数据从右下角开始向上填充”的原则勉勉强强一点点凑出下图。

可以用qrazybox扫出来了。Flag为hgame{Cr42y_qrc0de}

纯真说,可以用Python的Pillow库拼一下然后几张图放屏幕上一扫就出来了,大概我的能力还难以望纯真项背吧。

Week3

Reverse

坐牢局,一题10小时。Week4都没这么坐牢。
是我菜了。

kunmusic

题目给了kmusic.exe,kmusic.dll和kmusic.runtimeconfig.json。
exeinfope一下,知道了是.net框架的。
感谢纯真的提点,知道了.net框架的题目适合用dnSpy。

在kmusic-kmusic.ddl-kmusic-Program-Main()里发现疑似加密代码,动调后观察文件头发现是PE程序,内存窗口里dump出来再次拖入dnSpy得到真正的加密程序。

为了显示方便采用了VB语言,大体思路是用z3解方程然后再逆向解密。需要注意的是方程可能有多解,需要语义分析来增加约束条件。

exp1(z3):

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
from z3 import *
a,b,c,d,e,f,g,h,i,j,k,l,m = BitVecs('a b c d e f g h i j k l m',8)

x = Solver()
x.add(a + 52296 + b - 26211 + c - 11754 + (d ^ 41236) + e * 63747 + f - 52714 + g - 10512 + h * 12972 + i + 45505 + j - 21713 + k - 59122 + l - 12840 + (m ^ 21087) == 12702282)
x.add(a - 25228 + (b ^ 20699) + (c ^ 8158) + d - 65307 + e * 30701 + f * 47555 + g - 2557 + (h ^ 49055) + i - 7992 + (j ^ 57465) + (k ^ 57426) + l + 13299 + m - 50966 == 9946829)
x.add(a - 64801 + b - 60698 + c - 40853 + d - 54907 + e + 29882 + (f ^ 13574) + (g ^ 21310) + h + 47366 + i + 41784 + (j ^ 53690) + k * 58436 + l * 15590 + m + 58225 == 2372055)
x.add(a + 61538 + b - 17121 + c - 58124 + d + 8186 + e + 21253 + f - 38524 + g - 48323 + h - 20556 + i * 56056 + j + 18568 + k + 12995 + (l ^ 39260) + m + 25329 == 6732474)
x.add(a - 42567 + b - 17743 + c * 47827 + d - 10246 + (e ^ 16284) + f + 39390 + g * 11803 + h * 60332 + (i ^ 18491) + (j ^ 4795) + k - 25636 + l - 16780 + m - 62345 == 14020739)
x.add(a - 10968 + b - 31780 + (c ^ 31857) + d - 61983 + e * 31048 + f * 20189 + g + 12337 + h * 25945 + (i ^ 7064) + j - 25369 + k - 54893 + l * 59949 + (m ^ 12441) == 14434062)
x.add(a + 16689 + b - 10279 + c - 32918 + d - 57155 + e * 26571 + f * 15086 + (g ^ 22986) + (h ^ 23349) + (i ^ 16381) + (j ^ 23173) + k - 40224 + l + 31751 + m * 8421 == 7433598)
x.add(a + 28740 + b - 64696 + c + 60470 + d - 14752 + (e ^ 1287) + (f ^ 35272) + g + 49467 + h - 33788 + i + 20606 + (j ^ 44874) + k * 19764 + l + 48342 + m * 56511 == 7989404)
x.add(( a ^ 28978) + b + 23120 + c + 22802 + d * 31533 + (e ^ 39287) + f - 48576 + (g ^ 28542) + h - 43265 + i + 22365 + j + 61108 + k * 2823 + l - 30343 + m + 14780 == 3504803)
x.add(a * 22466 + (b ^ 55999) + c - 53658 + (d ^ 47160) + (e ^ 12511) + f * 59807 + g + 46242 + h + 3052 + (i ^ 25279) + j + 30202 + k * 22698 + l + 33480 + (m ^ 16757) == 11003580)
x.add(a * 57492 + (b ^ 13421) + c - 13941 + (d ^ 48092) + e * 38310 + f + 9884 + g - 45500 + h - 19233 + i + 58274 + j + 36175 + (k ^ 18568) + l * 49694 + (m ^ 9473) == 25546210)
x.add(a - 23355 + b * 50164 + (c ^ 34618) + d + 52703 + e + 36245 + f * 46648 + (g ^ 4858) + (h ^ 41846) + i * 27122 + (j ^ 42058) + k * 15676 + l - 31863 + m + 62510 == 11333836)
x.add(a * 30523 + (b ^ 7990) + c + 39058 + d * 57549 + (e ^ 53440) + f * 4275 + g - 48863 + (h ^ 55436) + (i ^ 2624) + (j ^ 13652) + k + 62231 + l + 19456 + m - 13195 == 13863722)
x.add(a == 236)
x.add(g ^ ord('g') == 89)
x.add(m ^ ord('v') == 243)

check = x.check()
print(check)

model = x.model()
print(model)

把上面的解再代入下面的加密代码即可。

exp2(decrypt):

1
2
3
4
5
6
7
8
num = [236,72,213,106,189,86,62,53,120,199,15,93,133]
a = [132, 47, 180, 7, 216, 45, 68, 6, 39, 246, 124, 2, 243, 137, 58, 172, 53, 200, 99, 91, 83, 13, 171, 80, 108, 235, 179, 58, 176, 28, 216, 36, 11, 80, 39, 162, 97, 58, 236, 130, 123, 176, 24, 212, 56, 89, 72]

flag = ''
for i in range(len(a)):
flag += chr(( a[i] ^ num[i%13] ))
print(flag)
# hgame{z3_1s_very_u5eful_1n_rever5e_engin3ering}

patch

问题应该出在gets函数的栈溢出漏洞,需要换成更安全的fgets。patch不出来啊问题是,ChatGPT也没用,只能找判断函数动调了。

按照出题人给的hint:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//检测是否patch成功的部分代码:
char to_stdin[73];
for(int i=0;i<36;i++)
{
to_stdin[i*2]='%';
to_stdin[i*2+1]='n';
}
to_stdin[72]='\n';
to_stdin[73]='\0';
write(writefd[1],to_stdin,74);
char stdout_arr[300]={0};
read(readfd[0],stdout_arr,300);
to_stdin[23]='\0';

if(strcmp(stdout_arr,to_stdin))
{
printf("\nthere are still bugs...\n");
}

然后一路狂飙去找长相差不多的汇编代码,报错直接跳,关键点在'%''n'这几个地方,然后改标志位进正确分支就可以了。

需要注意的是在init函数里注册了一个反动调的函数,原理是检测TracerId是否为0,不为0就给你掐了。在exit(0)的分支前面断一下改一下标志位就好了。

改标志位的位置在下面两个地方:

最后的Flag:

Week4

Reverse

shellcode

go写的玩意,看不懂但能做。如果动调的话需要在文件夹下创建一个inputdir文件夹并写入文件,出来的结果在outputdir文件夹里。逆向的目的就是根据outputdir里已有的flag.enc去找flag。

一个base64,解码完是视觉意义上的乱码。

喂给ChatGPT,它说是x86汇编,再多的它不会了。

喂给艾达女神,发现是TEA加密。

三分逆向七分猜,试了几次出了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
35
36
37
38
from ctypes import *
from Crypto.Util.number import *

def decrypt(v, k):
v0, v1 = c_uint32(v[0]), c_uint32(v[1])
delta = -0x543210DD
k0, k1, k2, k3 = k[0], k[1], k[2], k[3]

total = c_uint32(delta * 32)
for i in range(32):
v1.value -= ((v0.value<<4) + k2) ^ (v0.value + total.value) ^ ((v0.value>>5) + k3)
v0.value -= ((v1.value<<4) + k0) ^ (v1.value + total.value) ^ ((v1.value>>5) + k1)
total.value -= delta

return v0.value, v1.value

key = [22,33,44,55]
value = [0x20,0x69,0xB3,0xE4,0xD0,0x24,0x69,0x93,0x44,0xD1,0x16,0xA8,0xF5,0xD5,0x82,0xAA,0xDA,0xF0,0x79,0x36,0x06,0xFD,0x32,0x7F,0xD3,0xC0,0x60,0x34,0x39,0x49,0x21,0xB7,0xA2,0x69,0x72,0xE5,0xFA,0x51,0x6A,0x83]

cnt = 0
enc = []
flag = []
for i in range(len(value)//4):
v = value[i*4+3] * 16 ** 6 + value[i*4+2] * 16 ** 4 + value[i*4+1] * 16 ** 2 + value[i*4]
enc.append(v)
cnt += 1
if cnt % 2 == 0:
flag += decrypt(enc,key)
enc = []

f = ''
for i in flag:
ftmp = long_to_bytes(i).decode()
f1 = list(ftmp)
f1.reverse()
f += ''.join(f1)
print(f)
# hgame{th1s_1s_th3_tutu's_h0mew0rk}

vm

很清楚的虚拟机逆向。

Flag40字节,用judge函数去模拟虚拟机运行。

然后根据code里面的数据去模拟汇编指令,以case 0为例:

进入不同的分支则进行不同的操作,如0 2 0 3是将寄存器3的值mov到寄存器0中,而0 3 0 3则是将立即数3mov到寄存器0中。

由此,我们可以模拟出movpushpopaddsubxorshlshrcmpjmpjz/je等指令,也可以模拟读取等操作。

重要的是搞清楚a1所模拟的寄存器类型:e0、e1、e2、e3、e4、e5、ip、sp、zf,每32位取低位作为16位寄存器。这点可以从模拟shl的代码看出:

1
2
*(_DWORD *)(a1 + 4i64 * code[*(_DWORD *)(a1 + 24) + 2]) <<= *(_DWORD *)(a1 + 4i64 * code[*(_DWORD *)(a1 + 24) + 3]);// vm_shl
*(_DWORD *)(a1 + 4i64 * code[*(_DWORD *)(a1 + 24) + 2]) &= 0xFF00u;

在模拟pushpop的分支里我们可以找到模拟栈stack

另外注意每条指令执行完ip的增量(即指令长度),然后我们就可以把code里的vm机器码翻译成vm汇编并解读了。

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
0,   3,   2,   0,   mov e2,0
3, 0, 2, 3, add e2,e3
0, 0, 0, 0, read e0,input[e2]
0, 2, 1, 0, mov e1,e0
0, 3, 2, 50, mov e2,50
3, 0, 2, 3, add e2,e3
0, 0, 0, 0, read e0,input[e2(50)]
3, 0, 1, 0, add e1,e0
0, 3, 2, 100, mov e2,100
3, 0, 2, 3, add e2,e3
0, 0, 0, 0, read e0,input[e2(100)]
;前面部分为输入,以第0次读为例,执行完毕后e0=input[100],e1=input[0]+input[50],e2=100,e3=0(第0次读数)
3, 3, 1, 0, xor e1,e0
0, 3, 0, 8, mov e0,8
0, 2, 2, 1, mov e2,e1
3, 4, 1, 0, shl e1,e0(8) ;e1 &= 0x0000FF00
3, 5, 2, 0, shr e2,e0(8)
3, 0, 1, 2, add e1,e2
0, 2, 0, 1, mov e0,e1
1, 0, push e0
;加密并压栈,压栈数据为((((input[0]+input[50])^input[100])<<8)&ff00) + (((input[0]+input[50])^input[100])>>8)
;接着读下一个,直到读完所有40个数据开始判断
0, 3, 0, 1, mov e0,1
3, 0, 3, 0, add e3,e0
0, 2, 0, 3, mov e0,e3
0, 3, 1, 40, mov e1,40
4, cmp e0,e1 ;判断是否相等,相等为0,不等为1
6, 95, jz code[95] ;将ip设置为95(即跳过下面的jmp)
5, 0, jmp code[0] ;重头读
0, 3, 3, 0, mov e3,0 ;ip为95
;之后的指令判断依次pop出来的玩意是不是和input[150]及之后的数据对应相等,如相等继续判断下一个,不相等直接break
2, 1, pop e1 ;ip为99
0, 3, 2, 150, mov e2,150
3, 0, 2, 3, add e2,e3
0, 0, 0, 0, read e0,input[e2(150)]
4, cmp e0,e1 ;判断是否相等,相等为0,不等为1
7, 136, jnz code[136] ;将ip设置为136(retn)
0, 3, 0, 1, mov e0,1
3, 0, 3, 0, add e3,e0
0, 2, 0, 3, mov e0,e3
0, 3, 1, 40, mov e1,40
4, cmp e0,e1 ;判断是否相等,相等为0,不等为1
7, 99, jnz code[99] ;将ip设置为99(pop)
255, 255, retn ;返回最后一次判断的zf

根据条件爆破Flag。

exp:

1
2
3
4
5
6
7
8
9
10
11
a1 = [155,168,2,188,172,156,206,250,2,185,255,58,116,72,25,105,232,3,203,201,255,252,128,214,141,215,114,0,167,29,61,153,136,153,191,232,150,46,93,87]
a2 = [201,169,189,139,23,194,110,248,245,110,99,99,213,70,93,22,152,56,48,115,56,193,94,237,176,41,90,24,64,167,253,10,30,120,139,98,219,15,143,156]
enc = [18432,61696,16384,8448,13569,25600,30721,63744,6145,20992,9472,23809,18176,64768,26881,23552,44801,45568,60417,20993,20225,6657,20480,34049,52480,8960,63488,3072,52992,15617,17665,33280,53761,10497,54529,1537,41473,56832,42497,51713]

flag = ''
for i in range(40):
for ch in range(32,128):
if ((((ch+a1[i])^a2[i])<<8)&0xff00) + (((ch+a1[i])^a2[i])>>8) == enc[39-i]: # 压栈与弹栈的顺序相反
flag += chr(ch)
print(flag)
# hgame{y0ur_rever5e_sk1ll_i5_very_g0od!!}

写在最后

学到的东西越多,越发觉得自己学的东西还不够。
希望新的一年也能和Merak的各位共同努力。