Skip to content

Instantly share code, notes, and snippets.

@enkomio
Last active May 28, 2024 21:19
Show Gist options
  • Save enkomio/d6ac617d2aafa0c3c11aab3f8c91dcfc to your computer and use it in GitHub Desktop.
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
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
@wfjsw
Copy link

wfjsw commented Jul 1, 2021

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment