From eb642ed41a1026f7c00c339fd22ba56f048863c5 Mon Sep 17 00:00:00 2001 From: Pavel Tsakalidis Date: Sun, 16 Jul 2023 19:10:36 +0100 Subject: [PATCH] Update documentation for v2.1.3, including minor optimisations for PEFileExports --- CHANGELOG.md | 5 ++++ README.md | 2 +- Spartacus/Properties/AssemblyInfo.cs | 4 +-- Spartacus/Utils/PEFileExports.cs | 37 ++++++++-------------------- 4 files changed, 18 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb33704..b46d09c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Spartacus Changelog +## v2.1.3 + +* `[Update]` Proxy `--action exports` now supports wildcard DLL paths like `--dll C:\Windows\System32\*.dll` and also displays forwarded functions. +* `[Fix]` Rewrite PE file exporter from scratch. + ## v2.1.2 * `[New]` Added `--action exports` to `--mode proxy` that lists a file's exports, functionality similar to `dumpbin.exe /exports`. diff --git a/README.md b/README.md index d95b456..8197e78 100644 --- a/README.md +++ b/README.md @@ -217,7 +217,7 @@ To use this feature, simply run Spartacus with `--detect`. | `dll`, `com` | `--existing` | Switch to indicate that Spartacus should process an existing ProcMon event log file (PML). To indicate the event log file use --pml, useful when you have been running ProcMon for hours or used it in Boot Logging. | | `dll` | `--all` | By default any DLLs in the Windows or Program Files directories will be skipped. Use this to include those directories in the output. | | `proxy` | `--ghidra` | Path to Ghidra's 'analyzeHeadless.bat' file. Used when you want to proxy specific functions rather than just `DllMain`. | -| `proxy` | `--dll` | Path to the DLL you want to proxy, and can include multiple instances of this argument. | +| `proxy` | `--dll` | Path to the DLL you want to proxy, and can include multiple instances of this argument. In addition, can also contain wildcards like `C:\Windows\System32\*.dll` - however all paths have to end in `*.dll`. | | `proxy` | `--overwrite` | If the `--solution` path already exists, use this flag to overwrite it. | | `proxy` | `--only` | Generate proxy functions only for functions defined in this variable. Values are comma separated like `'WTSFreeMemory,WTSFreeMemoryExA,WTSSetUserConfigA'`. | | `proxy` | `--action` | Default action is to generate a VS solution. `--action prototypes`, takes as input a Windows SDK folder and parses *.h files in order to generate a database of function prototypes. `--action exports` displays a DLL's export functions and when complimented with `--prototypes` it will display if the function definition has been pre-generated. | diff --git a/Spartacus/Properties/AssemblyInfo.cs b/Spartacus/Properties/AssemblyInfo.cs index 01c03aa..1d6b52d 100644 --- a/Spartacus/Properties/AssemblyInfo.cs +++ b/Spartacus/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.1.2.0")] -[assembly: AssemblyFileVersion("2.1.2.0")] +[assembly: AssemblyVersion("2.1.3.0")] +[assembly: AssemblyFileVersion("2.1.3.0")] diff --git a/Spartacus/Utils/PEFileExports.cs b/Spartacus/Utils/PEFileExports.cs index 81ea49d..2f5e918 100644 --- a/Spartacus/Utils/PEFileExports.cs +++ b/Spartacus/Utils/PEFileExports.cs @@ -213,11 +213,7 @@ public List Extract(string dllFile) private List GenerateResult(uint[] ordinals, string[] functions, string[] forwards) { - if (ordinals == null || functions == null || forwards == null) - { - return new(); - } - else if (ordinals.Length == 0 || functions.Length == 0 || forwards.Length == 0) + if (ordinals.Length == 0 || functions.Length == 0 || forwards.Length == 0) { return new(); } @@ -241,7 +237,8 @@ private string[] ExtractTable(EXPORT_DIRECTORY_TABLE exportTable, uint sectionBa output[i] = ""; // If the index[i] is within the boundaries of the export table (virtual address + size), extract the string. Otherwise // it's a reference to a function offset rather than a forward. - if (index[i] > 0 && index[i] >= exportDataTable.VirtualAddress && index[i] <= (exportDataTable.VirtualAddress + exportDataTable.Size)) + bool betweenBounds = index[i] >= exportDataTable.VirtualAddress && index[i] <= (exportDataTable.VirtualAddress + exportDataTable.Size); + if (index[i] > 0 && betweenBounds) { output[i] = ReadString(index[i] - sectionBaseOffset); } @@ -266,14 +263,10 @@ private string ReadString(uint offset) List output = new(); do { - byte c = reader.ReadByte(); - if (c == 0x00) - { - break; - } - output.Add(c); - } while (true); - + output.Add(reader.ReadByte()); + } while (output.Last() != 0x00); + output.RemoveAt(output.Count - 1); // Last added byte was 0x00, remove it. + return Encoding.ASCII.GetString(output.ToArray()); } @@ -281,22 +274,14 @@ private uint[] ExtractTableIndex(uint offset, uint size) { uint[] output = new uint[size]; stream.Seek(offset, SeekOrigin.Begin); - for (int i = 0; i < size; i++) - { - output[i] = reader.ReadUInt32(); - } - return output; + return output.Select(o => reader.ReadUInt32()).ToArray(); } private uint[] ExtractOrdinals(EXPORT_DIRECTORY_TABLE exportTable, uint sectionBaseOffset) { uint[] output = new uint[exportTable.NumberOfNamePointers]; stream.Seek(exportTable.OrdinalTableRVA - sectionBaseOffset, SeekOrigin.Begin); - for (int i = 0; i < output.Length; i++) - { - output[i] = reader.ReadUInt16() + exportTable.OrdinalBase; - } - return output; + return output.Select(o => reader.ReadUInt16() + exportTable.OrdinalBase).ToArray(); } private EXPORT_DIRECTORY_TABLE GetExportDirectoryTable(uint offset) @@ -345,15 +330,13 @@ private bool IsPEPlus() private IMAGE_FILE_HEADER GetImageFileHeader() { - IMAGE_FILE_HEADER header = new(); - // Get the PE location - https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#signature-image-only stream.Seek(SIZEOF_IMAGE_DOS_HEADER, SeekOrigin.Begin); uint executableHeaderOffset = reader.ReadUInt32() + 4; // +4 as the first 4 bytes are PE\0\0 // Navigate to that offset. stream.Seek(executableHeaderOffset, SeekOrigin.Begin); - return BytesToStructure(reader.ReadBytes(Marshal.SizeOf(header))); + return ReadStructFromStream(); } private T ReadStructFromStream()