程序加壳脱壳原理和实现
理论
一个可运行的执行文件,至少会有一个代码段,程序的入口点指向代码段,程序运行的时候,从入口点开始执行代码段指令

为了将一个正常的程序进行加壳保护,至少要三部分逻辑配合
1、待加壳保护的程序
2、加壳逻辑
3、脱壳逻辑
为便于理解,以下仅描述主要的逻辑,以 windows 为例,运行加壳程序,将待加壳程序的二进制内容读入到内存块
将该内存块解析成 PE 结构,并对该 PE 结构的代码段执行加密操作,如异或一个 KEY 或使用定制的加密算法
将该 PE 结构进行新增代码段,新增节的内容填充为脱壳的代码逻辑,用于在程序运行的时候对原逻辑进行解密脱壳
修改内存块内容,将入口点 base 地址指向脱壳代码段,还需要修正其他的额外信息,如 PE 文件大小等其他一些属性
将该内存区块保存成执行文件,在发布的时候将该执行文件提供给客户
发布的程序在启动的时候,会先运行脱壳代码段指令,将原代码段解密释放出来,并通过内存映射到一个新的代码段中
解压完后跳转到代码段中运行,实际应用中的加壳脱壳更麻烦,还会涉及到段合并以及重定位等一系列复杂的操作

HelloWorld
为了便于理解,演示代码不使用增加节的复杂操作,这里将待加壳程序打包为一个数组
待加壳程序
编写一个程序代码如下,将代码文件保存为 hello.c,编译命令 gcc heelo.c -o hello
#include <stdio.h>int value = 102;int main() {printf("Hello, World: %d\n", value);return 0;
}
加壳脱壳代码
编写 packer 和 shell 程序,这里实现分为两步
一个是将待加壳程序进行打包为数组,再一个将数组和脱壳逻辑打包到一起,作为发布的程序
将下面代码保存为 packer.c,编译命令为 gcc packer.c -o packer,生成 packer 执行文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>void encrypt(unsigned char *data, unsigned int size, unsigned char key) {for (unsigned int i = 0; i < size; i++) {data[i] ^= key;}
}int main(int argc, char *argv[]) {if (argc != 3) {printf("用法: %s <原始程序> <输出文件>\n", argv[0]);return 1;}int src_fd = open(argv[1], O_RDONLY);if (src_fd == -1) {perror("打开源文件失败");return 1;}struct stat st;fstat(src_fd, &st);unsigned int size = st.st_size;unsigned char *buffer = (unsigned char *)malloc(size);if (!buffer) {perror("内存分配失败");close(src_fd);return 1;}if (read(src_fd, buffer, size) != size) {perror("读取文件失败");free(buffer);close(src_fd);return 1;}close(src_fd);encrypt(buffer, size, 0x42); // 使用0x42作为密钥FILE *out = fopen("encrypted_binary.h", "w");if (!out) {perror("创建输出文件失败");free(buffer);return 1;}fprintf(out, "unsigned char encrypted_data[] = {\n");for (unsigned int i = 0; i < size; i++) {fprintf(out, "0x%02x", buffer[i]);if (i < size - 1) fprintf(out, ", ");if ((i + 1) % 12 == 0) fprintf(out, "\n");}fprintf(out, "\n};\n");fclose(out);printf("已生成加密数据文件 encrypted_binary.h\n");printf("现在编译壳程序:\n");printf("gcc -o %s shell.c\n", argv[2]);free(buffer);return 0;
}
运行上述的 packer 程序 ./packer hello packed_hello,生成一个 encrypted_binary.h 数组文件

这个文件就是 hello 程序的内容,只不过转为了数组,将该数组文件 include 到 shell.c 代码中,shell.c 代码如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>/*
// 这将是我们的加密后的程序数据
// 实际应用中,这部分会通过某种算法加密,这里为了简单仅做了XOR运算
unsigned char encrypted_data[] = {// 这里会存放加密后的程序内容// 实际应用中这是通过工具自动生成的
};
*/
#include "encrypted_binary.h"unsigned int encrypted_size = sizeof(encrypted_data);// 简单的解密函数 (这里使用XOR作为示例)
void decrypt(unsigned char *data, unsigned int size, unsigned char key) {for (unsigned int i = 0; i < size; i++) {data[i] ^= key;}
}int main() {// 创建临时文件来存储解密后的程序char temp_path[] = "/tmp/temp_executable_XXXXXX";int fd = mkstemp(temp_path);if (fd == -1) {perror("创建临时文件失败");return 1;}// 解密程序数据unsigned char *decrypted_data = malloc(encrypted_size);if (!decrypted_data) {perror("内存分配失败");close(fd);unlink(temp_path);return 1;}memcpy(decrypted_data, encrypted_data, encrypted_size);decrypt(decrypted_data, encrypted_size, 0x42); // 使用0x42作为密钥// 将解密后的数据写入临时文件if (write(fd, decrypted_data, encrypted_size) != encrypted_size) {perror("写入临时文件失败");free(decrypted_data);close(fd);unlink(temp_path);return 1;}// 设置执行权限fchmod(fd, S_IRWXU);close(fd);free(decrypted_data);// 执行解密后的程序if (execl(temp_path, temp_path, NULL) == -1) {perror("执行程序失败");unlink(temp_path);return 1;}// 注意:如果execl成功,以下代码不会执行// 临时文件清理需要在子进程或信号处理中完成return 0;
}
编译命令为 gcc -o packed_hello shell.c ,生成 packed_hello 执行文件,该文件就是最终发布的程序
这里代码将执行文件保存为临时文件并使用 execl 的方式执行,实际上不会这么处理的
更正常的处理方式是通过 mmap 内存映射到方式进行,不过核心思想都是一样的,主要为了方便理解
运行加壳后的程序的输出结果,和原始程序的输出结果一致

