Skip to content

Commit

Permalink
Version 4.11
Browse files Browse the repository at this point in the history
  • Loading branch information
ufrisk committed Aug 16, 2021
1 parent 2be62c9 commit e942bbc
Show file tree
Hide file tree
Showing 12 changed files with 316 additions and 16 deletions.
38 changes: 37 additions & 1 deletion files/agent-find-rwx.py
@@ -1,6 +1,42 @@
# Example file to demonstrate remote python functionality with the LeechAgent.
#
# Example:
# pcileech.exe -device <device> -remote rpc://<spn or insecure>:host agent-execpy -in agent-find-rwx.py
#
# The python script will be executed in a child process to the LeechAgent in
# the user-context of the LeechAgent. If the agent is running as a service this
# is most likely SYSTEM. It's also possible to use this functionality to run
# Python scripts on the remote host without using the memory analysis functionality.
#
# Please check out agent installation instructions at:
# https://github.com/ufrisk/LeechCore/wiki/LeechAgent
# https://github.com/ufrisk/LeechCore/wiki/LeechAgent_Install
#


#
# Example to load LeechCore for Python connecting to the memory acqusition device
# specified in the PCILeech -device parameter. Please uncomment to activate.
# Guide at: https://github.com/ufrisk/LeechCore/wiki/LeechCore_API_Python
#
'''
import leechcorepyc
lc = leechcorepyc.LeechCore('existing')
print(lc)
'''


#
# Example to load MemProcFS for Python connecting to the memory acqusition device
# specified in the PCILeech -device parameter.
# For information about MemProcFS Python API please check out the wiki for API
# usage examples and a youtube demo.
# https://github.com/ufrisk/MemProcFS/wiki/API_Python
#
#
import memprocfs
vmm = memprocfs.Vmm()
for process in vmm.process_list():
for entry in process.maps.pte():
if '-rwx' in entry['flags']:
print(str(process.pid) + ': ' + process.name + ': ' + str(entry))
print(str(process.pid) + ': ' + process.name + ': ' + str(entry))
34 changes: 32 additions & 2 deletions includes/leechcore.h
Expand Up @@ -14,7 +14,7 @@
// (c) Ulf Frisk, 2020-2021
// Author: Ulf Frisk, pcileech@frizk.net
//
// Header Version: 2.6
// Header Version: 2.7
//

