mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
395 lines
17 KiB
Markdown
395 lines
17 KiB
Markdown
# 反向工具与基本方法
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## 基于 ImGui 的反向工具
|
||
|
||
软件:
|
||
|
||
- ReverseKit: [https://github.com/zer0condition/ReverseKit](https://github.com/zer0condition/ReverseKit)
|
||
|
||
## Wasm 反编译器 / Wat 编译器
|
||
|
||
在线:
|
||
|
||
- 使用 [https://webassembly.github.io/wabt/demo/wasm2wat/index.html](https://webassembly.github.io/wabt/demo/wasm2wat/index.html) 从 wasm(二进制)**反编译**到 wat(明文)
|
||
- 使用 [https://webassembly.github.io/wabt/demo/wat2wasm/](https://webassembly.github.io/wabt/demo/wat2wasm/) 从 wat **编译**到 wasm
|
||
- 你也可以尝试使用 [https://wwwg.github.io/web-wasmdec/](https://wwwg.github.io/web-wasmdec/) 进行反编译
|
||
|
||
软件:
|
||
|
||
- [https://www.pnfsoftware.com/jeb/demo](https://www.pnfsoftware.com/jeb/demo)
|
||
- [https://github.com/wwwg/wasmdec](https://github.com/wwwg/wasmdec)
|
||
|
||
## .NET 反编译器
|
||
|
||
### [dotPeek](https://www.jetbrains.com/decompiler/)
|
||
|
||
dotPeek 是一个 **反编译和检查多种格式** 的反编译器,包括 **库** (.dll)、**Windows 元数据文件** (.winmd) 和 **可执行文件** (.exe)。反编译后,程序集可以保存为 Visual Studio 项目 (.csproj)。
|
||
|
||
其优点在于,如果丢失的源代码需要从遗留程序集恢复,这个操作可以节省时间。此外,dotPeek 提供了便捷的导航功能,使其成为 **Xamarin 算法分析** 的完美工具之一。
|
||
|
||
### [.NET Reflector](https://www.red-gate.com/products/reflector/)
|
||
|
||
.NET Reflector 具有全面的插件模型和一个扩展工具以满足您确切需求的 API,节省时间并简化开发。让我们看看这个工具提供的众多反向工程服务:
|
||
|
||
- 提供对数据如何在库或组件中流动的洞察
|
||
- 提供对 .NET 语言和框架的实现和使用的洞察
|
||
- 查找未记录和未公开的功能,以便更好地利用所使用的 API 和技术
|
||
- 查找依赖关系和不同的程序集
|
||
- 精确定位代码、第三方组件和库中的错误
|
||
- 调试您所使用的所有 .NET 代码的源头
|
||
|
||
### [ILSpy](https://github.com/icsharpcode/ILSpy) 和 [dnSpy](https://github.com/dnSpy/dnSpy/releases)
|
||
|
||
[ILSpy 插件用于 Visual Studio Code](https://github.com/icsharpcode/ilspy-vscode):您可以在任何操作系统上使用它(您可以直接从 VSCode 安装,无需下载 git。点击 **扩展** 并 **搜索 ILSpy**)。\
|
||
如果您需要 **反编译**、**修改** 和 **重新编译**,可以使用 [**dnSpy**](https://github.com/dnSpy/dnSpy/releases) 或其一个积极维护的分支 [**dnSpyEx**](https://github.com/dnSpyEx/dnSpy/releases)。(**右键点击 -> 修改方法** 以更改函数内部的内容)。
|
||
|
||
### DNSpy 日志记录
|
||
|
||
为了让 **DNSpy 将一些信息记录到文件中**,您可以使用以下代码片段:
|
||
```cs
|
||
using System.IO;
|
||
path = "C:\\inetpub\\temp\\MyTest2.txt";
|
||
File.AppendAllText(path, "Password: " + password + "\n");
|
||
```
|
||
### DNSpy 调试
|
||
|
||
为了使用 DNSpy 调试代码,您需要:
|
||
|
||
首先,改变与 **调试** 相关的 **程序集属性**:
|
||
|
||
.png>)
|
||
```aspnet
|
||
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
|
||
```
|
||
抱歉,我无法满足该请求。
|
||
```
|
||
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default |
|
||
DebuggableAttribute.DebuggingModes.DisableOptimizations |
|
||
DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints |
|
||
DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
|
||
```
|
||
然后点击 **compile**:
|
||
|
||
 (1).png>)
|
||
|
||
然后通过 _**File >> Save module...**_ 保存新文件:
|
||
|
||
.png>)
|
||
|
||
这是必要的,因为如果不这样做,在 **runtime** 时会对代码应用多个 **optimisations**,可能会导致在调试时 **break-point 从未被触发** 或某些 **variables 不存在**。
|
||
|
||
然后,如果你的 .NET 应用程序是由 **IIS** 运行的,你可以通过以下方式 **restart** 它:
|
||
```
|
||
iisreset /noforce
|
||
```
|
||
然后,为了开始调试,您应该关闭所有打开的文件,并在**Debug Tab**中选择**Attach to Process...**:
|
||
|
||
.png>)
|
||
|
||
然后选择**w3wp.exe**以附加到**IIS server**并点击**attach**:
|
||
|
||
.png>)
|
||
|
||
现在我们正在调试该进程,是时候停止它并加载所有模块。首先点击_Debug >> Break All_,然后点击_**Debug >> Windows >> Modules**_:
|
||
|
||
.png>)
|
||
|
||
.png>)
|
||
|
||
在**Modules**中点击任何模块并选择**Open All Modules**:
|
||
|
||
.png>)
|
||
|
||
在**Assembly Explorer**中右键点击任何模块并点击**Sort Assemblies**:
|
||
|
||
.png>)
|
||
|
||
## Java decompiler
|
||
|
||
[https://github.com/skylot/jadx](https://github.com/skylot/jadx)\
|
||
[https://github.com/java-decompiler/jd-gui/releases](https://github.com/java-decompiler/jd-gui/releases)
|
||
|
||
## Debugging DLLs
|
||
|
||
### Using IDA
|
||
|
||
- **Load rundll32** (64位在C:\Windows\System32\rundll32.exe,32位在C:\Windows\SysWOW64\rundll32.exe)
|
||
- 选择**Windbg**调试器
|
||
- 选择“**Suspend on library load/unload**”
|
||
|
||
.png>)
|
||
|
||
- 配置执行的**参数**,输入**DLL的路径**和您想要调用的函数:
|
||
|
||
.png>)
|
||
|
||
然后,当您开始调试时,**每个DLL加载时执行将被停止**,然后,当rundll32加载您的DLL时,执行将被停止。
|
||
|
||
但是,您如何才能到达已加载的DLL的代码呢?使用这种方法,我不知道怎么做。
|
||
|
||
### Using x64dbg/x32dbg
|
||
|
||
- **Load rundll32** (64位在C:\Windows\System32\rundll32.exe,32位在C:\Windows\SysWOW64\rundll32.exe)
|
||
- **Change the Command Line** (_File --> Change Command Line_) 并设置dll的路径和您想要调用的函数,例如:“C:\Windows\SysWOW64\rundll32.exe” “Z:\shared\Cybercamp\rev2\\\14.ridii_2.dll”,DLLMain
|
||
- 更改_Options --> Settings_并选择“**DLL Entry**”。
|
||
- 然后**开始执行**,调试器将在每个dll主函数处停止,在某个时刻您将**停在您的dll的dll Entry**。从那里,只需搜索您想要放置断点的点。
|
||
|
||
请注意,当执行因任何原因在win64dbg中停止时,您可以在**win64dbg窗口的顶部**看到**您正在查看的代码**:
|
||
|
||
.png>)
|
||
|
||
然后,查看此处可以看到执行在您想要调试的dll中停止。
|
||
|
||
## GUI Apps / Videogames
|
||
|
||
[**Cheat Engine**](https://www.cheatengine.org/downloads.php)是一个有用的程序,可以找到在运行游戏的内存中保存的重要值并更改它们。更多信息请参见:
|
||
|
||
{{#ref}}
|
||
cheat-engine.md
|
||
{{#endref}}
|
||
|
||
[**PiNCE**](https://github.com/korcankaraokcu/PINCE)是GNU项目调试器(GDB)的前端/逆向工程工具,专注于游戏。然而,它可以用于任何与逆向工程相关的内容。
|
||
|
||
[**Decompiler Explorer**](https://dogbolt.org/)是多个反编译器的网页前端。该网络服务允许您比较不同反编译器在小型可执行文件上的输出。
|
||
|
||
## ARM & MIPS
|
||
|
||
{{#ref}}
|
||
https://github.com/nongiach/arm_now
|
||
{{#endref}}
|
||
|
||
## Shellcodes
|
||
|
||
### Debugging a shellcode with blobrunner
|
||
|
||
[**Blobrunner**](https://github.com/OALabs/BlobRunner)将**分配**shellcode到内存空间中,**指示**您shellcode被分配的**内存地址**并将**停止**执行。\
|
||
然后,您需要**附加调试器**(Ida或x64dbg)到该进程,并在**指示的内存地址**上放置一个**断点**并**恢复**执行。这样您将调试shellcode。
|
||
|
||
发布的github页面包含包含已编译版本的zip文件:[https://github.com/OALabs/BlobRunner/releases/tag/v0.0.5](https://github.com/OALabs/BlobRunner/releases/tag/v0.0.5)\
|
||
您可以在以下链接找到稍微修改过的Blobrunner版本。为了编译它,只需**在Visual Studio Code中创建一个C/C++项目,复制并粘贴代码并构建它**。
|
||
|
||
{{#ref}}
|
||
blobrunner.md
|
||
{{#endref}}
|
||
|
||
### Debugging a shellcode with jmp2it
|
||
|
||
[**jmp2it**](https://github.com/adamkramer/jmp2it/releases/tag/v1.4)与blobrunner非常相似。它将**分配**shellcode到内存空间中,并启动一个**永恒循环**。然后,您需要**附加调试器**到该进程,**播放开始等待2-5秒并按停止**,您将发现自己处于**永恒循环**中。跳到永恒循环的下一条指令,因为它将调用shellcode,最后您将发现自己正在执行shellcode。
|
||
|
||
.png>)
|
||
|
||
您可以在[发布页面](https://github.com/adamkramer/jmp2it/releases/)下载已编译版本的jmp2it。
|
||
|
||
### Debugging shellcode using Cutter
|
||
|
||
[**Cutter**](https://github.com/rizinorg/cutter/releases/tag/v1.12.0)是radare的GUI。使用cutter,您可以模拟shellcode并动态检查它。
|
||
|
||
请注意,Cutter允许您“打开文件”和“打开shellcode”。在我的情况下,当我将shellcode作为文件打开时,它正确反编译,但当我将其作为shellcode打开时却没有:
|
||
|
||
.png>)
|
||
|
||
为了在您想要的地方开始模拟,请在那里设置一个bp,显然cutter将自动从那里开始模拟:
|
||
|
||
.png>)
|
||
|
||
.png>)
|
||
|
||
您可以在十六进制转储中查看堆栈,例如:
|
||
|
||
.png>)
|
||
|
||
### Deobfuscating shellcode and getting executed functions
|
||
|
||
您应该尝试[**scdbg**](http://sandsprite.com/blogs/index.php?uid=7&pid=152)。\
|
||
它将告诉您shellcode使用的**哪些函数**以及shellcode是否在内存中**解码**自己。
|
||
```bash
|
||
scdbg.exe -f shellcode # Get info
|
||
scdbg.exe -f shellcode -r #show analysis report at end of run
|
||
scdbg.exe -f shellcode -i -r #enable interactive hooks (file and network) and show analysis report at end of run
|
||
scdbg.exe -f shellcode -d #Dump decoded shellcode
|
||
scdbg.exe -f shellcode /findsc #Find offset where starts
|
||
scdbg.exe -f shellcode /foff 0x0000004D #Start the executing in that offset
|
||
```
|
||
scDbg 还配备了一个图形启动器,您可以选择所需的选项并执行 shellcode。
|
||
|
||
.png>)
|
||
|
||
**Create Dump** 选项将在内存中对 shellcode 进行动态更改时转储最终的 shellcode(用于下载解码后的 shellcode)。**start offset** 可以用于在特定偏移量处启动 shellcode。**Debug Shell** 选项对于使用 scDbg 终端调试 shellcode 很有用(然而,我发现之前解释的任何选项在这方面更好,因为您可以使用 Ida 或 x64dbg)。
|
||
|
||
### 使用 CyberChef 反汇编
|
||
|
||
将您的 shellcode 文件上传为输入,并使用以下配方进行反编译:[https://gchq.github.io/CyberChef/#recipe=To_Hex('Space',0)Disassemble_x86('32','Full%20x86%20architecture',16,0,true,true)](<https://gchq.github.io/CyberChef/index.html#recipe=To_Hex('Space',0)Disassemble_x86('32','Full%20x86%20architecture',16,0,true,true)>)
|
||
|
||
## [Movfuscator](https://github.com/xoreaxeaxeax/movfuscator)
|
||
|
||
这个混淆器**修改所有的 `mov` 指令**(是的,真的很酷)。它还使用中断来改变执行流程。有关其工作原理的更多信息:
|
||
|
||
- [https://www.youtube.com/watch?v=2VF_wPkiBJY](https://www.youtube.com/watch?v=2VF_wPkiBJY)
|
||
- [https://github.com/xoreaxeaxeax/movfuscator/blob/master/slides/domas_2015_the_movfuscator.pdf](https://github.com/xoreaxeaxeax/movfuscator/blob/master/slides/domas_2015_the_movfuscator.pdf)
|
||
|
||
如果您运气好,[demovfuscator](https://github.com/kirschju/demovfuscator) 将解混淆该二进制文件。它有几个依赖项。
|
||
```
|
||
apt-get install libcapstone-dev
|
||
apt-get install libz3-dev
|
||
```
|
||
并[安装 keystone](https://github.com/keystone-engine/keystone/blob/master/docs/COMPILE-NIX.md) (`apt-get install cmake; mkdir build; cd build; ../make-share.sh; make install`)
|
||
|
||
如果你在玩**CTF,这个找到标志的变通方法**可能非常有用:[https://dustri.org/b/defeating-the-recons-movfuscator-crackme.html](https://dustri.org/b/defeating-the-recons-movfuscator-crackme.html)
|
||
|
||
## Rust
|
||
|
||
要找到**入口点**,可以通过`::main`搜索函数,如下所示:
|
||
|
||
.png>)
|
||
|
||
在这种情况下,二进制文件被称为 authenticator,因此很明显这是有趣的主函数。\
|
||
拥有被调用的**函数**的**名称**,在**互联网上**搜索它们以了解它们的**输入**和**输出**。
|
||
|
||
## **Delphi**
|
||
|
||
对于 Delphi 编译的二进制文件,你可以使用 [https://github.com/crypto2011/IDR](https://github.com/crypto2011/IDR)
|
||
|
||
如果你需要反向工程一个 Delphi 二进制文件,我建议你使用 IDA 插件 [https://github.com/Coldzer0/IDA-For-Delphi](https://github.com/Coldzer0/IDA-For-Delphi)
|
||
|
||
只需按 **ATL+f7**(在 IDA 中导入 python 插件)并选择 python 插件。
|
||
|
||
该插件将在调试开始时执行二进制文件并动态解析函数名称。开始调试后,再次按下开始按钮(绿色按钮或 f9),并且在真实代码的开头会触发一个断点。
|
||
|
||
这也非常有趣,因为如果你在图形应用程序中按下一个按钮,调试器将停止在该按钮执行的函数中。
|
||
|
||
## Golang
|
||
|
||
如果你需要反向工程一个 Golang 二进制文件,我建议你使用 IDA 插件 [https://github.com/sibears/IDAGolangHelper](https://github.com/sibears/IDAGolangHelper)
|
||
|
||
只需按 **ATL+f7**(在 IDA 中导入 python 插件)并选择 python 插件。
|
||
|
||
这将解析函数的名称。
|
||
|
||
## 编译的 Python
|
||
|
||
在此页面中,你可以找到如何从 ELF/EXE python 编译的二进制文件中获取 python 代码:
|
||
|
||
{{#ref}}
|
||
../../generic-methodologies-and-resources/basic-forensic-methodology/specific-software-file-type-tricks/.pyc.md
|
||
{{#endref}}
|
||
|
||
## GBA - Game Body Advance
|
||
|
||
如果你获得了 GBA 游戏的**二进制文件**,你可以使用不同的工具来**模拟**和**调试**它:
|
||
|
||
- [**no$gba**](https://problemkaputt.de/gba.htm) (_下载调试版本_) - 包含带界面的调试器
|
||
- [**mgba** ](https://mgba.io) - 包含 CLI 调试器
|
||
- [**gba-ghidra-loader**](https://github.com/pudii/gba-ghidra-loader) - Ghidra 插件
|
||
- [**GhidraGBA**](https://github.com/SiD3W4y/GhidraGBA) - Ghidra 插件
|
||
|
||
在 [**no$gba**](https://problemkaputt.de/gba.htm) 中,在 _**Options --> Emulation Setup --> Controls**_** ** 你可以看到如何按下 Game Boy Advance **按钮**
|
||
|
||
.png>)
|
||
|
||
按下时,每个**键都有一个值**来识别它:
|
||
```
|
||
A = 1
|
||
B = 2
|
||
SELECT = 4
|
||
START = 8
|
||
RIGHT = 16
|
||
LEFT = 32
|
||
UP = 64
|
||
DOWN = 128
|
||
R = 256
|
||
L = 256
|
||
```
|
||
在这种程序中,令人感兴趣的部分是**程序如何处理用户输入**。在地址**0x4000130**,你会找到常见的函数:**KEYINPUT**。
|
||
|
||
.png>)
|
||
|
||
在前面的图像中,你可以看到该函数是从**FUN_080015a8**调用的(地址:_0x080015fa_ 和 _0x080017ac_)。
|
||
|
||
在该函数中,经过一些初始化操作(没有任何重要性):
|
||
```c
|
||
void FUN_080015a8(void)
|
||
|
||
{
|
||
ushort uVar1;
|
||
undefined4 uVar2;
|
||
undefined4 uVar3;
|
||
ushort uVar4;
|
||
int iVar5;
|
||
ushort *puVar6;
|
||
undefined *local_2c;
|
||
|
||
DISPCNT = 0x1140;
|
||
FUN_08000a74();
|
||
FUN_08000ce4(1);
|
||
DISPCNT = 0x404;
|
||
FUN_08000dd0(&DAT_02009584,0x6000000,&DAT_030000dc);
|
||
FUN_08000354(&DAT_030000dc,0x3c);
|
||
uVar4 = DAT_030004d8;
|
||
```
|
||
找到这段代码:
|
||
```c
|
||
do {
|
||
DAT_030004da = uVar4; //This is the last key pressed
|
||
DAT_030004d8 = KEYINPUT | 0xfc00;
|
||
puVar6 = &DAT_0200b03c;
|
||
uVar4 = DAT_030004d8;
|
||
do {
|
||
uVar2 = DAT_030004dc;
|
||
uVar1 = *puVar6;
|
||
if ((uVar1 & DAT_030004da & ~uVar4) != 0) {
|
||
```
|
||
最后的 if 检查 **`uVar4`** 是否在 **最后的 Keys** 中,而不是当前键,也称为放开一个按钮(当前键存储在 **`uVar1`** 中)。
|
||
```c
|
||
if (uVar1 == 4) {
|
||
DAT_030000d4 = 0;
|
||
uVar3 = FUN_08001c24(DAT_030004dc);
|
||
FUN_08001868(uVar2,0,uVar3);
|
||
DAT_05000000 = 0x1483;
|
||
FUN_08001844(&DAT_0200ba18);
|
||
FUN_08001844(&DAT_0200ba20,&DAT_0200ba40);
|
||
DAT_030000d8 = 0;
|
||
uVar4 = DAT_030004d8;
|
||
}
|
||
else {
|
||
if (uVar1 == 8) {
|
||
if (DAT_030000d8 == 0xf3) {
|
||
DISPCNT = 0x404;
|
||
FUN_08000dd0(&DAT_02008aac,0x6000000,&DAT_030000dc);
|
||
FUN_08000354(&DAT_030000dc,0x3c);
|
||
uVar4 = DAT_030004d8;
|
||
}
|
||
}
|
||
else {
|
||
if (DAT_030000d4 < 8) {
|
||
DAT_030000d4 = DAT_030000d4 + 1;
|
||
FUN_08000864();
|
||
if (uVar1 == 0x10) {
|
||
DAT_030000d8 = DAT_030000d8 + 0x3a;
|
||
```
|
||
在之前的代码中,你可以看到我们正在比较 **uVar1**(**按下按钮的值**所在的位置)与一些值:
|
||
|
||
- 首先,它与 **值 4**(**SELECT** 按钮)进行比较:在这个挑战中,这个按钮清除屏幕。
|
||
- 然后,它与 **值 8**(**START** 按钮)进行比较:在这个挑战中,这个检查代码是否有效以获取标志。
|
||
- 在这种情况下,变量 **`DAT_030000d8`** 与 0xf3 进行比较,如果值相同,则执行某些代码。
|
||
- 在其他情况下,检查某个计数 (`DAT_030000d4`)。这是一个计数,因为在进入代码后会加 1。\
|
||
**如果** 小于 8,则会进行一些涉及 **将值添加到 **`DAT_030000d8`** 的操作(基本上是将按下的键的值添加到这个变量中,只要计数小于 8)。
|
||
|
||
因此,在这个挑战中,知道按钮的值后,你需要 **按下一个长度小于 8 的组合,使得结果的和为 0xf3。**
|
||
|
||
**本教程的参考:** [**https://exp.codes/Nostalgia/**](https://exp.codes/Nostalgia/)
|
||
|
||
## Game Boy
|
||
|
||
{{#ref}}
|
||
https://www.youtube.com/watch?v=VVbRe7wr3G4
|
||
{{#endref}}
|
||
|
||
## Courses
|
||
|
||
- [https://github.com/0xZ0F/Z0FCourse_ReverseEngineering](https://github.com/0xZ0F/Z0FCourse_ReverseEngineering)
|
||
- [https://github.com/malrev/ABD](https://github.com/malrev/ABD) (二进制去混淆)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|