相关文章:
程序加壳脱壳原理和实现
理论 一个可运行的执行文件,至少会有一个代码段,程序的入口点指向代码段,程序运行的时候,从入口点开始执行代码段指令 为了将一个正常的程序进行加壳保护,至少要三部分逻辑配合 1、待加壳保护的程序 2、加壳逻辑 3…...
【数据分析实战】使用 Matplotlib 绘制折线图
1、简述 在日常的数据分析、科研报告、项目可视化展示中,折线图是一种非常常见且直观的数据可视化方式。本文将带你快速上手 Matplotlib,并通过几个实际例子掌握折线图的绘制方法。 Matplotlib 是 Python 中最常用的数据可视化库之一,它能够…...
数据仓库标准库模型架构相关概念浅讲
数据仓库与模型体系及相关概念 数据仓库与数据库的区别可参考:数据库与数据仓库的区别及关系_数据仓库和数据库-CSDN博客 总之,数据库是为捕获数据而设计,数据仓库是为分析数据而设计 数据仓库集成工具 在一些大厂中,其会有自…...
亚洲区域健康人群免疫细胞marker
最近发现一篇文献,作者来自新加坡基因研究所,这篇文章大概是整理了619个亚洲人群的免疫多样性图集(AIDA),跨越了7个国家,最终使用了1,265,624个免疫细胞的单细胞数据,并最终确定了8种主要的免疫…...
tree-sitter的grammar.js解惑
❓问题1:grammar.js 不是用正则表达式 /.../ 吗?为什么有 print 这样的字符串? ✅ 回答: grammar.js 分成两类“终结符”表示法: 表达方式含义xxx直接匹配该字符串字面量/regex/匹配符合正则的文本 💡 …...
三极管以及mos管
三极管与mos管的高低电平导通判断 (1)三极管的高低电平导通判断 三极管中有2个PN结,分别称为发射结和集电极结,按材料划分为硅材料三极管(硅管),锗材料三极管(锗管)&am…...
第十七天 - Jenkins API集成 - 流水线自动化 - 练习:CI/CD流程优化
前言 在DevOps实践中,持续集成与持续交付(CI/CD)是现代软件工程的核心支柱。作为业界使用最广泛的自动化服务器,Jenkins凭借其强大的插件生态和灵活的流水线配置能力,成为企业级CI/CD落地的首选工具。本文将深入解析J…...
PPT模板之--个人简历
还在为制作 PPT 时毫无头绪、对着空白页面抓耳挠腮而烦恼吗?别担心,这里就是你的 PPT 灵感补给站!在这个快节奏的信息时代,一份吸睛又高效的 PPT 至关重要,它能在商务汇报中助你赢得先机,在课堂展示时让你脱…...
【远程工具】1.1 时间处理设计与实现(datetime库lib.rs)
一、设计原理与决策 时间单位选择 采用**秒(s)**作为基准单位,基于以下考虑: 国际单位制(SI)基本时间单位 整数秒(i64)方案优势: 精确无误差(相比浮点数&am…...
Nginx常用工具
Nginx常用工具 Nginx常用工具vscode配置Nginx插件在线生成Nginx配置文件Nginx可视化配置工具 Nginx常用工具 编写Nginx配置时,使用VSCodeNginx插件,能实现自动补全格式化配置. vscode配置Nginx插件 Nginx代码高亮插件: nginx-formatter Nginx代码格式化插件&#…...
应用安全系列之四十五:日志伪造(Log_Forging)之三
1、简介 针对Java的日志系统有多种,本文主要描述如何通过修改配置文件来解决logback和log4j的日志伪造问题。 2、logback 2.1、系统提供的解决方案 在logback.xml中配置编码器自动转义特殊字符: 复制 <configuration><appender name"C…...
springboot--页面的国际化
今天来实现页面中的国际化 首先,需要创建一个新的spring boot项目,导入前端模板,在我的博客中可以找到,然后将HTML文件放在templates包下,将其他的静态资源放在statics包下,如下图结构 页面的国际化主要在首…...
前端学习10—Ajax
1 AJAX 简介 AJAX 全称为 Asynchronous JavaScript And XML,就是异步的 JS 和 XML 通过 AJAX 可以在浏览器中向服务器发送异步请求,最大优势为:无刷新获取数据 AJAX 不是新的编程语言,而是一种将现有的标准组合在一起使用的新方…...
list的常见接口使用
今天,我们来讲解一下C关于STL标准库中的一个容器list的常见接口。 在我们之前c语言数据结构中,我们已经了解过了关于链表的知识点了,那么对于现在理解它也是相对来说比较容易的了。 数据结构--双向循环链表-CSDN博客 1. 定义与包含头文件 …...
一维差分数组
2.一维差分 - 蓝桥云课 问题描述 给定一个长度为 n 的序列 a。 再给定 m 组操作,每次操作给定 3 个正整数 l, r, d,表示对 a_{l} 到 a_{r} 中的所有数增加 d。 最终输出操作结束后的序列 a。 Update: 由于评测机过快,n, m 于 20…...
再次重拾jmeter之踩坑
1.添加“csv数据文件设置”,运行时提示 java.lang.IllegalArgumentException: Filename must not be null or empty检查多次后才发现因为我运行的是整个线程组,所以对应http请求下不能包括空的csv文件 2. 填写ip时不能加/,要在路径里加&…...
Flink的 RecordWriter 数据通道 详解
本文从基础原理到代码层面逐步解释 Flink 的RecordWriter 数据通道,尽量让初学者也能理解。 1. 什么是 RecordWriter? 通俗理解 RecordWriter 是 Flink 中负责将数据从一个任务(Task)发送到下游任务的组件。想象一下,…...
4-6记录(B树)
找左边右下或者右边左下 转化成了前驱后继的删除 又分好几种情况: 1. 只剩25,小于2,所以把父亲拉到25旁边,兄弟的70顶替父亲 对于25,25的后继就是70,25后继的后继是71(中序遍历) 2. 借左子树…...
06软件测试需求分析案例-添加用户
给职业顾问部的老师添加用户密码后,他们才能登录使用该软件。只有admin账户具有添加用户、修改用户信息、删除用户的权利。admin是经理或团队的第一个人的账号,后面招一个教师就添加一个账号。 通读需求是提取信息,提出问题,输出…...
Nacos服务发现和配置管理
目录 一、Nacos概述 1. Nacos 简介 2. Nacos 特性 2.1 服务发现与健康监测 2.2 动态配置管理 2.3 动态DNS服务 2.4 其他关键特性 二、 服务注册和发现 2.1 核心概念 2.2 Nacos注册中心 2.3 Nacos单机模式 2.4 案例——服务注册与发现 2.4.1 父工程 2.4.2 order-p…...
【KWDB 创作者计划】第一卷:基础架构篇
以下是KWDB技术白皮书第一卷:基础架构篇的完整内容展示,包含要求的三个核心章节的深度解析。我们将以技术严谨性结合可读性的方式呈现,实际交付时会进一步扩展示意图和代码示例。 目录 KWDB技术白皮书卷一:基础架构篇 1. 数…...
对接日本金融市场数据全指南:K线、实时行情与IPO新股
一、日本金融市场特色与数据价值 日本作为全球第三大经济体,其金融市场具有以下显著特点: 成熟稳定:日经225指数包含日本顶级蓝筹股独特交易时段:上午9:00-11:30,下午12:30-15:00(JST)高流动性…...
【愚公系列】《高效使用DeepSeek》066-纠纷解决话术
🌟【技术大咖愚公搬代码:全栈专家的成长之路,你关注的宝藏博主在这里!】🌟 📣开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主! 👉 江湖人称"愚公搬代码",用七年如一日的精神深耕技术领域,以"…...
操作系统 3.1-内存使用和分段
如何简单使用内存 这张幻灯片展示了计算机如何开始执行程序的基本过程,涉及到存储器、指令寄存器(IR)、运算器和控制器等计算机组件。 存储器:程序被加载到内存中。图中显示了一个指令 mov ax, [100],它的作用是将内存…...
禅道MCP Server开发实践与功能全解析
一、简介 1、MCP Server核心定义 MCP Server(Meta Command Protocol Server)是一种基于客户端-服务器架构的轻量级服务程序,采用统一的mcp协议格式,通过连接多样化数据源和工具为AI应用提供扩展能力。它作为中间层,实…...
Spring Boot 3.5新特性解析:自动配置再升级,微服务开发更高效
📝 摘要 Spring Boot 3.5作为Spring生态的最新版本,带来了多项令人振奋的改进。本文将深入解析其中最核心的自动配置增强特性,以及它们如何显著提升微服务开发效率。通过详细的代码示例和通俗易懂的讲解,您将全面了解这些新特性在…...
GNSS静态数据处理
1 安装数据处理软件:仪器之星(InStar )和 Trimble Business Center 做完控制点静态后,我们需要下载GNSS数据,对静态数据进行处理。在处理之前需要将相关软件在自己电脑上安装好: 仪器之星(InS…...
java家政APP源码,家政预约平台源码,家电上门维修、家电上门清洗
家政上门预约服务APP源码,开发功能涵盖了用户注册与登录、家政服务分类与选择、预约管理、支付与交易、地图与导航、评价与反馈、个人信息管理、消息通知、营销工具以及数据分析等多个方面。这些功能的实现不仅提高了家政服务的便捷性和效率,还为用户提供…...
LangGraph 架构详解
核心架构组件 LangGraph 的架构建立在一个灵活的基于图的系统上,使开发者能够定义和执行复杂的工作流。以下是主要架构组件: 1. 状态管理系统 LangGraph 的核心是其强大的状态管理系统,它允许应用程序在整个执行过程中维护一致的状态&…...
【LLM基础】Megatron-LM相关知识(主要是张量并行机制)
系列综述: 💞目的:本系列是个人整理为了Megatron-LM的,整理期间苛求每个知识点,平衡理解简易度与深入程度。 🥰来源:材料主要源于Megatron-LM相关材料进行的,每个知识点的修正和深入…...
