0x00 Preface

---

In previous articles 'Exploitation Analysis of Loading .NET Assemblies from Memory (execute-assembly)' and 'Exploitation Analysis of Loading .NET Assemblies from Memory (Assembly.Load)', methods for loading .NET assemblies from memory using C# were introduced. This time, we will go a step further to introduce methods for loading PE files from memory using C#.

0x01 Introduction

---

This article will cover the following:

  • Implementation Principles
  • Casey Smith's open-source PELoader.cs
  • Methods to extend PELoader.cs
  • Implementation details of SharpPELoaderGenerater
  • Exploitation methods

0x02 Implementation Principles of Loading PE Files into Memory

---

The implementation principles are as follows:

  1. Read the PE file and parse it according to the PE format
  2. Allocate memory, using ImageBase as the memory base address and SizeOfImage as the length
  3. Copy the PE file header into memory
  4. Parse the Section addresses and copy the Sections into memory
  5. Modify memory based on the relocation table
  6. Parse the import table and load the required DLLs
  7. Jump to the entry point AddressOfEntryPoint and execute the PE file

0x03 Casey Smith's open-source PELoader.cs

---

Currently available reference address:

https://github.com/re4lity/subTee-gits-backups/blob/master/PELoader.cs

This code can be compiled using csc.exe under .NET 4.0 or higher

The compilation command is as follows:

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

The code implements loading 64-bit mimikatz.exe in memory

PELoader.cs stores the encoded mimikatz.exe in the string KatzCompressed

If you want to perform a replacement, you can refer to my code at the following address:

An open-source project

After execution, the file base64.txt is generated. Use its content to replace the string KatzCompressed

0x04 Extending the methods of PELoader.cs

---

1. Adding support for compilation environments

PELoader.cs uses .Add() which makes it incompatible with .Net 3.5. This can be replaced to support .Net 3.5

2. Supporting 32-bit program loading

It is necessary to distinguish between the PE structures of 32-bit and 64-bit programs and recalculate the offsets

The extended PELoader.cs code has been uploaded to GitHub at the following address:

An open-source project

An open-source project

Corresponding to the code for loading 32-bit and 64-bit mimikatz in memory, respectively

Supports .Net 3.5 and newer versions

The compilation command for SharpMimikatz_x86.cs is as follows:

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

The compilation command for SharpMimikatz_x64.cs is as follows:

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 Implementation Details of SharpPELoaderGenerater

---

Using Casey Smith's open-source PELoader.cs as a template, attempt to implement automatic generation of PE file loading templates in C#

Here, taking SharpMimikatz_x64.cs as an example, the code can be divided into the following three parts:

  1. Front-end calling code
  2. Compressed string of the exe file
  3. Back-end calling code

The code generation method is as follows:

1. First half calling code

Since the first half calling code contains multiple escape characters, storing it directly in a string array is cumbersome. The approach here is to compress and encode the first half code before storing it in the string array, which also significantly reduces code length (from 31kb to 6kb).

The first half code can be compressed using the following C# code:

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();
}
}
}

After execution, files SharpMimikatz_x86_part.txt and SharpMimikatz_x64_part.txt are generated, containing the compressed and encoded first half of the calling code

The compressed string of the 2.exe file

Can be generated using the following code:

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

3. Call code for the latter part

Escape characters need to be used here

Can be defined using the following code:

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

As a code generation template, it is also necessary to distinguish whether the exe is 32-bit or 64-bit. The judgment method is as follows:

Through the Characteristics field in the IMAGE_FILE_HEADER structure, if the attribute IMAGE_FILE_32BIT_MACHINE exists, then the exe file is 32-bit

The complete code has been uploaded to GitHub, address as follows:

An open-source project

Usage example:

SharpPELoaderGenerater.exe test.exe

If test.exe is a 32-bit program, the file SharpPELoader_x86.cs will be generated

Compilation method:

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

If test.exe is a 64-bit program, it will generate the file SharpPELoader_x64.cs

Compilation method:

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

Additional note:

When writing test.exe in C++, it is necessary to manually add the DLLs that need to be called

Example code is as follows:

#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 Summary

---

This article introduces methods for implementing in-memory PE file loading via .NET. Using Casey Smith's open-source PELoader.cs as a template, we developed the code generation tool SharpPELoaderGenerate and share key details to note during code development.