Skip to content
KitploitKITPLOIT
工具漏洞利用博客
Log in
提交
工具漏洞利用博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

订阅源联系隐私© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
dconstruct — 一个用于 TLOU2 DC 脚本的反汇编器与实验性反编译器。 | Kitploit
工具/GitHubGitHub/deepquantum/dconstruct
静态分析逆向工程调试器二进制分析固件分析
GitHubdeepquantum/dconstruct

dconstruct

一个用于 TLOU2 DC 脚本的反汇编器与实验性反编译器。

查看仓库
232291个月前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

Version License: CC BY-NC-ND 4.0

dconstruct

完整反汇编与反编译动画

打开源 MP4

dconstruct 是一款用于《最后生还者 第二部》DC-Script 文件的逆向工程工具。它包含反汇编器和反编译器。

工具输出 .asm 文件(包含反汇编出的结构与字节码)以及 .dcpl(DC 伪语言)文件(包含类似 C 语言风格的伪代码)。

你也可以通过命令行对文件进行编辑,包括轻松替换整个结构。这使得创建仅需修改 .bin 文件中少量数值的 mod 变得极其简单。

主要特性

  • 速度优化。反汇编与反编译速度极快。
  • 对原始源代码(尤其是控制流)的精确重构。
  • 对反汇编中所有结构的精确自动解析。
  • 同时反汇编与反编译多个文件。反编译游戏中每一个 .bin 文件仅需几秒钟。
  • 通过 -e 标志进行编辑,生成可用于 mod 的新文件。
  • 加载自定义 sidbase 用于反汇编。

使用方法

首先,建议将解压后的 dconstruct 目录移动到一个安全的位置,例如 C:\Program Files。

为尽可能方便使用 dconstruct,建议将 dconstruct 文件夹内的 .\bin 目录添加到你的 PATH 环境变量中。更多信息请参考这里,或按照以下快速步骤操作:

  • 在 Windows 搜索栏中输入“环境变量”,你会看到“编辑系统环境变量”的选项。
  • 点击该选项后,会弹出一个标题为“系统属性”的窗口。在“确定”、“取消”和“应用”按钮的上方,有一个“环境变量...”按钮。
  • 点击此按钮后,会弹出另一个窗口。在这里,进入第二个表格(标题为“系统变量”),找到变量名为“Path”的条目,双击它。
  • 在此对话框中,选择右侧的“新建”选项。然后粘贴 .\bin 目录的路径。路径应类似这样:

示意图

  • 确保路径以“\bin”结尾,而不是“\dconstruct”。

  • 在所有打开的对话框中点击“确定”。

  • 要验证是否成功,请打开一个新的命令提示符窗口,输入 dconstruct --about。你应看到程序输出一些信息,且没有错误消息。

在命令行中运行如下命令,以生成你的第一个反汇编文件:```shell dconstruct my_bin_file.bin

这将输出一个名为 `my_bin_file.bin.asm` 的文件,位于输入文件所在目录。然后你可以用文本/代码编辑器打开该文件。我推荐使用诸如 VSCode 这类具有高级搜索功能且擅长处理大文件的编辑器。不推荐使用标准 Windows 记事本。

要反编译文件,运行命令时加上 `--decompile` 标志。

# 命令行参数

- `-i` - 输入文件或文件夹。如果第一个参数传递了输入路径,则可以省略。

- `-o` - 输出路径。如果输入路径是文件夹,则此项不能是文件。如果未指定输出,.txt 文件将放在输入文件旁边。如果输入是文件夹且未指定输出,程序将在当前工作目录下创建 "output" 目录,并将所有文件放入其中。

- sidbase 从与可执行文件相邻的 `sidbase.bin` 加载。

- `--no_decompile` - 不将反编译伪代码输出到 .dcpl 文件。该文件将放在 .asm 文件旁边。默认值为 false。

- `--no_optimize` - 不优化和清理 dcpl 代码。包括内联函数调用、移除未使用的变量、将兼容的 for 循环转换为 foreach 循环,以及将某些 if-else 链转换为 match 表达式。

- `--pascal_case` - 将游戏函数名称转换为 dcpl 输出中的帕斯卡命名风格,例如 get-boolean -> GetBoolean。

- `--graphs` - 生成包含所有反编译函数的控制流图的 .svg 文件。每个 .bin 文件拥有自己的文件夹,包含其所有图形。这会**显著**降低反编译速度,因此不建议同时反编译大量文件时使用。

- `--emit_once` - 禁止同一结构在反汇编中出现两次。如果一个结构多次出现,仅第一个实例会被完整输出,其他所有出现位置将被替换为 `ALREADY_EMITTED` 标签。这可以显著减小文件大小。

- `-e` - 进行编辑。更多信息见下方章节。

- `--edit_file` - 提供编辑文件。编辑文件每行包含一个编辑操作。它使用与 -e 标志相同的语法。

# 什么是反汇编器?

[反汇编器](https://en.wikipedia.org/wiki/Disassembler)是一种读取二进制指令(即[字节码](https://en.wikipedia.org/wiki/Bytecode)或[机器码](https://en.wikipedia.org/wiki/Machine_code))并将其逐条转换为人类可读版本(称为[助记符](https://en.wikipedia.org/wiki/Assembly_language#Mnemonics))的工具。反汇编器一般不会尝试解释这些指令的太多含义,只是将它们一对一转换为可读版本。例如,指令:```arm
15 00 00 00
4A 01 01 00
43 31 01 00
1C 00 00 01

