Last active
May 28, 2024 21:19
-
-
Save enkomio/d6ac617d2aafa0c3c11aab3f8c91dcfc to your computer and use it in GitHub Desktop.
This code extracts the real MSIL bytecode of the malware sample and rebuild a new assembly
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open System | |
open System.Linq | |
open System.Reflection | |
open System.Runtime.CompilerServices | |
open System.Collections | |
open System.Collections.Generic | |
open System.Diagnostics | |
open Microsoft.Diagnostics.Runtime | |
open dnlib.DotNet | |
open dnlib.DotNet.Emit | |
open dnlib.IO | |
let runAllStaticConstructor(assembly: Assembly) = | |
assembly.GetTypes() | |
|> Seq.iter(fun assemblyType -> | |
RuntimeHelpers.RunClassConstructor(assemblyType.TypeHandle) | |
) | |
let getMsilStorage(assembly: Assembly) = | |
let assemblyType = assembly.GetType("yasttpQOrHx2jEkiUL.P9ZBIKXMsRMxLdTfcG") | |
let storageField = assemblyType.GetField("k6dbsY0qhy", BindingFlags.NonPublic ||| BindingFlags.Static) | |
let hashTable = storageField.GetValue(null) :?> Hashtable | |
hashTable.Cast<DictionaryEntry>() | |
|> Seq.map(fun kv -> | |
// extract bytes array | |
let bytesField = | |
kv.Value.GetType().GetFields(BindingFlags.Instance ||| BindingFlags.NonPublic) | |
|> Seq.find(fun field -> field.FieldType.IsArray) | |
let msilBytes = bytesField.GetValue(kv.Value) :?> Byte array | |
(kv.Key :?> Int64, msilBytes)) | |
|> Map.ofSeq | |
let getAssemblyBaseAddress(assembly: Assembly) = | |
let pid = Process.GetCurrentProcess().Id | |
let dataTarget = DataTarget.AttachToProcess(pid, uint32 5000, AttachFlag.Passive) | |
let runtime = dataTarget.ClrVersions.[0].CreateRuntime() | |
let assemblyModule = runtime.Modules |> Seq.find(fun m -> m.Name.Equals(assembly.Location)) | |
let baseAddress = int32 assemblyModule.ImageBase | |
dataTarget.Dispose() | |
baseAddress | |
[<EntryPoint>] | |
let main argv = | |
let fullPath = @"PloutusD_d93342bd12ef44d92bf58ed2f0f88443385a0192804a5d0976352484c0d37685.exe" | |
let assembly = Assembly.LoadFile(fullPath) | |
// run all static constructor to fill the protection dictionary containing the real MSIL bytecode | |
runAllStaticConstructor(assembly) | |
// retrieve the dictionary via reflection. This is higly dependant to a specific sample, the field name may change from sample to sample | |
let msilStorage = getMsilStorage(assembly) | |
// get the malware base addres in order to calculate the method body address | |
let baseAddress = getAssemblyBaseAddress(assembly) | |
// use a modified version of dnlib in order to set the real MSIL bytecode | |
let dnModule = ModuleDefMD.Load(fullPath) | |
dnModule.Types | |
|> Seq.map(fun t -> t.Methods) | |
|> Seq.concat | |
|> Seq.filter(fun dnMethod -> dnMethod.HasBody) | |
|> Seq.iter(fun dnMethod -> | |
dnMethod.Body.KeepOldMaxStack <- true | |
// skip the body header | |
let offset = int32 dnMethod.Body.HeaderSize | |
let ilAddress = int32 dnMethod.RVA + baseAddress + offset | |
if msilStorage.ContainsKey(int64 ilAddress) then | |
let realMsilBytes = msilStorage.[int64 ilAddress] | |
dnMethod.Body.MaxStack <- uint16 50 | |
// set the body via raw buffer. This property is not present in the real dnlib code :P | |
dnMethod.Body.RawBody <- new List<Byte>(realMsilBytes) | |
Console.WriteLine("Rebuit: " + dnMethod.FullName) | |
) | |
// finally write back the new assembly | |
dnModule.Write(fullPath + "_rebuilt") | |
0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I'm aware that the name of the local variables can be lost, but in this case the issue is that the local variable list vanished. Probably the way that modified dnlib works overrides certain metadata of the methods.
Anyway my original issue is solved thanks to https://github.com/mobile46/de4dot as reference.