public static void MemMain(Process process, ImportResolver ir, bool setPageExecuteReadWrite = false) { // getting minimum & maximum address SystemInfo sysInfo; GetSystemInfo(out sysInfo); var procMinAddress = sysInfo.minimumApplicationAddress; var procMaxAddress = sysInfo.maximumApplicationAddress; // saving the values as long ints so I won't have to do a lot of casts later var procMinAddressL = (ulong)procMinAddress; var procMaxAddressL = (ulong)procMaxAddress; // opening the process with desired access level // IntPtr processHandle = OpenProcess(ProcessAccessFlags.QueryInformation | ProcessAccessFlags.VirtualMemoryRead, false, process.Id); var processHandle = OpenProcess(ProcessAccessFlags.All, false, process.Id); if ((ulong)processHandle == 0) { throw new Win32Exception(); } var sw = new StreamWriter(Specifics.RawRegionsDumpFileName); // this will store any information we get from VirtualQueryEx() var bytesRead = new IntPtr(0); // number of bytes read with ReadProcessMemory ulong totalBytesRead = 0; // expect 4 gig var progressTotal = new BigInteger(1024 * 1024 * 1024); progressTotal *= 4; BigInteger lastProgress = 0; Console.WriteLine("start scanning"); while (procMinAddressL < procMaxAddressL) { // 28 = sizeof(MEMORY_BASIC_INFORMATION) MemoryBasicInformation memBasicInfo; if ( (ulong) VirtualQueryEx(processHandle, procMinAddress, out memBasicInfo, new IntPtr(Marshal.SizeOf(typeof(MemoryBasicInformation)))) == 0) { throw new Win32Exception(); } if (setPageExecuteReadWrite) { AllocationProtectEnum oldProtection; if ( !VirtualProtectEx(processHandle, memBasicInfo.BaseAddress, new UIntPtr((ulong)memBasicInfo.RegionSize), AllocationProtectEnum.PageExecuteReadwrite, out oldProtection)) { //throw new Win32Exception(); } } var regionStartModule = ir.LookupAddress((ulong)memBasicInfo.BaseAddress); var regionEndModule = ir.LookupAddress((ulong)memBasicInfo.BaseAddress + (ulong)memBasicInfo.RegionSize); var isAccessible = memBasicInfo.Protect.HasFlag(AllocationProtectEnum.PageReadwrite) || memBasicInfo.Protect.HasFlag(AllocationProtectEnum.PageExecuteReadwrite); if (isAccessible && memBasicInfo.State.HasFlag(StateEnum.MemCommit)) { var buffer = new byte[(ulong)memBasicInfo.RegionSize]; // read everything in the buffer above var success = ""; if ( !ReadProcessMemory(processHandle, memBasicInfo.BaseAddress, buffer, memBasicInfo.RegionSize, ref bytesRead)) { success = "false"; } else { totalBytesRead += (ulong)bytesRead; } var regionStart = memBasicInfo.BaseAddress.ToString("X"); var regionEnd = ((ulong)memBasicInfo.BaseAddress + (ulong)memBasicInfo.RegionSize).ToString("X"); sw.WriteLine( $"region 0x{regionStart}({regionStartModule})-0x{regionEnd}({regionEndModule}): size {memBasicInfo.RegionSize}: {success}"); // then output this in the file /*for (int i = 0; i < mem_basic_info.RegionSize; i++) { * //sw.WriteLine("0x{0} : {1}", (mem_basic_info.BaseAddress + i).ToString("X"), (char)buffer[i]); * }*/ } else { var regionStart = memBasicInfo.BaseAddress.ToString("X"); var regionEnd = ((ulong)memBasicInfo.BaseAddress + (ulong)memBasicInfo.RegionSize).ToString("X"); sw.WriteLine( $"NOT READ region 0x{regionStart}({regionStartModule})-0x{regionEnd}({regionEndModule}): size {memBasicInfo.RegionSize}"); } // move to the next memory chunk procMinAddressL += (ulong)memBasicInfo.RegionSize; procMinAddress = new IntPtr((long)procMinAddressL); var progress = new BigInteger(totalBytesRead) * 100 / progressTotal; if (progress != lastProgress) { Console.WriteLine($"scanning memory: estimated {progress}%, totalSize: "); } lastProgress = progress; } Console.WriteLine($"end scanning. total MB: {totalBytesRead/1024/1024}"); sw.Close(); }
// 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 }); }
// 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"); } } } } }