Example #1
0
        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);
        }
Example #2
0
        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}");
            }
        }
Example #3
0
        public static void DoIt()
        {
            var name    = Specifics.ProcessName;
            var address = Specifics.StartAddress;
            var process = DebugProcessUtils.GetFirstProcessByName(name);

            using (Form form = new Form()) {
                form.Text = "Inspector";
                form.Size = new Size(600, 1080);

                var table         = new DataGridView();
                var bindingSource = new BindingSource();
                table.DataSource = bindingSource;

                var infoTable  = new DataGridView();
                var infoSource = new BindingSource();
                infoTable.DataSource = infoSource;

                var formClosed      = false;
                var cleanupFinished = false;

                var splitter = new Splitter();

                infoTable.Dock = DockStyle.Left;
                splitter.Dock  = DockStyle.Left;
                table.Dock     = DockStyle.Fill;
                form.Controls.AddRange(new Control[] { table, splitter, infoTable });

                Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) {
                    e.Cancel = true;
                    //form.Close();
                };

                var progress = new Progress <List <ContextManager.ThreadData> >(
                    (contexts) => {
                    AddContexts(bindingSource, contexts);
                }
                    );

                IProgress <ContextManager.Info> infoProgress = new Progress <ContextManager.Info>(
                    (info) => {
                    UpdateInfo(infoSource, info);
                }
                    );

                var specifics = new Specifics();

                Task.Factory.StartNew(
                    () => {
                    var logName  = Specifics.LogName;
                    var logName2 = Specifics.LogNameLatest;
                    using (var logFile = new Logger(logName, logName2)) {
                        ContextManager cg = null;
                        logFile.WriteLine("");
                        logFile.WriteLine("STARTING INSPECTOR");
                        try {
                            var importResolver = new ImportResolver(process);
                            importResolver.DumpDebug();
                            var logContext = new ContextTracer(importResolver);
                            var logFile2   = logFile;
                            cg             = new ContextManager(name, logFile, specifics, (cm, threadId, context, trace) => {
                                Debug.Assert(logFile2 != null, "logFile2 != null");
                                return(logContext.Log(cm, logFile2, process, threadId, context, trace));
                            }, importResolver);

                            //if (File.Exists(Specifics.appStateFileName)) {
                            //SaveAndRestore.Restore(Specifics.appStateFileName, cg);
                            //} else {
                            cg.EnableBreakPoint(importResolver.ResolveRelativeAddress(address), new ContextManager.BreakPointInfo {
                                Description = "starting breakpoint"
                            });
                            //}
                            cg.AntiAntiDebug();

                            /*try {
                             *  cg.InstallBreakPoint(address);
                             * } catch (InvalidOperationException e) {
                             *  Console.WriteLine($"Failed to install break points: {e.ToString()}");
                             * }*/

                            while (!formClosed)
                            {
                                logFile.WriteLine("main debugger loop");
                                cg.CurrentProcess = DebugProcessUtils.GetFirstProcessByName(cg.CurrentProcess.ProcessName);
                                cg.TestBreak();
                                Update(cg, progress);
                                infoProgress.Report(cg.CurrentInfo);
                                SaveAndRestore.Save(Specifics.AppStateFileName, cg);
                                Task.Delay(Specifics.MainLoopDelay).Wait();
                            }
                        }
                        catch (Exception e) {
                            Console.WriteLine($"Exception: {e.Message}");
                            Console.WriteLine(e.StackTrace);
                            logFile.WriteLine($"Exception: {e.Message}");
                            logFile.WriteLine(e.StackTrace);
                        }
                        // cleanup
                        Console.WriteLine("cleaning up");
                        SaveAndRestore.Save(Specifics.AppStateFileName, cg);
                        cg?.Stop();
                        cleanupFinished = true;
                    }
                },
                    TaskCreationOptions.LongRunning
                    );

                /*Task.Factory.StartNew(
                 *  () => {
                 *      while (true) {
                 *          cg.ResumeEvents();
                 *      }
                 *  },
                 *  TaskCreationOptions.LongRunning
                 * );*/

                form.FormClosing += (sender, e) => {
                    Console.WriteLine("form closing");
                    formClosed = true;
                    while (!cleanupFinished)
                    {
                        Console.WriteLine("waiting for cleanup");
                        Task.Delay(1000).Wait();
                    }
                };

                form.ShowDialog();
            }
        }
Example #4
0
        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();
        }
Example #5
0
        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);
        }
Example #6
0
        // 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");
                        }
                    }
                }
            }
        }