导语:本文介绍了通过.NET实现内存加载PE文件的方法,以Casey Smith开源的PELoader.cs为模板,编写代码生成工具SharpPELoaderGenerate,分享代码开发需要注意的细节。
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,地址如下:
https://github.com/3gstudent/Homework-of-C-Sharp/blob/master/SharpMimikatz_x86.cs
https://github.com/3gstudent/Homework-of-C-Sharp/blob/master/SharpMimikatz_x64.cs
分别对应内存加载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 #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,分享代码开发需要注意的细节。
本文为 3gstudent 原创稿件,授权嘶吼独家发布,如若转载,请注明原文地址