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 && offsetrva>=s.VirtualAddress && rva=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=begin && requested>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