Initial import of IC.Rebuild
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup><OutputType>Exe</OutputType><TargetFramework>net10.0</TargetFramework><ImplicitUsings>enable</ImplicitUsings><Nullable>enable</Nullable></PropertyGroup>
|
||||
<ItemGroup><Reference Include="Iced"><HintPath>$(APPDATA)\XIVLauncher\addon\Hooks\dev\Iced.dll</HintPath></Reference></ItemGroup>
|
||||
</Project>
|
||||
@@ -0,0 +1,125 @@
|
||||
using System.Reflection.PortableExecutable;
|
||||
using Iced.Intel;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
// Read-only on-disk signature audit. Does not open a process or modify the executable.
|
||||
if(args.Length<2) throw new ArgumentException("NativeAudit executable signature|rva:HEX [instruction-count]");
|
||||
using var input=File.OpenRead(args[0]);
|
||||
using var pe=new PEReader(input);
|
||||
var section=pe.PEHeaders.SectionHeaders.Single(x=>x.Name==".text");
|
||||
var bytes=pe.GetSectionData(section.VirtualAddress).GetContent().ToArray();
|
||||
if(args[1].StartsWith("xrefstr:") || args[1].StartsWith("xrefs:"))
|
||||
{
|
||||
int target;
|
||||
if(args[1].StartsWith("xrefstr:"))
|
||||
{
|
||||
var image=File.ReadAllBytes(args[0]);
|
||||
var offset=image.AsSpan().IndexOf(System.Text.Encoding.UTF8.GetBytes(args[1][8..]+"\0"));
|
||||
if(offset<0) throw new InvalidDataException("String not found");
|
||||
var dataSection=pe.PEHeaders.SectionHeaders.Single(s=>offset>=s.PointerToRawData && offset<s.PointerToRawData+s.SizeOfRawData);
|
||||
target=offset-dataSection.PointerToRawData+dataSection.VirtualAddress;
|
||||
}
|
||||
else target=Convert.ToInt32(args[1][6..].Split('-')[0],16);
|
||||
var endTarget=args[1].StartsWith("xrefs:") && args[1].Contains('-') ? Convert.ToInt32(args[1].Split('-')[1],16) : target;
|
||||
var directory=pe.PEHeaders.PEHeader!.ExceptionTableDirectory;
|
||||
var functions=pe.GetSectionData(directory.RelativeVirtualAddress).GetContent(0,directory.Size).ToArray();
|
||||
Console.WriteLine($"XREF TARGET RVA=0x{target:X}");
|
||||
bool IsStringPointer(ulong pointer)
|
||||
{
|
||||
if(pointer<pe.PEHeaders.PEHeader!.ImageBase) return false;
|
||||
var rva=(long)(pointer-pe.PEHeaders.PEHeader.ImageBase);
|
||||
var data=pe.PEHeaders.SectionHeaders.FirstOrDefault(s=>rva>=s.VirtualAddress && rva<s.VirtualAddress+s.SizeOfRawData);
|
||||
if(data.SizeOfRawData==0) return false;
|
||||
var content=pe.GetSectionData((int)rva).GetContent(0,Math.Min(128,data.VirtualAddress+data.SizeOfRawData-(int)rva)).ToArray();
|
||||
var end=Array.IndexOf(content,(byte)0);
|
||||
return end>=3 && content.Take(end).All(b=>b>=32 && b<127);
|
||||
}
|
||||
foreach(var dataSection in pe.PEHeaders.SectionHeaders.Where(s=>s.Name!=".text" && s.SizeOfRawData>0))
|
||||
{
|
||||
var raw=pe.GetSectionData(dataSection.VirtualAddress).GetContent().ToArray();
|
||||
for(int at=0;at+8<=raw.Length;at+=8)
|
||||
if(BinaryPrimitives.ReadUInt64LittleEndian(raw.AsSpan(at))==pe.PEHeaders.PEHeader!.ImageBase+(uint)target)
|
||||
{
|
||||
Console.WriteLine($"DATA REF RVA=0x{dataSection.VirtualAddress+at:X}");
|
||||
var before=at;
|
||||
while(before>=8 && at-before<8192 && IsStringPointer(BinaryPrimitives.ReadUInt64LittleEndian(raw.AsSpan(before-8)))) before-=8;
|
||||
Console.WriteLine($"STRING TABLE candidate RVA=0x{dataSection.VirtualAddress+before:X}, preceding={(at-before)/8}");
|
||||
}
|
||||
}
|
||||
for(int i=0;i+7<=bytes.Length;i++)
|
||||
{
|
||||
var callTarget=section.VirtualAddress+i+5+BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(i+1));
|
||||
var ripTarget=section.VirtualAddress+i+7+BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(i+3));
|
||||
bool call=(bytes[i]==0xE8 || bytes[i]==0xE9) && callTarget>=target && callTarget<=endTarget;
|
||||
bool rip=(bytes[i] is 0x48 or 0x4C) && bytes[i+1] is 0x8D or 0x8B && (bytes[i+2]&0xC7)==5 && ripTarget>=target && ripTarget<=endTarget;
|
||||
var rawTarget=BinaryPrimitives.ReadInt32LittleEndian(bytes.AsSpan(i));
|
||||
bool immediate=rawTarget>=target && rawTarget<=endTarget;
|
||||
if(!call && !rip && !immediate) continue;
|
||||
var rva=section.VirtualAddress+i;
|
||||
bool described=false;
|
||||
for(int at=0;at+12<=functions.Length;at+=12)
|
||||
{
|
||||
int begin=BinaryPrimitives.ReadInt32LittleEndian(functions.AsSpan(at)),end=BinaryPrimitives.ReadInt32LittleEndian(functions.AsSpan(at+4));
|
||||
if(rva>=begin && rva<end) { Console.WriteLine($"REF RVA=0x{rva:X}, FUNCTION=0x{begin:X}..0x{end:X}, {(call ? "CALL/JMP" : rip ? "RIP" : "IMM/CANDIDATE")} target=0x{(call ? callTarget : rip ? ripTarget : rawTarget):X}"); described=true; break; }
|
||||
}
|
||||
if(!described) Console.WriteLine($"REF RVA=0x{rva:X}, FUNCTION=leaf/unknown, {(call ? "CALL/JMP" : rip ? "RIP" : "IMM/CANDIDATE")} target=0x{(call ? callTarget : rip ? ripTarget : rawTarget):X}");
|
||||
}
|
||||
return;
|
||||
}
|
||||
var containing=args[1].StartsWith("func:",StringComparison.OrdinalIgnoreCase);
|
||||
var direct=containing || args[1].StartsWith("rva:",StringComparison.OrdinalIgnoreCase);
|
||||
var requested=direct ? Convert.ToInt32(args[1][(containing ? 5 : 4)..],16) : 0;
|
||||
if(containing)
|
||||
{
|
||||
var directory=pe.PEHeaders.PEHeader!.ExceptionTableDirectory;
|
||||
var functions=pe.GetSectionData(directory.RelativeVirtualAddress).GetContent(0,directory.Size).ToArray();
|
||||
bool found=false;
|
||||
for(int at=0;at+12<=functions.Length;at+=12)
|
||||
{
|
||||
int begin=BinaryPrimitives.ReadInt32LittleEndian(functions.AsSpan(at)),end=BinaryPrimitives.ReadInt32LittleEndian(functions.AsSpan(at+4));
|
||||
if(requested>=begin && requested<end)
|
||||
{
|
||||
Console.WriteLine($"CONTAINING RVA=0x{begin:X}..0x{end:X}");
|
||||
var unwind=BinaryPrimitives.ReadInt32LittleEndian(functions.AsSpan(at+8));
|
||||
for(int depth=0;depth<16;depth++)
|
||||
{
|
||||
var header=pe.GetSectionData(unwind).GetContent(0,4).ToArray();
|
||||
if(((header[0]>>3)&4)==0) break;
|
||||
var chainAt=(4+header[2]*2+3)&~3;
|
||||
var chain=pe.GetSectionData(unwind+chainAt).GetContent(0,12).ToArray();
|
||||
begin=BinaryPrimitives.ReadInt32LittleEndian(chain); end=BinaryPrimitives.ReadInt32LittleEndian(chain.AsSpan(4));
|
||||
unwind=BinaryPrimitives.ReadInt32LittleEndian(chain.AsSpan(8));
|
||||
Console.WriteLine($"CHAINED ROOT RVA=0x{begin:X}..0x{end:X}");
|
||||
}
|
||||
requested=begin;found=true;break;
|
||||
}
|
||||
}
|
||||
if(!found) throw new InvalidDataException("No unwind entry for requested RVA");
|
||||
}
|
||||
var start=direct ? requested-section.VirtualAddress : 0;
|
||||
if(start<0 || start>=bytes.Length) throw new ArgumentException("RVA outside .text");
|
||||
var pattern=direct ? "" : args[1].Replace(" ","");
|
||||
if(pattern.Length%2!=0) throw new ArgumentException("Odd signature length");
|
||||
var values=Enumerable.Range(0,pattern.Length/2).Select(i=>pattern.Substring(i*2,2)).ToArray();
|
||||
var count=args.Length>2 ? int.Parse(args[2]) : 100;
|
||||
int matches=0;
|
||||
for(int i=start;i<=(direct ? start : bytes.Length-values.Length);i++)
|
||||
{
|
||||
bool match=true;
|
||||
for(int j=0;j<values.Length;j++)
|
||||
if(values[j]!="??" && bytes[i+j]!=Convert.ToByte(values[j],16)) { match=false; break; }
|
||||
if(!match) continue;
|
||||
matches++;
|
||||
var decoder=Decoder.Create(64,new ByteArrayCodeReader(bytes,i,Math.Min(8192,bytes.Length-i)));
|
||||
decoder.IP=pe.PEHeaders.PEHeader!.ImageBase+(uint)section.VirtualAddress+(uint)i;
|
||||
Console.WriteLine($"MATCH RVA=0x{section.VirtualAddress+i:X}");
|
||||
Console.WriteLine("PREFIX "+Convert.ToHexString(bytes.AsSpan(i,Math.Min(64,bytes.Length-i))));
|
||||
var formatter=new IntelFormatter(); var output=new StringOutput();
|
||||
for(int n=0;n<count;n++)
|
||||
{
|
||||
decoder.Decode(out var instruction); formatter.Format(instruction,output);
|
||||
Console.WriteLine($"{instruction.IP:X16} {output.ToStringAndReset()}");
|
||||
if(instruction.FlowControl==FlowControl.Return) break;
|
||||
}
|
||||
}
|
||||
Console.WriteLine($"Matches: {matches}");
|
||||
Reference in New Issue
Block a user