Skip to content

Commit e62121e

Browse files
committed
Added Get-LibSymbols
Get-LibSymbols parses Microsoft .lib files and displays decorated and undecorated symbols.
1 parent 65cd074 commit e62121e

4 files changed

Lines changed: 313 additions & 2 deletions

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<Configuration>
3+
<ViewDefinitions>
4+
<View>
5+
<Name>SymbolTypeView</Name>
6+
<ViewSelectedBy>
7+
<TypeName>COFF.SymbolInfo</TypeName>
8+
</ViewSelectedBy>
9+
<ListControl>
10+
<ListEntries>
11+
<ListEntry>
12+
<ListItems>
13+
<ListItem>
14+
<PropertyName>SymbolType</PropertyName>
15+
</ListItem>
16+
<ListItem>
17+
<PropertyName>Module</PropertyName>
18+
</ListItem>
19+
<ListItem>
20+
<PropertyName>DecoratedName</PropertyName>
21+
</ListItem>
22+
<ListItem>
23+
<PropertyName>UndecoratedName</PropertyName>
24+
</ListItem>
25+
</ListItems>
26+
</ListEntry>
27+
</ListEntries>
28+
</ListControl>
29+
</View>
30+
</ViewDefinitions>
31+
</Configuration>

PETools/Get-LibSymbols.ps1

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
function Get-LibSymbols
2+
{
3+
<#
4+
.SYNOPSIS
5+
6+
Displays symbolic information from Windows lib files.
7+
8+
PowerSploit Function: Get-LibSymbols
9+
Author: Matthew Graeber (@mattifestation)
10+
License: BSD 3-Clause
11+
Required Dependencies: None
12+
Optional Dependencies: None
13+
14+
.DESCRIPTION
15+
16+
Get-LibSymbols parses and returns symbols in Windows .lib files
17+
in both decorated and undecorated form (for C++ functions).
18+
19+
.PARAMETER Path
20+
21+
Specifies a path to one or more lib file locations.
22+
23+
.EXAMPLE
24+
25+
C:\PS>Get-LibSymbols -Path msvcrt.lib
26+
27+
.EXAMPLE
28+
29+
C:\PS>ls *.lib | Get-LibSymbols
30+
31+
.INPUTS
32+
33+
System.String[]
34+
35+
You can pipe a file system path (in quotation marks) to Get-LibSymbols.
36+
37+
.OUTPUTS
38+
39+
COFF.SymbolInfo
40+
41+
.LINK
42+
43+
http://www.exploit-monday.com/
44+
#>
45+
[CmdletBinding()] Param (
46+
[Parameter(Position = 0, Mandatory = $True, ValueFromPipelineByPropertyName = $True)]
47+
[ValidateScript({ Test-Path $_ })]
48+
[Alias('FullName')]
49+
[String[]]
50+
$Path
51+
)
52+
53+
BEGIN
54+
{
55+
$Code = @'
56+
using System;
57+
using System.IO;
58+
using System.Text;
59+
using System.Runtime.InteropServices;
60+
61+
namespace COFF
62+
{
63+
public class HEADER
64+
{
65+
public ushort Machine;
66+
public ushort NumberOfSections;
67+
public DateTime TimeDateStamp;
68+
public uint PointerToSymbolTable;
69+
public uint NumberOfSymbols;
70+
public ushort SizeOfOptionalHeader;
71+
public ushort Characteristics;
72+
73+
public HEADER(BinaryReader br)
74+
{
75+
this.Machine = br.ReadUInt16();
76+
this.NumberOfSections = br.ReadUInt16();
77+
this.TimeDateStamp = (new DateTime(1970, 1, 1, 0, 0, 0)).AddSeconds(br.ReadUInt32());
78+
this.PointerToSymbolTable = br.ReadUInt32();
79+
this.NumberOfSymbols = br.ReadUInt32();
80+
this.SizeOfOptionalHeader = br.ReadUInt16();
81+
this.Characteristics = br.ReadUInt16();
82+
}
83+
}
84+
85+
public class IMAGE_ARCHIVE_MEMBER_HEADER
86+
{
87+
public string Name;
88+
public DateTime Date;
89+
public ulong Size;
90+
public string EndHeader;
91+
92+
public IMAGE_ARCHIVE_MEMBER_HEADER(BinaryReader br)
93+
{
94+
string tempName = Encoding.UTF8.GetString(br.ReadBytes(16));
95+
DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0);
96+
this.Name = tempName.Substring(0, tempName.IndexOf((Char) 47));
97+
this.Date = dt.AddSeconds(Convert.ToDouble(Encoding.UTF8.GetString(br.ReadBytes(12)).Split((Char) 20)[0]));
98+
br.ReadBytes(20); // Skip over UserID, GroupID, and Mode. They are useless fields.
99+
this.Size = Convert.ToUInt64(Encoding.UTF8.GetString(br.ReadBytes(10)).Split((Char) 20)[0]);
100+
this.EndHeader = Encoding.UTF8.GetString(br.ReadBytes(2));
101+
}
102+
}
103+
104+
public class Functions
105+
{
106+
[DllImport("dbghelp.dll", SetLastError=true, PreserveSig=true)]
107+
public static extern int UnDecorateSymbolName(
108+
[In] [MarshalAs(UnmanagedType.LPStr)] string DecoratedName,
109+
[Out] StringBuilder UnDecoratedName,
110+
[In] [MarshalAs(UnmanagedType.U4)] uint UndecoratedLength,
111+
[In] [MarshalAs(UnmanagedType.U4)] uint Flags);
112+
}
113+
}
114+
'@
115+
116+
Add-Type -TypeDefinition $Code
117+
118+
function Dispose-Objects
119+
{
120+
$BinaryReader.Close()
121+
$FileStream.Dispose()
122+
}
123+
}
124+
125+
PROCESS
126+
{
127+
foreach ($File in $Path)
128+
{
129+
# Resolve the absolute path of the lib file. [IO.File]::OpenRead requires an absolute path.
130+
$LibFilePath = Resolve-Path $File
131+
132+
# Pull out just the file name
133+
$LibFileName = Split-Path $LibFilePath -Leaf
134+
135+
$IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR = 60
136+
$IMAGE_ARCHIVE_START = "!<arch>`n" # Magic used for lib files
137+
$IMAGE_SIZEOF_LIB_HDR = $IMAGE_SIZEOF_ARCHIVE_MEMBER_HDR + $IMAGE_ARCHIVE_START.Length
138+
$IMAGE_ARCHIVE_END = "```n" # Footer of an archive header
139+
$SizeofCOFFFileHeader = 20
140+
141+
# Open the object file for reading
142+
$FileStream = [IO.File]::OpenRead($LibFilePath)
143+
144+
$FileLength = $FileStream.Length
145+
146+
# Validate lib header size
147+
if ($FileLength -lt $IMAGE_SIZEOF_LIB_HDR)
148+
{
149+
# You cannot parse the lib header if the file is not big enough to contain a lib header.
150+
Write-Error "$($LibFileName) is too small to store a lib header."
151+
$FileStream.Dispose()
152+
return
153+
}
154+
155+
# Open a BinaryReader object for the lib file
156+
$BinaryReader = New-Object IO.BinaryReader($FileStream)
157+
158+
$ArchiveStart = [Text.Encoding]::UTF8.GetString($BinaryReader.ReadBytes(8))
159+
160+
if ($ArchiveStart -ne $IMAGE_ARCHIVE_START)
161+
{
162+
Write-Error "$($LibFileName) does not contain a valid lib header."
163+
Dispose-Objects
164+
return
165+
}
166+
167+
# Parse the first archive header
168+
$ArchiveHeader = New-Object COFF.IMAGE_ARCHIVE_MEMBER_HEADER($BinaryReader)
169+
170+
if ($ArchiveHeader.EndHeader -ne $IMAGE_ARCHIVE_END)
171+
{
172+
Write-Error "$($LibFileName) does not contain a valid lib header."
173+
Dispose-Objects
174+
return
175+
}
176+
177+
# Check for the existence of symbols
178+
if ($ArchiveHeader.Size -eq 0)
179+
{
180+
Write-Warning "$($LibFileName) contains no symbols."
181+
Dispose-Objects
182+
return
183+
}
184+
185+
$NumberOfSymbols = $BinaryReader.ReadBytes(4)
186+
187+
# The offsets in the first archive header of a Microsoft lib file are stored in big-endian format
188+
if ([BitConverter]::IsLittleEndian)
189+
{
190+
[Array]::Reverse($NumberOfSymbols)
191+
}
192+
193+
$NumberOfSymbols = [BitConverter]::ToUInt32($NumberOfSymbols, 0)
194+
195+
$SymbolOffsets = New-Object UInt32[]($NumberOfSymbols)
196+
197+
foreach ($Offset in 0..($SymbolOffsets.Length - 1))
198+
{
199+
$SymbolOffset = $BinaryReader.ReadBytes(4)
200+
201+
if ([BitConverter]::IsLittleEndian)
202+
{
203+
[Array]::Reverse($SymbolOffset)
204+
}
205+
206+
$SymbolOffsets[$Offset] = [BitConverter]::ToUInt32($SymbolOffset, 0)
207+
}
208+
209+
$SymbolStringLength = $ArchiveHeader.Size + $IMAGE_SIZEOF_LIB_HDR - $FileStream.Position - 1
210+
# $SymbolStrings = [Text.Encoding]::UTF8.GetString($BinaryReader.ReadBytes($SymbolStringLength)).Split([Char] 0)
211+
212+
# Write-Output $SymbolStrings
213+
214+
# There will be many duplicate offset entries. Remove them.
215+
$SymbolOffsetsSorted = $SymbolOffsets | Sort-Object -Unique
216+
217+
$SymbolOffsetsSorted | ForEach-Object {
218+
# Seek to the each repective offset in the file
219+
$FileStream.Seek($_, 'Begin') | Out-Null
220+
221+
$ArchiveHeader = New-Object COFF.IMAGE_ARCHIVE_MEMBER_HEADER($BinaryReader)
222+
223+
# This is not a true COFF header. It's the same size and mostly resembles a standard COFF header
224+
# but Microsoft placed a marker (0xFFFF) in the first WORD to indicate that the 'object file'
225+
# consists solely of the module name and symbol.
226+
$CoffHeader = New-Object COFF.HEADER($BinaryReader)
227+
228+
# Check for 0xFFFF flag value
229+
if ($CoffHeader.NumberOfSections -eq [UInt16]::MaxValue)
230+
{
231+
# Get the total length of the module and symbol name
232+
$SymbolStringLength = $CoffHeader.NumberOfSymbols
233+
$Symbols = [Text.Encoding]::UTF8.GetString($BinaryReader.ReadBytes($SymbolStringLength)).Split([Char] 0)
234+
235+
$DecoratedSymbol = $Symbols[0]
236+
$UndecoratedSymbol = ''
237+
238+
# Default to a 'C' type symbol unless it starts with a '?'
239+
$SymbolType = 'C'
240+
241+
# Is the symbol a C++ type?
242+
if ($DecoratedSymbol.StartsWith('?'))
243+
{
244+
$StrBuilder = New-Object Text.Stringbuilder(512)
245+
# Magically undecorated the convoluted C++ symbol into a proper C++ function definition
246+
[COFF.Functions]::UnDecorateSymbolName($DecoratedSymbol, $StrBuilder, $StrBuilder.Capacity, 0) | Out-Null
247+
$UndecoratedSymbol = $StrBuilder.ToString()
248+
$SymbolType = 'C++'
249+
}
250+
else
251+
{
252+
$UndecoratedSymbol = $DecoratedSymbol.Substring(1).Split('@')[0]
253+
}
254+
255+
$SymInfo = @{
256+
DecoratedName = $DecoratedSymbol
257+
UndecoratedName = $UndecoratedSymbol
258+
Module = $Symbols[1]
259+
SymbolType = $SymbolType
260+
}
261+
262+
$ParsedSymbol = New-Object PSObject -Property $SymInfo
263+
$ParsedSymbol.PSObject.TypeNames[0] = 'COFF.SymbolInfo'
264+
265+
Write-Output $ParsedSymbol
266+
}
267+
}
268+
269+
# Close file and binaryreader objects
270+
Dispose-Objects
271+
}
272+
}
273+
274+
END {}
275+
}

PETools/PETools.psd1

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ PowerShellVersion = '2.0'
5252
# TypesToProcess = @()
5353

5454
# Format files (.ps1xml) to be loaded when importing this module
55-
FormatsToProcess = 'PETools.format.ps1xml', 'Get-ObjDump.format.ps1xml'
55+
FormatsToProcess = 'PETools.format.ps1xml', 'Get-ObjDump.format.ps1xml', 'Get-LibSymbols.format.ps1xml'
5656

5757
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
5858
# NestedModules = @()
@@ -74,7 +74,8 @@ ModuleList = @(@{ModuleName = 'PETools'; ModuleVersion = '1.0.0.0'; GUID = 'd150
7474

7575
# List of all files packaged with this module
7676
FileList = 'PETools.psm1', 'PETools.psd1', 'PETools.format.ps1xml', 'Get-DllLoadPath.ps1',
77-
'Get-PEHeader.ps1', 'Get-ObjDump.ps1', 'Get-ObjDump.format.ps1xml', 'Usage.md'
77+
'Get-PEHeader.ps1', 'Get-ObjDump.ps1', 'Get-ObjDump.format.ps1xml', 'Get-LibSymbols.ps1',
78+
'Usage.md'
7879

7980
# Private data to pass to the module specified in RootModule/ModuleToProcess
8081
# PrivateData = ''

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ An in-memory and on-disk PE parsing utility.
7272

7373
Displays information about one or more Windows object files.
7474

75+
#### `Get-LibSymbols`
76+
77+
Displays symbolic information from Windows lib files.
78+
7579
#### `Get-DllLoadPath`
7680

7781
Returns the path from which Windows will load a Dll for the given executable.

0 commit comments

Comments
 (0)