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

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

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

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
packer-tutorial — 一份关于如何为Windows编写打包器(packer)的教程! | Kitploit
工具/GitHubGitHub/frank2/packer-tutorial
逆向工程恶意软件分析二进制分析学习与教育
GitHubfrank2/packer-tutorial

packer-tutorial

一份关于如何为Windows编写打包器(packer)的教程!

查看仓库
31932152年前Kitploit 审核通过

最受欢迎

查看全部 →

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

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

PACKERS

目录

  1. 什么是加壳器?:介绍加壳器的用途,并指导了解开发加壳器所需的条件。
  2. 先决条件:学习本教程所需的工具。
  3. 初步体验:演示你将通过本教程构建的内容。
  4. 规划我们的 CMake 项目:一个迷你 CMake 教程,介绍如何为加壳器较为复杂的需求创建构建系统。
  5. 将二进制文件打包进你的存根:关于如何编写加壳器/存根组合中的加壳器部分,同时介绍 Windows 可执行文件格式。
    1. 管理资源
    2. 解析 PE 文件
    3. 操作 PE 文件
  6. 模拟加载器:关于如何构建一个最小的存根可执行文件来解包并加载目标可执行文件,同时介绍更高级的 Windows 可执行文件操作。
    1. 从内存中读取我们的 PE
    2. 加载我们的 PE 以执行
    3. 解析 API 导入
    4. 解析地址
    5. 转移执行
  7. 进一步练习:一些拓展你加壳器开发知识的练习。

看起来令人生畏?试试演示文稿版本,它总结了本 README。YouTube 视频即将推出!

什么是加壳器?

加壳器是一种在其自身地址空间(有时是另一个进程的地址空间)内解压缩并启动另一个程序的程序。它有时以攻击分析环境(如调试器和虚拟沙箱)的载体而闻名。它主要用于以下几件事:

  • 压缩:加壳器通常用于压缩给定二进制文件的代码。这是它为数不多的合法用途之一。参见 UPX 了解压缩加壳器的示例。
  • 混淆:加壳器也用于试图混淆或以其他方式保护程序免遭逆向工程。参见 Riot Games 的 packman 加壳器 了解反逆向加壳器的示例。
  • 规避:恶意软件经常使用各种加壳器来规避防病毒软件甚至 EDR。参见 这篇对 SmokeLoader 加壳器的分析 了解规避型加壳器的示例。

从根本上说,加壳器只有几个基本步骤:

  1. 压缩阶段:这是将原始可执行文件压缩、混淆或两者兼施,形成一个新的二进制文件的阶段。
  2. 解压缩阶段:这是加壳后的可执行文件解压缩或去混淆其原始可执行文件以进行加载的阶段。
  3. 加载阶段:这是加壳器模仿与宿主平台的可执行加载器类似的多种步骤的阶段。由于可执行二进制文件的复杂性,这是最复杂的步骤,具体取决于你希望模拟的深度。
  4. 执行阶段:这是代码从宿主存根可执行文件转移到新加载(即解包后)代码的阶段。

同样简单的是,加壳器只由几个部分组成:

  • 加壳器:这部分负责准备所谓的 存根可执行文件,并为其提供解包给定二进制文件所需的数据。这最终就是压缩阶段。
  • 存根:这个二进制文件是负责解压缩、加载和执行原始二进制文件的代码部分。如你所见,存根可执行文件承担了加壳器的大部分繁重工作。

构建加壳器可能很棘手,因为存根需要以某种方式构建并导入加壳器可执行文件中。对于 Windows 来说,学习 Visual Studio 的构建系统(超越简单编译)可能是一项繁重的任务。幸运的是,CMake 提供了一个简单、跨平台的构建系统,支持 Visual Studio,并且高度可定制!

本教程旨在教授以下内容:

  • 如何将加壳器的两个主要部分分段并集成到一个内聚、可测试的构建环境中。
  • 如何操作 Windows 可执行文件以添加额外的、可加载的数据。
  • 如何浏览 Windows 可执行文件以检索和加载任意数据。
  • 如何模拟 Windows 加载器的部分功能来加载和执行 Windows 可执行文件。

