Skip to content

Instantly share code, notes, and snippets.

@f-steff
Last active August 21, 2024 11:52
Show Gist options
  • Save f-steff/cb592cd2d0bdb1d858595118915f3a06 to your computer and use it in GitHub Desktop.
Save f-steff/cb592cd2d0bdb1d858595118915f3a06 to your computer and use it in GitHub Desktop.
Powershell script to search for a string inside an intel hex (iHex) formatted file, returning the address (with optional offset) to all found occurrences. Optionally return a 16 bit integer from an offset from the found address. Finally count of how many times the string was found.

FindIHexStringExtract.ps1

Note

I have also made a simpler version of this script, called FindIHexStringExtract .ps1 which can only find the address of substrings in an intel hex file. Check it out here: https://gist.github.com/f-steff/01ff67a566835a9ca3aba7a0537507c3

Overview

FindHexStringExtract.ps1 is an update to FindIHexString.ps1. It's designed to search for a specific string within an Intel HEX formatted file. The script outputs the memory addresses where the string is found, followed by a count of occurrences on the final line. Optionally a 16 bit integer can be extracted from an offset to the found addresses. This tool is a quick and effective way to locate and extract specific data within a HEX file, particularly useful for embedded systems developers or anyone working with microcontroller firmware.

Motivation

When dealing with Intel HEX files, particularly in embedded systems development, it's often necessary to search for specific strings or patterns. Unfortunately, there aren't many straightforward tools to perform such searches within Intel HEX files. FindIHexStringExtract.ps1 was created as a quickly hacked together solution to fill this gap, providing an easy-to-use script for searching and identifying string occurrences within these files.

The latest version of this script can always be found on this Github gist:

https://gist.github.com/f-steff/cb592cd2d0bdb1d858595118915f3a06

Usage

Command Syntax

.\FindIHexStringExtract.ps1 -HexFile <path> -SearchString <string> [-Offset <integer>] [-ExtractInt <integer>] [-MaxCount <integer>][-Help]

Parameters

  • -HexFile : (Required) The path to the Intel HEX file. Can be an absolute or relative path.
  • -SearchString : (Required) The string to search for within the Intel HEX file.
  • -Offset : (Optional) An address offset to apply to the found addresses. Can be positive or negative. Default is 0.
  • -ExtractInt : (Optional) An address offset to apply from the found addresses offset. Can be positive or negative. A 16 bit integer will be extracted from this address.
  • -MaxCount : (Optional) Stop searching after a specified number of SearchString has been found.
  • -Help: (Optional) Displays usage information and examples.

Examples

Basic Search

Search for the string "my_string" in a Intel HEX file:

.\FindHexStringExtract.ps1 -HexFile "C:\path\to\file.hex" -SearchString "my_string"
0xFFF80005
0xFFFF0005
2

Basic search, extracting 16-bit integer (both Small-Endian and Big-Endian) data 6 bytes after the found address

Search for the string "my_string" in a Intel HEX file:

.\FindHexStringExtract.ps1 -HexFile "C:\path\to\file.hex" -SearchString "my_string" -ExtractInt -6
0xFFF80005 0x1002 0x0201
0xFFFF0005 0x0400 0x0004
2

Basic search, extracting 16-bit integer (both Small-Endian and Big-Endian) data 6 bytes after the found address, stopping after the first found

Search for the string "my_string" in a Intel HEX file:

.\FindHexStringExtract.ps1 -HexFile "C:\path\to\file.hex" -SearchString "my_string" -ExtractInt -6 -MaxCount 1'
0xFFF80005 0x1002 0x0201
1

Search with Positive Offset

Search for "test" in the Intel HEX file, adding an offset of 5 to all found addresses:

.\FindHexStringExtract.ps1 -HexFile "file.hex" -SearchString "test" -Offset 5
0xFFF8000A
0xFFFF000A
2

Search with Negative Offset

Search for "test" in the Intel HEX file, subtracting 5 from all found addresses:

.\FindHexStringExtract.ps1 -HexFile "file.hex" -SearchString "test" -Offset -5
0xFFF80000
0xFFFF0000
2

Search for a string with special characters

