Unpack Game File (Snowbreak)

172|666

[UE4] Snowbreak: Containment Zone 参考大佬的解包教程

准备工作

UE Viewer | Gildor’s Homepage
Luigi Auriemma
CS.RIN.RU - Steam Underground Community • View topic - Collection of AES encryption keys for UE4/5 games AES key and quickbms scripts

  1. 下载 UE Viewer
  2. 下载 quickbms
  3. 获取尘白禁区的 AES key 和 quickbms scripts,文末提供的可能已经过时,因此建议去原网站查找下载

说明:UE Viewer可以对UE开发的游戏进行解包

  • 大部分游戏解包时还需要AES Key
  • 有的游戏使用customized packages,则还需要提前使用quickbms脚本提取资产。

游戏目录下\data\game\Game\Binaries\Win64\Game.exe —>Properties—>Details

可以看到尘白禁区使用UE4开发,版本为4.26.2(File Version)

image.png|222

开始解包

先将尘白禁区的资源文件使用quickbms scripts提取出来,需要用到quickbms_4gb_files.exeAES Keyunreal_tournament_4_0.4.27e_snowbreak.bms

注意:如果是从原网站下载的unreal_tournament_4_0.4.27e_snowbreak.bms文件,则需要修改set AES_KEY binary ""这一行,将AES key 填入" "之中

然后cmd中运行:(这一步有点久,耐心等待…)
.\quickbms_4gb_files.exe -o -F "{}.pak" .\unreal_tournament_4_0.4.27e_snowbreak.bms "input folder with paks" "output folder"

运行结束后,打开UE Viewer,在Path to game files: 中填入output folder的路径,Ovenide game detection选择游戏所依赖的UE版本,然后直接ok。(使用quickbms后就不需要在填写AES key了)

image.png|333

如果不在Ovenide game detection选择UE版本,则后续加载资源时需要选择

image.png|333

我勒个大雷啊!

image.png|666

后续

AES key and quickbms scripts

AES key:0xC14735FB5A872D2AFA76A5C38521AB8B8E21072C08525B913307608BD1182FA7
quickbms scripts:(文件名: unreal_tournament_4_0.4.27e_snowbreak.bms)

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
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
# Unreal Engine 4 - Unreal Tournament 4 (*WindowsNoEditor.pak) (script 0.4.27e)
# script for QuickBMS http://quickbms.aluigi.org

math NO_TAIL_INFO = 0 # set it to 1 for archives with corrupt/missing tail information (extract without index)
math VERSION = 3 # set it to 3 if NO_TAIL_INFO = 1 for most of modern games
math NEW = 1 # set to 0 for old Snowbreak versions

quickbmsver "0.12"
callfunction QUICKBMS_4GB_CHECK 1

# set your AES_KEY here as umodel hex ("0x1122...") or C string ("\x11\x22...")
# don't change AES_KEY_IS_SET, it will be handled automatically
set AES_KEY binary "0xC14735FB5A872D2AFA76A5C38521AB8B8E21072C08525B913307608BD1182FA7"

math TOC_FILE = 0
math ALTERNATIVE_MODE = 0
math AES_KEY_IS_SET = 0
math BASE_PATH_INCLUDED = 1
math DIR_FLAG = 1
math NAME_FROM_ARRAY = 0
math SKIP_COUNT = 0

get ARCHIVE_NAME basename
get ARCHIVE_PATH FILEPATH

math CHUNK_OFFSET_ABSOLUTE = -1 # default, enabled

# 1 = HIT
math WORKAROUND = 0

if NO_TAIL_INFO != 0
get OFFSET asize
math ALTERNATIVE_MODE = 1
else
goto -0xcc # version 11 (4.26-4.27)
savepos MAGIC_OFF
get MAGIC long

get VERSION long
endian guess VERSION
get OFFSET longlong
get SIZE longlong
getdstring HASH 20
math OFFSET ^ 0x1C1D1E1F
xmath SIZE "MAGIC_OFF - OFFSET - 1"

