Skip to content

Instantly share code, notes, and snippets.

@ThioJoe
Last active August 25, 2024 04:30
Show Gist options
  • Save ThioJoe/9b0b0b193783ebf4ceaf1c6c25d18c7a to your computer and use it in GitHub Desktop.
Save ThioJoe/9b0b0b193783ebf4ceaf1c6c25d18c7a to your computer and use it in GitHub Desktop.
Takes an XML file that contains windows embedded DLL string references and outputs a new version with the resolved actual string values.
# Windows XML String Resolver
# Author: ThioJoe
#
# Purpose: This script takes an XML file that contains string references (e.g., "@shell32.dll,-1234")
# and resolves them to their actual string values. It's particularly useful for working with
# Windows resource files or any XML that uses similar string reference formats.
#
# How to Use:
# 1. Open PowerShell and navigate to the path containing this script using the 'cd' command.
# 2. Run the following command to allow running scripts for the current session:
# Set-ExecutionPolicy -ExecutionPolicy unrestricted -Scope Process
# 3. Without closing the PowerShell window, run the script by typing the name of the script file starting with .\ for example:
# .\Windows_XML_String_Resolver.ps1
# Usage With Arguments: You can either run the script and enter the XML file path when prompted, or specify the file/path as a parameter (can be relative or absolute)
# Example 1: .\Windows_XML_String_Resolver.ps1 "C:\path\to\file.xml"
# Example 2: .\Windows_XML_String_Resolver.ps1 WhateverName.xml
param(
[string]$XmlFilePath
)
# Import necessary .NET classes
Add-Type @"
using System;
using System.Runtime.InteropServices;
using System.Text;
public class Windows {
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int LoadString(IntPtr hInstance, uint uID, StringBuilder lpBuffer, int nBufferMax);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool FreeLibrary(IntPtr hModule);
}
"@
function Get-LocalizedString {
param ( [string]$StringReference )
if ($StringReference -match '@(.+),-(\d+)') {
$dllPath = [Environment]::ExpandEnvironmentVariables($Matches[1])
$resourceId = [uint32]$Matches[2]
$hModule = [Windows]::LoadLibraryEx($dllPath, [IntPtr]::Zero, 0x00000800) # LOAD_LIBRARY_AS_DATAFILE
if ($hModule -eq [IntPtr]::Zero) {
$errorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Warning "Failed to load library: $dllPath. Error code: $errorCode"
return $StringReference
}
$stringBuilder = New-Object System.Text.StringBuilder 1024
$result = [Windows]::LoadString($hModule, $resourceId, $stringBuilder, $stringBuilder.Capacity)
[void][Windows]::FreeLibrary($hModule)
if ($result -ne 0) {
return $stringBuilder.ToString()
} else {
$errorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Warning "Failed to load string resource: $resourceId from $dllPath. Error code: $errorCode"
return $StringReference
}
} else {
return $StringReference
}
}
function Resolve-XmlStringReferences {
param (
[string]$XmlContent
)
$resolvedXml = $XmlContent
$stringRefPattern = '@[^,]+,-\d+'
$stringMatches = [regex]::Matches($XmlContent, $stringRefPattern)
foreach ($match in $stringMatches) {
$originalString = $match.Value
$resolvedString = Get-LocalizedString $originalString
if ($resolvedString -ne $originalString) {
$resolvedXml = $resolvedXml.Replace($originalString, [System.Security.SecurityElement]::Escape($resolvedString))
}
}
return $resolvedXml
}
# Main script logic
if (-not $XmlFilePath) {
$XmlFilePath = Read-Host "Please enter the path to the XML file"
}
# Remove quotes if present
$XmlFilePath = $XmlFilePath.Trim('"')
if (-not (Test-Path $XmlFilePath)) {
Write-Error "The specified file does not exist: $XmlFilePath"
exit 1
}
try {
$xmlContent = Get-Content $XmlFilePath -Raw
$resolvedXml = Resolve-XmlStringReferences $xmlContent
$outputPath = [System.IO.Path]::Combine(
[System.IO.Path]::GetDirectoryName($XmlFilePath),
[System.IO.Path]::GetFileNameWithoutExtension($XmlFilePath) + "-resolved.xml"
)
$resolvedXml | Out-File $outputPath -Encoding UTF8
Write-Host "Resolved XML saved to: $outputPath"
} catch {
Write-Error "An error occurred: $_"
exit 1
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment