public static List <ulong> LookForByteArray(Process process, ImportResolver ir, byte[] toLookFor) { var matches = new List <ulong>(); foreach (ProcessModule mod in process.Modules) { if (mod.ModuleName.Equals(Specifics.TraceModuleName)) { byte[] mem; try { mem = DebugProcessUtils.ReadBytes(process, (ulong)mod.BaseAddress, mod.ModuleMemorySize); } catch { throw new Exception("could not read process main module memory"); } for (var i = 0; i < mem.Length; i++) { var equal = true; for (var j = 0; j < toLookFor.Length; j++) { if (mem[i + j] != toLookFor[j]) { equal = false; break; } } if (equal) { matches.Add((ulong)mod.BaseAddress + (ulong)i); } } break; } } return(matches); }
private static int ScanModules(Process process, StreamWriter sw, out ProcessModule traceModule, out byte[] traceMemory) { var totalSize2 = 0; traceModule = null; traceMemory = new byte[] {}; foreach (ProcessModule mod in process.Modules) { if (mod.ModuleName.Equals(Specifics.TraceModuleName)) { traceModule = mod; } var result = "success"; try { var mem = DebugProcessUtils.ReadBytes(process, (ulong)mod.BaseAddress, mod.ModuleMemorySize); if (mod.ModuleName.Equals(Specifics.TraceModuleName)) { Console.WriteLine($"mem: {mem.Length}"); traceMemory = mem; } totalSize2 += mod.ModuleMemorySize; } catch { result = "failed"; } sw.WriteLine($"module: {mod.ModuleName}, size: {mod.ModuleMemorySize} result: {result}"); } sw.Close(); return(totalSize2); }
public void InstallBreakPoint(ulong startAddress) { //const byte INT3Trap = 0xCC; /*if (CheckBreakPointActive()) { throw new Exception("only one breakpoint at a time supported"); }*/ LoggerInstance.WriteLine($"installing breakpoint at {startAddress:X}"); if (BreakPoints[startAddress].OriginalCode == null) { byte[] mem = DebugProcessUtils.ReadBytes(CurrentProcess, startAddress, AssemblyUtil.InfiniteLoop.Length); BreakPoints[startAddress].OriginalCode = mem; } DebugProcessUtils.WriteBytes(CurrentProcess, startAddress, AssemblyUtil.InfiniteLoop); BreakPoints[startAddress].IsSoftware = true; BreakPoints[startAddress].IsActive = true; }
private static int ScanFunctions(Process process, ImportResolver ir, StreamWriter sw) { var totalSize = 0; foreach (var mi in ir.ModuleFunctions) { var result = "success"; try { DebugProcessUtils.ReadBytes(process, mi.Address, (int)mi.Size); totalSize += (int)mi.Size; } catch { result = "failed"; } sw.WriteLine($"function: {mi.Module.ModuleName}.{mi.FunctionName}, size: {mi.Size} result: {result}"); } return(totalSize); }
public static void TraceMain(Process process, ImportResolver ir, List <ulong> matches, Logger logger) { MemScan.MemMain(process, ir, true); var patchSite = matches.First(); var asmSizes = File.ReadAllBytes(Specifics.ReadAsmSizesDumpFileName); var branches = File.ReadAllBytes(Specifics.ReadAsmBranchDumpFileName); logger.WriteLine($"patch site: {patchSite:X}"); EmulatedMemTracer.TraceState traceState = null; SimpleMemTracer.TraceIt(process, patchSite, logger, false, (x, y, z) => { traceState = EmulatedMemTracer.InstallTracer(x, y, z, ir, asmSizes, branches); }); if (traceState != null) { // TODO: fix race var threadId = BitConverter.ToUInt32(DebugProcessUtils.ReadBytes(process, traceState.TraceLogAddress, 4), 0); Console.WriteLine($"thread id: {threadId:X}"); } }
private void UninstallBreakPoint(ulong address) { if (BreakPoints[address].IsActive) { if (BreakPoints[address].IsSoftware) { byte[] mem = DebugProcessUtils.ReadBytes(CurrentProcess, address, AssemblyUtil.InfiniteLoop.Length); if (mem[0] == AssemblyUtil.InfiniteLoop[0] && mem[1] == AssemblyUtil.InfiniteLoop[1]) { LoggerInstance.WriteLine($"{nameof(UninstallBreakPoint)}: restoring inf-jmp with original code at 0x{address:X}"); DebugProcessUtils.WriteBytes(CurrentProcess, address, BreakPoints[address].OriginalCode); } else if (mem[0] != BreakPoints[address].OriginalCode[0] && mem[1] != BreakPoints[address].OriginalCode[1]) { LoggerInstance.WriteLine($"{nameof(UninstallBreakPoint)}: code changed (self-modifying?) at 0x{address:X}"); } else { LoggerInstance.WriteLine($"{nameof(UninstallBreakPoint)}: code was intact at 0x{address:X}"); } } else if (BreakPoints[address].IsHardware && BreakPoints[address].HardwareBreakPointHandle != 0) { RemoveHardwareBreakpoint(BreakPoints[address].HardwareBreakPointHandle); /*foreach (ProcessThread thread in CurrentProcess.Threads) { setHardwareBreakPoint((uint)thread.Id, address, true, true); }*/ } } BreakPoints[address].IsActive = false; }
// returns true if new breakpoint was set public TraceReturn Log(ContextManager cm, Logger logFile, Process process, int threadId, Win32Imports.ContextX64 context, bool trace) { if (!trace) { // end trace return(new TraceReturn { StepOver = false }); } // http://stackoverflow.com/questions/14698350/x86-64-asm-maximum-bytes-for-an-instruction var breakAddress = context.Rip; var mem = DebugProcessUtils.ReadBytes(process, breakAddress, AssemblyUtil.MaxInstructionBytes); var distance = (long)(breakAddress - cm.LastBreakAddress); var decodedInstruction = AssemblyUtil.Disassemble(process, breakAddress); var hex = DebugProcessUtils.BytesToHex(mem.Take(decodedInstruction.Length).ToArray()); var moduleAddressTuple = _importResolver.LookupAddress(breakAddress); var module = moduleAddressTuple.Item1; var relativeAddress = breakAddress - moduleAddressTuple.Item2; var hitCounts = _oldState.ContainsKey(threadId) ? _oldState[threadId].HitCounts : new Dictionary <ulong, ulong>(); var stackDepth = 0; if (_oldState.ContainsKey(threadId)) { stackDepth = _oldState[threadId].StackDepth; } if (decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Icall) { stackDepth++; } else if (decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Iret) { stackDepth--; } if (breakAddress == _importResolver.ResolveRelativeAddress(Specifics.StartAddress)) { // reset trace state when we hit first breakpoint // except for oldHitCounts, which will be added back at end of function _oldState.Remove(threadId); } else if (breakAddress != cm.LastBreakAddress) { #if UseDebugger // we're not interested in logging threads where breakpoint was not found if (!_oldState.ContainsKey(threadId)) { logFile.WriteLine($"ignoring trace for thread {threadId}"); return(new TraceReturn { Ignore = true, StepOver = false }); } #endif } // IMPORTANT: continues the trace var retVal = false; ulong lastCallAddressInTraceModule = _oldState.ContainsKey(threadId) ? _oldState[threadId].LastCallAddressInTraceModule : 0; // clean up breakpoints as soon as they are hit cm.DisableBreakPointOnHit(breakAddress); #if UseDebugger if (decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Icall && !module.Equals(Specifics.TraceModuleName)) { // step over call var returnAddress = breakAddress + (ulong)decodedInstruction.Length; logFile.WriteLine($"installing return breakpoint at {returnAddress:X}"); cm.EnableBreakPoint(returnAddress, new ContextManager.BreakPointInfo { Description = "return breakpoint" }); stackDepth--; // since the RET will not be executed retVal = true; } else { if (module.Equals(Specifics.TraceModuleName)) { if (decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Iret) { } else if (decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Ijmp) { } else { var cAddress = breakAddress + (ulong)decodedInstruction.Length; logFile.WriteLine($"setting continuation at 0x{cAddress:X} for {decodedInstruction.Mnemonic} of size {decodedInstruction.Length}"); cm.EnableBreakPoint(cAddress, new ContextManager.BreakPointInfo { Description = $"for {decodedInstruction.Mnemonic} of size {(ulong) decodedInstruction.Length}" }); } /*foreach (var condJump in new string[] { * "JE", "JZ", "JNE", "JNZ", "JG", "JNLE", "JGE", "JNL", "JL", "JNGE", * "JLE", "JNG", "JA", "JNBE", "JAE", "JNB", "JB", "JNAE", "JBE", "JNA" }) { * if (decodedInstruction.Mnemonic.Equals(condJump)) { * var nextTarget = breakAddress + decodedInstruction.Size; * var jumpMatch = Regex.Match("0x([a-z0-9]+)", decodedInstruction.Operands); * if (jumpMatch.Success) { * var offset = ulong.Parse(jumpMatch.Groups[0].Value, System.Globalization.NumberStyles.AllowHexSpecifier); * var jumpTarget = (ulong) (new BigInteger(breakAddress) + (long) offset); * //logFile.WriteLine($"jump calc: offset: {(long) offset}, target: 0x{jumpTarget:X}"); * } * * //cm.continuationBreakAddress += * break; * } * }*/ if (decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Icall) { // step over instruction lastCallAddressInTraceModule = breakAddress; lastCallAddressInTraceModule += (ulong)decodedInstruction.Length; } logFile.WriteLine($"setting next trace for {threadId}"); ContextManager.setTrace((uint)threadId); } else { if (lastCallAddressInTraceModule == 0) { logFile.WriteLine("In external module, but no call to get back to."); } else if (!cm.BreakPoints.ContainsKey(lastCallAddressInTraceModule)) { logFile.WriteLine($"In external module, returning to {lastCallAddressInTraceModule:X}"); cm.EnableBreakPoint(lastCallAddressInTraceModule, new ContextManager.BreakPointInfo { Description = $"return from external module: {module}+{relativeAddress}" }); } } } #else throw new NotImplementedException(); /* * cm.continuationBreakAddress = breakAddress; * //cm.continuationBreakAddress += decodedInstruction.Size; * if (decodedInstruction.Mnemonic.Equals("RET")) { * var stackPointer = context.Rsp; * if (!decodedInstruction.Operands.Equals("")) { * var offset = ulong.Parse(decodedInstruction.Operands); * stackPointer += offset; * } * ulong returnAddress = BitConverter.ToUInt64(DebugProcessUtils.ReadBytes(process, stackPointer, 8), 0); * cm.continuationBreakAddress = returnAddress; * }*/ #endif if (hitCounts.ContainsKey(breakAddress)) { hitCounts[breakAddress]++; } else { hitCounts[breakAddress] = 1; } var registers = _oldState.ContainsKey(threadId) ? AssemblyUtil.FormatContextDiff(context, _oldState[threadId].Context, _oldState[threadId].SdInstruction) : AssemblyUtil.FormatContext(context); //logFile.WriteLine(registers); var previous = ""; var lineBreak = false; if (_oldState.ContainsKey(threadId)) { previous = _oldState[threadId].Line; if (_oldState[threadId].Instruction.Mnemonic == ud_mnemonic_code.UD_Iret || _oldState[threadId].Instruction.Mnemonic == ud_mnemonic_code.UD_Icall) { lineBreak = true; } } var asm = $"{ decodedInstruction.Mnemonic } { decodedInstruction.Operands}"; double logdist = distance == 0 ? 0.0 : distance < 0 ? -Math.Log(-distance, 2) : Math.Log(distance, 2); var pattern = ""; pattern += Regex.Escape("["); pattern += "(?<reg1>[A-Z0-9]{3})"; pattern += "((?<sign1>[" + Regex.Escape("+") + Regex.Escape("-") + "])(?<reg2>[A-Z0-9]{3})(" + Regex.Escape("*") + "(?<multiplier>[0-9]+))?)?"; pattern += "((?<sign2>[" + Regex.Escape("+") + Regex.Escape("-") + "])0x(?<offset>[0-9a-f]+))?"; pattern += Regex.Escape("]"); var rex = new Regex(pattern); // TODO: rewrite offset code to use SharpDisasm structure instead of string parsing var operands = decodedInstruction.Operands.ToString(); var match = rex.Matches(operands); BigInteger memAddress = 0; if (match.Count > 0) { var reg1 = match[0].Groups[rex.GroupNumberFromName("reg1")].Value; var reg2 = match[0].Groups[rex.GroupNumberFromName("reg2")].Value; long sign1 = match[0].Groups[rex.GroupNumberFromName("sign1")].Value.Equals("+") ? 1 : -1; long sign2 = match[0].Groups[rex.GroupNumberFromName("sign2")].Value.Equals("+") ? 1 : -1; var multiplierString = match[0].Groups[rex.GroupNumberFromName("multiplier")].Value; var multiplier = multiplierString.Equals("") ? 1 : long.Parse(multiplierString); var offsetHex = match[0].Groups[rex.GroupNumberFromName("offset")].Value; var reg1Value = (ulong)_fieldMap[reg1].GetValue(context); ulong reg2Value = 0; if (!reg2.Equals("")) { reg2Value = (ulong)_fieldMap[reg2].GetValue(context); } var offset = offsetHex.Equals("") ? 0 : long.Parse(offsetHex, System.Globalization.NumberStyles.AllowHexSpecifier); memAddress = new BigInteger(reg1Value) + sign1 * new BigInteger(reg2Value) * multiplier + sign2 * new BigInteger(offset); } else if (decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Ipop || decodedInstruction.Mnemonic == ud_mnemonic_code.UD_Ipush) { memAddress = context.Rsp; } //var module = DebugProcessUtils.GetModuleByAddress(process, breakAddress); var oldLine = $"d: {logdist,8:#.##}, thread: {threadId,6:D}, mem: {memAddress,16:X} "; var moduleAndAddress = $"{module}+0x{relativeAddress:X}:{breakAddress:X}"; oldLine += $"instr: {hex,30},"; var hits = hitCounts[breakAddress]; oldLine += $" {moduleAndAddress,64}(h{hits,2})(s{stackDepth,2}): {asm,-40} "; logFile.WriteLine(previous + registers); if (lineBreak) { logFile.WriteLine(""); } _oldState[threadId] = new State { Context = context, Instruction = decodedInstruction, Line = oldLine, HitCounts = hitCounts, StackDepth = stackDepth, LastCallAddressInTraceModule = lastCallAddressInTraceModule }; return(new TraceReturn { StepOver = retVal }); }
public static TraceState InstallTracer(Process process, ulong patchSite, Logger logger, byte[] asmSizes, ImportResolver ir) { var processHandle = Win32Imports.OpenProcess(Win32Imports.ProcessAccessFlags.All, false, process.Id); var traceState = new TraceState(); if ((ulong)processHandle == 0) { logger.WriteLine("could not open process"); return(null); } const int patchSiteSize = 24; const int codeSize = 1024; const int traceLogSize = 1024; var originalCode = DebugProcessUtils.ReadBytes(process, (ulong)process.MainModule.BaseAddress, process.MainModule.ModuleMemorySize); var originalCodeAddress = Win32Imports.VirtualAllocEx( processHandle, new IntPtr(0), new IntPtr(originalCode.Length), Win32Imports.AllocationType.Commit | Win32Imports.AllocationType.Reserve, Win32Imports.MemoryProtection.ReadWrite); if ((ulong)originalCodeAddress == 0) { logger.WriteLine($"could not allocate memory for {nameof(originalCodeAddress)}"); return(null); } var traceLogAddress = Win32Imports.VirtualAllocEx( processHandle, new IntPtr(0), new IntPtr(traceLogSize), Win32Imports.AllocationType.Commit | Win32Imports.AllocationType.Reserve, Win32Imports.MemoryProtection.ReadWrite); if ((ulong)traceLogAddress == 0) { logger.WriteLine($"could not allocate memory for {nameof(traceLogAddress)}"); return(null); } var injectedCodeAddress = Win32Imports.VirtualAllocEx( processHandle, new IntPtr(0), new IntPtr(codeSize), Win32Imports.AllocationType.Commit | Win32Imports.AllocationType.Reserve, Win32Imports.MemoryProtection.ExecuteReadWrite); if ((ulong)injectedCodeAddress == 0) { logger.WriteLine($"could not allocate memory for {nameof(injectedCodeAddress)}"); return(null); } var asmSizesAddress = Win32Imports.VirtualAllocEx( processHandle, new IntPtr(0), new IntPtr(asmSizes.Length), Win32Imports.AllocationType.Commit | Win32Imports.AllocationType.Reserve, Win32Imports.MemoryProtection.ReadWrite); if ((ulong)asmSizesAddress == 0) { logger.WriteLine($"could not allocate memory for {nameof(asmSizesAddress)}"); return(null); } traceState.CodeAddress = (ulong)injectedCodeAddress; traceState.TraceLogAddress = (ulong)traceLogAddress; int addrSize = Marshal.SizeOf(typeof(IntPtr)); var c1 = Assembler.CreateContext <Action>(); // save rax c1.Push(c1.Rax); // push return address c1.Lea(c1.Rax, Memory.QWord(CodeContext.Rip, -addrSize)); c1.Push(c1.Rax); // push "call" address c1.Mov(c1.Rax, (ulong)injectedCodeAddress); c1.Push(c1.Rax); // "call" codeAddress c1.Ret(); c1.Nop(); c1.Nop(); c1.Nop(); var patchSiteCodeJit = AssemblyUtil.GetAsmJitBytes(c1); Debug.Assert(patchSiteCodeJit.Length == patchSiteSize, "patch site size incorrect"); var c = Assembler.CreateContext <Action>(); c.Push(c.Rbp); c.Mov(c.Rbp, c.Rsp); c.Pushf(); c.Push(c.Rbx); c.Push(c.Rcx); c.Push(c.Rdx); // log thread id var getThreadContext = ir.LookupFunction("KERNEL32.DLL:GetCurrentThreadId"); //c.Call(getThreadContext); c.Lea(c.Rax, Memory.QWord(CodeContext.Rip, 13)); // skips next instructions c.Push(c.Rax); c.Mov(c.Rax, getThreadContext); c.Push(c.Rax); c.Ret(); c.Mov(c.Rbx, (ulong)traceLogAddress); c.Mov(Memory.Word(c.Rbx), c.Eax); // reserve space for instruction counter, the 01-08 is just so AsmJit doesn't shorten the instruction // we overwrite the value to 0 later on c.Mov(c.Rax, (ulong)0x0102030405060708); var smcLastRipJit = AssemblyUtil.GetAsmJitBytes(c).Length - addrSize; c.Lea(c1.Rax, Memory.QWord(CodeContext.Rip, -7 - addrSize)); c.Mov(c.Rcx, Memory.QWord(c.Rax)); c.Inc(c.Rcx); c.Xchg(Memory.QWord(c.Rax), c.Rcx); // find return address and store size of instruction at return address in rcx c.Mov(c.Rdx, Memory.QWord(c.Rbp, addrSize)); c.Mov(c.Rbx, c.Rdx); c.Mov(c.Rax, (ulong)process.MainModule.BaseAddress); c.Sub(c.Rdx, c.Rax); c.Mov(c.Rax, (ulong)asmSizesAddress); c.Add(c.Rdx, c.Rax); c.Movzx(c.Rcx, Memory.Byte(c.Rdx)); // RESTORE ORIGINAL CODE // get address of original code in Rdx c.Mov(c.Rdx, c.Rbx); c.Mov(c.Rax, (ulong)process.MainModule.BaseAddress); c.Sub(c.Rdx, c.Rax); // TODO: compare Rdx with process.MainModule.ModuleMemorySize - patchSiteSize and abort if greater c.Mov(c.Rax, (ulong)originalCodeAddress); c.Add(c.Rdx, c.Rax); // restore call site 1 c.Mov(c.Rax, Memory.QWord(c.Rdx)); c.Mov(Memory.QWord(c.Rbx), c.Rax); // restore call site 2 c.Mov(c.Rax, Memory.QWord(c.Rdx, addrSize)); c.Mov(Memory.QWord(c.Rbx, addrSize), c.Rax); // restore call site 3 c.Mov(c.Rax, Memory.QWord(c.Rdx, addrSize * 2)); c.Mov(Memory.QWord(c.Rbx, addrSize * 2), c.Rax); // CLEAN UP AND RETURN // put new patch in place // add instruction size to return address, so we write patch at next instruction c.Add(c.Rbx, c.Rcx); // restore patch site 1 c.Mov(c.Rax, BitConverter.ToUInt64(patchSiteCodeJit, 0)); c.Mov(Memory.QWord(c.Rbx), c.Rax); // restore patch site 2 c.Mov(c.Rax, BitConverter.ToUInt64(patchSiteCodeJit, addrSize)); c.Mov(Memory.QWord(c.Rbx, addrSize), c.Rax); // restore patch site 3 c.Mov(c.Rax, BitConverter.ToUInt64(patchSiteCodeJit, addrSize * 2)); c.Mov(Memory.QWord(c.Rbx, addrSize * 2), c.Rax); // end put new patch in place // restore rax from call site c.Mov(c.Rax, Memory.QWord(c.Rbp, 2 * addrSize)); c.Pop(c.Rdx); c.Pop(c.Rcx); c.Pop(c.Rbx); c.Popf(); c.Pop(c.Rbp); c.Ret((ulong)8); // overwrite some pieces of the code with values computed later on var codeSiteCodeJit = AssemblyUtil.GetAsmJitBytes(c); for (var j = 0; j < 8; j++) { codeSiteCodeJit[smcLastRipJit + j] = 0; } var i = 0; var nextOffset1 = 0; using (var sw = new StreamWriter(Specifics.PatchAsmDumpFileName)) { foreach (var s in codeSiteCodeJit.Select((b1) => $"b: 0x{b1:X2}")) { string asm1S; try { var asm1 = AssemblyUtil.Disassemble(codeSiteCodeJit.Skip(i).Take(AssemblyUtil.MaxInstructionBytes).ToArray()); asm1S = i == nextOffset1?asm1.ToString() : ""; if (i == nextOffset1) { nextOffset1 += asm1.Length; } } catch (DecodeException) { asm1S = "failed"; } sw.WriteLine($"{s}: ASM: {asm1S}"); i++; } } if (codeSiteCodeJit.Length > codeSize) { throw new Exception("did not reserve enough memory for code"); } // write process memory DebugProcessUtils.WriteBytes(process, (ulong)originalCodeAddress, originalCode); DebugProcessUtils.WriteBytes(process, (ulong)asmSizesAddress, asmSizes); DebugProcessUtils.WriteBytes(process, (ulong)injectedCodeAddress, codeSiteCodeJit); DebugProcessUtils.WriteBytes(process, patchSite, patchSiteCodeJit); return(traceState); }
// see also StackWalk64, but I'm not sure I can use that, because I don't have a function table public static void LogStackTrace(ImportResolver ir, Logger log, Process process, ulong stackPointer) { int size = 4096; var mem = DebugProcessUtils.ReadBytes(process, stackPointer, 4096); var ptrSize = Marshal.SizeOf(typeof(IntPtr)); for (var offset = 0; offset + ptrSize < size; offset += 1) { ulong ptr = (ulong)BitConverter.ToInt64(mem, offset); if (ptr == 0) { continue; } Tuple <string, ulong> ret = null; try { ret = ir.LookupAddress(ptr); } catch (Exception) { // ignored } string module = "lookup-failed"; ulong relative = 0; if (ret != null) { module = ret.Item1; var functionAddress = ret.Item2; relative = ptr - functionAddress; } byte[] ptrMem = null; try { ptrMem = DebugProcessUtils.ReadBytes(process, ptr, ptrSize); } catch (Exception) { // ignored } ulong data = 0; if (ptrMem != null) { data = (ulong)BitConverter.ToInt64(ptrMem, 0); } for (ulong potentialCallOffset = 0; potentialCallOffset <= 6; potentialCallOffset++) { try { var callLocation = ptr - potentialCallOffset; var instr = Disassemble(process, callLocation); var asm = FormatInstruction(instr); if (instr.Mnemonic == ud_mnemonic_code.UD_Icall || potentialCallOffset == 0) { log.WriteLine($"stack call {offset}-{potentialCallOffset}: {module}+0x{relative:X} 0x{ptr:X}: asm 0x{data:X} {asm}"); } } catch (Exception) { if (potentialCallOffset == 0) { log.WriteLine($"stack trace {offset}-{potentialCallOffset}: {module}+0x{relative:X} 0x{ptr:X}: asm 0x{data:X} exception"); } } } } }
public static Instruction[] DisassembleMany(Process process, ulong address, ulong targetOffset) { var mem = DebugProcessUtils.ReadBytes(process, address, (int)(targetOffset - address)); return(DisassembleMany(mem, targetOffset)); }
public static Instruction ReadOneAndDisassemble(Process process, ulong address) { var mem = DebugProcessUtils.ReadBytes(process, address, MaxInstructionBytes); return(Disassemble(mem)); }
public static void InstallTracer(Process process, ulong patchSite, Logger logger) { var processHandle = Win32Imports.OpenProcess(Win32Imports.ProcessAccessFlags.All, false, process.Id); var retVal = new TraceState(); if ((ulong)processHandle == 0) { logger.WriteLine("could not open process"); return; } const int patchSiteSize = 24; const int patchAreaSize = 100; const int codeSize = 1024 + patchAreaSize * 100; var originalCode = DebugProcessUtils.ReadBytes(process, patchSite, patchAreaSize); // minus patchSiteSize because we need to restore patchSiteSize bytes var ci = new diStorm.CodeInfo(0, originalCode.Take(patchAreaSize - patchSiteSize).ToArray(), diStorm.DecodeType.Decode64Bits, 0); var dr = new diStorm.DecodedResult(patchAreaSize); diStorm.diStorm3.Decode(ci, dr); var decodedInstructions = dr.Instructions; var bogusAddress = Win32Imports.VirtualAllocEx( processHandle, new IntPtr(0), new IntPtr(patchSiteSize * 2), Win32Imports.AllocationType.Commit | Win32Imports.AllocationType.Reserve, Win32Imports.MemoryProtection.ReadWrite); var codeAddress = Win32Imports.VirtualAllocEx( processHandle, new IntPtr(0), new IntPtr(codeSize), Win32Imports.AllocationType.Commit | Win32Imports.AllocationType.Reserve, Win32Imports.MemoryProtection.ExecuteReadWrite); retVal.CodeAddress = (ulong)codeAddress; if ((ulong)codeAddress == 0) { logger.WriteLine("could not allocate memory"); return; } var patchSiteCode = new byte[patchSiteSize]; var codeSiteCode = new byte[codeSize]; var patchPtr = 0; var ptr = 0; //patchSiteCode[patchPtr++] = 0xCC; // push rax, see RESTORE_RAX patchSiteCode[patchPtr++] = 0x50; // lea rax, [rip-8] patchSiteCode[patchPtr++] = 0x48; patchSiteCode[patchPtr++] = 0x8D; patchSiteCode[patchPtr++] = 0x05; patchSiteCode[patchPtr++] = 0xF8; patchSiteCode[patchPtr++] = 0xFF; patchSiteCode[patchPtr++] = 0xFF; patchSiteCode[patchPtr++] = 0xFF; // push rax (rip) patchSiteCode[patchPtr++] = 0x50; // mov rax, constant patchSiteCode[patchPtr++] = 0x48; patchSiteCode[patchPtr++] = 0xB8; foreach (var b in BitConverter.GetBytes((ulong)codeAddress)) { patchSiteCode[patchPtr++] = b; } // push rax patchSiteCode[patchPtr++] = 0x50; // ret patchSiteCode[patchPtr++] = 0xC3; patchPtr += 3; if (patchPtr != patchSiteSize) { logger.WriteLine($"made a mistake: {patchPtr} != {patchSiteSize}"); return; } // push rbp codeSiteCode[ptr++] = 0x55; // mov rbp, rsp codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x89; codeSiteCode[ptr++] = 0xE5; // push flags codeSiteCode[ptr++] = 0x9C; // push rbx codeSiteCode[ptr++] = 0x53; // push rcx codeSiteCode[ptr++] = 0x51; // push rdx codeSiteCode[ptr++] = 0x52; // reserve space for last Rip address // mov rax, constant codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0xB8; var smcLastRip = ptr; ptr += 8; // lea rax, [rip-7-8] codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x8D; codeSiteCode[ptr++] = 0x05; codeSiteCode[ptr++] = 0xF1; codeSiteCode[ptr++] = 0xFF; codeSiteCode[ptr++] = 0xFF; codeSiteCode[ptr++] = 0xFF; // mov rcx, [rax] codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x8B; codeSiteCode[ptr++] = 0x08; // inc rcx codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0xFF; codeSiteCode[ptr++] = 0xC1; // xchg [rax], rcx codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x87; codeSiteCode[ptr++] = 0x08; // find return address // mov rbx, [rbp+8] codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x8B; codeSiteCode[ptr++] = 0x5D; codeSiteCode[ptr++] = 0x08; // call pastEndCode codeSiteCode[ptr++] = 0xE8; var callPastEndCodeImmediate = ptr; ptr += 4; var callPastEndCodeBaseOffset = ptr; // END FUNCTION // put new patch in place // mov rax, constant codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0xB8; foreach (var b in patchSiteCode.Skip(0).Take(8)) { codeSiteCode[ptr++] = b; } // restore patch site 1 // mov [rbx], rax codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x89; codeSiteCode[ptr++] = 0x03; // mov rax, constant codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0xB8; foreach (var b in patchSiteCode.Skip(8).Take(8)) { codeSiteCode[ptr++] = b; } // restore patch site 2 // mov [rbx+8], rax codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x89; codeSiteCode[ptr++] = 0x43; codeSiteCode[ptr++] = 0x08; // mov rax, constant codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0xB8; foreach (var b in patchSiteCode.Skip(16).Take(8)) { codeSiteCode[ptr++] = b; } // restore patch site 3 // mov [rbx+16], rax codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x89; codeSiteCode[ptr++] = 0x43; codeSiteCode[ptr++] = 0x10; // end put new patch in place // mov rax,[rbp+16] codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x8B; codeSiteCode[ptr++] = 0x45; codeSiteCode[ptr++] = 0x10; // pop rdx codeSiteCode[ptr++] = 0x5A; // pop rcx codeSiteCode[ptr++] = 0x59; // pop rbx codeSiteCode[ptr++] = 0x5B; // pop flags codeSiteCode[ptr++] = 0x9D; // pop rbp codeSiteCode[ptr++] = 0x5D; // ret 8 codeSiteCode[ptr++] = 0xC2; codeSiteCode[ptr++] = 0x08; codeSiteCode[ptr++] = 0x00; codeSiteCode[callPastEndCodeImmediate] = (byte)(ptr - callPastEndCodeBaseOffset); // imul rcx,rcx,constant codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x6B; codeSiteCode[ptr++] = 0xC9; // = size of restore function var multiplyImmediate = ptr; codeSiteCode[ptr++] = 0x00; // lea rcx, rcx+eax+constant codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x8D; codeSiteCode[ptr++] = 0x4C; codeSiteCode[ptr++] = 0x08; var leaRcxConstant = ptr; codeSiteCode[ptr++] = 0x00; // jmp rcx codeSiteCode[ptr++] = 0xFF; codeSiteCode[ptr++] = 0xE1; codeSiteCode[leaRcxConstant] = (byte)(ptr - smcLastRip); // restore function uint offset = 0; var lastReturn = 0; foreach (var instr in decodedInstructions.Select((value, index) => new { Value = value, Index = index })) { var startOfRestore = ptr; Console.WriteLine($"instruction {instr.Value.Mnemonic}, size: {instr.Value.Size}"); // reserve space for call site restore 1 // mov rax, constant codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0xB8; var originalOffset = 0; var patchOffset = 0; foreach (var b in originalCode.Skip((int)offset).Take(8)) { if (originalOffset < instr.Value.Size) { codeSiteCode[ptr++] = b; } else { codeSiteCode[ptr++] = patchSiteCode[patchOffset++]; } originalOffset++; } // restore call site 1 // mov [rbx], rax codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x89; codeSiteCode[ptr++] = 0x03; // reserve space for call site restore 2 // mov rax, constant codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0xB8; foreach (var b in originalCode.Skip((int)offset + 8).Take(8)) { if (originalOffset < instr.Value.Size) { codeSiteCode[ptr++] = b; } else { codeSiteCode[ptr++] = patchSiteCode[patchOffset++]; } originalOffset++; } // restore call site 2 // mov [rbx+8], rax codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x89; codeSiteCode[ptr++] = 0x43; codeSiteCode[ptr++] = 0x08; // reserve space for call site restore 3 // mov rax, constant codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0xB8; foreach (var b in originalCode.Skip((int)offset + 16).Take(8)) { if (originalOffset < instr.Value.Size) { codeSiteCode[ptr++] = b; } else { codeSiteCode[ptr++] = patchSiteCode[patchOffset++]; } originalOffset++; } // restore call site 3 // mov [rbx+16], rax codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x89; codeSiteCode[ptr++] = 0x43; codeSiteCode[ptr++] = 0x10; // prepare rbx to put new patch in place // add rbx,instrSize codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0x83; codeSiteCode[ptr++] = 0xC3; codeSiteCode[ptr++] = (byte)(instr.Value.Size); // return lastReturn = ptr; codeSiteCode[ptr++] = 0xC3; var endOfRestore = ptr; Console.WriteLine($"mul: {endOfRestore - startOfRestore}"); codeSiteCode[multiplyImmediate] = (byte)(endOfRestore - startOfRestore); offset += instr.Value.Size; } ptr = lastReturn; // we set rbx to bogus so that patch is not written for last instruction in set // mov rbx, constant codeSiteCode[ptr++] = 0x48; codeSiteCode[ptr++] = 0xBB; foreach (var b in BitConverter.GetBytes((ulong)bogusAddress)) { codeSiteCode[ptr++] = b; } // return codeSiteCode[ptr] = 0xC3; DebugProcessUtils.WriteBytes(process, (ulong)codeAddress, codeSiteCode); DebugProcessUtils.WriteBytes(process, patchSite, patchSiteCode); }