被反汇编成以下人类可读版本:```arm LookupPointer r0, 0 LoadStaticU64Imm r1, 1 Move r49, r1 CallFf r0, r0, 1

字节码中的所有数字均以[十六进制](https://en.wikipedia.org/wiki/hexadecimal)表示。每行中的第一列代表`opcode`,即要执行的指令类型。下一列是目标寄存器,操作的结果将存储在该寄存器中。最后两列是操作数1和2,它们可以是寄存器或字面数值,操作将对这些数值执行。并非所有指令都使用全部4个字节,例如,第一条`LookupPointer`指令仅需要一个操作数。

dconstruct反汇编器还会添加一些额外信息,旨在使指令的阅读更加容易。它还会插入标签(例如`L_0`)以使代码中的分支更易于跟踪。```arm
15 00 00 00   LookupPointer        r0, 0         r0 = ST[0] -> <is-player-abby?>
4A 01 01 00   LoadStaticU64Imm     r1, 1         r1 = ST[1] -> <player>
43 31 01 00   Move                 r49, r1       r49 = player
1C 00 00 01   CallFf               r0, r0, 1     r0 = is-player-abby?(player)
2F 0D 00 00   BranchIfNot          r0, 0xD       IF NOT r0 => L_0

当你希望查看文件的原始内容而不让程序进行过多猜测时,这很有用。但对于大块代码来说,由于完全没有结构,读起来可能会很困难。这时候就需要反编译器了。

什么是反编译器?

反编译器是编译器的逆过程。编译器 是一种接收人类编写的代码(如 C、Java、C++ 等)并生成机器指令的程序。以《最后生还者 第二部》和许多其他顽皮狗游戏为例,所使用的脚本语言称为 'DC',它基本上是编程语言 Racket>) 的一个变体,而“机器”就是游戏本身,在运行游戏时执行这些指令。本质上,程序员编写 DC 代码,然后使用编译器将其转换为随游戏一起发布的 .bin 文件。

反编译器 接收上述反汇编代码,并生成所谓的伪代码。伪代码是_尝试_重建用于生成原始指令的原始源代码。这意味着使代码的理解变得更加容易,然而,生成伪代码的过程相当复杂,因为有许多不同版本的源代码可以生成最终的字节码,此外还有编译过程中发生的优化。

目前,dconstruct 反编译器的输出在语法上_不_类似于原始的 DC。DC(即 Racket)是一种函数式编程语言,具有独特的语法,对于不习惯它的程序员来说简直难以辨认。因此,我目前选择让伪代码更像 C 语言,这样对大多数人来说应该更容易阅读。不过,计划创建更多语法,包括 Racket 以及 Python 版本。

示例

sqrt-sign() 函数

原始字节```arm

43 00 31 00 15 01 00 00 43 02 00 00 43 31 02 00 1B 01 01 01 43 02 00 00 40 03 01 00 24 02 02 03 2F 0B 02 00 40 02 02 00 2D 0C 00 00 40 02 03 00 43 03 02 00 15 04 04 00 43 05 01 00 43 31 05 00 1C 04 04 01 07 03 03 04 43 01 03 00 00 01 01 00

由此看来,几乎不可能看出代码在做什么。

## 带有标签和符号表的反汇编代码```arm
sqrt-sign = script-lambda [0x9A8D8] {
    [1 args]
    0000   0x09A928   43 00 31 00   Move                 r0, r49       r0 = arg_0
    0001   0x09A930   15 01 00 00   LookupPointer        r1, 0         r1 = ST[0] -> <absf>
    0002   0x09A938   43 02 00 00   Move                 r2, r0        r2 = arg_0
    0003   0x09A940   43 31 02 00   Move                 r49, r2       r49 = arg_0
    0004   0x09A948   1B 01 01 01   Call                 r1, r1, 1     r1 = absf(arg_0)
    0005   0x09A950   43 02 00 00   Move                 r2, r0        r2 = arg_0
    0006   0x09A958   40 03 01 00   LoadStaticFloatImm   r3, 1         r3 = ST[1] -> <0.000000>
    0007   0x09A960   24 02 02 03   FGreaterThanEqual    r2, r2, r3    r2 = r2 >= r3
    0008   0x09A968   2F 0B 02 00   BranchIfNot          r2, 0xB       IF NOT r2 => L_0
    0009   0x09A970   40 02 02 00   LoadStaticFloatImm   r2, 2         r2 = ST[2] -> <1.000000>
    000A   0x09A978   2D 0C 00 00   Branch               0xC           GOTO => L_1
  L_0:
    000B   0x09A980   40 02 03 00   LoadStaticFloatImm   r2, 3         r2 = ST[3] -> <-1.000000>
  L_1:
    000C   0x09A988   43 03 02 00   Move                 r3, r2        r3 = -1.000000
    000D   0x09A990   15 04 04 00   LookupPointer        r4, 4         r4 = ST[4] -> <sqrt>
    000E   0x09A998   43 05 01 00   Move                 r5, r1        r5 = RET_absf
    000F   0x09A9A0   43 31 05 00   Move                 r49, r5       r49 = RET_absf
    0010   0x09A9A8   1C 04 04 01   CallFf               r4, r4, 1     r4 = sqrt(RET_absf)
    0011   0x09A9B0   07 03 03 04   FMul                 r3, r3, r4    -1.000000 = -1.000000 * RET_sqrt
    0012   0x09A9B8   43 01 03 00   Move                 r1, r3        r1 = -1.000000
    0013   0x09A9C0   00 01 01 00   Return               r1            Return

    SYMBOL TABLE:
    0000   0x09A9C8    function: absf
    0001   0x09A9D0    float: 0.000000
    0002   0x09A9D8    float: 1.000000
    0003   0x09A9E0    float: -1.000000
    0004   0x09A9E8    function: sqrt
}

代码现在可读性大大提高,但即使只有单一分支,对于不习惯阅读汇编的人来说,读起来仍然很烦人。

Control flow graph

这里不过多赘述,控制流图(CFG) 沿各种分支指令将汇编代码分割成"节点"。在我们分析代码以找出程序"流"可能在何处分叉到不同路径时,这至关重要,可能需要我们生成变量、if语句、for循环等。这些图需要在后台生成,但你可以使用 --graphs 程序标志将它们打印成图像。

Final pseudo code```rust

u64? sqrt-sign(f32 arg_0) { f32 var_1; if (arg_0 >= 0.00) { var_1 = 1.00; } else { var_1 = -1.00; } return var_1 * sqrt(absf(arg_0)); }

该函数的目的现在非常明确:我们取参数的绝对值,计算其平方根,然后将结果乘以参数的原始符号。例如,`sqrt-sign(-9) = -3`。

## 优化遍次

dconstruct 会自动对伪代码应用优化遍次。以下是一些示例:

### 函数调用内联
下载工具