如果你已经熟悉 C++ 和 CMake,可以随意跳过直接进入打包部分。否则,请继续阅读!

先决条件

  • C++ 知识:如果你不懂 C++,本教程就没什么用,因为我们会大量使用指针运算。
  • Visual Studio:Visual Studio 包含一个功能齐全的 Windows C++ 编译器。本项目已在 Visual Studio 2019 上测试,但更新版本应该也没问题。
  • CMake:CMake 是我们用来为 Visual Studio 编译器构建系统提供支持的构建系统。

初步体验

首先,让我们以某种方式证明这个构建系统有效,并且能创建正确的加壳可执行文件。安装好 CMake 和 Visual Studio 后,使用你选择的终端导航到本仓库的根目录,并执行以下命令:``` $ mkdir build $ cd build $ cmake ../

这将创建构建 packer 教程代码所需的项目文件。然后运行:```
$ cmake --build ./ --config Release

这将以 Release 模式构建 packer 项目。然后,你可以运行以下测试:``` $ ctest -C Release ./

如果一切顺利,test\_pack 和 test_unpack 应该会成功。如果你想亲自查看打包的结果,你应该会看到:```
$ ./packed.exe
I'm just a little guy!

让我们来聊聊实现这一切所涉及的所有关键部分。

勾勒我们的 CMake 项目

因此,我们了解了加壳器的主要组件,但如何在开发周期中立即测试我们的加壳器呢?我们需要在整体项目中添加第三个二进制文件,以便正确测试我们二进制的加壳与脱壳过程。总体而言,我们需要三个项目:

  • 加壳器
  • stub(存根)
  • 用于加壳的虚拟可执行文件

我们还需要一个压缩库,以确保二进制文件能够压缩进 stub 可执行文件中。这里使用 zlib 会很合适。

我们的项目层级结构,初始阶段应该如下所示:``` packer/ +---+ CMakeLists.txt + dummy/ | | | +---+ CMakeLists.txt | + src/ | | | +---+ main.cpp | + stub/ | | | +---+ CMakeLists.txt | + src/ | | | +---+ main.cpp | + src/ | | | +---+ main.cpp | + zlib-1.2.13/ | +---+ CMakeLists.txt + ...

每个文件夹中的 main.cpp 目前可以简单地写成这样:```cpp
#include <iostream>

int main(int argc, char *argv[]) {
    std::cout << "I'm just a little guy!" << std::endl;
    
    return 0;
}

现在,我们应该确定我们要处理的是一个简单的依赖链,CMake 通过一些配置可以很好地为我们解析它:

  • packer 和 stub 依赖于 zlib
  • packer 依赖于 stub
  • dummy 依赖于 packer(因为它需要由 packer 打包)

让我们从 根项目(即 packer)开始进行我们的 CMake 配置。

CMake 通常要求指定一个最低版本,因为它已经存在很长时间,并支持长期使用旧版本。之后,我们可以将 packer 项目声明为一个 C++ 项目。```cmake

target a cmake version, you can target a lower version if you like

cmake_minimum_required(VERSION 3.24)

declare our packer as a C++ project (since zlib is a C project and the compilation

detection might get confused)

project(packer CXX)

我们还希望将我们的打包器声明为"MultiThreaded"(即 /MT)而不是"MultiThreadedDLL"(即 /MD),这样我们就不必担心运行时 DLL 依赖。```cmake
# this line will mark our packer as MultiThreaded, instead of MultiThreadedDLL
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")

变量设置语句中尖括号内的语句称为生成器表达式,它帮助我们在需要的地方解析配置时数据,你会在整个文件中经常看到它。这个生成器表达式的作用是:当检测到配置为 Debug 时,输出字符串 Debug,否则不输出任何内容。这样,当选择 Debug 编译配置时,会提供 MultiThreadedDebug 运行时库;而当选择非调试编译配置(如 Release)时,则提供 MultiThreaded。请参阅条件生成器表达式以深入理解其中的原理。有关 CMAKE_MSVC_RUNTIME_LIBRARY 变量的更多信息,请参阅 CMake 文档。