get FSIZE asize
savepos CUR_POS
if CUR_POS = FSIZE
string COMP1 = ""
else
get CHECK byte
if CHECK > 1
goto -1 0 SEEK_CUR
endif
getdstring COMP1 32
getdstring COMP2 32
string COMP1 l COMP1
string COMP2 l COMP2
endif

if VERSION >= 3
goto MAGIC_OFF
goto -1 0 SEEK_CUR
get ENCRYPTED byte
if ENCRYPTED != 0
callfunction SET_AES_KEY 1
log MEMORY_FILE5 OFFSET SIZE
encryption "" ""
else
log MEMORY_FILE5 OFFSET SIZE
endif
math TOC_FILE5 = -5
endif

goto 0
callfunction GET_BASE_PATH 1
endif

get FILES long TOC_FILE5
getdstring DUMMY 12 TOC_FILE5
get HASHES_OFFSET longlong TOC_FILE5
math HASHES_OFFSET - OFFSET
get HASHES_SIZE longlong TOC_FILE5
getdstring DUMMY 24 TOC_FILE5
get NAMES_OFFSET longlong TOC_FILE5
math NAMES_OFFSET - OFFSET
get NAMES_SIZE longlong TOC_FILE5
getdstring DUMMY 24 TOC_FILE5
savepos BASE_INDEX_OFF TOC_FILE5
goto NAMES_OFFSET TOC_FILE5

math CHUNK_SIZE = 0x10000 # just in case...
for i = 0 < FILES
callfunction GET_NAME_AND_OFFSET 1
if NAME = ""
continue NEXT0
endif

savepos TMP_OFF TOC_FILE

get OFFSET longlong TOC_FILE
get ZSIZE longlong TOC_FILE
get SIZE longlong TOC_FILE
get ZIP long TOC_FILE
getdstring HASH 20 TOC_FILE

math CHUNKS = 0
math ENCRYPTED = 0
if VERSION >= 3
if ZIP != 0
get CHUNKS long TOC_FILE
for x = 0 < CHUNKS
get CHUNK_OFFSET longlong TOC_FILE
get CHUNK_END_OFFSET longlong TOC_FILE
putarray 0 x CHUNK_OFFSET
putarray 1 x CHUNK_END_OFFSET
next x
endif
get ENCRYPTED byte TOC_FILE
get CHUNK_SIZE long TOC_FILE
endif

#if ALTERNATIVE_MODE != 0
savepos TMP_OFF TOC_FILE
math OFFSET + TMP_OFF
#endif

#comtype copy
callfunction COMPRESSION_TYPE 1

if CHUNKS > 0
log NAME 0 0
append
math TMP_SIZE = SIZE
if CHUNK_OFFSET_ABSOLUTE < 0 && OFFSET != 0
getarray CHUNK_OFFSET 0 0
if CHUNK_OFFSET u< OFFSET
math CHUNK_OFFSET_ABSOLUTE = 0
else
math CHUNK_OFFSET_ABSOLUTE = 1
endif
endif
for x = 0 < CHUNKS
getarray CHUNK_OFFSET 0 x
getarray CHUNK_END_OFFSET 1 x
math CHUNK_ZSIZE = CHUNK_END_OFFSET
math CHUNK_ZSIZE - CHUNK_OFFSET
math CHUNK_XSIZE = CHUNK_ZSIZE
if ENCRYPTED != 0
callfunction SET_AES_KEY 1
math CHUNK_XSIZE x 16
endif
if TMP_SIZE u< CHUNK_SIZE
math CHUNK_SIZE = TMP_SIZE
endif
math CHUNK_OFFSET = OFFSET

if ZIP == 0
log NAME CHUNK_OFFSET CHUNK_SIZE 0 CHUNK_XSIZE
else
clog NAME CHUNK_OFFSET CHUNK_ZSIZE CHUNK_SIZE 0 CHUNK_XSIZE
endif

math TMP_SIZE - CHUNK_SIZE
math OFFSET + CHUNK_XSIZE
next x
append

else
# the file offset points to an entry containing
# the "same" OFFSET ZSIZE SIZE ZIP HASH ZERO fields,
# just an additional backup... so let's skip them
savepos BASE_OFF TOC_FILE
math BASE_OFF - TMP_OFF
math OFFSET + BASE_OFF
math XSIZE = ZSIZE
if ENCRYPTED != 0
callfunction SET_AES_KEY 1
math XSIZE x 16
endif
if ZIP == 0
math BLOCK = 0x40000000
xmath FSIZE "OFFSET + ZSIZE"
log NAME 0 0
append
for OFFSET = OFFSET < FSIZE
xmath DIFF "FSIZE - OFFSET"
if DIFF < BLOCK
math XSIZE = DIFF
if ENCRYPTED != 0
math XSIZE x 16
endif
log NAME OFFSET DIFF 0 XSIZE
else
log NAME OFFSET BLOCK
endif
math OFFSET + BLOCK
next
append
else
clog NAME OFFSET ZSIZE SIZE 0 XSIZE
endif
endif
encryption "" ""

if ALTERNATIVE_MODE != 0
if CHUNKS == 0
math OFFSET + XSIZE
endif
goto OFFSET

get TMP1 longlong
get CHECK byte
if TMP1 == 0 && CHECK != 0
goto OFFSET
continue NEXT1
else
goto OFFSET
endif
xmath CHECK "0x800 - (OFFSET % 0x800)"
if CHECK <= 16
padding 0x800
endif

savepos OFFSET
get TMP1 longlong
get TMP2 longlong
if TMP2 == 0
padding 0x800
else
goto OFFSET
endif

label NEXT1
endif

label NEXT0
next i

print "\nEntries ignored: %SKIP_COUNT%"
for i = 0 < SKIP_COUNT
getarray NAME 7 i
print "Ignored entry: %NAME%"
next i

startfunction SET_AES_KEY_ASK
math AES_KEY_IS_SET = 1
print "The archive is encrypted, you need to provide the key"
if AES_KEY == ""
set KEY unknown "???"
else
set KEY binary AES_KEY
endif

if KEY == ""
math AES_KEY_IS_SET = -1
set AES_KEY string "No key provided, encryption disabled"
elif KEY strncmp "0x"
string KEY << 2
string AES_KEY h KEY
else
set AES_KEY binary KEY
endif

if NEW = 1
callfunction CONVERT 1
endif
print "KEY: %AES_KEY%"
endfunction

startfunction SET_AES_KEY
if AES_KEY_IS_SET == 0
callfunction SET_AES_KEY_ASK 1
endif
if AES_KEY_IS_SET > 0
encryption aes AES_KEY "" 0 32
endif
endfunction

startfunction GET_BASE_PATH
get NAMESZ long TOC_FILE5
getdstring BASE_PATH NAMESZ TOC_FILE5
if NAMESZ != 0x0A && NAMESZ < 0xFF
string BASE_PATH | "../../../"
math BASE_PATH_INCLUDED = 0
endif
endfunction

startfunction CHECK_UNICODE
if NAMESZ >= 0
getdstring RESULT NAMESZ TOC_FILE5
else
math NAMESZ n NAMESZ
math NAMESZ * 2
getdstring RESULT NAMESZ TOC_FILE5
set RESULT unicode RESULT
endif
endfunction

startfunction GET_NAME_AND_OFFSET
if NAME_FROM_ARRAY = 1
if CURR_NAME < DIR_FILES
getarray NAME 5 CURR_NAME
getarray OFFSET 6 CURR_NAME
goto OFFSET
math CURR_NAME + 1
if CURR_NAME = DIR_FILES
math NAME_FROM_ARRAY = 0
endif
endif
else
if DIR_FLAG = 1
get DIR_COUNT long TOC_FILE5
math DIR_FLAG = 0
endif

