Skip to content

Instantly share code, notes, and snippets.

@enkomio
Last active March 21, 2023 15:44
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • 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
@AshrafBasry
Copy link

AshrafBasry commented May 26, 2020

@enkomio
where did you get the modified version of dnlib ?

@enkomio
Copy link
Author

enkomio commented May 29, 2020

Hi,

I uploaded it to my GitHub account. You can find it at: https://github.com/enkomio/Conferences/tree/master/HackInBo2018/Ploutus.D%20Unpacker

@wfjsw
Copy link

wfjsw commented Jun 14, 2021

During the rebuild process the local variable of these method seems lost. Are there possible solutions to restore them?

@enkomio
Copy link
Author

enkomio commented Jun 15, 2021

Hi,
unfortunately the local variables cannot be set to the original values. This information is lost during the compilation step and no metadata is saved regarding the name of the local variables.

@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