CMake 允许你将源代码组织成层级结构,就像 Visual Studio 通过其界面创建项目时自动做的那样;它还会在你的文件夹中递归搜索匹配的文件名。在我们的配置文件中,我们对头文件(.hpp)、代码文件(.cpp)和资源脚本(.rc)设置了全局递归。对于我们的示例,我们实际上只需要 main.cpp,但对于更大的项目,了解这一点会很有用。```cmake

this will collect header, source and resource files into convenient variables

file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/src/.cpp) file(GLOB_RECURSE HDR_FILES ${PROJECT_SOURCE_DIR}/src/.hpp) file(GLOB_RECURSE RC_FILES ${PROJECT_SOURCE_DIR}/src/*.rc)

this will give you source groups in the resulting Visual Studio project

source_group(TREE "${PROJECT_SOURCE_DIR}" PREFIX "Header Files" FILES ${HDR_FILES}) source_group(TREE "${PROJECT_SOURCE_DIR}" PREFIX "Source Files" FILES ${SRC_FILES}) source_group(TREE "${PROJECT_SOURCE_DIR}" PREFIX "Resource Files" FILES ${RC_FILES})

这里我应当说明,更准确地说,CMake 是一种 **构建系统**(make system)。它会根据给定的编译器,为当前环境生成相应的“构建系统”。在 Linux 上,这会为检测到(或指定)的编译器生成一个 makefile。而在 Windows 上,它会生成一个与我们所用 Visual Studio 版本兼容的 Visual Studio 工程,这意味着一旦你创建了 MSVC 构建系统,之后就可以直接使用 Visual Studio 完成一切操作——只要你愿意!归根结底,你是在用 CMake 来配置 Visual Studio。因此,你现在所做的,本质上就是在 Visual Studio 中创建项目里的文件树,就像 GUI 在创建项目时替你完成的那样。

接下来,我们需要添加我们所依赖的项目:```cmake
# this will add zlib as a build target
add_subdirectory(${PROJECT_SOURCE_DIR}/zlib-1.2.13)

# this will add our stub project
add_subdirectory(${PROJECT_SOURCE_DIR}/stub)

# this will add our test dummy project
add_subdirectory(${PROJECT_SOURCE_DIR}/dummy)

我之前提到过一点,packer 项目依赖 stub 项目。无论如何,packer 需要保留 stub 并对其进行操作,最终得到我们打包后的可执行文件。我们可以使用 Windows 资源文件 来最终将我们的 stub 可执行文件嵌入到 packer 二进制文件中,无论构建配置如何!除此之外,我们还可以使用 CMake 为我们生成这些文件,这样我们对构建出的可执行文件的引用在 CMake 项目中就是可靠的!现在让我们生成资源文件,以便我们可以将它们包含到项目中:```cmake

this will make sure our stub data will be included in the resources of our packer

despite where it may reside in cmake's build system

file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$/stub.hpp" CONTENT "#pragma once\n#define IDB_STUB 1000\n") file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/$/stub.rc" CONTENT "#include <winresrc.h>\n#include "stub.hpp"\nIDB_STUB STUB "$<TARGET_FILE:stub>"\n")

`CMAKE_CURRENT_BINARY_DIR` 是包含当前构建目录的字符串。Visual Studio 会根据配置将二进制文件转储到不同文件夹中,因此我们使用 `$<CONFIG>` 生成器表达式来获取当前构建配置。我们还使用 `$<TARGET_FILE:stub>` 生成器表达式来输出存根二进制文件编译后的可执行文件名。当我们将这些文件(生成的 RC 文件和生成的头文件)包含到项目中时,就可以成功地将存根二进制文件集成到打包器项目中。
下载工具