1+ <#
2+ . SYNOPSIS
3+ Author: Dump-GUY (@vinopaljiri)
4+ Get-PDInvokeImports is tool which is able to detect P/Invoke, Dynamic P/Invoke and D/Invoke used in assembly.
5+ It uses dnlib to parse assembly and .NET reflection to load dnlib.
6+ This PS module could be useful and helpful during reversing .NET assemblies for fast revealing calls to unmanaged API functions used in assembly.
7+ Sometimes malware assemblies are full of junk code where the main functionality is implemeted by direct WIN API or NTAPI calls.
8+
9+ . DESCRIPTION
10+ Get-PDInvokeImports enables you to get fast overview what P/Invoke, Dynamic P/Invoke and D/Invoke are used in assembly.
11+ It will show you what functions are used + MDTokens, where are declared, and all location where are used from code.
12+ This PS module enables you to export all locations where are detected P/Invoke, Dynamic P/Invoke and D/Invokereferenced from code to DnSpy Bookmarks.xml
13+ Example: Imagine 1MB assembly full of junk code + CF obfuscation where main functionality is reached via unmanaged WinAPI\NTAPI calls.
14+
15+ . PARAMETER PathToAssembly
16+ Mandatory parameter.
17+ Specifies the Assembly path to scan.
18+
19+ . PARAMETER PathToDnlib
20+ Optional parameter.
21+ System Path to dnlib.dll.
22+ If powershell is running from the location of dnlib.dll - this parameter could be ignored otherwise specify this parameter.
23+
24+ . PARAMETER ExportDnSpyBookmarks
25+ Optional parameter.
26+ Used to export all detected P/Invoke, Dynamic P/Invoke and D/Invoke locations referenced from code to DnSpy Bookmarks XML file (DnSpy_Bookmarks.xml)
27+ Similar to DnSpy-Analyze-UsedBy (Nice overview where all PInvoke and DInvoke are used in whole code)
28+ So it is possible to import it to DnSpy via Bookmarks Window
29+
30+ . EXAMPLE
31+ PS> Import-Module .\Get-PDInvokeImports.ps1
32+ PS> Get-PDInvokeImports -PathToAssembly 'C:\testfiles\malware.exe' -PathToDnlib "C:\dnlib.dll" -ExportDnSpyBookmarks
33+ PS> Get-PDInvokeImports -PathToAssembly 'C:\testfiles\malware.exe'
34+ PS> Get-PDInvokeImports -PathToAssembly .\malware.exe -ExportDnSpyBookmarks
35+
36+ . LINK
37+ https://github.com/Dump-GUY/Get-PDInvokeImports
38+ https://docs.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke
39+ https://github.com/TheWover/DInvoke
40+ https://bohops.com/2022/04/02/unmanaged-code-execution-with-net-dynamic-pinvoke/
41+ https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.typebuilder.definepinvokemethod?view=netframework-4.6.1
42+ #>
43+ function Get-PDInvokeImports
44+ {
45+ [CmdletBinding ()]
46+ param
47+ (
48+ [Parameter (Mandatory = $true )]
49+ [ValidateNotNullOrEmpty ()]
50+ [string ]$PathToAssembly ,
51+
52+ [Parameter (Mandatory = $false )]
53+ [string ]$PathToDnlib = [System.Environment ]::CurrentDirectory + " \dnlib.dll" ,
54+
55+ [Parameter (Mandatory = $false )]
56+ [switch ]$ExportDnSpyBookmarks = $false
57+ )
58+
59+ # DInvoke imports reveal - type filter
60+ function Get_DInvoke_Types ($AllTypes )
61+ {
62+ foreach ($type in $AllTypes )
63+ {
64+ # get only types which have CustomAttribute = UnmanagedFunctionPointerAttribute and are defined as CallingConvention.StdCall or CallingConvention.Winapi or also possible CallingConvention.Cdecl
65+ if ((" UnmanagedFunctionPointerAttribute" -in $type.CustomAttributes.AttributeType.Name.String ) -and (" CallingConvention" -in $type.CustomAttributes.ConstructorArguments.type.TypeName ) -and ($type.CustomAttributes.ConstructorArguments.Value -in @ (1 , 2 , 3 )))
66+ {
67+ [System.Object []]$DInvoke_Types += $type
68+ }
69+ }
70+ return $DInvoke_Types
71+ }
72+ # DInvoke imports - add all Used by methods
73+ function Get_DInvoke_ALL ($DInvoke_Types , $All_Types )
74+ {
75+ foreach ($DinvokeType in $DInvoke_Types )
76+ {
77+ foreach ($type in $All_Types )
78+ {
79+ foreach ($method in $type.Methods )
80+ {
81+ if ($DinvokeType.FullName -in $method.MethodBody.Instructions.operand.fullname )
82+ {
83+ [System.Object []]$DInvoke_UsedBy += $method
84+ }
85+ }
86+ }
87+ Add-Member - InputObject $DinvokeType - NotePropertyName " Used By Methods MDTokens" - NotePropertyValue $DInvoke_UsedBy.MDToken
88+ Add-Member - InputObject $DinvokeType - NotePropertyName " Used By Methods" - NotePropertyValue $DInvoke_UsedBy.FullName
89+ [System.Object []]$DInvoke_All += $DinvokeType
90+ if ($DInvoke_UsedBy )
91+ {
92+ Clear-Variable - Name DInvoke_UsedBy
93+ }
94+ }
95+ return $DInvoke_All
96+ }
97+ # PInvoke imports reveal - Methods filter
98+ function Get_PInvoke_Methods ($AllTypes )
99+ {
100+ foreach ($type in $AllTypes )
101+ {
102+ foreach ($method in $type.Methods )
103+ { # get only Methods with PinvokeImpl attribute
104+ if ($method.Attributes.value__ -band [dnlib.DotNet.MethodAttributes ]::PinvokeImpl.value__)
105+ {
106+ [System.Object []]$PInvoke_Methods += $method
107+ }
108+ }
109+ }
110+ return $PInvoke_Methods
111+ }
112+ # PInvoke imports - add all Used by methods
113+ function Get_PInvoke_ALL ($PInvoke_methods , $All_Types )
114+ {
115+ foreach ($PinvokeMethod in $PInvoke_methods )
116+ {
117+ foreach ($type in $All_Types )
118+ {
119+ foreach ($method in $type.Methods )
120+ {
121+ if ($PinvokeMethod.FullName -in $method.MethodBody.Instructions.operand.fullname )
122+ {
123+ [System.Object []]$PInvoke_UsedBy += $method
124+ }
125+ }
126+ }
127+ Add-Member - InputObject $PinvokeMethod - NotePropertyName " Used By Methods MDTokens" - NotePropertyValue $PInvoke_UsedBy.MDToken
128+ Add-Member - InputObject $PinvokeMethod - NotePropertyName " Used By Methods" - NotePropertyValue $PInvoke_UsedBy.FullName
129+ [System.Object []]$PInvoke_All += $PinvokeMethod
130+ if ($PInvoke_UsedBy )
131+ {
132+ Clear-Variable - Name PInvoke_UsedBy
133+ }
134+ }
135+ return $PInvoke_All
136+ }
137+
138+ # Dynamic PInvoke imports reveal - Methods filter which are implementing DefinePInvokeMethod
139+ function Get_PInvoke_Dynamic_Methods ($AllTypes )
140+ {
141+ foreach ($type in $AllTypes )
142+ {
143+ foreach ($method in $type.Methods )
144+ {
145+ if ($method.MethodBody.Instructions.operand.fullname -match ' DefinePInvokeMethod' )
146+ {
147+ [System.Object []]$PInvokeDynamic_methods += $method
148+ }
149+ }
150+ }
151+ return $PInvokeDynamic_methods
152+ }
153+
154+ # Dynamic PInvoke imports - add all Used by methods
155+ function Get_PInvoke_Dynamic_ALL ($PInvokeDynamic_Methods , $All_Types )
156+ {
157+ foreach ($PinvokeDynamicMethod in $PInvokeDynamic_Methods )
158+ {
159+ foreach ($type in $All_Types )
160+ {
161+ foreach ($method in $type.Methods )
162+ {
163+ if ($PinvokeDynamicMethod.FullName -in $method.MethodBody.Instructions.operand.fullname )
164+ {
165+ [System.Object []]$PInvokeDynamic_UsedBy += $method
166+ }
167+ }
168+ }
169+ Add-Member - InputObject $PinvokeDynamicMethod - NotePropertyName " Used By Methods MDTokens" - NotePropertyValue $PInvokeDynamic_UsedBy.MDToken
170+ Add-Member - InputObject $PinvokeDynamicMethod - NotePropertyName " Used By Methods" - NotePropertyValue $PInvokeDynamic_UsedBy.FullName
171+ [System.Object []]$PInvokeDynamic_ALL += $PinvokeDynamicMethod
172+ if ($PInvokeDynamic_UsedBy )
173+ {
174+ Clear-Variable - Name PInvokeDynamic_UsedBy
175+ }
176+ }
177+ return $PInvokeDynamic_ALL
178+ }
179+
180+ # Export detected PInvoke, Dynamic PInvoke and Dinvoke to DnSpy bookmarks XML file - all location where they are referenced (similar to DnSpy-Analyze-UsedBy)
181+ function Get_DnSpy_BookmarksXML ($PInvoke_ALL , $PInvokeDynamic_ALL , $DInvoke_ALL , $All_Types )
182+ {
183+ $xmltemplate_start = @"
184+ <?xml version="1.0" encoding="utf-8"?>
185+ <settings>
186+ <section _="eaa1be38-7a55-44af-ad93-5b7ee2327edd">
187+
188+ "@
189+ $xmltemplate_end = @"
190+ </section>
191+ </settings>
192+ "@
193+ # PInvoke part
194+ $counter = 1
195+ foreach ($PInvoke in $PInvoke_ALL )
196+ {
197+ foreach ($type in $All_Types )
198+ {
199+ foreach ($method in $type.Methods )
200+ {
201+ if ($PInvoke.FullName -in $method.MethodBody.Instructions.operand.fullname )
202+ {
203+ for ($i = 0 ; $i -lt $method.MethodBody.Instructions.count ; $i ++ )
204+ {
205+ if ($PInvoke.FullName -in $method.MethodBody.Instructions [$i ].operand.fullname)
206+ {
207+ $xmltemplate_body += @"
208+ <section _="Bookmark" IsEnabled="True" Name="PInvoke$ ( $counter ) _$ ( $PInvoke.Name.String ) ">
209+ <section _="BML" __BMT="DotNetBody" AssemblyFullName="$ ( $method.Module.Assembly.FullName ) " ModuleName="$ ( $method.Module.Location ) " Offset="$ ( $method.MethodBody.Instructions [$i ].offset) " Token="$ ( $method.MDToken.Raw ) " TokenString="$ ( [System.Net.WebUtility ]::HtmlEncode($method.FullName )) " />
210+ </section>
211+
212+ "@
213+ $counter ++
214+ }
215+ }
216+ }
217+ }
218+ }
219+ }
220+ # Dynamic PInvoke part
221+ $counter = 1
222+ foreach ($PInvokeDynamic in $PInvokeDynamic_ALL )
223+ {
224+ foreach ($type in $All_Types )
225+ {
226+ foreach ($method in $type.Methods )
227+ {
228+ if ($PInvokeDynamic.FullName -in $method.MethodBody.Instructions.operand.fullname )
229+ {
230+ for ($i = 0 ; $i -lt $method.MethodBody.Instructions.count ; $i ++ )
231+ {
232+ if ($PInvokeDynamic.FullName -in $method.MethodBody.Instructions [$i ].operand.fullname)
233+ {
234+ $xmltemplate_body += @"
235+ <section _="Bookmark" IsEnabled="True" Name="DynamicPInvoke$ ( $counter ) _$ ( $PInvokeDynamic.Name.String ) ">
236+ <section _="BML" __BMT="DotNetBody" AssemblyFullName="$ ( $method.Module.Assembly.FullName ) " ModuleName="$ ( $method.Module.Location ) " Offset="$ ( $method.MethodBody.Instructions [$i ].offset) " Token="$ ( $method.MDToken.Raw ) " TokenString="$ ( [System.Net.WebUtility ]::HtmlEncode($method.FullName )) " />
237+ </section>
238+
239+ "@
240+ $counter ++
241+ }
242+ }
243+ }
244+ }
245+ }
246+ }
247+ # DInvoke part
248+ $counter = 1
249+ foreach ($DInvoke in $DInvoke_ALL )
250+ {
251+ foreach ($type in $All_Types )
252+ {
253+ foreach ($method in $type.Methods )
254+ {
255+ if ($DInvoke.FullName -in $method.MethodBody.Instructions.operand.fullname )
256+ {
257+ for ($i = 0 ; $i -lt $method.MethodBody.Instructions.count ; $i ++ )
258+ {
259+ if ($DInvoke.FullName -in $method.MethodBody.Instructions [$i ].operand.fullname)
260+ {
261+ $xmltemplate_body += @"
262+ <section _="Bookmark" IsEnabled="True" Name="DInvoke$ ( $counter ) _$ ( $DInvoke.Name.String ) ">
263+ <section _="BML" __BMT="DotNetBody" AssemblyFullName="$ ( $method.Module.Assembly.FullName ) " ModuleName="$ ( $method.Module.Location ) " Offset="$ ( $method.MethodBody.Instructions [$i ].offset) " Token="$ ( $method.MDToken.Raw ) " TokenString="$ ( [System.Net.WebUtility ]::HtmlEncode($method.FullName )) " />
264+ </section>
265+
266+ "@
267+ $counter ++
268+ }
269+ }
270+ }
271+ }
272+ }
273+ }
274+ return ($xmltemplate_start + $xmltemplate_body + $xmltemplate_end )
275+ }
276+
277+ # #################################### MAIN PART #####################################
278+ # loading dnlib.dll via reflection
279+ if (Test-Path - Path $PathToDnlib - PathType Leaf)
280+ {
281+ [System.Reflection.Assembly ]::LoadFile($PathToDnlib ) | Out-Null
282+ }
283+ else
284+ {
285+ Write-Host " 'dnlib.dll' not found in current or specified directory !!!`n " - ForegroundColor Red
286+ Break
287+ }
288+
289+ # creating moduledef for sepcified assembly path
290+ if (Test-Path - Path $PathToAssembly - PathType Leaf)
291+ {
292+ $ModuleDefMD = [dnlib.DotNet.ModuleDefMD ]::Load($PathToAssembly )
293+ }
294+ else
295+ {
296+ Write-Host " 'PathToAssembly' error!!! Assembly not found in specified directory !!!`n " - ForegroundColor Red
297+ Break
298+ }
299+ # getting ALL Types
300+ # warning - $ModuleDefMD.Types - doesnt give all nested -> use $ModuleDefMD.GetTypes() -gives ALL
301+ $All_Types = $ModuleDefMD.GetTypes ()
302+ # #################################### MAIN PART #####################################
303+
304+ # #################################### PINVOKE PART #####################################
305+
306+ Write-Host " Found PInvoke Imports:" - ForegroundColor Green
307+ # getting only potential Methods used for PInvoke - filtering
308+ [System.Object []]$PInvoke_Methods = Get_PInvoke_Methods - AllTypes $All_Types
309+ if ($PInvoke_Methods )
310+ {
311+ # getting all methods where PInvoke is used add them as property
312+ [System.Object []]$PInvoke_ALL = Get_PInvoke_ALL - PInvoke_methods $PInvoke_Methods - All_Types $All_Types
313+ # result -> potential PInvoke imports
314+ $PInvoke_ALL | Select-Object - Property Name, MDToken, DeclaringType, " Used By Methods" , " Used By Methods MDTokens"
315+ }
316+ else
317+ {
318+ Write-Host " NONE`n " - ForegroundColor Red
319+ }
320+
321+ # #################################### PINVOKE PART #####################################
322+
323+ # #################################### DYNAMIC PINVOKE PART #####################################
324+
325+ Write-Host " Found Dynamic PInvoke Imports:" - ForegroundColor Green
326+ # getting only potential Methods using Dynamic PInvoke (DefinePInvokeMethod) - filtering
327+ [System.Object []]$PInvokeDynamic_Methods = Get_PInvoke_Dynamic_Methods - AllTypes $All_Types
328+ if ($PInvokeDynamic_Methods )
329+ {
330+ # getting all methods where Dynamic PInvoke is used add them as property
331+ [System.Object []]$PInvokeDynamic_ALL = Get_PInvoke_Dynamic_ALL - PInvokeDynamic_Methods $PInvokeDynamic_Methods - All_Types $All_Types
332+ # result -> potential Dynamic PInvoke imports
333+ $PInvokeDynamic_ALL | Select-Object - Property Name, MDToken, DeclaringType, " Used By Methods" , " Used By Methods MDTokens"
334+ }
335+ else
336+ {
337+ Write-Host " NONE`n " - ForegroundColor Red
338+ }
339+
340+ # #################################### DYNAMIC PINVOKE PART #####################################
341+
342+ # #################################### DINVOKE PART #####################################
343+ Write-Host " Found DInvoke Imports:" - ForegroundColor Green
344+ # getting only potential types used for DInvoke - filtering
345+ [System.Object []]$DInvoke_Types = Get_DInvoke_Types - AllTypes $All_Types
346+ if ($DInvoke_Types )
347+ {
348+ # getting all methods where DInvoke is used add them as property
349+ [System.Object []]$DInvoke_ALL = Get_DInvoke_ALL - DInvoke_Types $DInvoke_Types - All_Types $All_Types
350+ # result -> potential DInvoke imports
351+ $DInvoke_ALL | Select-Object - Property Name, MDToken, DeclaringType, " Used By Methods" , " Used By Methods MDTokens"
352+ }
353+ else
354+ {
355+ Write-Host " NONE`n " - ForegroundColor Red
356+ }
357+ # #################################### DINVOKE PART #####################################
358+ # Export all used PInvoke, Dynamic PInvoke and DInvoke to DnSpy bookmarks XML file - contains all methods location where they are used
359+ if ($ExportDnSpyBookmarks )
360+ {
361+ $BookmarksXML = Get_DnSpy_BookmarksXML - PInvoke_ALL $PInvoke_ALL - PInvokeDynamic_ALL $PInvokeDynamic_ALL - DInvoke_ALL $DInvoke_ALL - All_Types $All_Types
362+ Set-Content - Path .\DnSpy_Bookmarks.xml - Value $BookmarksXML - Encoding UTF8
363+ }
364+ # Cleanup Vars
365+ if ($PInvoke_Methods )
366+ {
367+ Clear-Variable - Name PInvoke_Methods, PInvoke_ALL
368+ }
369+ if ($PInvokeDynamic_Methods )
370+ {
371+ Clear-Variable - Name PInvokeDynamic_Methods, PInvokeDynamic_ALL
372+ }
373+ if ($DInvoke_Types )
374+ {
375+ Clear-Variable - Name DInvoke_Types, DInvoke_ALL
376+ }
377+
378+ }
0 commit comments