植物大战僵尸资源解密分析
分析的时候定位main.pak字符串,具体的分析流程有点多,有空的时候整理好贴上。
主要流程
- 对main.pak使用win32api进行大小等的判断,如果不存在就准备从资源中获取
- 通过一个主要的函数结构体对main.pak的文件头部分进行读取,在读取的过程中每个字节会xor 0xf7,然后做检测。
- 继续通过win32api对main.pak进行读取
主要结构
struct PAK{
DWORD magic = 0xbac04ac0; // 文件指纹
DWORD align = 0; // 对齐?
file_header file_header_table[x]; // 若干个file_header结构体
BYTE mark = 0x80; // 标志文件头描述结构体部分结束
FILE_CONTENT f[x]; // 文件内容若干,和文件头一一对应
}
// 文件头描述结构体
struct file_header{
BYTE mark; // 0x0代表文件 0x80文件头部分结束
BYTE file_name_length; // 文件名长度
char* file_name; // 文件名
DWORD file_size; // 文件大小
FILETIME file_time; // 文件时间结构体,应该是8bytes
}
解压方式
- 先对main.pak逐字节xor 0xf7
- 然后逐个读取文件头信息保存
- 最后创建相应目录,释放文件
解压脚本如下:
import struct
import os
fname = "main.pak"
outfile = "main.pak.xor"
file_path = ".\extract\"
def fmt(b): # 测试用16进制输出bytes数组
for i in b:
tmp = str(hex(i))[2:]
while len(tmp) < 2:
tmp = '0' + tmp
print(tmp,end=' ')
print()
class Pak:
def __init__(self, pak):
self.pak = pak # pak文件bytes数组
self.pointer = 0 # index指针
def walk(self, length):
b = self.pak[self.pointer: self.pointer+length]
self.pointer += length
return b
class Header:
def __init__(self, fname, fsize):
self.fname = fname
self.fsize = fsize
#==============================================================================
# 对文件进行预处理
'''
with open(fname, 'rb') as f:
tmp = f.read()
with open(outfile, 'wb') as f:
for i in tmp:
f.write((i^0xf7).to_bytes(1, 'little'))
print('output file ok!')
'''
# 在0x1A9D0附近达到文件头结尾
# 开始释放处理文件
print('start to process file...')
with open(outfile, 'rb') as f:
pak = f.read()
header = [] # header列表
magic = b'xC0x4AxC0xBAx00x00x00x00'
pak = Pak(pak) # 创建Pak对象
if pak.walk(8) == magic:
print(" > magic check ok!")
while 1:
mark = pak.walk(1)
if mark != b'x00': # 0x00 代表有文件 0x80表示没有
fmt(mark)
break
print(" > get a file: ",end='')
fname_len = pak.walk(1)[0]
fname = pak.walk(fname_len) # 文件名
print(fname)
fsize = struct.unpack("<I", pak.walk(4))[0] # 文件长度
print(" file size = " + str(fsize))
header.append(Header(fname,fsize)) # 添加到列表
pak.walk(8) # 时间戳
print("total: "+str(len(header))+ " files")
if not os.path.exists(file_path): # 不存在文件释放路径, 则创建
os.mkdir(file_path)
print(' > create dir ok!')
else:
print(" > file_path dir exist!")
for i in range(len(header)):
h = header[i] # 取出当前文件头信息
sfname = h.fname.decode()
p = sfname.split('\')
_p = file_path
for i in range(len(p)-1): # 递归创建路径
_p += p[i]
_p += '\'
# print(_p)
if not os.path.exists(_p): # 不存在文件释放路径, 则创建
os.mkdir(_p)
print(' > create dir {} ok!'.format(_p))
else:
print(" > dir exist!")
# 开始输出文件
for i in range(len(header)):
h = header[i]
fname = file_path + h.fname.decode()
fsize = h.fsize
print(" > excract: "+ fname)
with open(fname, 'wb') as f:
tmp = pak.walk(fsize)
f.write(tmp)
print(" ok!")
打包的脚本也写了一个:
import struct
import os
fname = "main.pak.xor"
outfile = "main.pak"
pack_dir = ".\extract"
class Header:
def __init__(self, fname, fsize):
self.fname = fname
self.fsize = fsize
def get_file(_dir):
_file = []
try:
tmp = os.listdir(_dir)
print("遍历文件夹"+_dir)
for i in tmp:
i = _dir +'\'+ i
if os.path.isdir(i):
_file += get_file(i)
else:
_file.append(i)
except:
pass
return _file
files = get_file(pack_dir)
print("total:" + str(len(files)) + " files")
header = []
for i in range(len(files)):
header.append(Header((files[i].replace(pack_dir,''))[1:], os.path.getsize(files[i]))) # 录入文件头信息
print(' > start to pack!')
output = open('main.pak.xor', 'wb')
magic = b'xC0x4AxC0xBAx00x00x00x00'
output.write(magic) # 写入文件头
for i in range(len(files)):
output.write(b'x00') # 表示有文件
output.write(struct.pack("<B", len(header[i].fname))) # 文件名长度
output.write(header[i].fname.encode()) # 文件名
output.write(struct.pack("<I", header[i].fsize)) # 文件大小
output.write(bytes.fromhex('8543CFCC0EFCCB01')) # 时间戳 应该不影响
output.write(b'x80') # 写结束标志
# 开始输出文件
for i in range(len(files)):
print(' > pack file: '+ files[i])
with open(files[i], 'rb') as f:
tmp = f.read()
output.write(tmp)
output.close()
print(' > ok!')
with open(fname, 'rb') as f:
tmp = f.read()
with open(outfile, 'wb') as f:
for i in tmp:
f.write((i^0xf7).to_bytes(1, 'little'))
print('output file ok!')