通过 .NET 实现内存加载 PE 文件

发布于 2025-02-18 03:28:36 字数 6494 浏览 6 评论 0

0x00 前言

在之前的文章 《从内存加载.NET 程序集(execute-assembly) 的利用分析》 和《从内存加载.NET 程序集(Assembly.Load) 的利用分析》曾介绍了使用 C Sharp 从内存加载.NET 程序集的方法,这次将要更近一步,介绍使用 C Sharp 从内存加载 PE 文件的方法

0x01 简介

本文将要介绍以下内容:

  • 实现原理
  • Casey Smith 开源的 PELoader.cs
  • 扩展 PELoader.cs 的方法
  • SharpPELoaderGenerater 的实现细节
  • 利用方法

0x02 内存加载 PE 文件的实现原理

实现原理如下:

  1. 读取 PE 文件,按照 PE 格式进行解析
  2. 申请内存,ImageBase 作为内存基地址,SizeOfImage 作为长度
  3. 将 PE 文件头复制到内存中
  4. 解析 Section 的地址并将 Section 复制到内存中
  5. 基于重定位表修改内存
  6. 解析导入表,加载所需的 Dll
  7. 跳转到入口地址 AddressOfEntryPoint,执行 PE 文件

0x03 Casey Smith 开源的 PELoader.cs

目前可供参考的地址:https://github.com/re4lity/subTee-gits-backups/blob/master/PELoader.cs

这个代码可以使用.NET 4.0 或者更高版本下的 csc.exe 进行编译

编译命令如下:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /unsafe PELoader.cs

代码实现了在内存中加载 64 位的 mimikatz.exe

PELoader.cs 在字符串 KatzCompressed 存储经过编码后的 mimikatz.exe

如果想要进行替换,可以参考我的代码,地址如下:https://github.com/3gstudent/Homework-of-C-Sharp/blob/master/GzipandBase64.cs

执行后生成文件 base64.txt,将其中的内容用来替换字符串 KatzCompressed

0x04 扩展 PELoader.cs 的方法

1.增加支持的编译环境

PELoader.cs 因为使用了 .Add() 导致不支持.Net3.5,可以对此进行替换使其支持.Net3.5

2.支持 32 位程序的加载

需要区分 32 位和 64 位程序 PE 结构的不同,重新计算偏移

扩展 PELoader.cs 的代码已上传至 github,地址如下:

分别对应内存加载 32 位和 64 位 mimikatz 的代码

支持.Net3.5 及更新版本

SharpMimikatz_x86.cs 的编译命令如下:

C:\Windows\Microsoft.NET\Framework\v3.5\csc.exe /unsafe /platform:x86 SharpMimikatz_x86.cs
or
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe /unsafe /platform:x86 SharpMimikatz_x86.cs

SharpMimikatz_x64.cs 的编译命令如下:

C:\Windows\Microsoft.NET\Framework64\v3.5\csc.exe /unsafe /platform:x64 SharpMimikatz_x64.cs
or
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /unsafe /platform:x64 SharpMimikatz_x64.cs

0x05 SharpPELoaderGenerater 的实现细节

以 Casey Smith 开源的 PELoader.cs 为模板,尝试用 c#实现自动生成加载 PE 文件的模板

这里以 SharpMimikatz_x64.cs 为例,代码可分成以下三部分:

  1. 前半部分调用代码
  2. exe 文件经过压缩后的字符串
  3. 后半部分的调用代码

代码的生成方法如下:

1.前半部分调用代码

由于前半部分的调用代码存在多个转义字符,直接保存在 string 数组中比较麻烦,这里的思路是将前半部分的代码先经过压缩编码后再保存到 string 数组中,这样还能够大幅缩小代码长度(31kb 压缩至 6kb)

压缩前半部分代码可使用如下 c#代码:

using System;
using System.IO;
using System.IO.Compression;

namespace GenerateCode
{
    class Program
    {
        static byte[] Compress(byte[] raw)
        {
            using (MemoryStream memory = new MemoryStream())
            {
                using (GZipStream gzip = new GZipStream(memory,
                CompressionMode.Compress, true))
                {
                    gzip.Write(raw, 0, raw.Length);
                }
                return memory.ToArray();
            }
        }
        static void Main(string[] args)
        {
            byte[] AsBytes = File.ReadAllBytes(@"SharpMimikatz_x86_part.cs");
            byte[] compress = Compress(AsBytes);
            String AsBase64String = Convert.ToBase64String(compress);

            StreamWriter sw = new StreamWriter(@"SharpMimikatz_x86_part.txt");
            sw.Write(AsBase64String);
            sw.Close();

            byte[] AsBytes2 = File.ReadAllBytes(@"SharpMimikatz_x64_part.cs");
            byte[] compress2 = Compress(AsBytes2);
            String AsBase64String2 = Convert.ToBase64String(compress2);

            StreamWriter sw2 = new StreamWriter(@"SharpMimikatz_x64_part.txt");
            sw2.Write(AsBase64String2);
            sw2.Close();
        }
    }
}

执行后生成文件 SharpMimikatz_x86_part.txt 和 SharpMimikatz_x64_part.txt,内容为压缩编码后的前半部分调用代码

2.exe 文件经过压缩后的字符串

可使用以下代码生成:

byte[] AsBytes = File.ReadAllBytes(@"mimikatz.exe");
byte[] compress = Compress(AsBytes);
string source = Convert.ToBase64String(compress);

3.后半部分的调用代码

这里需要使用转义字符

可使用以下代码进行定义:

string source3_x86 = "\";\r\n    }\r\n } ";

作为代码生成模板,还需要区分 exe 是 32 位还是 64 位,判断方法如下:

通过 IMAGE_FILE_HEADER 结构体中的 Characteristics 字段,如果存在属性 IMAGE_FILE_32BIT_MACHINE ,那么该 exe 文件为 32 位

完整代码已上传至 github,地址如下:

https://github.com/3gstudent/Homework-of-C-Sharp/blob/master/SharpPELoaderGenerater.cs

用法实例:

SharpPELoaderGenerater.exe test.exe

如果 test.exe 为 32 位的程序,将会生成文件 SharpPELoader_x86.cs

编译方法:

C:\Windows\Microsoft.NET\Framework\v3.5\csc.exe /unsafe /platform:x86 SharpPELoader_x86.cs
or
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe /unsafe /platform:x86 SharpPELoader_x86.cs

如果 test.exe 为 64 位的程序,将会生成文件 SharpPELoader_x64.cs

编译方法:

C:\Windows\Microsoft.NET\Framework64\v3.5\csc.exe /unsafe /platform:x64 SharpPELoader_x64.cs
or
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe /unsafe /platform:x64 SharpPELoader_x64.cs

补充:

使用 c++编写 test.exe 时需要注意手动加上需要调用的 dll

实例代码如下:

#include "stdafx.h"
#include <windows.h>
#pragma comment(lib,"User32.lib")
int main(int argc, char *argv[])
{
    LoadLibrary(L"User32.dll");
    MessageBox(NULL, NULL, NULL, MB_OK);
    return 0;
}

0x06 小结

本文介绍了通过.NET 实现内存加载 PE 文件的方法,以 Casey Smith 开源的 PELoader.cs 为模板,编写代码生成工具 SharpPELoaderGenerate,分享代码开发需要注意的细节。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

上一篇:

下一篇:

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

墟烟

暂无简介

文章
评论
27 人气
更多

推荐作者

櫻之舞

文章 0 评论 0

弥枳

文章 0 评论 0

m2429

文章 0 评论 0

野却迷人

文章 0 评论 0

我怀念的。

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文