记录一下关于Unity程序的基本逆向思路

Who is he

题目源于【SCTF2019】,考点为Unity程序逆向

dnspy分析

题目是Unity程序,只有一段视频,一个输入框和一个点击按钮

因为.NET应用把所有的C#代码都打包在了Assembly-CSharp.dll,所以把Who is he/Who is he_Data/Managed/Assembly-CSharp.dll文件放入到dnspy分析

可以看到程序只是判断了提交的数据与程序某一段密文的解密结果是否相同,其中数据加密流程如下,是一个CBC模式的DES,而且给出了key,iv(和key一样)和密文

image-20210321172137884

image-20210321172049937

直接写解密脚本

这里需要注意的是C#里面字符串默认是Unicode,每个字符后面补”\x00”,所以这里的key为”1\x002\x003\x004\x00”

1
2
3
4
5
6
7
8
9
10
import base64
from Crypto.Cipher import DES

key = "1\x002\x003\x004\x00"
des = DES.new(key, DES.MODE_CBC, key)
cipher = "1Tsy0ZGotyMinSpxqYzVBWnfMdUcqCMLu0MA+22Jnp+MNwLHvYuFToxRQr0c+ONZc6Q7L0EAmzbycqobZHh4H23U4WDTNmmXwusW4E+SZjygsntGkO2sGA=="
cipher = base64.b64decode(cipher)
plain = des.decrypt(cipher).decode()
print(plain)
# He_P1ay_Basketball_Very_We11!Hahahahaha!

但是这个flag是假的,所以这个Assembly-CSharp.dll其实打包的不是真正的源码,所以我们就得动态调式程序来获取真实值

cheat engine分析

开外挂器查看其内存,在ce中搜索弹窗里面出现的字符串”Emmmmm”,发现一共有两处

image-20210321200237714

两处Emmmmm的上下文我们都发现了不同的的密文和key

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import base64
from Crypto.Cipher import DES

key = "t\x00e\x00s\x00t\x00"
# key = "1\x002\x003\x004\x00"
des = DES.new(key, DES.MODE_CBC, key)
cipher = "1Tsy0ZGotyMinSpxqYzVBWnfMdUcqCMLu0MA+22Jnp+MNwLHvYuFToxRQr0c+ONZc6Q7L0EAmzbycqobZHh4H23U4WDTNmmXwusW4E+SZjygsntGkO2sGA=="
cipher = '78 00 5A 00 57 00 44 00 5A 00 61 00 4B 00 45 00 68 00 57 00 4E 00 4D 00 43 00 62 00 69 00 47 00 59 00 50 00 42 00 49 00 6C 00 59 00 33 00 2B 00 61 00 72 00 6F 00 7A 00 4F 00 39 00 7A 00 6F 00 6E 00 77 00 72 00 59 00 4C 00 69 00 56 00 4C 00 34 00 6E 00 6A 00 53 00 65 00 7A 00 32 00 52 00 59 00 4D 00 32 00 57 00 77 00 73 00 47 00 6E 00 73 00 6E 00 6A 00 43 00 44 00 6E 00 48 00 73 00 37 00 4E 00 34 00 33 00 61 00 46 00 76 00 4E 00 45 00 35 00 34 00 6E 00 6F 00 53 00 61 00 64 00 50 00 39 00 46 00 38 00 65 00 45 00 70 00 76 00 54 00 73 00 35 00 51 00 50 00 47 00 2B 00 4B 00 4C 00 30 00 54 00 44 00 45 00 2F 00 34 00 30 00 6E 00 62 00 55 00 3D 00'
# cipher = '71 00 2B 00 77 00 38 00 39 00 59 00 32 00 32 00 72 00 4F 00 62 00 66 00 7A 00 78 00 67 00 73 00 71 00 75 00 63 00 35 00 51 00 78 00 62 00 62 00 68 00 39 00 5A 00 49 00 41 00 48 00 45 00 54 00 2F 00 4E 00 6E 00 63 00 6D 00 69 00 71 00 45 00 6F 00 36 00 37 00 52 00 72 00 44 00 76 00 7A 00 33 00 34 00 63 00 64 00 41 00 6B 00 30 00 42 00 61 00 6C 00 4B 00 57 00 68 00 4A 00 47 00 6C 00 32 00 43 00 42 00 59 00 4D 00 6C 00 72 00 38 00 70 00 50 00 41 00 3D 00'
cipher = cipher.replace(' 00', '').split(' ')
cipher = ''.join([chr(int(_, 16)) for _ in cipher])
cipher = base64.b64decode(cipher)
plain = des.decrypt(cipher).decode()
print(plain)
# [right] She_P1ay_Black_Hole_Very_Wel1!LOL!XD!
# [tip] Oh no!This is a trick!!!

所以我们最终的flag是flag{She_P1ay_Black_Hole_Very_Wel1!LOL!XD!}

Snake

题目来源于【2019 红帽杯】

dnspy分析

用dnspy打开Assembly-CSharp.dll,发现它的接口都是在Interface.dll中引入的

image-20210322141353479

IDA分析

Interface.dll拖到IDA分析,找到输出flag的代码,代码量还挺大的,

image-20210322144354551

但是观察到该函数只接受一个a1参数,而且a1有一个约束条件,只在[0, 99]的范围内

1
__int64 __fastcall GameObject(int a1) { ... }

image-20210322144611115

所以我们可以直接导入这个ddl进行爆破

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
import ctypes
from multiprocessing import Pool, Manager

def func(d):

print(d)
dll = ctypes.WinDLL("Interface.dll")
dll.GameObject(d)

if __name__ == "__main__":

num = 5
data = range(100)
pool = Pool(processes=num)
manager = Manager()
jobs = []
for d in data:
job = pool.apply_async(func, (d, ))
jobs.append(job)
pool.close()
pool.join()

# 19
# You win! flag is
# flag{Ch4rp_W1th_R$@}

参考

SCTF2019 Writeup——De1ta