• 逆向实战 | PvZ资源包逆向


    植物大战僵尸资源解密分析

    分析的时候定位main.pak字符串,具体的分析流程有点多,有空的时候整理好贴上。

    主要流程

    1. 对main.pak使用win32api进行大小等的判断,如果不存在就准备从资源中获取
    2. 通过一个主要的函数结构体对main.pak的文件头部分进行读取,在读取的过程中每个字节会xor 0xf7,然后做检测。
    3. 继续通过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
    }
    

    解压方式

    1. 先对main.pak逐字节xor 0xf7
    2. 然后逐个读取文件头信息保存
    3. 最后创建相应目录,释放文件

    解压脚本如下:

    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!')
    
    

    本文来自博客园,作者:Mz1,转载请注明原文链接:https://www.cnblogs.com/Mz1-rc/p/15265455.html

    如果有问题可以在下方评论或者email:mzi_mzi@163.com

  • 相关阅读:
    Java基础6
    多行文本框回车换行设置
    CentOS7系统配置国内yum源和epel源
    System Integrity Protection (SIP) iOS10.15安装软件提示文件损坏问题解决方法
    转载:Spring Boot 不使用默认的 parent,改用自己的项目的 paren
    微软Speech语音合成技术
    反编译工具
    线性链表的代码实现
    不一样的鸡汤,你有房吗?你有车吗?你有房车吗?
    递归,迭代和回调
  • 原文地址:https://www.cnblogs.com/Mz1-rc/p/15265455.html
Copyright © 2020-2023  润新知