diff --git a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp index cdb219e9..c82097b8 100644 --- a/examples/user/hyperdbg_app/code/hyperdbg-app.cpp +++ b/examples/user/hyperdbg_app/code/hyperdbg-app.cpp @@ -1,18 +1,5 @@ #include "pch.h" -#include -#include -#include -#include "../dependencies/libipt/intel-pt.h" -#include - -#pragma comment(lib, "dbghelp.lib") - -static UINT64 g_ImageBase = 0; -static UINT64 g_CodeBase = 0; -static UINT64 g_CodeSize = 0; -static UINT8 * g_Code = NULL; - static int ShowMessages(const char * Text) { @@ -21,482 +8,7 @@ ShowMessages(const char * Text) } static int -ReadImage(uint8_t * Buffer, size_t Size, const struct pt_asid * Asid, uint64_t Ip, void * Context) -{ - (void)Asid; - (void)Context; - - if (g_Code == NULL || Ip < g_CodeBase || Ip >= g_CodeBase + g_CodeSize) - return -pte_nomap; - - uint64_t Available = g_CodeBase + g_CodeSize - Ip; - size_t Count = (Size < Available) ? Size : (size_t)Available; - - memcpy(Buffer, g_Code + (Ip - g_CodeBase), Count); - return (int)Count; -} - -typedef struct _PROC_BASIC_INFO -{ - LONG ExitStatus; - PVOID PebBaseAddress; - ULONG_PTR Reserved[4]; -} PROC_BASIC_INFO; - -typedef LONG(NTAPI * PFN_NT_QIP)(HANDLE, ULONG, PVOID, ULONG, PULONG); - -static BOOLEAN -CaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd) -{ - HMODULE Ntdll = GetModuleHandleA("ntdll.dll"); - PFN_NT_QIP NtQip = Ntdll ? (PFN_NT_QIP)GetProcAddress(Ntdll, "NtQueryInformationProcess") : NULL; - PROC_BASIC_INFO Pbi = {0}; - ULONG Ret = 0; - SIZE_T Got = 0; - UINT64 Base = 0; - IMAGE_DOS_HEADER Dos; - IMAGE_NT_HEADERS64 Nt; - UINT64 SectionBase; - - if (NtQip == NULL || NtQip(Process, 0, &Pbi, sizeof(Pbi), &Ret) < 0 || Pbi.PebBaseAddress == NULL) - return FALSE; - - if (!ReadProcessMemory(Process, (PBYTE)Pbi.PebBaseAddress + 0x10, &Base, sizeof(Base), &Got) || Base == 0) - return FALSE; - - if (!ReadProcessMemory(Process, (PVOID)Base, &Dos, sizeof(Dos), &Got) || Dos.e_magic != IMAGE_DOS_SIGNATURE) - return FALSE; - - if (!ReadProcessMemory(Process, (PBYTE)Base + Dos.e_lfanew, &Nt, sizeof(Nt), &Got) || Nt.Signature != IMAGE_NT_SIGNATURE) - return FALSE; - - g_ImageBase = Base; - SectionBase = Base + Dos.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + Nt.FileHeader.SizeOfOptionalHeader; - - for (WORD i = 0; i < Nt.FileHeader.NumberOfSections; i++) - { - IMAGE_SECTION_HEADER Section; - - if (!ReadProcessMemory(Process, (PBYTE)SectionBase + (UINT64)i * sizeof(Section), &Section, sizeof(Section), &Got)) - return FALSE; - - if (memcmp(Section.Name, ".text", 6) != 0) - continue; - - UINT64 Start = Base + Section.VirtualAddress; - UINT64 Size = Section.Misc.VirtualSize ? Section.Misc.VirtualSize : Section.SizeOfRawData; - - if (Size == 0) - return FALSE; - - g_Code = (UINT8 *)malloc((size_t)Size); - if (g_Code == NULL) - return FALSE; - - if (!ReadProcessMemory(Process, (PVOID)Start, g_Code, (SIZE_T)Size, &Got) || Got != Size) - { - free(g_Code); - g_Code = NULL; - return FALSE; - } - - g_CodeBase = Start; - g_CodeSize = Size; - *TextStart = Start; - *TextEnd = Start + Size - 1; - return TRUE; - } - - return FALSE; -} - -static BOOLEAN -ResolveFunction(HANDLE Process, const char * Path, const char * Name, UINT64 * Start, UINT64 * End) -{ - union - { - SYMBOL_INFO Info; - BYTE Buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; - } Symbol = {0}; - BOOLEAN Ok = FALSE; - - SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); - if (!SymInitialize(Process, NULL, FALSE)) - return FALSE; - - if (SymLoadModuleEx(Process, NULL, Path, NULL, (DWORD64)g_ImageBase, 0, NULL, 0) != 0) - { - Symbol.Info.SizeOfStruct = sizeof(SYMBOL_INFO); - Symbol.Info.MaxNameLen = MAX_SYM_NAME; - - if (SymFromName(Process, Name, &Symbol.Info) && Symbol.Info.Address != 0) - { - *Start = Symbol.Info.Address; - *End = Symbol.Info.Address + (Symbol.Info.Size ? Symbol.Info.Size : 0x200) - 1; - Ok = TRUE; - } - } - - SymCleanup(Process); - return Ok; -} - -static BOOLEAN -PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE Type) -{ - HYPERTRACE_PT_OPERATION_PACKETS Op = {}; - Op.PtOperationType = Type; - return hyperdbg_u_pt_operation(&Op); -} - -static BOOLEAN -PtFilter(UINT32 ProcessId, UINT64 Start, UINT64 End) -{ - HYPERTRACE_PT_OPERATION_PACKETS Op = {}; - - Op.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER; - Op.TraceUser = 1; - Op.TargetProcessId = ProcessId; - - if (End > Start) - { - Op.NumAddrRanges = 1; - Op.AddrRanges[0].Start = Start; - Op.AddrRanges[0].End = End; - } - - if (!hyperdbg_u_pt_operation(&Op)) - return FALSE; - - printf("[+] PT filter: cr3=0x%llx traceuser=%u ranges=%u buffer=0x%llx\n", - (unsigned long long)Op.TargetCr3, - Op.TraceUser, - Op.NumAddrRanges, - (unsigned long long)Op.BufferSize); - return TRUE; -} - -static const char * -PacketName(enum pt_packet_type Type) -{ - switch (Type) - { - case ppt_psb: return "PSB"; case ppt_psbend: return "PSBEND"; case ppt_pad: return "PAD"; - case ppt_fup: return "FUP"; case ppt_tip: return "TIP"; case ppt_tip_pge: return "TIP.PGE"; - case ppt_tip_pgd: return "TIP.PGD"; case ppt_tnt_8: return "TNT8"; case ppt_tnt_64: return "TNT64"; - case ppt_mode: return "MODE"; case ppt_pip: return "PIP"; case ppt_vmcs: return "VMCS"; - case ppt_cbr: return "CBR"; case ppt_tsc: return "TSC"; case ppt_tma: return "TMA"; - case ppt_mtc: return "MTC"; case ppt_cyc: return "CYC"; case ppt_ovf: return "OVF"; - case ppt_stop: return "STOP"; case ppt_exstop: return "EXSTOP"; case ppt_mnt: return "MNT"; - case ppt_ptw: return "PTW"; default: return "?"; - } -} - -static uint64_t -ReconstructIp(const struct pt_packet_ip * Packet, uint64_t * LastIp) -{ - uint64_t Value = *LastIp; - - switch (Packet->ipc) - { - case pt_ipc_update_16: Value = (Value & ~0xffffull) | (Packet->ip & 0xffffull); break; - case pt_ipc_update_32: Value = (Value & ~0xffffffffull) | (Packet->ip & 0xffffffffull); break; - case pt_ipc_update_48: Value = (Value & ~0xffffffffffffull) | (Packet->ip & 0xffffffffffffull); break; - case pt_ipc_sext_48: - Value = Packet->ip & 0xffffffffffffull; - if (Value & 0x800000000000ull) - Value |= 0xffff000000000000ull; - break; - default: Value = Packet->ip; break; - } - - *LastIp = Value; - return Value; -} - -static UINT64 -DecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) -{ - struct pt_config Config; - struct pt_packet_decoder * Decoder; - UINT64 Count = 0; - uint64_t LastIp = 0; - int Status; - - pt_config_init(&Config); - Config.begin = (uint8_t *)Buffer; - Config.end = (uint8_t *)Buffer + Size; - - Decoder = pt_pkt_alloc_decoder(&Config); - if (Decoder == NULL) - { - printf("[-] core %u: cannot allocate packet decoder\n", Cpu); - return 0; - } - - for (;;) - { - Status = pt_pkt_sync_forward(Decoder); - if (Status < 0) - break; - - for (;;) - { - struct pt_packet Packet; - - Status = pt_pkt_next(Decoder, &Packet, sizeof(Packet)); - if (Status < 0) - break; - - Count++; - - switch (Packet.type) - { - case ppt_tnt_8: - case ppt_tnt_64: - printf(" %-8s %2u ", PacketName(Packet.type), Packet.payload.tnt.bit_size); - for (uint8_t Bit = 0; Bit < Packet.payload.tnt.bit_size && Bit < 64; Bit++) - putchar(((Packet.payload.tnt.payload >> (Packet.payload.tnt.bit_size - 1 - Bit)) & 1) ? 'T' : 'N'); - putchar('\n'); - break; - - case ppt_tip: - case ppt_fup: - case ppt_tip_pge: - case ppt_tip_pgd: - if (Packet.payload.ip.ipc == pt_ipc_suppressed) - printf(" %-8s (ip suppressed)\n", PacketName(Packet.type)); - else - { - uint64_t Ip = ReconstructIp(&Packet.payload.ip, &LastIp); - printf(" %-8s 0x%016llx exe+0x%llx\n", - PacketName(Packet.type), (unsigned long long)Ip, (unsigned long long)(Ip - g_ImageBase)); - } - break; - - case ppt_pip: - printf(" %-8s cr3=0x%llx\n", PacketName(Packet.type), (unsigned long long)Packet.payload.pip.cr3); - break; - - case ppt_cbr: - //printf(" %-8s ratio=%u\n", PacketName(Packet.type), Packet.payload.cbr.ratio); - break; - - case ppt_tsc: - printf(" %-8s tsc=0x%llx\n", PacketName(Packet.type), (unsigned long long)Packet.payload.tsc.tsc); - break; - - default: - //printf(" %-8s\n", PacketName(Packet.type)); - break; - } - } - } - - pt_pkt_free_decoder(Decoder); - return Count; -} - -static UINT64 -DecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) -{ - struct pt_config Config; - struct pt_insn_decoder * Decoder; - struct pt_image * Image; - UINT64 Count = 0; - int Status; - - pt_config_init(&Config); - Config.begin = (uint8_t *)Buffer; - Config.end = (uint8_t *)Buffer + Size; - - Decoder = pt_insn_alloc_decoder(&Config); - if (Decoder == NULL) - { - printf("[-] core %u: cannot allocate instruction decoder\n", Cpu); - return 0; - } - - Image = pt_insn_get_image(Decoder); - pt_image_set_callback(Image, ReadImage, NULL); - - for (;;) - { - Status = pt_insn_sync_forward(Decoder); - if (Status < 0) - break; - - for (;;) - { - struct pt_insn Insn; - - while (Status & pts_event_pending) - { - struct pt_event Event; - Status = pt_insn_event(Decoder, &Event, sizeof(Event)); - if (Status < 0) - break; - } - - if (Status < 0 || (Status & pts_eos)) - break; - - Status = pt_insn_next(Decoder, &Insn, sizeof(Insn)); - if (Status < 0) - break; - - ZydisDisassembledInstruction Disasm; - ZydisMachineMode Mode = (Insn.mode == ptem_32bit) ? ZYDIS_MACHINE_MODE_LEGACY_32 : ZYDIS_MACHINE_MODE_LONG_64; - - if (ZYAN_SUCCESS(ZydisDisassembleIntel(Mode, Insn.ip, Insn.raw, Insn.size, &Disasm))) - printf(" 0x%016llx exe+0x%-6llx %s\n", - (unsigned long long)Insn.ip, - (unsigned long long)(Insn.ip - g_ImageBase), - Disasm.text); - else - printf(" 0x%016llx (undecodable)\n", (unsigned long long)Insn.ip); - - Count++; - } - - if (Status >= 0 && (Status & pts_eos)) - break; - } - - pt_insn_free_decoder(Decoder); - return Count; -} - -static void -RunAndTrace(const char * Path, const char * Function, BOOLEAN Packets, int PinCore) -{ - STARTUPINFOA Startup = {0}; - PROCESS_INFORMATION Process = {0}; - HYPERTRACE_PT_MMAP_PACKETS Mmap = {0}; - HYPERTRACE_PT_OPERATION_PACKETS Sizes = {}; - UINT64 TextStart = 0; - UINT64 TextEnd = 0; - UINT64 FilterStart = 0; - UINT64 FilterEnd = 0; - UINT64 Total = 0; - - Startup.cb = sizeof(Startup); - - if (!CreateProcessA(Path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &Startup, &Process)) - { - printf("[-] cannot launch '%s' (error 0x%x)\n", Path, GetLastError()); - return; - } - - printf("[+] launched '%s' (pid %u, suspended)\n", Path, Process.dwProcessId); - - if (PinCore >= 0) - { - DWORD_PTR Mask = (DWORD_PTR)1 << PinCore; - if (SetProcessAffinityMask(Process.hProcess, Mask)) - printf("[+] pinned target to core %d (all trace should land on this core)\n", PinCore); - else - printf("[!] could not pin to core %d (error 0x%x); running unpinned\n", PinCore, GetLastError()); - } - else - { - printf("[*] target unpinned (scheduler may migrate it across cores)\n"); - } - - if (!CaptureImage(Process.hProcess, &TextStart, &TextEnd)) - { - printf("[-] cannot read target image / .text section\n"); - TerminateProcess(Process.hProcess, 1); - goto Cleanup; - } - - printf("[+] image base 0x%llx, .text 0x%llx-0x%llx (%llu bytes)\n", - (unsigned long long)g_ImageBase, - (unsigned long long)TextStart, - (unsigned long long)TextEnd, - (unsigned long long)g_CodeSize); - - FilterStart = TextStart; - FilterEnd = TextEnd; - - if (Function != NULL && ResolveFunction(Process.hProcess, Path, Function, &FilterStart, &FilterEnd)) - printf("[+] IP filter narrowed to '%s' 0x%llx-0x%llx (%llu bytes)\n", - Function, - (unsigned long long)FilterStart, - (unsigned long long)FilterEnd, - (unsigned long long)(FilterEnd - FilterStart + 1)); - else - printf("[!] IP filter: whole .text (symbol '%s' not found - build the target with a PDB)\n", - Function ? Function : "(none)"); - - if (!PtFilter(Process.dwProcessId, FilterStart, FilterEnd) || - !PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE)) - { - printf("[-] cannot enable Intel PT\n"); - TerminateProcess(Process.hProcess, 1); - goto Cleanup; - } - - if (!hyperdbg_u_pt_mmap(&Mmap)) - { - printf("[-] pt_mmap failed\n"); - PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); - TerminateProcess(Process.hProcess, 1); - goto Cleanup; - } - - printf("[+] PT enabled, %u per-core buffers mapped\n", Mmap.NumCpus); - printf("[*] resuming target and waiting for it to exit...\n"); - - ResumeThread(Process.hThread); - WaitForSingleObject(Process.hProcess, INFINITE); - printf("[+] target exited, decoding trace\n"); - - PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE); - - Sizes.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE; - if (!hyperdbg_u_pt_operation(&Sizes)) - { - printf("[-] cannot query PT sizes\n"); - PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); - goto Cleanup; - } - - for (UINT32 i = 0; i < Mmap.NumCpus; i++) - { - UINT32 Cpu = Mmap.Cpus[i].CpuId; - UINT64 Bytes = (Cpu < Sizes.NumCpus) ? Sizes.BytesPerCpu[Cpu] : 0; - - if (Bytes == 0) - continue; - - if (Bytes > Mmap.Cpus[i].Size) - Bytes = Mmap.Cpus[i].Size; - - printf("\n[*] core %u: %llu bytes of trace\n", Cpu, (unsigned long long)Bytes); - Total += Packets - ? DecodeCorePackets(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes) - : DecodeCore(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes); - } - - printf("\n[+] decoded %llu %s total\n", (unsigned long long)Total, Packets ? "packet(s)" : "instruction(s)"); - - PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); - -Cleanup: - if (g_Code != NULL) - { - free(g_Code); - g_Code = NULL; - } - if (Process.hThread != NULL) - CloseHandle(Process.hThread); - if (Process.hProcess != NULL) - CloseHandle(Process.hProcess); -} - -static int -LoadVmmAndTrace() +LoadVmm() { hyperdbg_u_set_text_message_callback((PVOID)ShowMessages); @@ -515,52 +27,18 @@ LoadVmmAndTrace() printf("[+] HyperDbg VMM is running\n"); - printf("[*] loading HyperTrace...\n"); - - if ( hyperdbg_u_load_hypertrace_module() == 1) - { - printf("[-] cannot load the HyperDbg HyperTrace\n"); - return 1; - } - - printf("[+] HyperDbg HyperTrace is running\n"); - return 0; } int -main2(int argc, char ** argv) +main(int argc, char ** argv) { - const char * function = "main"; - BOOLEAN packets = FALSE; - int pinCore = 0; - - if (argc < 2) + if (LoadVmm() != 0) { - printf("HyperDbg Intel PT tracer\n"); - printf("usage: %s [function] [-p] [-c core]\n", argv[0]); - printf(" [function] symbol to IP-filter (default 'main'; pass '*' for whole .text)\n"); - printf(" -p dump raw PT packets (TNT/TIP/FUP/PSB/...) instead of instructions\n"); - printf(" -c core pin the target to this logical core (default 0; -1 = unpinned)\n"); return 1; } - for (int i = 2; i < argc; i++) - { - if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--packets") == 0) - packets = TRUE; - else if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) - pinCore = atoi(argv[++i]); - else if (strcmp(argv[i], "*") == 0) - function = NULL; - else - function = argv[i]; - } - - if (LoadVmmAndTrace() != 0) - return 1; - - RunAndTrace(argv[1], function, packets, pinCore); + hyperdbg_u_run_command((CHAR*)"lm"); printf("[*] unloading HyperDbg VMM...\n"); diff --git a/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp b/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp index c82097b8..cdb219e9 100644 --- a/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp +++ b/examples/user/hyperdbg_app/code/hyperdbg-ipt.cpp @@ -1,5 +1,18 @@ #include "pch.h" +#include +#include +#include +#include "../dependencies/libipt/intel-pt.h" +#include + +#pragma comment(lib, "dbghelp.lib") + +static UINT64 g_ImageBase = 0; +static UINT64 g_CodeBase = 0; +static UINT64 g_CodeSize = 0; +static UINT8 * g_Code = NULL; + static int ShowMessages(const char * Text) { @@ -8,7 +21,482 @@ ShowMessages(const char * Text) } static int -LoadVmm() +ReadImage(uint8_t * Buffer, size_t Size, const struct pt_asid * Asid, uint64_t Ip, void * Context) +{ + (void)Asid; + (void)Context; + + if (g_Code == NULL || Ip < g_CodeBase || Ip >= g_CodeBase + g_CodeSize) + return -pte_nomap; + + uint64_t Available = g_CodeBase + g_CodeSize - Ip; + size_t Count = (Size < Available) ? Size : (size_t)Available; + + memcpy(Buffer, g_Code + (Ip - g_CodeBase), Count); + return (int)Count; +} + +typedef struct _PROC_BASIC_INFO +{ + LONG ExitStatus; + PVOID PebBaseAddress; + ULONG_PTR Reserved[4]; +} PROC_BASIC_INFO; + +typedef LONG(NTAPI * PFN_NT_QIP)(HANDLE, ULONG, PVOID, ULONG, PULONG); + +static BOOLEAN +CaptureImage(HANDLE Process, UINT64 * TextStart, UINT64 * TextEnd) +{ + HMODULE Ntdll = GetModuleHandleA("ntdll.dll"); + PFN_NT_QIP NtQip = Ntdll ? (PFN_NT_QIP)GetProcAddress(Ntdll, "NtQueryInformationProcess") : NULL; + PROC_BASIC_INFO Pbi = {0}; + ULONG Ret = 0; + SIZE_T Got = 0; + UINT64 Base = 0; + IMAGE_DOS_HEADER Dos; + IMAGE_NT_HEADERS64 Nt; + UINT64 SectionBase; + + if (NtQip == NULL || NtQip(Process, 0, &Pbi, sizeof(Pbi), &Ret) < 0 || Pbi.PebBaseAddress == NULL) + return FALSE; + + if (!ReadProcessMemory(Process, (PBYTE)Pbi.PebBaseAddress + 0x10, &Base, sizeof(Base), &Got) || Base == 0) + return FALSE; + + if (!ReadProcessMemory(Process, (PVOID)Base, &Dos, sizeof(Dos), &Got) || Dos.e_magic != IMAGE_DOS_SIGNATURE) + return FALSE; + + if (!ReadProcessMemory(Process, (PBYTE)Base + Dos.e_lfanew, &Nt, sizeof(Nt), &Got) || Nt.Signature != IMAGE_NT_SIGNATURE) + return FALSE; + + g_ImageBase = Base; + SectionBase = Base + Dos.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader) + Nt.FileHeader.SizeOfOptionalHeader; + + for (WORD i = 0; i < Nt.FileHeader.NumberOfSections; i++) + { + IMAGE_SECTION_HEADER Section; + + if (!ReadProcessMemory(Process, (PBYTE)SectionBase + (UINT64)i * sizeof(Section), &Section, sizeof(Section), &Got)) + return FALSE; + + if (memcmp(Section.Name, ".text", 6) != 0) + continue; + + UINT64 Start = Base + Section.VirtualAddress; + UINT64 Size = Section.Misc.VirtualSize ? Section.Misc.VirtualSize : Section.SizeOfRawData; + + if (Size == 0) + return FALSE; + + g_Code = (UINT8 *)malloc((size_t)Size); + if (g_Code == NULL) + return FALSE; + + if (!ReadProcessMemory(Process, (PVOID)Start, g_Code, (SIZE_T)Size, &Got) || Got != Size) + { + free(g_Code); + g_Code = NULL; + return FALSE; + } + + g_CodeBase = Start; + g_CodeSize = Size; + *TextStart = Start; + *TextEnd = Start + Size - 1; + return TRUE; + } + + return FALSE; +} + +static BOOLEAN +ResolveFunction(HANDLE Process, const char * Path, const char * Name, UINT64 * Start, UINT64 * End) +{ + union + { + SYMBOL_INFO Info; + BYTE Buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + } Symbol = {0}; + BOOLEAN Ok = FALSE; + + SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS); + if (!SymInitialize(Process, NULL, FALSE)) + return FALSE; + + if (SymLoadModuleEx(Process, NULL, Path, NULL, (DWORD64)g_ImageBase, 0, NULL, 0) != 0) + { + Symbol.Info.SizeOfStruct = sizeof(SYMBOL_INFO); + Symbol.Info.MaxNameLen = MAX_SYM_NAME; + + if (SymFromName(Process, Name, &Symbol.Info) && Symbol.Info.Address != 0) + { + *Start = Symbol.Info.Address; + *End = Symbol.Info.Address + (Symbol.Info.Size ? Symbol.Info.Size : 0x200) - 1; + Ok = TRUE; + } + } + + SymCleanup(Process); + return Ok; +} + +static BOOLEAN +PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE Type) +{ + HYPERTRACE_PT_OPERATION_PACKETS Op = {}; + Op.PtOperationType = Type; + return hyperdbg_u_pt_operation(&Op); +} + +static BOOLEAN +PtFilter(UINT32 ProcessId, UINT64 Start, UINT64 End) +{ + HYPERTRACE_PT_OPERATION_PACKETS Op = {}; + + Op.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_FILTER; + Op.TraceUser = 1; + Op.TargetProcessId = ProcessId; + + if (End > Start) + { + Op.NumAddrRanges = 1; + Op.AddrRanges[0].Start = Start; + Op.AddrRanges[0].End = End; + } + + if (!hyperdbg_u_pt_operation(&Op)) + return FALSE; + + printf("[+] PT filter: cr3=0x%llx traceuser=%u ranges=%u buffer=0x%llx\n", + (unsigned long long)Op.TargetCr3, + Op.TraceUser, + Op.NumAddrRanges, + (unsigned long long)Op.BufferSize); + return TRUE; +} + +static const char * +PacketName(enum pt_packet_type Type) +{ + switch (Type) + { + case ppt_psb: return "PSB"; case ppt_psbend: return "PSBEND"; case ppt_pad: return "PAD"; + case ppt_fup: return "FUP"; case ppt_tip: return "TIP"; case ppt_tip_pge: return "TIP.PGE"; + case ppt_tip_pgd: return "TIP.PGD"; case ppt_tnt_8: return "TNT8"; case ppt_tnt_64: return "TNT64"; + case ppt_mode: return "MODE"; case ppt_pip: return "PIP"; case ppt_vmcs: return "VMCS"; + case ppt_cbr: return "CBR"; case ppt_tsc: return "TSC"; case ppt_tma: return "TMA"; + case ppt_mtc: return "MTC"; case ppt_cyc: return "CYC"; case ppt_ovf: return "OVF"; + case ppt_stop: return "STOP"; case ppt_exstop: return "EXSTOP"; case ppt_mnt: return "MNT"; + case ppt_ptw: return "PTW"; default: return "?"; + } +} + +static uint64_t +ReconstructIp(const struct pt_packet_ip * Packet, uint64_t * LastIp) +{ + uint64_t Value = *LastIp; + + switch (Packet->ipc) + { + case pt_ipc_update_16: Value = (Value & ~0xffffull) | (Packet->ip & 0xffffull); break; + case pt_ipc_update_32: Value = (Value & ~0xffffffffull) | (Packet->ip & 0xffffffffull); break; + case pt_ipc_update_48: Value = (Value & ~0xffffffffffffull) | (Packet->ip & 0xffffffffffffull); break; + case pt_ipc_sext_48: + Value = Packet->ip & 0xffffffffffffull; + if (Value & 0x800000000000ull) + Value |= 0xffff000000000000ull; + break; + default: Value = Packet->ip; break; + } + + *LastIp = Value; + return Value; +} + +static UINT64 +DecodeCorePackets(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) +{ + struct pt_config Config; + struct pt_packet_decoder * Decoder; + UINT64 Count = 0; + uint64_t LastIp = 0; + int Status; + + pt_config_init(&Config); + Config.begin = (uint8_t *)Buffer; + Config.end = (uint8_t *)Buffer + Size; + + Decoder = pt_pkt_alloc_decoder(&Config); + if (Decoder == NULL) + { + printf("[-] core %u: cannot allocate packet decoder\n", Cpu); + return 0; + } + + for (;;) + { + Status = pt_pkt_sync_forward(Decoder); + if (Status < 0) + break; + + for (;;) + { + struct pt_packet Packet; + + Status = pt_pkt_next(Decoder, &Packet, sizeof(Packet)); + if (Status < 0) + break; + + Count++; + + switch (Packet.type) + { + case ppt_tnt_8: + case ppt_tnt_64: + printf(" %-8s %2u ", PacketName(Packet.type), Packet.payload.tnt.bit_size); + for (uint8_t Bit = 0; Bit < Packet.payload.tnt.bit_size && Bit < 64; Bit++) + putchar(((Packet.payload.tnt.payload >> (Packet.payload.tnt.bit_size - 1 - Bit)) & 1) ? 'T' : 'N'); + putchar('\n'); + break; + + case ppt_tip: + case ppt_fup: + case ppt_tip_pge: + case ppt_tip_pgd: + if (Packet.payload.ip.ipc == pt_ipc_suppressed) + printf(" %-8s (ip suppressed)\n", PacketName(Packet.type)); + else + { + uint64_t Ip = ReconstructIp(&Packet.payload.ip, &LastIp); + printf(" %-8s 0x%016llx exe+0x%llx\n", + PacketName(Packet.type), (unsigned long long)Ip, (unsigned long long)(Ip - g_ImageBase)); + } + break; + + case ppt_pip: + printf(" %-8s cr3=0x%llx\n", PacketName(Packet.type), (unsigned long long)Packet.payload.pip.cr3); + break; + + case ppt_cbr: + //printf(" %-8s ratio=%u\n", PacketName(Packet.type), Packet.payload.cbr.ratio); + break; + + case ppt_tsc: + printf(" %-8s tsc=0x%llx\n", PacketName(Packet.type), (unsigned long long)Packet.payload.tsc.tsc); + break; + + default: + //printf(" %-8s\n", PacketName(Packet.type)); + break; + } + } + } + + pt_pkt_free_decoder(Decoder); + return Count; +} + +static UINT64 +DecodeCore(UINT32 Cpu, const UINT8 * Buffer, UINT64 Size) +{ + struct pt_config Config; + struct pt_insn_decoder * Decoder; + struct pt_image * Image; + UINT64 Count = 0; + int Status; + + pt_config_init(&Config); + Config.begin = (uint8_t *)Buffer; + Config.end = (uint8_t *)Buffer + Size; + + Decoder = pt_insn_alloc_decoder(&Config); + if (Decoder == NULL) + { + printf("[-] core %u: cannot allocate instruction decoder\n", Cpu); + return 0; + } + + Image = pt_insn_get_image(Decoder); + pt_image_set_callback(Image, ReadImage, NULL); + + for (;;) + { + Status = pt_insn_sync_forward(Decoder); + if (Status < 0) + break; + + for (;;) + { + struct pt_insn Insn; + + while (Status & pts_event_pending) + { + struct pt_event Event; + Status = pt_insn_event(Decoder, &Event, sizeof(Event)); + if (Status < 0) + break; + } + + if (Status < 0 || (Status & pts_eos)) + break; + + Status = pt_insn_next(Decoder, &Insn, sizeof(Insn)); + if (Status < 0) + break; + + ZydisDisassembledInstruction Disasm; + ZydisMachineMode Mode = (Insn.mode == ptem_32bit) ? ZYDIS_MACHINE_MODE_LEGACY_32 : ZYDIS_MACHINE_MODE_LONG_64; + + if (ZYAN_SUCCESS(ZydisDisassembleIntel(Mode, Insn.ip, Insn.raw, Insn.size, &Disasm))) + printf(" 0x%016llx exe+0x%-6llx %s\n", + (unsigned long long)Insn.ip, + (unsigned long long)(Insn.ip - g_ImageBase), + Disasm.text); + else + printf(" 0x%016llx (undecodable)\n", (unsigned long long)Insn.ip); + + Count++; + } + + if (Status >= 0 && (Status & pts_eos)) + break; + } + + pt_insn_free_decoder(Decoder); + return Count; +} + +static void +RunAndTrace(const char * Path, const char * Function, BOOLEAN Packets, int PinCore) +{ + STARTUPINFOA Startup = {0}; + PROCESS_INFORMATION Process = {0}; + HYPERTRACE_PT_MMAP_PACKETS Mmap = {0}; + HYPERTRACE_PT_OPERATION_PACKETS Sizes = {}; + UINT64 TextStart = 0; + UINT64 TextEnd = 0; + UINT64 FilterStart = 0; + UINT64 FilterEnd = 0; + UINT64 Total = 0; + + Startup.cb = sizeof(Startup); + + if (!CreateProcessA(Path, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &Startup, &Process)) + { + printf("[-] cannot launch '%s' (error 0x%x)\n", Path, GetLastError()); + return; + } + + printf("[+] launched '%s' (pid %u, suspended)\n", Path, Process.dwProcessId); + + if (PinCore >= 0) + { + DWORD_PTR Mask = (DWORD_PTR)1 << PinCore; + if (SetProcessAffinityMask(Process.hProcess, Mask)) + printf("[+] pinned target to core %d (all trace should land on this core)\n", PinCore); + else + printf("[!] could not pin to core %d (error 0x%x); running unpinned\n", PinCore, GetLastError()); + } + else + { + printf("[*] target unpinned (scheduler may migrate it across cores)\n"); + } + + if (!CaptureImage(Process.hProcess, &TextStart, &TextEnd)) + { + printf("[-] cannot read target image / .text section\n"); + TerminateProcess(Process.hProcess, 1); + goto Cleanup; + } + + printf("[+] image base 0x%llx, .text 0x%llx-0x%llx (%llu bytes)\n", + (unsigned long long)g_ImageBase, + (unsigned long long)TextStart, + (unsigned long long)TextEnd, + (unsigned long long)g_CodeSize); + + FilterStart = TextStart; + FilterEnd = TextEnd; + + if (Function != NULL && ResolveFunction(Process.hProcess, Path, Function, &FilterStart, &FilterEnd)) + printf("[+] IP filter narrowed to '%s' 0x%llx-0x%llx (%llu bytes)\n", + Function, + (unsigned long long)FilterStart, + (unsigned long long)FilterEnd, + (unsigned long long)(FilterEnd - FilterStart + 1)); + else + printf("[!] IP filter: whole .text (symbol '%s' not found - build the target with a PDB)\n", + Function ? Function : "(none)"); + + if (!PtFilter(Process.dwProcessId, FilterStart, FilterEnd) || + !PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_ENABLE)) + { + printf("[-] cannot enable Intel PT\n"); + TerminateProcess(Process.hProcess, 1); + goto Cleanup; + } + + if (!hyperdbg_u_pt_mmap(&Mmap)) + { + printf("[-] pt_mmap failed\n"); + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); + TerminateProcess(Process.hProcess, 1); + goto Cleanup; + } + + printf("[+] PT enabled, %u per-core buffers mapped\n", Mmap.NumCpus); + printf("[*] resuming target and waiting for it to exit...\n"); + + ResumeThread(Process.hThread); + WaitForSingleObject(Process.hProcess, INFINITE); + printf("[+] target exited, decoding trace\n"); + + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_PAUSE); + + Sizes.PtOperationType = HYPERTRACE_PT_OPERATION_REQUEST_TYPE_SIZE; + if (!hyperdbg_u_pt_operation(&Sizes)) + { + printf("[-] cannot query PT sizes\n"); + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); + goto Cleanup; + } + + for (UINT32 i = 0; i < Mmap.NumCpus; i++) + { + UINT32 Cpu = Mmap.Cpus[i].CpuId; + UINT64 Bytes = (Cpu < Sizes.NumCpus) ? Sizes.BytesPerCpu[Cpu] : 0; + + if (Bytes == 0) + continue; + + if (Bytes > Mmap.Cpus[i].Size) + Bytes = Mmap.Cpus[i].Size; + + printf("\n[*] core %u: %llu bytes of trace\n", Cpu, (unsigned long long)Bytes); + Total += Packets + ? DecodeCorePackets(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes) + : DecodeCore(Cpu, (const UINT8 *)(ULONG_PTR)Mmap.Cpus[i].UserVa, Bytes); + } + + printf("\n[+] decoded %llu %s total\n", (unsigned long long)Total, Packets ? "packet(s)" : "instruction(s)"); + + PtOperation(HYPERTRACE_PT_OPERATION_REQUEST_TYPE_DISABLE); + +Cleanup: + if (g_Code != NULL) + { + free(g_Code); + g_Code = NULL; + } + if (Process.hThread != NULL) + CloseHandle(Process.hThread); + if (Process.hProcess != NULL) + CloseHandle(Process.hProcess); +} + +static int +LoadVmmAndTrace() { hyperdbg_u_set_text_message_callback((PVOID)ShowMessages); @@ -27,18 +515,52 @@ LoadVmm() printf("[+] HyperDbg VMM is running\n"); + printf("[*] loading HyperTrace...\n"); + + if ( hyperdbg_u_load_hypertrace_module() == 1) + { + printf("[-] cannot load the HyperDbg HyperTrace\n"); + return 1; + } + + printf("[+] HyperDbg HyperTrace is running\n"); + return 0; } int -main(int argc, char ** argv) +main2(int argc, char ** argv) { - if (LoadVmm() != 0) + const char * function = "main"; + BOOLEAN packets = FALSE; + int pinCore = 0; + + if (argc < 2) { + printf("HyperDbg Intel PT tracer\n"); + printf("usage: %s [function] [-p] [-c core]\n", argv[0]); + printf(" [function] symbol to IP-filter (default 'main'; pass '*' for whole .text)\n"); + printf(" -p dump raw PT packets (TNT/TIP/FUP/PSB/...) instead of instructions\n"); + printf(" -c core pin the target to this logical core (default 0; -1 = unpinned)\n"); return 1; } - hyperdbg_u_run_command((CHAR*)"lm"); + for (int i = 2; i < argc; i++) + { + if (strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--packets") == 0) + packets = TRUE; + else if (strcmp(argv[i], "-c") == 0 && i + 1 < argc) + pinCore = atoi(argv[++i]); + else if (strcmp(argv[i], "*") == 0) + function = NULL; + else + function = argv[i]; + } + + if (LoadVmmAndTrace() != 0) + return 1; + + RunAndTrace(argv[1], function, packets, pinCore); printf("[*] unloading HyperDbg VMM...\n"); diff --git a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj index 0f4ae87e..305d9cc1 100644 --- a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj +++ b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj @@ -15,8 +15,8 @@ - + 16.0 diff --git a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters index 221a1596..d7dd0c26 100644 --- a/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters +++ b/examples/user/hyperdbg_app/hyperdbg_app.vcxproj.filters @@ -20,10 +20,10 @@ - + code - + code