Search for "$VER:" in the Intel HEX file, requires a slightly different format, as Powershell will reach to some special characters. To overcome this problem, the search string must be wrapped in single-quotes like this: '$VER:'

.\FindHexStringExtract.ps1 -HexFile "file.hex" -SearchString '$VER:'
0xFFF70000
0xFFFE0000
2

Display Help

Display usage information and examples:

.\FindHexStringExtract.ps1 -Help

Error Messages

  • Search string cannot be empty.
    Occurs when the -SearchString parameter is provided with an empty string. The script requires a non-empty search string to function.

  • Offset must be a numeric value.
    Occurs when the -Offset parameter is provided with a non-numeric value. The offset must be a valid integer.

  • File not found: .
    Occurs when the file specified by the -HexFile parameter does not exist. Ensure the path is correct.

Return Codes

  • 0: The script executed successfully. All operations were completed without errors.
  • 1: The script encountered an error. This could be due to an invalid search string, non-numeric offset, or file not found. Read the exact error description.

Notes

  • The script was designed as a quick solution to address a specific need. It performs the required operations efficiently but may lack advanced features of more sophisticated tools.
  • For lack of a better tool, FindIHexStringExtract is a valuable resource for developers working with Intel HEX files, providing an easy and reliable way to locate specific data within such files.
