2.13 PE结构:实现PE代码段加密

news2024/11/18 21:37:17

代码加密功能的实现原理,首先通过创建一个新的.hack区段,并对该区段进行初始化,接着我们向此区段内写入一段具有动态解密功能的ShellCode汇编指令集,并将程序入口地址修正为ShellCode地址位置处,当解密功能被运行后则可释放加密的.text节,此时再通过一个JMP指令跳转到原始OEP位置,则可继续执行解密后的区段。

如下是一段异或解密功能实现,用于实现循环0x88异或解密代码功能;

00408001  MOV EAX, main.00401000        (代码段首地址)
00408006  XOR BYTE PTR DS : [EAX], 88   (与0x88异或)
00408009  INC EAX
0040800A  CMP EAX, main.00404B46        (代码段结束位置)
0040800F  JNZ SHORT main.00408006       (写入原始OEP)
00408011  POPAD
00408012  MOV EAX, main.00401041        (写入新OEP)
00408017  JMP EAX

有了上述加密流程,则下一步就是对内部的变量进行填充,我们可以提取出这些汇编指令的机器码并存储到Code[]数组内,通过对数组中的特定位置进行替换来完善跳转功能,此处我们需要提取如下几个位置的特征字段;

  • 00408001 数组下标第2位替换为ImageBase + pSection->VirtualAddress
  • 0040800A 数组下标第11位替换为ImageBase + pSection->VirtualAddress + pSection->Misc.VirtualSize
  • 0040800F 数组下标第19位替换为ImageBase + BaseRVA
  • 00408012 原始OEP位置替换为pSection->VirtualAddress

根据上述流程我们可以编写一个AddPacking函数,该函数通过传入一个待加密程序路径,则可将一段解密代码Code[]写入到程序节表中的最后一个节.hack所在内存空间,并动态修正当前入口地址为.hack节的首地址,最终执行解密后自动跳转回原始OEP位置执行功能,如下代码所示;

// 对文件执行加壳操作
void AddPacking(LPSTR szFileName)
{
  // 打开文件
  HANDLE hFile = CreateFileA(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

  // 创建文件映射
  HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
  HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0);

  // 找到PE文件头
  PIMAGE_DOS_HEADER DosHdr = (PIMAGE_DOS_HEADER)lpBase;
  PIMAGE_NT_HEADERS NtHdr = (PIMAGE_NT_HEADERS)((DWORD)lpBase + DosHdr->e_lfanew);

  DWORD ImageBase = NtHdr->OptionalHeader.ImageBase;
  DWORD BaseRVA = NtHdr->OptionalHeader.AddressOfEntryPoint;
  PIMAGE_FILE_HEADER FileHdr = &NtHdr->FileHeader;
  PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(NtHdr);

  // 首先得到最后一个节的指针,然后找到里面的虚拟偏移值,填入到程序OEP位置即可。
  DWORD SectionNum = FileHdr->NumberOfSections;

  char Code[] =
  {
    "\x60"
    "\xb8\x00\x00\x00\x00"
    "\x80\x30\x88"
    "\x40"
    "\x3d\xff\x4f\x40\x00"
    "\x75\xf5"
    "\x61"
    "\xb8\x00\x00\x00\x00"
    "\xff\xe0"
  };

  DWORD dwWrite = 0;

  // 写入代码节开始位置
  *(DWORD *)&Code[2] = ImageBase + pSection->VirtualAddress;
  printf("[+] 写入代码节开始位置: 0x%08X \n", ImageBase + pSection->VirtualAddress);

  // 写入代码节终止位置
  *(DWORD *)&Code[11] = ImageBase + pSection->VirtualAddress + pSection->Misc.VirtualSize;
  printf("[+] 写入代码节结束位置:0x%08X \n", ImageBase + pSection->VirtualAddress + pSection->Misc.VirtualSize);

  // 写入原来的的OEP位置
  *(DWORD *)&Code[19] = ImageBase + BaseRVA;
  printf("[+] 写入原始OEP 0x%08X \n", ImageBase + BaseRVA);

  // 拿到最后一个节的文件偏移
  pSection = pSection + (SectionNum - 1);
  printf("[+] 得到最后一个节的实际地址: 0x%08X \n", pSection->PointerToRawData);

  // 设置指针到最后一个节文件偏移位置
  SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
  WriteFile(hFile, (LPVOID)Code, sizeof(Code), &dwWrite, NULL);
  FlushViewOfFile(lpBase, 0);

  // 修正当前入口点地址
  printf("[+] 修正入口点地址为: 0x%08X \n", pSection->VirtualAddress);
  *(DWORD *)&NtHdr->OptionalHeader.AddressOfEntryPoint = pSection->VirtualAddress;
  UnmapViewOfFile(lpBase);
}

读者可自行运行上述代码片段,传入d://lyshark.exe对该程序中的.text节进行解密,运行后读者可打开x64dbg调试器,观察入口地址处的变化,此时入口地址已经跳转到.hack节内,此节中的汇编指令则用于对.text代码节进行解密操作,当解密结束后则会跳转到原始地址0x45C865位置处,如下图所示;

当加密功能写入后,则接下来就可以对.text代码节进行加密了,加密的实现也非常容易,如下函数,通过定位到第一个节,并通过ReadFile函数将这个节读入内存,通过循环对这个区域进行加密,最后调用WriteFile函数再将加密后的数据回写到代码节,此时加密功能就实现了,如下所示;

// 将特定的节进行加密,此处只加密Text节
void EncrySection(LPSTR szFileName, DWORD Key)
{
  // 打开文件
  HANDLE hFile = CreateFileA(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
    NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

  // 创建文件映射
  HANDLE hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
  HANDLE lpBase = MapViewOfFile(hMap, FILE_MAP_READ | FILE_SHARE_WRITE, 0, 0, 0);

  // 定位PE文件节
  PIMAGE_DOS_HEADER DosHdr = (PIMAGE_DOS_HEADER)lpBase;
  PIMAGE_NT_HEADERS NtHdr = (PIMAGE_NT_HEADERS)((DWORD)lpBase + DosHdr->e_lfanew);
  PIMAGE_FILE_HEADER FileHdr = &NtHdr->FileHeader;
  PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(NtHdr);
  
  printf("[-] 节虚拟地址: 0x%08X 虚拟大小: 0x%08X\n", pSection->VirtualAddress, pSection->Misc.VirtualSize);
  printf("[-] 读入FOA基地址: 0x%08X 节表长度: 0x%08X \n", pSection->PointerToRawData, pSection->SizeOfRawData);
  printf("[-] 已对 %s 节 --> XOR加密/解密 --> XOR密钥: %d \n\n", pSection->Name, Key);

  // 分配内存空间
  DWORD dwRead = 0;
  PBYTE pByte = (PBYTE)malloc(pSection->SizeOfRawData);

  SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
  memset(pByte, 0, pSection->SizeOfRawData);
  ReadFile(hFile, pByte, pSection->SizeOfRawData, &dwRead, NULL);

  // 对代码节加密
  for (int x = 0; x < pSection->SizeOfRawData; x++)
  {
    pByte[x] ^= Key;
  }

  // 写出加密后的数据
  SetFilePointer(hFile, pSection->PointerToRawData, 0, FILE_BEGIN);
  WriteFile(hFile, pByte, pSection->SizeOfRawData, 0, FILE_BEGIN);
  pSection->Characteristics = 0xE0000020;

  free(pByte);
  FlushViewOfFile(lpBase, 0);
  UnmapViewOfFile(lpBase);
}

读者通过AddPacking函数对文件加壳后,接着就可以调用EncrySection函数对目标程序进行异或处理,此处分别传入d://lyshark.exe路径,以及一个加密密钥0x88,等待加密结束即可,此时运行程序即可实现对代码段的解密运行,这样也就起到了保护了代码段的作用,如下是解密之前的代码段;

当解密后,代码段将会被展开,并输出如下图所示的样子,此时程序即可被正确执行;

本文作者: 王瑞
本文链接: https://www.lyshark.com/post/5912e71.html
版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1001433.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

js中如何实现字符串去重?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用 Set 数据结构⭐ 使用循环遍历⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感…

Python自动化测试(1)-自动化测试及基本技术手段概述

生产力概述 在如今以google为首的互联网时代&#xff0c;软件的开发和生产模式都已经发生了变化&#xff0c; 在《参与感》一书提到&#xff1a;某位从微软出来的工程师很困惑&#xff0c;微软在google还有facebook这些公司发展的时候&#xff0c;为何为感觉没法有效还击&…

Android性能监测

1.使用adb top可以查看当前Android设备的CPU和内存&#xff08;mem&#xff09;使用情况 adb shell top打印的信息如下&#xff1a; PID 表示进程号 USER 表示进程所属用户组 PR 进程的优先级&#xff0c;值越小&#xff0c;优先级越高 NI 进程的nice值&#xff0c;决定了CPU…

SpringMVC实现文件上传和下载

目录 前言 一. SpringMVC文件上传 1. 配置多功能视图解析器 2. 前端代码中&#xff0c;将表单标记为多功能表单 3. 后端利用MultipartFile 接口&#xff0c;接收前端传递到后台的文件 4. 文件上传示例 1. 相关依赖&#xff1a; 2. 逆向生成对应的类 3. 后端代码&#xf…

python-turtle库

turtle库 turtle.done() - - - done函数让窗口进入消息循环&#xff0c;等待关闭 设置画布大小和位置 turtle.screensize(canvwidthNone, canvheightNone, bgNone) 画布的宽高背景颜色 turtle.setup(width0.5, height0.75, startxNone, startyNone) 宽高为整数时表示像素&…

leetcode第362场周赛

2848. 与车相交的点 核心思想&#xff1a;差分数组。统计覆盖区间&#xff0c;把nums中有的区间1&#xff0c;维护区间我们用差分数组&#xff0c;然后求出差分数组的前缀和即是我们维护的区间&#xff0c;判断区间有哪些值是大于0的即可。 2849. 判断能否在给定时间到达单元格…

新型温湿度传感器解决方案:满足多样化应用需求

在许多环境和应用场景中&#xff0c;温度和湿度的监控和管理都至关重要。例如&#xff0c;在农业大棚中&#xff0c;精确控制温湿度对于作物的生长影响巨大&#xff1b;在仓储物流中&#xff0c;保持适宜的温湿度可以延长物品的保存期限&#xff1b;在HVAC系统中&#xff0c;精…

QT子线程或自定义类操作访问主界面UI控件的几种方法

前言 QT创建窗体工程&#xff0c;一般在MainWindow或Dialog类里可以直接通过ui指针访问控件&#xff0c;但是添加新的类后又如何访问呢&#xff0c;可以通过以下几种方式&#xff1a; 将ui指针公开后直接访问 &#xff08;1&#xff09;例如有个自己定义的类CustomCl…

Rokid Jungle--Station pro

介绍和功能开发 YodaOS-Master操作系统&#xff1a;以交换计算为核心&#xff0c;实现单目SLAM空间交互&#xff0c;具有高精度、实时性和稳定性。发布UXR2.0SDK&#xff0c;为构建空间内容提供丰富的开发套件 多模态交互 算法原子化 多种开发工具协同 多生态支持 骁龙XR2…

代码随想录 -- day48 -- 198.打家劫舍、213.打家劫舍II 、337.打家劫舍III

198.打家劫舍 dp[i]&#xff1a;考虑下标i&#xff08;包括i&#xff09;以内的房屋&#xff0c;最多可以偷窃的金额为dp[i]。 dp[i] max(dp[i - 2] nums[i], dp[i - 1]); class Solution { public:int rob(vector<int>& nums) {if (nums.size() 0) return 0;if…

十六、Webpack常见的插件和模式

一、认识插件Plugin Webpack的另一个核心是Plugin&#xff0c;官方有这样一段对Plugin的描述&#xff1a; While loaders are used to transform certain types of modules, plugins can be leveraged to perform a wider range of tasks like bundle optimization, asset m…

系统转换-系统维护-净室软件工程-基于构件

系统转换-系统维护-净室软件工程-基于构件 系统转换系统转换系统维护净室软件工程基于构建的软件工程 系统转换 改造 继承 集成 淘汰 系统转换 直接转换 并行转换 分段转换&#xff1a;子系统之间的并行转换 系统维护 重点 净室软件工程 预防大于检查 基于构建的软件工程 复用而…

win11无法加载文件,因为在此系统上禁止运行脚本

问题背景&#xff1a; 最近升级了windows11&#xff0c;文件右键打开终端&#xff0c;默认是使用的powershell。 后面安装npm包依赖的时候&#xff0c;遇到了无法加载文件&#xff0c;因为在此系统上禁止运行脚本。 提示中可以通过访问链接查看&#xff1a;https:\go.micros…

Java笔记040-反射/反射机制、Class类

目录 反射(reflection) 一个需求引出反射 反射机制 Java反射机制原理图 Java反射机制可以完成 反射相关的主要类 反射机制的优点和缺点 反射调用优化-关闭访问检查 Class类 基本介绍 代码解释部分 类加载方法 应用实例&#xff1a;Class02.java 获取Class类对象 …

【MFC】实现简单UDP通信

创建项目&#xff0c;初始化套接字 创建一个基于对话框的MFC项目&#xff08;名称为UDP&#xff09;&#xff0c;高级功能选中Windows套接字 这个时候在CUDP类的InitInstance()方法中就会出现这样的代码用来初始化套接字 if (!AfxSocketInit()) {AfxMessageBox(IDP_SOCKETS_…

APC注入进阶“Early Bird”

序 APC注入在前面的章节已经给大家讲过&#xff0c;基本原理也是老生常谈的内容了&#xff1a; APC注入可以让一个线程在它正常的执行路径运行之前执行一些其他的代码&#xff0c;每一个线程都有一个附加的APC队列&#xff0c;他们在线程处于可警告的时候才被处理&#xff1b;…

【JAVA-Day04】Java关键字和示例:深入了解常用关键字的用法

Java关键字和示例&#xff1a;深入了解常用关键字的用法 摘要Java 关键字、标识符和命名规范一、Java 关键字常用关键字DEMO1. 示例代码使用 if 和 else 关键字&#xff1a;2. 示例代码使用 for 循环&#xff1a;3. 示例代码使用 switch 关键字&#xff1a;4. 示例代码使用 wh…

2022年第一界HiPChips解读

High Performance Chiplet and Interconnect Architectures&#xff0c;2022年6月19日&#xff0c;第一届会议&#xff08;连同第49界ISCA会议&#xff09;于美国纽约举行。 第一届会议议程及slice&#xff1a;(HiPChips Chiplet Workshop ISCA Conference)[https://www.open…

使用TCP方式拉取Canal数据

1 Canal对接Kafka联调 1.1 配置修改 canal.properties 修改 zk&#xff1a; canal.zkServers 10.51.50.219:2181instance.properties 开启配置项&#xff1a; canal.mq.dynamicTopic 是 Canal 的 MQ 动态 Topic 配置项&#xff1a; test_javaedge_01 是kafka 的 topicte…

混合云代表,青云科技QingCloud从此走向小而美之路

你有你的活法&#xff0c; 我有我的活法 只要活出自己&#xff0c; 活出个样子都是美事 【全球元观察 &#xff5c; 热点关注】 刚注意到青云科技QingCloud的最新发布2023年半年度报告&#xff0c;2023上半年同比减亏约7000万元&#xff0c;研发人员减少83人。从这两个数字…