if DIR_COUNT = 0
math DIR_FLAG = 1
callfunction GET_NAME_AND_OFFSET 1
else
math DIR_COUNT - 1
get NAMESZ signed_long TOC_FILE5
callfunction CHECK_UNICODE 1
string DIR_NAME = RESULT
get DIR_FILES long TOC_FILE5
if DIR_FILES = 0
callfunction GET_NAME_AND_OFFSET 1
else
for y = 0 < DIR_FILES
get NAMESZ signed_long TOC_FILE5
callfunction CHECK_UNICODE 1
string NAME = RESULT
string NAME p "%s%s" DIR_NAME NAME
if BASE_PATH_INCLUDED == 0
string NAME p "%s%s" BASE_PATH NAME
endif
putarray 5 y NAME

get OFFSET long TOC_FILE5
savepos TMP_INDEX_OFF TOC_FILE5
if OFFSET != 0x80000000 && OFFSET != 0x7FFFFFFF
xmath INDEX_OFF "BASE_INDEX_OFF + OFFSET"
goto INDEX_OFF TOC_FILE5
get FLAGS long TOC_FILE5
xmath HAS_SIZE "FLAGS & 0x3F"
xmath IS_64 "FLAGS >> 28"
if HAS_SIZE = 0x3F
get CHUNK_SIZE long TOC_FILE5
endif
if IS_64 = 0xE
get OFFSET long TOC_FILE5
else
get OFFSET longlong TOC_FILE5
endif
math OFFSET ^ 0x1F1E1D1C
else
putarray 7 SKIP_COUNT NAME
math SKIP_COUNT + 1
string NAME = ""
putarray 5 y NAME
endif
putarray 6 y OFFSET
goto TMP_INDEX_OFF TOC_FILE5
next y
math NAME_FROM_ARRAY = 1
math CURR_NAME = 0
callfunction GET_NAME_AND_OFFSET 1
endif
endif
endif
endfunction

startfunction COMPRESSION_TYPE
if COMP1 = ""
comtype zlib
endif

if ZIP = 1 && COMP1 = "zlib"
comtype zlib
elif ZIP = 1 && COMP1 = "zstd"
comtype zstd
elif ZIP = 1 && COMP1 = "oodle"
comtype oodle
elif ZIP = 1 && COMP1 = "lz4"
comtype lz4
elif ZIP = 1 && COMP1 = "gzip"
comtype gzip
elif ZIP = 2 && COMP2 = "zlib"
comtype zlib
elif ZIP = 2 && COMP2 = "zstd"
comtype zstd
elif ZIP = 2 && COMP2 = "oodle"
comtype oodle
elif ZIP = 2 && COMP2 = "lz4"
comtype lz4
elif ZIP = 2 && COMP2 = "gzip"
comtype gzip
elif ZIP = 3 || ZIP = 4 || ZIP = 0x10 # 3 - Faith of Danschant, 4 - Days Gone, 10 - Ashen
comtype oodle
if WORKAROUND == 2
comtype lz4
endif
endif
endfunction

startfunction QUICKBMS_4GB_CHECK
math TMP64 = 0x10000000
math TMP64 * 16
if TMP64 == 0
print "You must use quickbms_4gb_files.exe with this script!"
cleanexit
endif
endfunction

startfunction CONVERT
get TMP_NAME basename
string TMP_NAME l TMP_NAME
encryption md5 TMP_NAME
encryption "" ""
set MEMORY_FILE binary QUICKBMS_HEXHASHL

encryption aes AES_KEY "" 1 32
log MEMORY_FILE2 0 32 MEMORY_FILE
encryption "" ""
goto 0 MEMORY_FILE2
getdstring AES_KEY 32 MEMORY_FILE2
endfunction

Welcome to my other publishing channels