#ifndef __LEECHCORE_H__
Expand Down Expand Up @@ -396,7 +396,7 @@ EXPORTED_FUNCTION BOOL LcCommand(
#define LC_OPT_FPGA_DELAY_WRITE 0x0300000700000000 // RW - uS
#define LC_OPT_FPGA_DELAY_READ 0x0300000800000000 // RW - uS
#define LC_OPT_FPGA_RETRY_ON_ERROR 0x0300000900000000 // RW
#define LC_OPT_FPGA_DEVICE_ID 0x0300008000000000 // R
#define LC_OPT_FPGA_DEVICE_ID 0x0300008000000000 // RW - bus:dev:fn (ex: 04:00.0 == 0x0400).
#define LC_OPT_FPGA_FPGA_ID 0x0300008100000000 // R
#define LC_OPT_FPGA_VERSION_MAJOR 0x0300008200000000 // R
#define LC_OPT_FPGA_VERSION_MINOR 0x0300008300000000 // R
Expand Down Expand Up @@ -433,6 +433,14 @@ EXPORTED_FUNCTION BOOL LcCommand(

#define LC_CMD_AGENT_EXEC_PYTHON 0x8000000100000000 // RW - [lo-dword: optional timeout in ms]
#define LC_CMD_AGENT_EXIT_PROCESS 0x8000000200000000 // - [lo-dword: process exit code]
#define LC_CMD_AGENT_VFS_LIST 0x8000000300000000 // RW
#define LC_CMD_AGENT_VFS_READ 0x8000000400000000 // RW
#define LC_CMD_AGENT_VFS_WRITE 0x8000000500000000 // RW
#define LC_CMD_AGENT_VFS_OPT_GET 0x8000000600000000 // RW
#define LC_CMD_AGENT_VFS_OPT_SET 0x8000000700000000 // RW

#define LC_CMD_AGENT_VFS_REQ_VERSION 0xfeed0001
#define LC_CMD_AGENT_VFS_RSP_VERSION 0xfeee0001

#define LC_STATISTICS_VERSION 0xe1a10002
#define LC_STATISTICS_ID_OPEN 0x00
Expand All @@ -445,6 +453,28 @@ EXPORTED_FUNCTION BOOL LcCommand(
#define LC_STATISTICS_ID_COMMAND 0x07
#define LC_STATISTICS_ID_MAX 0x07

typedef struct tdLC_CMD_AGENT_VFS_REQ {
DWORD dwVersion;
DWORD _FutureUse;
CHAR uszPathFile[2*MAX_PATH]; // file path to list/read/write
union {
QWORD qwOffset; // offset to read/write
QWORD fOption; // option to get/set (qword data in *pb)
};
DWORD dwLength; // length to read
DWORD cb;
BYTE pb[0];
} LC_CMD_AGENT_VFS_REQ, *PLC_CMD_AGENT_VFS_REQ;

typedef struct tdLC_CMD_AGENT_VFS_RSP {
DWORD dwVersion;
DWORD dwStatus; // ntstatus of read/write
DWORD cbReadWrite; // number of bytes read/written
DWORD _FutureUse[2];
DWORD cb;
BYTE pb[0];
} LC_CMD_AGENT_VFS_RSP, *PLC_CMD_AGENT_VFS_RSP;

static LPCSTR LC_STATISTICS_NAME[] = {
"LcOpen",
"LcRead",
Expand Down
Binary file modified includes/lib64/leechcore.lib
Binary file not shown.
Binary file modified includes/lib64/vmm.lib
Binary file not shown.
30 changes: 28 additions & 2 deletions includes/vmmdll.h
Expand Up @@ -7,7 +7,7 @@
// (c) Ulf Frisk, 2018-2021
// Author: Ulf Frisk, pcileech@frizk.net
//
// Header Version: 4.0
// Header Version: 4.2
//

#include "leechcore.h"
Expand Down Expand Up @@ -306,6 +306,7 @@ typedef struct _SERVICE_STATUS {

#define VMMDLL_VFS_FILELIST_EXINFO_VERSION 1
#define VMMDLL_VFS_FILELIST_VERSION 2
#define VMMDLL_VFS_FILELISTBLOB_VERSION 0xf88f0001

typedef struct tdVMMDLL_VFS_FILELIST_EXINFO {
DWORD dwVersion;
Expand All @@ -331,8 +332,24 @@ typedef struct tdVMMDLL_VFS_FILELIST2 {
HANDLE h;
} VMMDLL_VFS_FILELIST2, *PVMMDLL_VFS_FILELIST2;

typedef struct tdVMMDLL_VFS_FILELISTBLOB_ENTRY {
ULONG64 ouszName; // byte offset to string from VMMDLL_VFS_FILELISTBLOB.uszMultiText
ULONG64 cbFileSize; // -1 == directory
VMMDLL_VFS_FILELIST_EXINFO ExInfo; // optional ExInfo
} VMMDLL_VFS_FILELISTBLOB_ENTRY, *PVMMDLL_VFS_FILELISTBLOB_ENTRY;

typedef struct tdVMMDLL_VFS_FILELISTBLOB {
DWORD dwVersion; // VMMDLL_VFS_FILELISTBLOB_VERSION
DWORD cbStruct;
DWORD cFileEntry;
DWORD cbMultiText;
LPSTR uszMultiText;
DWORD _FutureUse[8];
VMMDLL_VFS_FILELISTBLOB_ENTRY FileEntry[0];
} VMMDLL_VFS_FILELISTBLOB, *PVMMDLL_VFS_FILELISTBLOB;

/*
* Helper functions for callbacks into the VMM_VFS_FILELIST structure.
* Helper functions for callbacks into the VMM_VFS_FILELIST2 structure.
*/
EXPORTED_FUNCTION
VOID VMMDLL_VfsList_AddFile(_In_ HANDLE pFileList, _In_ LPSTR uszName, _In_ ULONG64 cb, _In_opt_ PVMMDLL_VFS_FILELIST_EXINFO pExInfo);
Expand All @@ -355,6 +372,15 @@ EXPORTED_FUNCTION
_Success_(return) BOOL VMMDLL_VfsListU(_In_ LPSTR uszPath, _Inout_ PVMMDLL_VFS_FILELIST2 pFileList);
_Success_(return) BOOL VMMDLL_VfsListW(_In_ LPWSTR wszPath, _Inout_ PVMMDLL_VFS_FILELIST2 pFileList);

/*
* List a directory of files in MemProcFS and return a VMMDLL_VFS_FILELISTBLOB.
* CALLER FREE: VMMDLL_MemFree(return)
* -- uszPath
* -- return
*/
EXPORTED_FUNCTION
_Success_(return != NULL) PVMMDLL_VFS_FILELISTBLOB VMMDLL_VfsListBlobU(_In_ LPSTR uszPath);

/*
* Read select parts of a file in MemProcFS.
* -- [uw]szFileName
Expand Down
177 changes: 176 additions & 1 deletion pcileech/executor.c
Expand Up @@ -12,6 +12,7 @@
#define EXEC_IO_CONSOLE_BUFFER_SIZE 0x800
#define EXEC_IO_DMAOFFSET_IS 0x80000
#define EXEC_IO_DMAOFFSET_OS 0x81000

typedef struct tdEXEC_IO {
QWORD magic;
struct {
Expand Down Expand Up @@ -390,7 +391,7 @@ VOID ActionExecShellcode()
if(pFile) { fclose(pFile); }
}

VOID ActionSvcExecPy()
VOID ActionAgentExecPy()
{
BOOL result;
DWORD cbResult = 0;
Expand Down Expand Up @@ -437,3 +438,177 @@ VOID ActionSvcExecPy()
LcMemFree(pbResult);
}

#ifdef _WIN32

DWORD ActionAgentForensic_OutFileDirectory(_Out_writes_z_(MAX_PATH) LPSTR szFilePrefix, _In_ LPSTR szUniqueTag)
{
SYSTEMTIME st;
GetLocalTime(&st);
_snprintf_s(
szFilePrefix,
MAX_PATH,
_TRUNCATE,
"%s%sforensic-%i%02i%02i-%02i%02i%02i-%s",
ctxMain->cfg.szFileOut[0] ? ctxMain->cfg.szFileOut : "",
ctxMain->cfg.szFileOut[0] ? "\\" : "",
st.wYear,
st.wMonth,
st.wDay,
st.wHour,
st.wMinute,
st.wSecond,
szUniqueTag);
return (DWORD)strlen(szFilePrefix);
}

VOID ActionAgentForensic_GetFile(_In_ LPSTR szRemoteFile, _In_ LPSTR szOutFile, _In_ QWORD qwSize)
{
FILE *hFile = NULL;
LC_CMD_AGENT_VFS_REQ Req = { 0 };
PLC_CMD_AGENT_VFS_RSP pRsp = NULL;
HANDLE hConsole;
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
Req.dwVersion = LC_CMD_AGENT_VFS_REQ_VERSION;
strncpy_s(Req.uszPathFile, _countof(Req.uszPathFile), szRemoteFile, _TRUNCATE);
if(fopen_s(&hFile, szOutFile, "wb")) {
printf("AGENT-ELASTIC: failed open local file %s\n", szOutFile);
goto fail;
}
printf(" Local File: %s\n Progress: 0%%", szOutFile);
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hConsole, &consoleInfo);
consoleInfo.dwCursorPosition.X -= 4;
while((Req.dwLength = min(0x01000000, (DWORD)(qwSize - Req.qwOffset)))) {
if(!LcCommand(ctxMain->hLC, LC_CMD_AGENT_VFS_READ, sizeof(LC_CMD_AGENT_VFS_REQ), (PBYTE)&Req, (PBYTE*)&pRsp, NULL) || !pRsp || !pRsp->cb) {
printf("\nAGENT-FORENSIC: Failed reading remote file.\n");
goto fail;
}
if(pRsp->cb != fwrite(pRsp->pb, 1, pRsp->cb, hFile)) {
LocalFree(pRsp);
printf("\nAGENT-FORENSIC: failed write to local file %s\n", szOutFile);
break;
}
LocalFree(pRsp);
Req.qwOffset += Req.dwLength;
SetConsoleCursorPosition(hConsole, consoleInfo.dwCursorPosition);
printf("%3lli%%", ((Req.qwOffset * 100) / qwSize));
}
printf("\n");
fail:
if(hFile) { fclose(hFile); }
}

/*
* Retrieve forensic mode JSON data from the remote system. This is achieved by
* starting MemProcFS as a child-process remotely and accessing its virtual file
* system. The JSON data retrieved is compatible with ElasticSearch.
*/
VOID ActionAgentForensic()
{
CHAR szPercent[4] = { 0 }, szRemoteFile[MAX_PATH] = { 0 }, szLocalFile[MAX_PATH] = { 0 };
CHAR szTag[18] = { 0 };
LPSTR szFile;
DWORD i, cPercent = 0, cbResult = 0, cchLocalFileDirectory;
PBYTE pbResult = NULL;
PLC_CMD_AGENT_VFS_REQ pReq = NULL;
PLC_CMD_AGENT_VFS_RSP pRsp = NULL;
PVMMDLL_VFS_FILELISTBLOB pVfsList;
HANDLE hConsole;
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;

// Initial setup
if(!(pReq = LocalAlloc(LMEM_ZEROINIT, sizeof(LC_CMD_AGENT_VFS_REQ) + 1))) { goto fail; }
pReq->dwVersion = LC_CMD_AGENT_VFS_REQ_VERSION;
strncpy_s(pReq->uszPathFile, _countof(pReq->uszPathFile), "\\forensic\\forensic_enable.txt", _TRUNCATE);

// Enable/verify forensic mode '1' - in-memory database
pReq->cb = 1;
pReq->pb[0] = '1';
if(!LcCommand(ctxMain->hLC, LC_CMD_AGENT_VFS_WRITE, sizeof(LC_CMD_AGENT_VFS_REQ) + 1, (PBYTE)pReq, NULL, NULL)) {
printf("AGENT-FORENSIC: Failed to connect to the remote system or enable memory analysis.\n");
goto fail;
}
pReq->cb = 0;
pReq->dwLength = 3;
if(!LcCommand(ctxMain->hLC, LC_CMD_AGENT_VFS_READ, sizeof(LC_CMD_AGENT_VFS_REQ), (PBYTE)pReq, (PBYTE*)&pRsp, NULL) || !pRsp || !pRsp->cb || pRsp->pb[0] != '1') {
printf("AGENT-FORENSIC: Failed start remote forensic mode memory analysis.\n");
goto fail;
}
LocalFree(pRsp); pRsp = NULL;

// Get Unique Tag
strncpy_s(pReq->uszPathFile, _countof(pReq->uszPathFile), "\\sys\\unique-tag.txt", _TRUNCATE);
pReq->cb = 0;
pReq->dwLength = 17;
if(!LcCommand(ctxMain->hLC, LC_CMD_AGENT_VFS_READ, sizeof(LC_CMD_AGENT_VFS_REQ), (PBYTE)pReq, (PBYTE*)&pRsp, NULL) || !pRsp || !pRsp->cb || (pRsp->cb > 17)) {
printf("AGENT-FORENSIC: Failed retrieving unique tag.\n");
goto fail;
}
memcpy(szTag, pRsp->pb, pRsp->cb);
LocalFree(pRsp); pRsp = NULL;
printf("AGENT-FORENSIC: Remote System Tag: %s\n", szTag);

// Watch for progress until 100%
printf("AGENT-FORENSIC: Connected. Remote forensic memory analysis: 0%%");
hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfo(hConsole, &consoleInfo);
consoleInfo.dwCursorPosition.X -= 4;
cPercent = 0;
strncpy_s(pReq->uszPathFile, _countof(pReq->uszPathFile), "\\forensic\\progress_percent.txt", _TRUNCATE);
pReq->cb = 0;
pReq->dwLength = 3;
while(cPercent != 100) {
Sleep(250);
if(!LcCommand(ctxMain->hLC, LC_CMD_AGENT_VFS_READ, sizeof(LC_CMD_AGENT_VFS_REQ), (PBYTE)pReq, (PBYTE*)&pRsp, NULL) || !pRsp) {
printf("\nAGENT-FORENSIC: Failed to retrieve progress percent ...\n");
goto fail;
}
memcpy(szPercent, pRsp->pb, min(3, pRsp->cb));
LocalFree(pRsp); pRsp = NULL;
cPercent = atoi(szPercent);
SetConsoleCursorPosition(hConsole, consoleInfo.dwCursorPosition);
printf("%3i%%", cPercent);
}

// Retrieve /forensic/json directory info and print file list:
strncpy_s(pReq->uszPathFile, _countof(pReq->uszPathFile), "\\forensic\\json", _TRUNCATE);
pReq->dwLength = 0;
if(!LcCommand(ctxMain->hLC, LC_CMD_AGENT_VFS_LIST, sizeof(LC_CMD_AGENT_VFS_REQ), (PBYTE)pReq, (PBYTE*)&pRsp, NULL) || !pRsp) {
printf("AGENT-FORENSIC: Failed to retrieve file info.\n");
goto fail;
}
pVfsList = (PVMMDLL_VFS_FILELISTBLOB)pRsp->pb; // sanity/security checks on remote deta done in leechcore
pVfsList->uszMultiText = pVfsList->uszMultiText + (QWORD)pVfsList; // fixup relative uszMultiText offset
if(pVfsList->cFileEntry > 16) {
printf("AGENT-FORENSIC: Too many files on remote system (%i).\n", pVfsList->cFileEntry);
goto fail;
}
cchLocalFileDirectory = ActionAgentForensic_OutFileDirectory(szLocalFile, szTag);
CreateDirectoryA(szLocalFile, NULL);
printf("\nRemote Files:\n");
for(i = 0; i < pVfsList->cFileEntry; i++) {
if(pVfsList->FileEntry[i].cbFileSize != -1) {
szFile = pVfsList->uszMultiText + pVfsList->FileEntry[i].ouszName;
printf(" %s\t\t[%lli MB]\n", szFile, pVfsList->FileEntry[i].cbFileSize / (1024 * 1024));
if(!strcmp(szFile, "general.json") || !strcmp(szFile, "registry.json") || !strcmp(szFile, "timeline.json")) {
_snprintf_s(szRemoteFile, _countof(szRemoteFile), _TRUNCATE, "\\forensic\\json\\%s", szFile);
_snprintf_s(szLocalFile + cchLocalFileDirectory, _countof(szLocalFile) - cchLocalFileDirectory, _TRUNCATE, "\\%s", szFile);
ActionAgentForensic_GetFile(szRemoteFile, szLocalFile, pVfsList->FileEntry[i].cbFileSize);
}
}
}
printf("Completed!\n\n");
fail:
LocalFree(pReq);
LocalFree(pRsp);
}

#endif /* _WIN32 */
#ifdef LINUX

VOID ActionAgentForensic()
{
printf("Command 'agent-elastic' is only supported on Windows.\n");
}

#endif /* LINUX */
7 changes: 6 additions & 1 deletion pcileech/executor.h
Expand Up @@ -57,6 +57,11 @@ VOID ActionExecShellcode();
/*
* Try execute python code on a remote host in the context of the LeechSvc.
*/
VOID ActionSvcExecPy();
VOID ActionAgentExecPy();

/*
* Retrieve remote elasticsearch forensic information.
*/
VOID ActionAgentForensic();

#endif /* __EXECUTOR_H__ */

0 comments on commit e942bbc

Please sign in to comment.