feat(analysis): add self-modifying code detector
This commit is contained in:
1 parent
a6c2b4f7d4
commit
210cf5c6b1
1 file changed
+131
@@ -0,0 +1,131 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using Chip8.Core;
|
||||||
|
|
||||||
|
namespace Chip8.Analysis;
|
||||||
|
|
||||||
|
public sealed class SelfModifyingCodeReport
|
||||||
|
{
|
||||||
|
public SelfModifyingCodeReport(IReadOnlyList<SmcEvent> events, IReadOnlyList<InstructionInfo> staticWriters, IReadOnlyList<ushort> staticTargets)
|
||||||
|
{
|
||||||
|
this.events = events;
|
||||||
|
this.staticWriters = staticWriters;
|
||||||
|
this.staticTargets = staticTargets;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<SmcEvent> events { get; }
|
||||||
|
public IReadOnlyList<InstructionInfo> staticWriters { get; }
|
||||||
|
public IReadOnlyList<ushort> staticTargets { get; }
|
||||||
|
|
||||||
|
public bool detected => events.Count > 0;
|
||||||
|
public bool hasStaticSuspicion => staticWriters.Count > 0;
|
||||||
|
|
||||||
|
public IReadOnlyList<ushort> modifiedAddresses =>
|
||||||
|
events.Select(item => item.targetAddress).Distinct().OrderBy(value => value).ToList();
|
||||||
|
|
||||||
|
public string render()
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
builder.Append("self-modifying code: ").Append(detected ? "detected" : "none").Append('\n');
|
||||||
|
foreach (var smcEvent in events)
|
||||||
|
{
|
||||||
|
builder.Append(" ").Append(smcEvent).Append('\n');
|
||||||
|
}
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class SelfModifyingCodeDetector
|
||||||
|
{
|
||||||
|
public SelfModifyingCodeDetector(ushort entry = InstructionDecoder.defaultBaseAddress, ushort baseAddress = InstructionDecoder.defaultBaseAddress)
|
||||||
|
{
|
||||||
|
this.entry = entry;
|
||||||
|
this.baseAddress = baseAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ushort entry { get; }
|
||||||
|
public ushort baseAddress { get; }
|
||||||
|
|
||||||
|
public SelfModifyingCodeReport detect(byte[] program, int maxSteps, Platform platform = Platform.Chip8, Quirks? quirks = null, int seed = 1, bool stopOnSpin = true)
|
||||||
|
{
|
||||||
|
var cfg = ControlFlowGraph.build(program, entry, baseAddress);
|
||||||
|
var watched = cfg.reachableByteAddresses();
|
||||||
|
var staticFindings = staticSuspects(cfg, watched);
|
||||||
|
|
||||||
|
var machine = new Machine(quirks, seed, platform);
|
||||||
|
machine.loadRom(program);
|
||||||
|
|
||||||
|
var snapshot = new Dictionary<ushort, byte>();
|
||||||
|
foreach (ushort address in watched)
|
||||||
|
{
|
||||||
|
snapshot[address] = machine.memory[address];
|
||||||
|
}
|
||||||
|
|
||||||
|
var events = new List<SmcEvent>();
|
||||||
|
int spinRun = 0;
|
||||||
|
|
||||||
|
for (int step = 0; step < maxSteps; step++)
|
||||||
|
{
|
||||||
|
if (machine.exited)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ushort writerAddress = machine.pc;
|
||||||
|
machine.step();
|
||||||
|
|
||||||
|
foreach (ushort address in watched)
|
||||||
|
{
|
||||||
|
byte current = machine.memory[address];
|
||||||
|
if (current != snapshot[address])
|
||||||
|
{
|
||||||
|
events.Add(new SmcEvent(step, writerAddress, address, snapshot[address], current));
|
||||||
|
snapshot[address] = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stopOnSpin && machine.pc == writerAddress)
|
||||||
|
{
|
||||||
|
spinRun++;
|
||||||
|
if (spinRun >= 2)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
spinRun = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var staticTargets = staticFindings.targets;
|
||||||
|
return new SelfModifyingCodeReport(events, staticFindings.writers, staticTargets);
|
||||||
|
}
|
||||||
|
|
||||||
|
static (IReadOnlyList<InstructionInfo> writers, IReadOnlyList<ushort> targets) staticSuspects(ControlFlowGraph cfg, HashSet<ushort> codeBytes)
|
||||||
|
{
|
||||||
|
var writers = new List<InstructionInfo>();
|
||||||
|
var indexTargets = new SortedSet<ushort>();
|
||||||
|
|
||||||
|
foreach (var block in cfg.blocksInOrder)
|
||||||
|
{
|
||||||
|
foreach (var info in block.instructions)
|
||||||
|
{
|
||||||
|
if (info.loadsIndex && info.indexValue.HasValue && codeBytes.Contains(info.indexValue.Value))
|
||||||
|
{
|
||||||
|
indexTargets.Add(info.indexValue.Value);
|
||||||
|
}
|
||||||
|
if (info.writesMemory)
|
||||||
|
{
|
||||||
|
writers.Add(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool codeTargeted = indexTargets.Count > 0;
|
||||||
|
var flaggedWriters = codeTargeted ? writers : new List<InstructionInfo>();
|
||||||
|
return (flaggedWriters, indexTargets.ToList());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user