由于今天研究的游戏的题材属于历史类题材,再加上其他某些原因,故本教程对研究对象不便透漏,游戏名称已进行打码处理。
有坛友问我,干嘛非要解包游戏?难道只为了做mod版?我回答道:做解包是为了研究游戏程序的安全性,解包工具和分析文章只不过是研究游戏软件安全的副产品,而非让某些人取搞什么mod版,这也是我为什么拒绝透露游戏名称的原因。在此我要提醒大家:
本文仅限技术交流和软件安全性评估,切勿用于非法目的!
第一次写技术文章,技术有所欠缺,请见谅!大佬勿喷!
好了,文章开始!
20年前的一个暑假,我的父亲像平常一样下班回到家。我看到父亲提着一个纸袋,好奇地问:“爸爸,这里面是什么东西啊?”父亲从纸袋里拿出一个软件盒子,盒子上印着一位中国战士正在100年前的华东地区重要城市英勇抗击敌人的画面。父亲说:“给你,快打开放到电脑光驱里安装吧,这是从同事那儿借来的枪战游戏。”接着,父亲又递给我一张纸条,告诉我这是游戏的作弊码,要想看剧情但是卡关了就用作弊码。我启动了游戏,里面逼真的场景、激烈的交火画面,尤其是启用作弊码后能够零伤害杀敌,这让9岁的我感到无比快乐,无显示释放多巴胺,这一切都深深地烙印在我的记忆里,终生难忘……
三年以后,我小学六年级,又多次重温了这款游戏。当时,玩完游戏后的我,很想听游戏的特效声音文件,于是对游戏根目录翻来覆去的查找,根本就没有音频,突然,一个200MB的文件引起了我的注意,我想,资源文件肯定压缩在了这个大文件里。接着,我就到某度上搜索如何解压,有没有解压工具,搜索了很久,一无所获……
20年后的今天,我自制了一款个人账号台账管理软件《吾爱记账号》,其中账号数据的存储层就用到了文件首尾特征字符+Zlib压缩的存储方案,这也是现在市面上的应用软件最常用的压缩算法之一。
于是乎,我心想:既然我现在才知道用Zlib来保存数据,那么20年前的国产游戏会不会把Zlib作为一种高水平技术的游戏Pack文件打包方式呢?
那好,别从这儿傻站着纸上谈兵,开始行动!
需要准备的工具:
1、游戏本体(必备)
2、010 Editor(或者与其功能相同的Hex十六进制字节编辑软件,本文以010 Editor为例)
3、Visual Studio 2022(只要支持.NET Framework 4.5及以上的开发框架,其他版本的IDE也可)
4、IDA(如果要修改EXE实现加载解包后的PCK文件就需要用到)
首先,我们要获取游戏本体,随着全社会对破解版软件的打击力度的持续加大,再加上这款游戏年代久远,很多下载站已经把下载链接删除了,找的我好艰辛。最终,我从某云游戏平台的云电脑上预装的单机游戏提取到了游戏本体,打包压缩,用微信传回自己的电脑。
打开游戏安装文件夹
001.png (187.09 KB, 下载次数: 0)
下载附件
2024-10-30 16:08 上传
懂游戏懂电脑的同学们已经看出来了,我们不难发现,游戏公司只把背景音乐和过场动画视频留在了外面(如下图蓝框所示),而游戏的角色mod、地图、音效、UI界面都打包在了一个与游戏EXE文件同名的PCK文件中(如下图红框所示),这就是我们今天要研究的重点。
002.png (187.2 KB, 下载次数: 0)
下载附件
2024-10-30 16:08 上传
打开软件010 Editor,将文件sxxxxxai.pck拖入其中
003.png (190.01 KB, 下载次数: 0)
下载附件
2024-10-30 16:17 上传
我们发现,文件前面部分几乎都是乱码,不要着急,因为许多游戏Pack文件喜欢压缩数据,并把内部文件目录放在后面,也有的喜欢对其十六进制字节进行异或运算(即XOR)后存储(至于与什么数值异或运算就需要动态调试游戏EXE了)。
不过这都不是重点,接下来我们进行一个大胆的猜想,这款国产射击游戏诞生于20年前国人对数据安全意识相对薄弱、计算机技术刚刚起步、家用计算机的硬件性能普遍低下的时代,那么这个游戏不太可能使用很先进很高端的加密,甚至谈不上加密。刚才我提到过,Zlib是一种在应用软件中非常常用的压缩算法,鉴于此,这个Pack文件不排除会使用Zlib压缩算法的可能。
先来了解Zlib头部特征,通过借助AI大数据模型,向AI提问,AI回答告诉我,Zilb头部可能有三种,并提供了参考资料,AI给的参考资料在文章后面给出。
这三种头部如下表所示
文件头 压缩方式
78 01 No Compression/low
78 9C Default Compression
78 DA Best Compression
第一种,表示无压缩或低压缩率压缩;第二种,表示保持默认压缩;第三种,表示最好的压缩率压缩。
咦?第一个怎么这么眼熟?这不就是PCK文件头部吗?
004.png (190.1 KB, 下载次数: 0)
下载附件
2024-10-30 16:19 上传
此时相信大家的思路已经有点眉目了,这个Pack文件的压缩算法正是Zlib!
然后我们再做一个猜想,我们知道,计算机程序在加载数据的时候如果有压缩就得先解压缩才能加载数据,所以我们可以判断即便使用的是Zlib压缩也不能先拼和数据再压缩,这样会大大消耗内存资源,更何况在当年计算机技术水平不发达的年代,它一定是先压缩再拼接数据。
为了验证这个想法的真实性,我们先做个实验。
按Ctrl + F,如图所示,调出搜索框,将搜索类型设置为“Hex Bytes”,输入文件头部前两个十六进制字节78 01,按回车搜索。
005.png (196.41 KB, 下载次数: 0)
下载附件
2024-10-30 16:19 上传
这时候,010 Editor为我们列出了在当前PCK文件中十六进制字节78 01出现的次数及所在位置。共出现了5129次。
006.png (196.25 KB, 下载次数: 0)
下载附件
2024-10-30 16:20 上传
我们不妨尝试一下,从文件的0000h位置开始,到第二次出现十六进制字节78 01前面的地方选中复制,这时010 Editor的底部状态栏告诉我们选中了664个字节,十六进制数量为298h,还告诉我们起始、终止位置等信息,我说这些虽然现在没用,但是在后面是要派上用场的。
007.png (183.93 KB, 下载次数: 0)
下载附件
2024-10-30 16:20 上传
按快捷键Ctrl + Shift +N,新建一个Hex形式的文件,并将刚才选中的内容复制过来,如图所示。
008.png (155.39 KB, 下载次数: 0)
下载附件
2024-10-30 16:20 上传
保存文件,因为我们现在还不知道文件是什么格式,所以拓展名可以随便写,以保存到游戏根目录下为例,文件命名为Untitled1.bin。
009.png (70.09 KB, 下载次数: 0)
下载附件
2024-10-30 16:20 上传
保存后,我们来尝试解压这个文件,解压Zlib的方法有很多,我们这里就以C#为例进行解压。尽管C#的系统自带库支持Zlib解压,不过我感觉78 01开头的Zlib拿到系统自带的类库来解压会有兼容性风险,解压不出来,所以为了稳妥起见,决定使用免费开源、兼容稳定的第三方NuGet包——DoNetZip。
打开Visual Studio 2022,新建一个C#项目,引入NuGet包DoNetZIP,写入如下代码:
010.png (231.24 KB, 下载次数: 0)
下载附件
2024-10-30 16:20 上传
[C#] 纯文本查看 复制代码
using Ionic.Zlib;// 此为第三方NuGet包DotNetZip的引用,请自行安装
using System.IO;
namespace UnZlib
{
internal static class Program
{
///
/// 程序主函数
///
static void Main()
{
// 输入和输出文件名
string infilePath = "Untitled1.bin";
string outfilePath = Path.GetFileName(infilePath)+ "_Decompresed"+Path.GetExtension(infilePath);
// 将欲解压的文件载入到字节集数组
byte[] inputByte = File.ReadAllBytes(infilePath);
// 调用Zlib并解压数据
byte[] outputByte = Zlib.DeZlibcompress(inputByte);
// 写到文件
File.WriteAllBytes(outfilePath, outputByte);
}
///
/// Zlib操作类(本文章只涉及解压,压缩操作略)
///
public static class Zlib
{
///
/// Zlib解压缩
///
///
///
public static byte[] DeZlibcompress(byte[] data)
{
// 创建数据输入流和输出流
using (var compressedInput = new MemoryStream(data))
using (var decompressedOutput = new MemoryStream())
{
// 执行解压吃粽子
using (var decompressor = new ZlibStream(compressedInput, Ionic.Zlib.CompressionMode.Decompress))
{
decompressor.CopyTo(decompressedOutput);
}
//返回已处理的数据
return decompressedOutput.ToArray();
}
}
}
}
}
编写后,运行,生成了文件Untitled1_Decompresed.bin
011.png (20.57 KB, 下载次数: 0)
下载附件
2024-10-30 16:22 上传
将这个文件拖入到010 Editor。我们看到,解压后文件的Hex内容已经不是那么杂乱无章的了,很有顺序。显然,这大概率是一个图像文件,我们尝试修改拓展名为JPG,并使用图片浏览器来查看。
012.png (104.11 KB, 下载次数: 0)
下载附件
2024-10-30 16:22 上传
然而…现实给了我一记耳光。
013.png (42.78 KB, 下载次数: 0)
下载附件
2024-10-30 16:22 上传
图片无法读取,但不能就这么快下结论,来问一下AI吧。
问了AI,告诉我说这是TGA格式的图片。
014.png (56.98 KB, 下载次数: 0)
下载附件
2024-10-30 16:23 上传
改了文件拓展名,用第三方图片浏览器查看,一个游戏光标浮现在眼前,完美解压!
015.png (99.17 KB, 下载次数: 0)
下载附件
2024-10-30 16:23 上传
有的同学积极地回答道:这个Pack文件包里面有5129个文件!
我不是喜欢给别人泼冷水的人,但是我可以告诉你,它到底是不是有5129个文件真的不一定。
毕竟Zlib在压缩解压操作时受算法、配置等多重因素的影响,必出现偶然的状况,字节数据分割不对是无法解压的。
这个时候怎么办?
我们就要寻找它的文件目录区,看看目录区是否记录了文件偏移、大小这些信息。
回到010 Editor,继续查看sxxxxxai.pck,将文件拉到最后面。
发现了这里有文件目录数据,还有游戏厂商自定义的文件特征,我们的解包成败与否就取决于这里。
016.png (184.02 KB, 下载次数: 0)
下载附件
2024-10-30 16:24 上传
我们看到,每个文件前面都带着完整的文件夹路径,文件路径与文件路径之间基本上都是乱码。
再次猜想,文件路径的长度是不一样的,如果游戏EXE在根据文件名读取对应的数据的时候,那就一定要知道文件名的长度,并记录到相关的区域中,文件路径与文件路径之间的乱码或为这些信息。
那就尝试一下随机选中一个完整的路径名,看看他的长度
017.png (226.07 KB, 下载次数: 0)
下载附件
2024-10-30 16:24 上传
此时010 Editor告诉我们当前选中了32个字节,十六进制长度为20h,我们向下推理,后面可能会存在字节20h,然而直到下一个字节也没有发现。
018.png (224.62 KB, 下载次数: 0)
下载附件
2024-10-30 16:24 上传
那好,那我们就向前找,这时发现文件名数据部分的向前第四个字节为21h,这与我们要寻找的20h非常相近,而且刚才选中的文件名与文件名之间的字节是17个,众所周知,计算机通常都是以2的N次方来计数,但是17个字节就不太符合常理,文件名最后一个字节76h后面填充了一个00h,基于上面的信息可以确认,文件名数据占用若干字节,将占用的长度写在文件名前面,并占用四个字节,后面的16个字节中有12个字节记录的或许为文件偏移、文件压缩前后大小的信息,后四个字节为下一个文件名的长度。
如图将25 B3 3E 0D拼在一起,即0D3EB325h,用Windows自带的的计算器转换成十进制。
019.png (224.86 KB, 下载次数: 0)
下载附件
2024-10-30 16:25 上传
十进制结果为632503821,如果转换成MB的话,那就是600多MB,而整个PCK文件也只有200多MB,所以这个计算不成立。
020.png (33.53 KB, 下载次数: 0)
下载附件
2024-10-30 16:25 上传
那么把这四个字节顺序倒过来计算呢?
021.png (33.06 KB, 下载次数: 0)
下载附件
2024-10-30 16:25 上传
我们得出了222212901的数值,换算成MB为211.23MB,当然一个WAV音频文件封装在PCK文件包里不可能211MB,但数值符合文件偏移值。
用上述方法把25 B3 3E 0D后面的八字节每四字节为一组,分成B8 34 00 00和07 9D 00 00 ,从右往左输入转换为十进制的结果为47156和40199,符合压缩前后大小值。
022.PNG (225.92 KB, 下载次数: 0)
下载附件
2024-10-30 20:22 上传
综上所述。初步得出的结论为是:
"
第一条的说法已经得到了验证,现在验证一下第二条和第三条。
按快捷键Ctrl +G,打开跳转到指定字节对话框,将刚才的25 B3 3E 0D从右往左输入,也就是0D3EB325h并回车。
023.PNG (21.89 KB, 下载次数: 0)
下载附件
2024-10-30 16:28 上传
你会发现,010 Editor定位的地方的字节值也是78 01!
024.PNG (245.62 KB, 下载次数: 0)
下载附件
2024-10-30 16:28 上传
我们刚才分析到25 B3 3E 0D是文件偏移,07 9D 00 00是文件压缩后大小,那就尝试选定文件偏移+压缩后大小,把它复制到空白的文件里。
按快捷键Ctrl + Shift +A,调出选中指定字节对话框,点击“Opinions”,选择第一个选项,选中方式为选中起始地址+要选中的大小,从Start和Size两个项目分别填入0D3EB325h和00009D07h
025.PNG (235.49 KB, 下载次数: 0)
下载附件
2024-10-30 16:28 上传
选中字节后,可以看到最后一个字节的后两个字节还是78 01
026.PNG (236.67 KB, 下载次数: 0)
下载附件
2024-10-30 16:28 上传
随后单独复制出来,用刚才写的C#程序进行解压,发现文件大小正是刚才十六进制34B8h所对应的十进制值
27.png (42.1 KB, 下载次数: 0)
下载附件
2024-10-30 16:28 上传
把解压后的文件拖进010 Editor,文件头部为WAV音频的头部
028.PNG (173.36 KB, 下载次数: 0)
下载附件
2024-10-30 16:28 上传
将拓展名改为WAV,播放正常
029.PNG (27.14 KB, 下载次数: 0)
下载附件
2024-10-30 16:28 上传
文件目录区和压缩方式我们搞清楚了,但是还有两个棘手的问题:数据区到底多长?文件数量到底多少个?不知道数据区或者文件目录区的大小,无法定位目录区的字节地址,不知道文件数量,批量解压操作的for循环次数不知道写多少,这两个问题不解决,我们只能解出无目录和文件名的文件。所以还要到文件尾部寻找线索。
再一次回到文件尾部,找到最后一个文件的文件路径名,然后按文件路径长度四字节、文件路径内容若干字节、文件偏移四字节、文件压缩前后大小个四字节选中。
030.png (185.4 KB, 下载次数: 0)
下载附件
2024-10-30 16:29 上传
在最后一个文件的压缩大小信息结束后,紧接着的四字节是03 00 01 00,再往后的四个字节为5D 13 7B 0D,然后就是PCK文件特征签名了。
031.png (178.51 KB, 下载次数: 0)
下载附件
2024-10-30 16:29 上传
这时候,我们发现03 00 01 00在文件的最后四字节中再次出现,这里的数据长度为272个字节,且可以被4整除,那么就可以判定这个游戏的Pack文件的特征区在文件尾部,首尾字节为03 00 01 00。
知道了数据区的规则,知道了目录表的组成和方法,又知道了Pack文件的特征区长度,并且还知道里面有一串明文的签名,那就不难猜出5D 13 7B 0D和C5 06 00 00是干什么的了。
032.png (186.22 KB, 下载次数: 0)
下载附件
2024-10-30 16:29 上传
没错!5D 13 7B 0D为数据区长度,因为数据区的首个文件数据从0h开始,转到地址0D7B0Dh后发现这个区域的字节内容完全符合前期的推理
033.png (242.92 KB, 下载次数: 0)
下载附件
2024-10-30 16:29 上传
而C5 06 00 00转为000006C5h并换算成十进制为1733,符合一个游戏的资源文件数量。
034.png (185.27 KB, 下载次数: 0)
下载附件
2024-10-30 16:29 上传
最后用表格来捋一下整体算法,以表格的形式列出
0035.png (140.71 KB, 下载次数: 0)
下载附件
2024-10-30 16:29 上传
算法我们捋完了,现在开始写代码吧。
如果要写代码,要分两个大步骤来进行,先获取保内文件信息,生成带有文件名、文件偏移、压缩前后大小的列表,然后再执行解包。
而两个大步骤又分成若干个小步骤。
先说关于第一大步的操作流程:
(1)创建一个FileStream文件流,并seek到记录数据区总长度信息的位置,可以通过PCK文件大小减去272所得的值来seek。
(2)得到了数据区总长度后,用PCK文件大小减去数据区总长度再减去272,获得文件目录区总长度。
(3)随后seek到PCK文件大小减去8的位置,得到包内文件数量信息的十六进制字节值。
(3)创建一个for循环,循环次数为包内文件数量,依次获取单个文件目录长度、偏移、压缩前后大小,并将处理好的数据加入到List中。
(4)将这个List返回出来,等待使用。
第二大步就简单了
用for循环按照List里的每个所对应的数据进行拷贝、创建即可。
在运行调试时,Zlib突然在某一次循环抛出了异常,说数据无法识别
0036.png (211.55 KB, 下载次数: 0)
下载附件
2024-10-30 16:31 上传
先前的分析思路完全正确,但是突然遇到了不能识别的数据,这就要从返回的文件列表来寻找答案
在解压函数开始的位置下断点,提取到了文件列表。
经查看,第164行发现了问题,文件解压前后大小一样,这似乎意味着这个文件并没有压缩,为了印证这个说法是否正确,我们到010 Editor跳转到这个文件的所在偏移位置。
0037.png (83.25 KB, 下载次数: 0)
下载附件
2024-10-30 16:31 上传
果不其然,很明显的明文在这里,完全能够证明这个文件没有压缩。
038.png (238.75 KB, 下载次数: 0)
下载附件
2024-10-30 16:47 上传
那么就要在处理解压后数据的时候附加条件,什么情况下调用Zlib解压在返回数据,什么情况下原封不动的返回数据。
加了判断条件后,没再出现错误,于是编写了GUI界面,完美解压!
039.png (166.72 KB, 下载次数: 0)
下载附件
2024-10-30 17:00 上传
文件数量为1733
039-1.png (33.42 KB, 下载次数: 0)
下载附件
2024-10-30 17:00 上传
040.png (1001.89 KB, 下载次数: 0)
下载附件
2024-10-30 17:00 上传
041.png (184.09 KB, 下载次数: 0)
下载附件
2024-10-30 17:00 上传
042.png (149.41 KB, 下载次数: 0)
下载附件
2024-10-30 17:00 上传
这里给出基于C#编写的代码
老规矩,隐藏5天,回帖可见,5天后开放
无名小银,如果您要查看本帖隐藏内容请回复
本来到这里文章应该要结束的,但是有些同学想知道游戏能不能加载解包后的数据?
答案是否定的!
经过亲测,删除sxxxxxai.pck后运行游戏会闪退,这时你打开游戏根目录下的Logs文件夹中Errors.log的会看到有无法加载sxxxxxai.pck的记录
043.png (21.7 KB, 下载次数: 0)
下载附件
2024-10-30 17:02 上传
不过,这不代表就不可以加载解包后的数据,凡事都有个为什么,接下来就来聊关于让游戏加载解包后的数据的话题。
打开IDA,将游戏EXE文件sxxxxxai.exe拖进去。
044.png (144.01 KB, 下载次数: 0)
下载附件
2024-10-30 17:02 上传
我猜测,这个游戏不太可能有壳、压缩、混淆代码等一切妨碍调试的措施,那就尝试搜索pck
045.png (12.79 KB, 下载次数: 0)
下载附件
2024-10-30 17:02 上传
皆大欢喜,发现了不得了的东西
046.png (25.27 KB, 下载次数: 0)
下载附件
2024-10-30 17:02 上传
看到了"-nousepck"字样,它酷似应用程序的附加命令,顾名思义,就是告诉EXE不使用PCK来加载游戏。现在试一试在CMD里加上这条参数运行游戏。
047.png (23.83 KB, 下载次数: 0)
下载附件
2024-10-30 17:02 上传
顺利进入游戏!
048.png (26.53 KB, 下载次数: 0)
下载附件
2024-10-30 17:02 上传
不用想,这条命令是为了让游戏开发工程师调试游戏的时候省去打包的繁琐步骤而设计的。
我们也不能为了实现加载解包后的数据来玩游戏调出命令行输入附加参数来启动游戏,太麻烦了,所以只能将加载PCK的相关操作nop掉,以达到任何情况下都不执行加载PCK的操作。
按Ctrl + T,继续搜索,经过多次搜索,0x041ECA8处发现了一个疑似操作函数的代码
49.png (157.47 KB, 下载次数: 0)
下载附件
2024-10-30 17:04 上传
它被0x0041EC9F处调用,其操作为
[Asm] 纯文本查看 复制代码
using System;
using System.Reflection;
namespace PCKExtractetor
{
///
/// 公共函数
///
internal class PublicFunction
{
///
/// 8字节补零
/// (字节集 欲填充0x00的8字节字节集数组)
/// 返回处理后的8字节数组
///
public static byte[] EightByteConverter(byte[] bytes)
{
// 补到8字节用于Bit转换
byte[] newByees = new byte[8];
Array.Copy(bytes, 0, newByees, 0, bytes.Length);
for (int i = bytes.Length; i
这是一条汇编命令,意思是上一个操作的零标志位ZF为1时,程序会跳转到名为“loc_41ECA8”的代码位置。如果零标志位为0(即上一个操作的结果不为零),则程序将顺序执行下一条指令。这里的ZF标记位可能是对是否调用了"-nousepck"的结果,不管那么多了,只要实现最终目的就可以完成任务。不懂汇编的看参考资料。
修改有两种方法:
1、直接将0x0041EC9F处改成空操作,即nop,使EXE根本就不存在这条操作,后续按正常流程执行。
2、将0x0041EC9F的跳转条件由jz改成jnz,即零标志位ZF不是0的时候则跳转,这样实现了不加命令参数"-nousepck"也会加载包外文件的目的,但是这样做就意味着加了命令参数后依然要加载PCK,有严重的弊端,因此第一条方案才是完美解决方法。
50.png (159.25 KB, 下载次数: 0)
下载附件
2024-10-30 17:04 上传
最后说说如何学好游戏资源文件逆向吧,说说我的经验
首先,要弄明白ASCII码,要了解常见的图片和音频格式的文件头特征,这是最基本的,因为很多游戏是把多个数据拼合在一个文件的,根本谈不上加密和压缩,懂这两个后,静态分析Pack文件会很快。
[color=]其次,要会汇编语言,还要会用IDE、OD等常用的分析工具,,因为很多游戏的Pack文件的字节是经过异或运算的,需要通过跑EXE,下断点来确定数据处理流程,一些加壳的有混淆的还要掌握脱壳技术。
[color=]还有一点,就是多看别人的案例,你会发现各大游戏厂商的Pack加密方式形形色色。
最后,就是要善用AI,因为无论是搜文件头部特征的资料亦或是代码查错亦或是分析思路,AI能够帮你回答。
[color=]以上是给初学者的建议,大家可以根据自己的学习习惯来酌情制定方案。
[color=]以上就是对这款游戏PCK资源文件的分析,文章结束
Thank you for reading!
最后给大家个留个课后作业
如图所示,这是单机小游戏《极乐浪子》的PFP包文件,其结构原理与本次分析的游戏大致相同,只有三次不同:没有压缩、文件特征区在前面、文件目录长度只占了一个字节,试分析解包算法,并写出解包代码,C、Java、Python均可,语言不限。过段时间我会在编程区发布答案。根据论坛规定,论坛禁止发布游戏,所以游戏本体需自己百度,这个游戏是一家叫PlayFirst的游戏厂商出品的游戏,找到这家厂商的其他游戏的PFP文件来解包也是可以的,因为算法一模一样。
171533m8edd4q03r3jyql0.png (187.28 KB, 下载次数: 0)
下载附件
2024-10-30 18:54 上传
参考资料:
1、Ascii(256个) 编码表 完整码表 ASCII编码 ASCII表 ASCII码 二进制 十进制 八进制 十六进制
https://blog.csdn.net/u011149152/article/details/139168143
2、杂项 - zlib
https://www.cnblogs.com/ainsliaea/p/15780903.html
3、[汇编语言]基础知识
https://blog.csdn.net/weixin_51304981/article/details/126492628
4、汇编语言指令大全 附指令详解
https://blog.csdn.net/luxiaol/article/details/134921043