83 lines
3.8 KiB
PowerShell
83 lines
3.8 KiB
PowerShell
param(
|
|
[Parameter(Mandatory)][string]$AssemblyPath,
|
|
[Parameter(Mandatory)][string]$OutputPath,
|
|
[string]$CecilPath = (Join-Path $env:APPDATA 'XIVLauncher\addon\Hooks\dev\Mono.Cecil.dll')
|
|
)
|
|
$ErrorActionPreference = 'Stop'
|
|
# Read the target as data with Cecil. Never load or execute the target assembly.
|
|
Add-Type -Path $CecilPath
|
|
$icInput = (Resolve-Path -LiteralPath $AssemblyPath).Path
|
|
$icAssembly = [Mono.Cecil.AssemblyDefinition]::ReadAssembly($icInput)
|
|
try {
|
|
$icSelected = $icAssembly.MainModule.Types | Where-Object {
|
|
$_.FullName -match '^StatusEffectList(Entry)?$|^SamplePlugin\.Hook\.(ActorRadiusHook|NoActionMoveHook|YMove|StatusCheck)$'
|
|
}
|
|
$icTypes = foreach ($icType in $icSelected) {
|
|
$icMethods = foreach ($icMethod in $icType.Methods) {
|
|
$icState = 'NoBody'
|
|
$icError = $null
|
|
$icCodeSize = $null
|
|
$icInstructions = $null
|
|
if ($icMethod.HasBody) {
|
|
try {
|
|
$icBody = $icMethod.get_Body()
|
|
$icCodeSize = $icBody.CodeSize
|
|
$icInstructions = @($icBody.Instructions | ForEach-Object { $_.ToString() })
|
|
# Parsing success alone is not validation of a protected method body.
|
|
$icState = 'ParsedUnvalidated'
|
|
$icMissingOperands = @($icBody.Instructions | Where-Object {
|
|
$_.OpCode.OperandType.ToString() -ne 'InlineNone' -and $null -eq $_.Operand
|
|
})
|
|
if ($icCodeSize -gt 1048576 -or $icInstructions.Count -eq 0 -or
|
|
$icBody.Instructions[0].OpCode.Name -eq 'ldelem.i1' -or $icMissingOperands.Count -gt 0) {
|
|
$icState = 'SuspiciousBody'
|
|
$icInstructions = $null
|
|
}
|
|
} catch {
|
|
$icState = 'ReadError'
|
|
$icError = $_.Exception.GetBaseException().Message
|
|
}
|
|
}
|
|
[ordered]@{
|
|
Signature = $icMethod.FullName
|
|
Token = $icMethod.MetadataToken.ToUInt32()
|
|
RVA = ('0x{0:X}' -f $icMethod.RVA)
|
|
BodyState = $icState
|
|
DeclaredCodeSize = $icCodeSize
|
|
Error = $icError
|
|
UnvalidatedIL = $icInstructions
|
|
}
|
|
}
|
|
[ordered]@{
|
|
Name = $icType.FullName
|
|
Attributes = $icType.Attributes.ToString()
|
|
DeclaredSize = $icType.ClassSize
|
|
PackingSize = $icType.PackingSize
|
|
Fields = @($icType.Fields | ForEach-Object {
|
|
[ordered]@{Name=$_.Name; Type=$_.FieldType.FullName; Offset=$_.Offset}
|
|
})
|
|
NestedTypes = @($icType.NestedTypes | ForEach-Object {
|
|
[ordered]@{
|
|
Name=$_.FullName
|
|
DeclaredSize=$_.ClassSize
|
|
DelegateInvoke=@($_.Methods | Where-Object Name -eq 'Invoke' | ForEach-Object { $_.FullName })
|
|
}
|
|
})
|
|
Methods = @($icMethods)
|
|
}
|
|
}
|
|
$icReport = [ordered]@{
|
|
Source = $icInput
|
|
SHA256 = (Get-FileHash -LiteralPath $icInput -Algorithm SHA256).Hash
|
|
Assembly = $icAssembly.Name.FullName
|
|
Runtime = $icAssembly.MainModule.RuntimeVersion
|
|
References = @($icAssembly.MainModule.AssemblyReferences | ForEach-Object {
|
|
[ordered]@{Name=$_.Name; Version=$_.Version.ToString()}
|
|
})
|
|
Types = @($icTypes)
|
|
Limitation = 'Metadata only. Method-body parsing is not proof of executable logic. Target assembly was not executed.'
|
|
}
|
|
$icReport | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $OutputPath -Encoding utf8
|
|
"Saved metadata audit: $OutputPath"
|
|
} finally { $icAssembly.Dispose() }
|