diff --git a/.gitignore b/.gitignore index 2017cc5..18b32c6 100644 --- a/.gitignore +++ b/.gitignore @@ -14,5 +14,6 @@ leechrpc_c.c leechrpc_s.c /files/lib /files/temp +/files/ARM64 /files/x86/lib /leechcorepyc/pkg_linux diff --git a/LeechCore.sln b/LeechCore.sln index 15b70cf..6100fc3 100644 --- a/LeechCore.sln +++ b/LeechCore.sln @@ -17,30 +17,40 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "leechagent", "leechagent\le EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Debug|ARM64.Build.0 = Debug|ARM64 {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Debug|x64.ActiveCfg = Debug|x64 {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Debug|x64.Build.0 = Debug|x64 {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Debug|x86.ActiveCfg = Debug|Win32 {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Debug|x86.Build.0 = Debug|Win32 + {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Release|ARM64.ActiveCfg = Release|ARM64 + {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Release|ARM64.Build.0 = Release|ARM64 {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Release|x64.ActiveCfg = Release|x64 {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Release|x64.Build.0 = Release|x64 {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Release|x86.ActiveCfg = Release|Win32 {3476ABD2-5DEA-43E6-A676-8BE25F74535A}.Release|x86.Build.0 = Release|Win32 + {1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Debug|ARM64.ActiveCfg = Debug|x64 {1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Debug|x64.ActiveCfg = Debug|x64 {1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Debug|x64.Build.0 = Debug|x64 {1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Debug|x86.ActiveCfg = Debug|Win32 + {1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Release|ARM64.ActiveCfg = Release|x64 {1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Release|x64.ActiveCfg = Release|x64 {1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Release|x64.Build.0 = Release|x64 {1DD086DE-3BC9-458F-A2E1-F20142AA4977}.Release|x86.ActiveCfg = Release|Win32 + {931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Debug|ARM64.ActiveCfg = Debug|x64 {931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Debug|x64.ActiveCfg = Debug|x64 {931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Debug|x64.Build.0 = Debug|x64 {931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Debug|x86.ActiveCfg = Debug|Win32 {931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Debug|x86.Build.0 = Debug|Win32 + {931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Release|ARM64.ActiveCfg = Release|x64 {931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Release|x64.ActiveCfg = Release|x64 {931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Release|x64.Build.0 = Release|x64 {931BE9A5-0C7A-4D2F-8E37-7E3010A9979F}.Release|x86.ActiveCfg = Release|Win32 diff --git a/README.md b/README.md index 2ceba45..66864b2 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,11 @@ Please find a summary of the supported software based memory acquisition methods Please find a summary of the supported hardware based memory acquisition methods listed below. All hardware based memory acquisition methods are supported on both Windows and Linux. The FPGA based methods however have a performance penalty on Linux and will max out at approx: 90MB/s compared to 150MB/s on Windows due to less optimized drivers. | Device | Type | Interface | Speed | 64-bit memory access | PCIe TLP access | Plugin | Project
Sponsor | | -------------------------------------------------------------------------------| ---- | --------- | ----- | -------------------- | --------------- | ------ | ------------------ | -| [Screamer PCIe Squirrel](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/PCIeSquirrel) | USB-C | 150MB/s | Yes | Yes | No | 💖 | -| [Enigma X1](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/EnigmaX1) | USB-C | 180MB/s | Yes | Yes | No | 💖 | -| [PCIeScreamerR04](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/ScreamerM2) | USB-C | 150MB/s | Yes | Yes | No | 💖 | -| [ScreamerM2](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/ScreamerM2) | USB3 | 150MB/s | Yes | Yes | No | 💖 | -| [AC701/FT601](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/ac701_ft601) | USB3 | 150MB/s | Yes | Yes | No | | +| [Screamer PCIe Squirrel](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/PCIeSquirrel) | USB-C | 190MB/s | Yes | Yes | No | 💖 | +| [Enigma X1](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/EnigmaX1) | USB-C | 200MB/s | Yes | Yes | No | 💖 | +| [PCIeScreamerR04](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/ScreamerM2) | USB-C | 190MB/s | Yes | Yes | No | 💖 | +| [ScreamerM2](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/ScreamerM2) | USB3 | 190MB/s | Yes | Yes | No | 💖 | +| [AC701/FT601](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/ac701_ft601) | USB3 | 190MB/s | Yes | Yes | No | | | [PCIeScreamer](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/pciescreamer) | USB3 | 100MB/s | Yes | Yes | No | | | [SP605/FT601](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/sp605_ft601) | USB3 | 75MB/s | Yes | Yes | No | | | [Acorn/FT2232H](https://github.com/ufrisk/LeechCore/wiki/Device_FPGA) | [FPGA](https://github.com/ufrisk/pcileech-fpga/tree/master/acorn_ft2232h)| USB2 | 25MB/s | Yes | Yes | No | | @@ -203,3 +203,7 @@ v1.0-1.8 [v2.12](https://github.com/ufrisk/LeechCore/releases/tag/v2.12) * Support for MemProcFS v5. + +[v2.13](https://github.com/ufrisk/LeechCore/releases/tag/v2.13) +* FPGA performance improvements. +* ARM64 Windows support. diff --git a/includes/lib32/leechcore.lib b/includes/lib32/leechcore.lib index cf587cc..ee94b87 100644 Binary files a/includes/lib32/leechcore.lib and b/includes/lib32/leechcore.lib differ diff --git a/includes/lib64/leechcore.lib b/includes/lib64/leechcore.lib index 77b15d1..0b746bc 100644 Binary files a/includes/lib64/leechcore.lib and b/includes/lib64/leechcore.lib differ diff --git a/includes/libarm64/leechcore.lib b/includes/libarm64/leechcore.lib new file mode 100644 index 0000000..c241f37 Binary files /dev/null and b/includes/libarm64/leechcore.lib differ diff --git a/leechagent/version.h b/leechagent/version.h index efebb42..e36b896 100644 --- a/leechagent/version.h +++ b/leechagent/version.h @@ -2,9 +2,9 @@ #define STRINGIZE(s) STRINGIZE2(s) #define VERSION_MAJOR 2 -#define VERSION_MINOR 12 -#define VERSION_REVISION 1 -#define VERSION_BUILD 50 +#define VERSION_MINOR 13 +#define VERSION_REVISION 0 +#define VERSION_BUILD 51 #define VER_FILE_DESCRIPTION_STR "LeechAgent Memory Acquisition Service" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD diff --git a/leechcore/device_fpga.c b/leechcore/device_fpga.c index af281a6..7ef7455 100644 --- a/leechcore/device_fpga.c +++ b/leechcore/device_fpga.c @@ -343,7 +343,7 @@ typedef struct tdTLP_CALLBACK_BUF_MRd_SCATTER { _Success_(return) BOOL TLP_ToString(_In_ PBYTE pbTlp, _In_ DWORD cbTlp, _Out_ LPSTR *pszTlpText, _Out_opt_ PDWORD pcbTlpText) { - DWORD i, iMax, cbHexAscii, cchHdr, cbResult; + DWORD i, iMax, cbHexAscii = 0, cchHdr, cbResult; CHAR szHdr[MAX_PATH]; LPSTR szResult, tp = ""; DWORD hdrDwBuf[4]; @@ -1916,6 +1916,7 @@ VOID DeviceFPGA_RxTlpAsynchronous(_In_ PLC_CONTEXT ctxLC, _In_ PDEVICE_CONTEXT_F if(status && (status != FT_IO_PENDING)) { break; } + fAsyncInProgress = TRUE; } // 2: process (partial) tlp result while(oBuffer + 32 <= cbBuffer) { @@ -1930,69 +1931,95 @@ VOID DeviceFPGA_RxTlpAsynchronous(_In_ PLC_CONTEXT ctxLC, _In_ PDEVICE_CONTEXT_F if(cbBuffer > 0x00f00000) { break; } if(prxbuf && (cbBytesToRead <= prxbuf->cbReadTotal)) { break; } // 3: read overlapped - status = fAsync ? - ctx->dev.pfnFT_GetOverlappedResult(ctx->dev.hFTDI, &ctx->async.oOverlapped, &cbRead, TRUE) : - ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, pbBuffer + cbBuffer, cbReadMax, &cbRead, NULL); + if(fAsync) { + status = ctx->dev.pfnFT_GetOverlappedResult(ctx->dev.hFTDI, &ctx->async.oOverlapped, &cbRead, TRUE); + fAsyncInProgress = TRUE; + } else { + status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, pbBuffer + cbBuffer, cbReadMax, &cbRead, NULL); + } if(status) { break; } } + if(fAsyncInProgress) { + // dummy clear overlapped result + ctx->dev.pfnFT_GetOverlappedResult(ctx->dev.hFTDI, &ctx->async.oOverlapped, &cbRead, TRUE); + } } VOID DeviceFPGA_RxTlpSynchronous(_In_ PLC_CONTEXT ctxLC, _In_ PDEVICE_CONTEXT_FPGA ctx, _In_opt_ DWORD dwBytesToRead) { DWORD status; - DWORD i, j, cdwTlp = 0, cbReadRxBuf; + BOOL fRetry = FALSE; + DWORD i = 0, j, cdwTlp = 0, cbReadRxBuf; BYTE pbTlp[TLP_RX_MAX_SIZE]; PDWORD pdwTlp = (PDWORD)pbTlp; - DWORD dwStatus, *pdwData; + DWORD dwStatus, *pdwData, cbRx; // larger read buffer slows down FT_ReadPipe so set it fairly tight if possible. + ctx->rxbuf.cb = 0; cbReadRxBuf = ctx->dev.f2232h ? ctx->rxbuf.cbMax : min(ctx->rxbuf.cbMax, dwBytesToRead ? max(0x4000, (0x1000 + dwBytesToRead + (dwBytesToRead >> 1))) : (DWORD)-1); - status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, ctx->rxbuf.pb, cbReadRxBuf, &ctx->rxbuf.cb, NULL); - if(status == 0x20 && ctx->perf.RETRY_ON_ERROR) { - DeviceFPGA_ReInitializeFTDI(ctx); // try recovery if possible. - status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, ctx->rxbuf.pb, ctx->rxbuf.cbMax, &ctx->rxbuf.cb, NULL); - } - if(status) { - ctx->dev.pfnFT_AbortPipe(ctx->dev.hFTDI, 0x82); - return; - } - for(i = 0; i < ctx->rxbuf.cb; i += 32) { // index in 32-bit (DWORD) - while(*(PDWORD)(ctx->rxbuf.pb + i) == 0x55556666) { // skip over ftdi workaround dummy fillers - i += 4; - if(i + 32 > ctx->rxbuf.cb) { return; } + while(TRUE) { + // read data: + status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, ctx->rxbuf.pb + ctx->rxbuf.cb, cbReadRxBuf - ctx->rxbuf.cb, &cbRx, NULL); + if(status == 0x20 && ctx->perf.RETRY_ON_ERROR) { + DeviceFPGA_ReInitializeFTDI(ctx); // try recovery if possible. + status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, ctx->rxbuf.pb + ctx->rxbuf.cb, ctx->rxbuf.cbMax - ctx->rxbuf.cb, &cbRx, NULL); } - dwStatus = *(PDWORD)(ctx->rxbuf.pb + i); - pdwData = (PDWORD)(ctx->rxbuf.pb + i + 4); - if((dwStatus & 0xf0000000) != 0xe0000000) { - continue; + if(status) { + ctx->dev.pfnFT_AbortPipe(ctx->dev.hFTDI, 0x82); + return; } - for(j = 0; j < 7; j++) { - if((dwStatus & 0x03) == 0x00) { // PCIe TLP - pdwTlp[cdwTlp] = *pdwData; - cdwTlp++; - if(cdwTlp >= TLP_RX_MAX_SIZE / sizeof(DWORD)) { return; } + ctx->rxbuf.cb += cbRx; + // process retrieved data and send to respective consumer functions. + while(i + 32 <= ctx->rxbuf.cb) { // index in 32-bit (DWORD) + if(*(PDWORD)(ctx->rxbuf.pb + i) == 0x55556666) { // skip over ftdi workaround dummy fillers + i += 4; + continue; } - if((dwStatus & 0x07) == 0x04) { // PCIe TLP and LAST - if(cdwTlp >= 3) { - if(ctxLC->fPrintf[LC_PRINTF_VVV]) { - TLP_Print(ctxLC, pbTlp, cdwTlp << 2, FALSE); - } - if(ctx->read_callback.pfn) { - DeviceFPGA_RxTlp_UserCallback(ctxLC, ctx, pbTlp, cdwTlp << 2); - } - if(ctx->hRxTlpCallbackFn) { - ctx->hRxTlpCallbackFn(ctx->pMRdBufferX, pbTlp, cdwTlp << 2); + dwStatus = *(PDWORD)(ctx->rxbuf.pb + i); + pdwData = (PDWORD)(ctx->rxbuf.pb + i + 4); + if((dwStatus & 0xf0000000) != 0xe0000000) { + lcprintfvv(ctxLC, "Device Info: FPGA: Bad no-header data received! Should not happen!\n"); + i += 4; + continue; + } + for(j = 0; j < 7; j++) { + if((dwStatus & 0x03) == 0x00) { // PCIe TLP + pdwTlp[cdwTlp] = *pdwData; + cdwTlp++; + if(cdwTlp >= TLP_RX_MAX_SIZE / sizeof(DWORD)) { return; } + } + if((dwStatus & 0x07) == 0x04) { // PCIe TLP and LAST + if(cdwTlp >= 3) { + if(ctxLC->fPrintf[LC_PRINTF_VVV]) { + TLP_Print(ctxLC, pbTlp, cdwTlp << 2, FALSE); + } + if(ctx->read_callback.pfn) { + DeviceFPGA_RxTlp_UserCallback(ctxLC, ctx, pbTlp, cdwTlp << 2); + } + if(ctx->hRxTlpCallbackFn) { + ctx->hRxTlpCallbackFn(ctx->pMRdBufferX, pbTlp, cdwTlp << 2); + } + } else { + lcprintf(ctxLC, "Device Info: FPGA: Bad PCIe TLP received! Should not happen!\n"); } - } else { - printf("Device Info: FPGA: Bad PCIe TLP received! Should not happen!\n"); + cdwTlp = 0; } - cdwTlp = 0; + pdwData++; + dwStatus >>= 4; } - pdwData++; - dwStatus >>= 4; + i += 8 * 4; + } + // return upon (successful) finish! + if((cdwTlp == 0) || fRetry || (ctx->rxbuf.cbMax - ctx->rxbuf.cb < 0x400)) { + return; } + // read retry should be attempted (in case of partial tlp received at the end) + lcprintfvv(ctxLC, "Device Info: FPGA: Partial read - read retry attempted!\n"); + cbReadRxBuf = min(ctx->rxbuf.cbMax, cbReadRxBuf + 0x1000); + usleep(min(20, ctx->perf.DELAY_READ)); + fRetry = TRUE; } } @@ -2119,6 +2146,284 @@ VOID DeviceFPGA_ReadScatter_Impl(_In_ PLC_CONTEXT ctxLC, _In_ DWORD cMEMs, _Inou ctx->pMRdBufferX = NULL; } + + +//------------------------------------------------------------------------------- +// TLP NEW ASYNC handling functionality below: +//------------------------------------------------------------------------------- + +#define MEM_SCATTER_DO_PROCESS(pMEM) (!pMEM->f && pMEM->cb && !(pMEM->cb & 7) && ((pMEM->qwA & 0xfff) + pMEM->cb <= 0x1000) && !MEM_SCATTER_ADDR_ISINVALID(pMEM)) + +#define FPGA_ASYNC_BUFFER_SIZE 0x10000 + +typedef struct tdFPGA_ASYNC_MEM_INFO { + BOOL f; + BOOL fTiny; + WORD cb; + WORD cbOffset; + DWORD iMEM; +} FPGA_ASYNC_MEM_INFO, *PFPGA_ASYNC_MEM_INFO; + +typedef struct tdFPGA_ASYNC_INFO { + WORD wReqId; + BYTE bEccBit; + BYTE iTag; + BOOL fTiny; + DWORD iMEM; + DWORD cTagAvail; + DWORD cbReadOngoing; + DWORD cMEMsCompleted; + FPGA_ASYNC_MEM_INFO tag[0x70]; + PPMEM_SCATTER ppMEMs; +} FPGA_ASYNC_INFO, *PFPGA_ASYNC_INFO; + +VOID DeviceFPGA_ReadScatter_NewAsync_Impl_Tx(_In_ PLC_CONTEXT ctxLC, _In_ PDEVICE_CONTEXT_FPGA ctx, _In_ DWORD cMEMs, _Inout_ PPMEM_SCATTER ppMEMs, _In_ PFPGA_ASYNC_INFO pai) +{ + WORD o, cb; + DWORD j, cbFlush = 0; + BOOL fTiny, f32, fTx = FALSE; + PFPGA_ASYNC_MEM_INFO pmi; + PMEM_SCATTER pMEM; + DWORD tx[4] = { 0 }; + PTLP_HDR_MRdWr64 hdrRd64 = (PTLP_HDR_MRdWr64)tx; + PTLP_HDR_MRdWr32 hdrRd32 = (PTLP_HDR_MRdWr32)tx; + while(TRUE) { + if((pai->iMEM >= cMEMs) || (pai->cTagAvail < 32) || (pai->cbReadOngoing >= ctx->perf.MAX_SIZE_RX)) { break; } + pMEM = ppMEMs[pai->iMEM]; + if(!MEM_SCATTER_DO_PROCESS(pMEM)) { + pai->iMEM++; + continue; + } + f32 = (pMEM->qwA < 0x100000000); + fTiny = ctx->fAlgorithmReadTiny || (pMEM->cb != 0x1000); + + + o = 0; + while(o < pMEM->cb) { + cb = (WORD)(fTiny ? min(0x80, pMEM->cb - o) : pMEM->cb); + while((pmi = &pai->tag[pai->iTag]) && pmi->f) { + pai->iTag = (pai->iTag == 0x6f) ? 0 : (pai->iTag + 1); + } + pmi->f = TRUE; + pmi->fTiny = fTiny; + pmi->cb = cb; + pmi->cbOffset = o; + pmi->iMEM = pai->iMEM; + if(f32) { + hdrRd32->h.TypeFmt = TLP_MRd32; + hdrRd32->h.Length = (WORD)((cb < 0x1000) ? cb >> 2 : 0); + hdrRd32->RequesterID = ctx->wDeviceId; + hdrRd32->Tag = pai->iTag + pai->bEccBit; + hdrRd32->FirstBE = 0xf; + hdrRd32->LastBE = 0xf; + hdrRd32->Address = (DWORD)(pMEM->qwA + o); + } else { + hdrRd64->h.TypeFmt = TLP_MRd64; + hdrRd64->h.Length = (WORD)((cb < 0x1000) ? cb >> 2 : 0); + hdrRd64->RequesterID = ctx->wDeviceId; + hdrRd64->Tag = pai->iTag + pai->bEccBit; + hdrRd64->FirstBE = 0xf; + hdrRd64->LastBE = 0xf; + hdrRd64->AddressHigh = (DWORD)(pMEM->qwA >> 32); + hdrRd64->AddressLow = (DWORD)(pMEM->qwA + o); + } + for(j = 0; j < 4; j++) { + ENDIAN_SWAP_DWORD(tx[j]); + } + + cbFlush += cb; + if(ctx->perf.RX_FLUSH_LIMIT && (cbFlush >= (fTiny ? 0x1000 : ctx->perf.RX_FLUSH_LIMIT))) { + // flush is only used by the SP605. + DeviceFPGA_TxTlp(ctxLC, ctx, (PBYTE)tx, f32 ? 12 : 16, FALSE, TRUE); + usleep(ctx->perf.DELAY_WRITE); + cbFlush = 0; + } else { + DeviceFPGA_TxTlp(ctxLC, ctx, (PBYTE)tx, f32 ? 12 : 16, FALSE, FALSE); + } + o += cb; + pai->cTagAvail--; + pai->cbReadOngoing += cb; + pai->iTag = (pai->iTag == 0x6f) ? 0 : (pai->iTag + 1); + } + pai->iMEM++; + fTx = TRUE; + } + if(fTx) { + DeviceFPGA_TxTlp(ctxLC, ctx, NULL, 0, TRUE, TRUE); + } +} + +/* +* Generic callback function that may be used by TLP capable devices to aid the +* collection of memory read completions. Receives single TLP packet. +* -- pai +* -- pb +* -- cb +*/ +VOID DeviceFPGA_ReadScatter_NewAsync_Impl_RxTlpCB(_Inout_ PFPGA_ASYNC_INFO pai, _In_ PBYTE pb, _In_ DWORD cb) +{ + PTLP_HDR_CplD hdrC = (PTLP_HDR_CplD)pb; + PTLP_HDR hdr = (PTLP_HDR)pb; + PDWORD buf = (PDWORD)pb; + DWORD o, c, iTag; + PMEM_SCATTER pMEM; + PFPGA_ASYNC_MEM_INFO pmi; + BOOL fTiny, fCompleted; + buf[0] = _byteswap_ulong(buf[0]); + buf[1] = _byteswap_ulong(buf[1]); + buf[2] = _byteswap_ulong(buf[2]); + if(cb < ((DWORD)hdr->Length << 2) + 12) { return; } // Insufficient length + if(hdrC->RequesterID != pai->wReqId) { return; } // RequesterID mismatch + if(pai->bEccBit != (hdrC->Tag & 0x80)) { return; } // ECC bit mismatch + iTag = hdrC->Tag & 0x7f; + if(iTag >= 0x70) { return; } // tag out of range + pmi = &pai->tag[iTag]; + pMEM = pai->ppMEMs[pmi->iMEM]; + if(!pmi->f) { return; } // non active tag + if((hdr->TypeFmt == TLP_Cpl) && hdrC->Status) { + pai->cbReadOngoing -= pmi->cb; + if(MEM_SCATTER_STACK_PEEK(pMEM, 1) != (DWORD)-1) { + MEM_SCATTER_STACK_SET(pMEM, 1, (DWORD)-1); + pai->cMEMsCompleted++; + } + ZeroMemory(pmi, sizeof(FPGA_ASYNC_MEM_INFO)); + pai->cTagAvail++; + return; + } + if(hdr->TypeFmt == TLP_CplD) { + fTiny = pai->fTiny || (pMEM->cb != 0x1000); + if(fTiny) { + if(hdrC->ByteCount > pmi->cb) { return; } + o = pmi->cbOffset + pmi->cb - hdrC->ByteCount; + } else { + o = 0x1000 - (hdrC->ByteCount ? hdrC->ByteCount : 0x1000); + } + c = (DWORD)hdr->Length << 2; + if(o + c > pMEM->cb) { return; } + memcpy(pMEM->pb + o, pb + 12, c); + MEM_SCATTER_STACK_ADD(pMEM, 1, c); + pai->cbReadOngoing -= c; + fCompleted = (pMEM->cb == MEM_SCATTER_STACK_PEEK(pMEM, 1)); + if(fCompleted) { + pai->cMEMsCompleted++; + } + if(fCompleted || fTiny) { + ZeroMemory(pmi, sizeof(FPGA_ASYNC_MEM_INFO)); + pai->cTagAvail++; + return; + } + } +} + +VOID DeviceFPGA_ReadScatter_NewAsync_Impl(_In_ PLC_CONTEXT ctxLC, _In_ PDEVICE_CONTEXT_FPGA ctx, _In_ DWORD cMEMs, _Inout_ PPMEM_SCATTER ppMEMs) +{ + PMEM_SCATTER pMEM; + BOOL fAsyncInProgress = FALSE, fAsync; + DWORD iMEM, status, cbRead, cbReadMax; + DWORD cbBytesToRead = 0, cEmptyRead = 0, cbBuffer = 0, oBuffer = 0; + PBYTE pbBuffer; + DWORD cdwTlpDataConsumed; + FPGA_ASYNC_INFO ai = { 0 }; + // 0: split too large reads + if(cMEMs > 1024) { + for(iMEM = 0; iMEM < cMEMs; iMEM += 1024) { + DeviceFPGA_ReadScatter_NewAsync_Impl(ctxLC, ctx, min(1024, cMEMs - iMEM), ppMEMs + iMEM); + } + return; + } + // 1: init + ai.ppMEMs = ppMEMs; + ai.cTagAvail = 0x70; + ai.wReqId = ctx->wDeviceId; + ai.bEccBit = ctx->RxEccBit; + ai.fTiny = ctx->fAlgorithmReadTiny; + ctx->pMRdBufferX = &ai; + ctx->hRxTlpCallbackFn = (VOID(*)(PVOID, PBYTE, DWORD))DeviceFPGA_ReadScatter_NewAsync_Impl_RxTlpCB; + pbBuffer = ctx->rxbuf.pb; + // 2: pre-check MEMs and set fAsync (if large enough read). + for(iMEM = 0; iMEM < cMEMs; iMEM++) { + pMEM = ppMEMs[iMEM]; + if(MEM_SCATTER_DO_PROCESS(pMEM)) { + cbBytesToRead += pMEM->cb; + } else { + ai.cMEMsCompleted++; + } + } + if(cMEMs == ai.cMEMsCompleted) { + return; + } + fAsync = !ctx->dev.f2232h && !ai.fTiny && (cbBytesToRead > 0x4000); + cbReadMax = min(FPGA_ASYNC_BUFFER_SIZE, max(0x1800, cbBytesToRead + (cbBytesToRead >> 1))); + // 3: clear potential lingering dirty TLPs/Cpls (if required) + // TODO: IMPLEMENT THIS + // 4: transmit initial data: + DeviceFPGA_ReadScatter_NewAsync_Impl_Tx(ctxLC, ctx, cMEMs, ppMEMs, &ai); + // 5: initial read + usleep(15); + status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, ctx->rxbuf.pb, cbReadMax, &cbRead, NULL); + if(status && (status != FT_IO_PENDING)) { + return; + } + // 6: do main read loop + while(TRUE) { + // 6.1: check no-read ops and update buffer pointer with previously read data + cbBuffer += cbRead; + if((cbRead == 0) || (cbRead == 0x14)) { + cEmptyRead++; + if(cEmptyRead >= 0x30) { + break; + } + } + // 6.2: submit async read (if target read is large enough to gain from it) + if(fAsync) { + status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, pbBuffer + cbBuffer, cbReadMax, &cbRead, &ctx->async.oOverlapped); + if(status && (status != FT_IO_PENDING)) { + break; + } + fAsyncInProgress = TRUE; + } + // 6.3: process (partial) tlp result + while(oBuffer + 32 <= cbBuffer) { + cdwTlpDataConsumed = DeviceFPGA_RxTlpAsynchronous_Tlp(ctxLC, ctx, (cbBuffer - oBuffer) >> 2, (PDWORD)(pbBuffer + oBuffer)); + if(cdwTlpDataConsumed == (DWORD)-1) { + break; + } + oBuffer += cdwTlpDataConsumed << 2; + cEmptyRead = 0; + } + // 6.4: check exit criteria + if(cbBuffer >= 0x01000000 - cbReadMax) { + break; + } + if(cMEMs == ai.cMEMsCompleted) { + break; + } + // 6.5: transmit additional TLPs + DeviceFPGA_ReadScatter_NewAsync_Impl_Tx(ctxLC, ctx, cMEMs, ppMEMs, &ai); + // 6.6: read (overlapped) result + if(fAsync) { + status = ctx->dev.pfnFT_GetOverlappedResult(ctx->dev.hFTDI, &ctx->async.oOverlapped, &cbRead, TRUE); + fAsyncInProgress = FALSE; + } else { + status = ctx->dev.pfnFT_ReadPipe(ctx->dev.hFTDI, 0x82, pbBuffer + cbBuffer, cbReadMax, &cbRead, NULL); + } + if(status) { + break; + } + } + if(fAsyncInProgress) { + // clear any on-going overlapped reads + ctx->dev.pfnFT_GetOverlappedResult(ctx->dev.hFTDI, &ctx->async.oOverlapped, &cbRead, TRUE); + } + ctx->RxEccBit = ctx->RxEccBit ? 0 : 0x80; +} + + + +//------------------------------------------------------------------------------- +// TLP handling (cont.) functionality below: +//------------------------------------------------------------------------------- + VOID DeviceFPGA_ReadScatter(_In_ PLC_CONTEXT ctxLC, _In_ DWORD cMEMs, _Inout_ PPMEM_SCATTER ppMEMs) { PDEVICE_CONTEXT_FPGA ctx = (PDEVICE_CONTEXT_FPGA)ctxLC->hDevice; @@ -2134,7 +2439,11 @@ VOID DeviceFPGA_ReadScatter(_In_ PLC_CONTEXT ctxLC, _In_ DWORD cMEMs, _Inout_ PP MEM_SCATTER_STACK_PUSH(pMEM, 0); } } + if(ctx->async.fEnabled) { + DeviceFPGA_ReadScatter_NewAsync_Impl(ctxLC, ctx, cMEMs, ppMEMs); + } else { DeviceFPGA_ReadScatter_Impl(ctxLC, cMEMs, ppMEMs); + } for(i = 0; i < cMEMs; i++) { pMEM = ppMEMs[i]; if(!pMEM->f && MEM_SCATTER_ADDR_ISVALID(pMEM)) { @@ -2229,18 +2538,25 @@ VOID DeviceFPGA_ProbeMEM(_In_ PLC_CONTEXT ctxLC, _In_ QWORD pa, _In_ DWORD cPage _Success_(return) LINUX_NO_OPTIMIZE BOOL DeviceFPGA_WriteMEM_TXP(_In_ PLC_CONTEXT ctxLC, _Inout_ PDEVICE_CONTEXT_FPGA ctx, _In_ QWORD pa, _In_ BYTE bFirstBE, _In_ BYTE bLastBE, _In_ PBYTE pb, _In_ DWORD cb) { - static BYTE bTag = 0; + static BYTE bTag = 0x70; DWORD txbuf[36], i, cbTlp; PBYTE pbTlp = (PBYTE)txbuf; PTLP_HDR_MRdWr32 hdrWr32 = (PTLP_HDR_MRdWr32)txbuf; PTLP_HDR_MRdWr64 hdrWr64 = (PTLP_HDR_MRdWr64)txbuf; + if(bTag == 0x7f) { + bTag = 0xe0; + } else if(bTag == 0xff) { + bTag = 0x70; + } else { + bTag++; + } memset(pbTlp, 0, 16); if(pa < 0x100000000) { hdrWr32->h.TypeFmt = TLP_MWr32; hdrWr32->h.Length = (WORD)(cb + 3) >> 2; hdrWr32->FirstBE = bFirstBE; hdrWr32->LastBE = bLastBE; - hdrWr32->Tag = bTag++; + hdrWr32->Tag = bTag; hdrWr32->RequesterID = ctx->wDeviceId; hdrWr32->Address = (DWORD)pa; for(i = 0; i < 3; i++) { @@ -2253,7 +2569,7 @@ BOOL DeviceFPGA_WriteMEM_TXP(_In_ PLC_CONTEXT ctxLC, _Inout_ PDEVICE_CONTEXT_FPG hdrWr64->h.Length = (WORD)(cb + 3) >> 2; hdrWr64->FirstBE = bFirstBE; hdrWr64->LastBE = bLastBE; - hdrWr64->Tag = bTag++; + hdrWr64->Tag = bTag; hdrWr64->RequesterID = ctx->wDeviceId; hdrWr64->AddressHigh = (DWORD)(pa >> 32); hdrWr64->AddressLow = (DWORD)pa; diff --git a/leechcore/leechcore.vcxproj b/leechcore/leechcore.vcxproj index bd8e004..f92e8ae 100644 --- a/leechcore/leechcore.vcxproj +++ b/leechcore/leechcore.vcxproj @@ -1,6 +1,10 @@  + + Debug + ARM64 + Debug Win32 @@ -9,6 +13,10 @@ Debug x64 + + Release + ARM64 + Release Win32 @@ -39,6 +47,13 @@ Unicode false + + DynamicLibrary + true + v143 + Unicode + false + DynamicLibrary false @@ -56,6 +71,15 @@ false false + + DynamicLibrary + false + v143 + true + Unicode + false + false + @@ -67,12 +91,18 @@ + + + + + + $(SolutionDir)files\ @@ -82,6 +112,10 @@ $(SolutionDir)files\$(PlatformShortName)\ $(SolutionDir)files\temp\$(ProjectName)\$(PlatformShortName)\ + + $(SolutionDir)files\temp\$(ProjectName)\$(PlatformShortName)\ + $(SolutionDir)files\$(PlatformShortName)\ + $(SolutionDir)files\ $(SolutionDir)files\temp\$(ProjectName)\$(PlatformShortName)\ @@ -90,6 +124,10 @@ $(SolutionDir)files\$(PlatformShortName)\ $(SolutionDir)files\temp\$(ProjectName)\$(PlatformShortName)\ + + $(SolutionDir)files\temp\$(ProjectName)\$(PlatformShortName)\ + $(SolutionDir)files\$(PlatformShortName)\ + Level3 @@ -139,6 +177,39 @@ copy "$(ProjectDir)\leechcore.h" "$(SolutionDir)\includes\" /y if not exist "$(SolutionDir)\includes\lib32" mkdir "$(SolutionDir)\includes\lib32" copy "$(OutDir)\lib\leechcore.lib" "$(SolutionDir)\includes\lib32\" /y +copy "$(ProjectDir)\leechcore.h" "$(SolutionDir)\includes\" /y + + + + + + + + Stub + + + None + /robust %(AdditionalOptions) + + + + + Level3 + Disabled + true + true + CompileAsC + + + rpcrt4.lib;setupapi.lib;winusb.lib;ws2_32.lib;%(AdditionalDependencies) + $(OutDir)\lib\$(TargetName).pdb + Windows + $(OutDir)\lib\$(TargetName).lib + + + if not exist "$(SolutionDir)\includes\libarm64" mkdir "$(SolutionDir)\includes\libarm64" +copy "$(OutDir)\lib\leechcore.lib" "$(SolutionDir)\includes\libarm64\" /y + copy "$(ProjectDir)\leechcore.h" "$(SolutionDir)\includes\" /y @@ -172,6 +243,7 @@ copy "$(ProjectDir)\leechcore.h" "$(SolutionDir)\includes\" /y Windows $(OutDir)\lib\$(TargetName).lib UseLinkTimeCodeGeneration + true if not exist "$(SolutionDir)\includes\lib64" mkdir "$(SolutionDir)\includes\lib64" @@ -213,6 +285,44 @@ copy "$(ProjectDir)\leechcore.h" "$(SolutionDir)\includes\" /y if not exist "$(SolutionDir)\includes\lib32" mkdir "$(SolutionDir)\includes\lib32" copy "$(OutDir)\lib\leechcore.lib" "$(SolutionDir)\includes\lib32\" /y +copy "$(ProjectDir)\leechcore.h" "$(SolutionDir)\includes\" /y + + + + + + + + Stub + + + None + /robust %(AdditionalOptions) + + + + + Level3 + MaxSpeed + true + true + true + true + CompileAsC + + + true + true + rpcrt4.lib;setupapi.lib;winusb.lib;ws2_32.lib;%(AdditionalDependencies) + $(OutDir)\lib\$(TargetName).pdb + Windows + $(OutDir)\lib\$(TargetName).lib + UseLinkTimeCodeGeneration + + + if not exist "$(SolutionDir)\includes\libarm64" mkdir "$(SolutionDir)\includes\libarm64" +copy "$(OutDir)\lib\leechcore.lib" "$(SolutionDir)\includes\libarm64\" /y + copy "$(ProjectDir)\leechcore.h" "$(SolutionDir)\includes\" /y diff --git a/leechcore/leechcore.vcxproj.user b/leechcore/leechcore.vcxproj.user index 3e6d071..12cd692 100644 --- a/leechcore/leechcore.vcxproj.user +++ b/leechcore/leechcore.vcxproj.user @@ -6,4 +6,7 @@ WindowsRemoteDebugger + + WindowsRemoteDebugger + \ No newline at end of file diff --git a/leechcore/version.h b/leechcore/version.h index 75a6c22..a5592a0 100644 --- a/leechcore/version.h +++ b/leechcore/version.h @@ -2,9 +2,9 @@ #define STRINGIZE(s) STRINGIZE2(s) #define VERSION_MAJOR 2 -#define VERSION_MINOR 12 -#define VERSION_REVISION 1 -#define VERSION_BUILD 50 +#define VERSION_MINOR 13 +#define VERSION_REVISION 0 +#define VERSION_BUILD 51 #define VER_FILE_DESCRIPTION_STR "LeechCore Memory Acquisition Library" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD diff --git a/leechcorepyc/pkggen_linux.sh b/leechcorepyc/pkggen_linux.sh index eb6ae81..ffce8b9 100644 --- a/leechcorepyc/pkggen_linux.sh +++ b/leechcorepyc/pkggen_linux.sh @@ -39,7 +39,7 @@ leechcorepyc = Extension( setup( name='leechcorepyc', - version='2.12.1', # VERSION_END + version='2.13.0', # VERSION_END description='LeechCore for Python', long_description='LeechCore for Python : native extension for physical memory access', url='https://github.com/ufrisk/LeechCore', diff --git a/leechcorepyc/version.h b/leechcorepyc/version.h index a704de0..b9c36be 100644 --- a/leechcorepyc/version.h +++ b/leechcorepyc/version.h @@ -2,9 +2,9 @@ #define STRINGIZE(s) STRINGIZE2(s) #define VERSION_MAJOR 2 -#define VERSION_MINOR 12 -#define VERSION_REVISION 1 -#define VERSION_BUILD 50 +#define VERSION_MINOR 13 +#define VERSION_REVISION 0 +#define VERSION_BUILD 51 #define VER_FILE_DESCRIPTION_STR "LeechCore Memory Acquisition Library : Python API" #define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD