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+ }
0 commit comments