param (
[string]$HexFile,
[string]$SearchString,
$Offset = 0,
$ExtractInt = $null,
$MaxCount = $null,
[switch]$Help
)
# To disable these debug outputs, comment the lines below.
#$global:flow_Debug = $true
#$global:InputHex_Debug = $true
#$global:WindowAscii_Debug = $true
#$global:WindowHex_Debug = $true
# To disable normal output, comment this line.
#$global:SuppressOutput_Debug = $true
if ($Help) {
Write-Host "FindHexStringExtract is an update to FindIHexString. It searches through an intel hex formatted"
Write-Host "file, outputting the address of any occourcenses of the provided string, with an optional offset."
Write-Host "With the ExtractInt option, it's possible to provide an offset to a 16 bit number, relative to"
Write-Host "the found string. The number is always output as both Small Endian (SE) and Big Endian (BE)"
Write-Host "Last line in the outout is always the number of strings found within the intel hex file."
Write-Host ""
Write-Host "Note: Should extracted data with offset reach into undefined memory, NA is returned."
Write-Host " The search buffer allows for +/- 512 bytes from the found position."
Write-Host ""
Write-Host "Version 1.2 2024-08-21"
Write-Host "Latest version available from https://gist.github.com/f-steff/01ff67a566835a9ca3aba7a0537507c3"
Write-Host ""
Write-Host "Usage: .\FindHexStringExtract.ps1 -HexFile <path> -SearchString <string> [-Offset <integer>] [-Help]"
Write-Host ""
Write-Host "Arguments:"
Write-Host " -HexFile <path> : Path to the HEX file (absolute or relative)"
Write-Host " -SearchString <string> : String to search for in the HEX file"
Write-Host " -Offset <integer> : Optional address offset, positive or negative (default is 0)"
Write-Host " -ExtractInt <integer> : Optional offset for extracting two bytes, positive or negative"
Write-Host " -MaxCount <integer> : Optional number of SearchStrings to find."
Write-Host " -Help : Display this help message and exit"
Write-Host ""
Write-Host "Examples:"
Write-Host ' .\FindHexStringExtract.ps1 -HexFile file.hex -SearchString "test"'
Write-Host ' .\FindHexStringExtract.ps1 -HexFile file.hex -SearchString "test" -Offset 10'
Write-Host ' .\FindHexStringExtract.ps1 -HexFile file.hex -SearchString "test" -Offset -20'
Write-Host ' .\FindHexStringExtract.ps1 -HexFile file.hex -SearchString ''$VER:'' -ExtractInt 6'
Write-Host ' .\FindHexStringExtract.ps1 -HexFile file.hex -SearchString ''$VER:'' -ExtractInt -6 -MaxCount 2'
Write-Host ""
Write-Host "Example outputs:"
Write-Host ' 0xfff80000'
Write-Host ' 0xffff0000'
Write-Host ' 2'
Write-Host ""
Write-Host ' 0xfff80000 0x1002 0x0201'
Write-Host ' 0xffff0000 0x0400 0x0004'
Write-Host ' 2'
exit 0
}
# Validate the search string
if ([string]::IsNullOrWhiteSpace($SearchString)) {
Write-Error "Search string cannot be empty."
exit 1
}
# Validate the offset input
if (-not ([int]::TryParse($Offset, [ref]$null))) {
Write-Error "Offset must be a numeric value."
exit 1
}
# Validate the ExtractInt input if provided
if ($ExtractInt -ne $null) {
try {
$ExtractInt = [int]$ExtractInt
} catch {
Write-Error "ExtractInt must be a valid integer."
exit 1
}
# Ensure ExtractInt is within the ±512 range
if ($ExtractInt -lt -512 -or $ExtractInt -gt 512) {
Write-Error "ExtractInt must be between -512 and 512."
exit 1
}
}
# Validate the ExtractInt input if provided
if ($MaxCount -ne $null) {
try {
$MaxCount = [int]$MaxCount
} catch {
Write-Error "MaxCount (if specified) must be a valid integer."
exit 0
}
}
# Convert offset to integer
$Offset = [int]$Offset
# Validate the file path
if (-not (Test-Path $HexFile)) {
Write-Error "File not found: $HexFile"
exit 1
}
# Initialize variables
$foundCount = 0
$extendedAddress = 0
$memoryWindow = @{}
$foundAddresses = @{}
$memoryWindowSize = 2048
$lastPosition = 0
$previousEndAddress = $null
# Function to add data to the memory window
function Grow-MemoryWindow {
param (
[int64]$address,
[string]$data
)
# Add new data to the memory window
for ($i = 0; $i -lt $data.Length; $i += 2) {
$byteValue = [convert]::ToByte($data.Substring($i, 2), 16)
$memoryWindow[$address + ($i / 2)] = $byteValue
}
$currentWindowData = -join ($memoryWindow.GetEnumerator() | Sort-Object Key | ForEach-Object { "{0:X2}" -f $_.Value })
if ($global:WindowHex_Debug -eq $true) {
Write-Host "Current memory window data (Hex): $currentWindowData"
}
}
# Function to trim the memory window if it exceeds the size limit
function Trim-MemoryWindow {
if ($memoryWindow.Count -gt $memoryWindowSize) {
$keysToRemove = $memoryWindow.Keys | Sort-Object | Select-Object -First ($memoryWindow.Count - $memoryWindowSize)
foreach ($key in $keysToRemove) {
$memoryWindow.Remove($key)
}
if ($global:flow_Debug -eq $true) {
Write-Host "Memory window trimmed to $memoryWindowSize bytes."
}
}
}
# Function to search the memory window for the search string
function Search-MemoryWindow {
param (
[string]$searchString,
[int]$startAddress
)
$asciiData = -join ($memoryWindow.GetEnumerator() | Sort-Object Key | ForEach-Object { [char]$_.Value })
if ($global:WindowAscii_Debug -eq $true) {
Write-Host "ASCII data in memory window: $asciiData"
}
$position = $asciiData.IndexOf($searchString, $startAddress)
if ($position -ge 0) {
$exactAddress = $memoryWindow.Keys | Sort-Object | Select-Object -Skip $position -First 1
if ($global:flow_Debug -eq $true) {
Write-Host "Found '$searchString' at position $position (address: 0x$([System.Convert]::ToString($exactAddress, 16)))."
}
return $exactAddress
}
return $null
}
# Function to get the integer value from the memory window for a given address. Returns SE and BE 16 bit integers.
function GetInt-MemoryWindow {
param (
[int64]$address
)
$lowByte = $memoryWindow[$address]
$highByte = $memoryWindow[$address + 1]
if ($lowByte -eq $null -or $highByte -eq $null) {
return "0xNANA 0xNANA"
}
$seInt = "0x{0:X2}{1:X2}" -f $lowByte, $highByte
$beInt = "0x{0:X2}{1:X2}" -f $highByte, $lowByte
return "$seInt $beInt"
}
# Function to search and process the memory window
function Process-MemoryWindow {
param (
[string]$searchString,
[switch]$FullWindowSearch
)
$searchStart = 0
if (-not $FullWindowSearch) {
$searchStart = $lastPosition
}
$searchAddress = Search-MemoryWindow -searchString $searchString -startAddress $searchStart
while ($searchAddress -ne $null) {
$foundAddressKey = $searchAddress + $Offset
if (-not $foundAddresses.ContainsKey($foundAddressKey)) {
$foundAddresses[$foundAddressKey] = $true
$output = "0x" + [System.Convert]::ToString($foundAddressKey, 16)
if ($ExtractInt -ne $null) {
$extractAddress = $foundAddressKey + $ExtractInt
$output += " " + (GetInt-MemoryWindow -address $extractAddress)
}
if (-not ($global:SuppressOutput_Debug -eq $true)) {
Write-Output $output
}
$global:foundCount++
}
$keys = $memoryWindow.Keys | Sort-Object
$lastPosition = [array]::IndexOf($keys, $searchAddress)
$searchAddress = Search-MemoryWindow -searchString $searchString -startAddress ($lastPosition + $SearchString.Length)
}
}
# Read the file line by line
$lines = Get-Content $HexFile # intel-hex formatted file.
foreach ($line in $lines) {
$recordType = $line.Substring(7, 2)
if ($recordType -eq "04") {
$newExtendedAddress = [convert]::ToInt32($line.Substring(9, 4), 16) * 65536
$newStartAddress = $memoryWindow.Keys | Sort-Object | Select-Object -First 1
$gap = $newExtendedAddress - $newStartAddress
# Search and process memory window before clearing if gap is larger than the window size
if ($gap -gt $memoryWindowSize) {
Process-MemoryWindow -searchString $SearchString -FullWindowSearch
$memoryWindow.Clear()
if ($global:flow_Debug -eq $true) {
Write-Host "Cleared memory window due to large gap ($gap bytes) between segments."
}
}
$extendedAddress = $newExtendedAddress
if ($global:flow_Debug -eq $true) {
Write-Host "Extended Address updated: $([System.Convert]::ToString($extendedAddress, 16))"
}
}
if ($recordType -eq "00") {
$address = [convert]::ToInt64($line.Substring(3, 4), 16) + $extendedAddress
$byteCount = [convert]::ToInt32($line.Substring(1, 2), 16)
$endAddress = $address + ($byteCount * 2)
$previousEndAddress = $endAddress
if ($global:flow_Debug -eq $true) {
Write-Host "Processing data record at address: $([System.Convert]::ToString($address, 16))"
}
$data = $line.Substring(9, $line.Length - 11)
if ($global:InputHex_Debug -eq $true) {
Write-Host "Data extracted: $data"
}
Grow-MemoryWindow -address $address -data $data
if ($memoryWindow.Count -ge $memoryWindowSize) {
Process-MemoryWindow -searchString $SearchString
}
Trim-MemoryWindow
if ($MaxCount -ne $null) {
if ($global:foundCount -eq $MaxCount) {
if ($global:flow_Debug -eq $true) {
Write-Host "Stopping as MaxCount of $MaxCount has been reached."
}
break
}
}
}
if ($recordType -eq "01") {
Process-MemoryWindow -searchString $SearchString
if ($global:flow_Debug -eq $true) {
Write-Host "Final line of the intel hex file has been read."
}
}
}
if ($global:flow_Debug -eq $true) {
Write-Host "Total occurrences found: $global:foundCount"
}
if (-not ($global:SuppressOutput_Debug -eq $true)) {
Write-Output $global:foundCount
}
exit 0
0.1 2024-08-16 Initial version. Loosely based on my FindHexString.ps1 - https://gist.github.com/f-steff/01ff67a566835a9ca3aba7a0537507c3
1.0 2024-08-16 Updated with proper gist url.
1.1 2024-08-16 When attempting ExtractInt with an offset that points to undefined memory, return values are set to 0xNANA 0xNANA
1.2 2024-08-21 Now correctly searches the last half buffer before finishes a scan.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment