Files
MemProcFS-plugins/vmm_example/vmmdll_example.c
2018-12-05 13:49:54 +01:00

582 lines
24 KiB
C

// vmmdll_example.c - Memory Process File System / Virtual Memory Manager DLL API usage examples
//
// Note that this is not a complete list of the VMM API. For the complete list please consult the vmmdll.h header file.
//
// (c) Ulf Frisk, 2018
// Author: Ulf Frisk, pcileech@frizk.net
//
#include <Windows.h>
#include <stdio.h>
#include <conio.h>
#include "vmmdll.h"
#pragma comment(lib, "vmm")
// ----------------------------------------------------------------------------
// Initialize from type of device, FILE, FPGA or Total Meltdown (CVE-2018-1038).
// Ensure only one is active below at one single time!
// INITIALIZE_FROM_FILE contains file name to a raw memory dump.
// ----------------------------------------------------------------------------
#define _INITIALIZE_FROM_FILE "z:\\media\\vm\\memdump\\WIN10-17134-48-1.raw"
//#define _INITIALIZE_FROM_FPGA
//#define _INITIALIZE_FROM_TOTALMELTDOWN
// ----------------------------------------------------------------------------
// Utility functions below:
// ----------------------------------------------------------------------------
VOID ShowKeyPress()
{
printf("PRESS ANY KEY TO CONTINUE ...\n");
Sleep(250);
_getch();
}
VOID PrintHexAscii(_In_ PBYTE pb, _In_ DWORD cb)
{
DWORD szMax;
LPSTR sz;
VMMDLL_UtilFillHexAscii(pb, cb, 0, NULL, &szMax);
if(!(sz = LocalAlloc(0, szMax))) { return; }
VMMDLL_UtilFillHexAscii(pb, cb, 0, sz, &szMax);
printf(sz);
LocalFree(sz);
}
VOID CallbackList_AddFile(_Inout_ HANDLE h, _In_ LPSTR szName, _In_ ULONG64 cb, _In_ PVOID pvReserved)
{
printf(" FILE: '%s'\tSize: %i\n", szName, (DWORD)cb);
}
VOID CallbackList_AddDirectory(_Inout_ HANDLE h, _In_ LPSTR szName, _In_ PVOID pvReserved)
{
printf(" DIR: '%s'\n", szName);
}
// ----------------------------------------------------------------------------
// Main entry point which contains various sample code how to use PCILeech DLL.
// Please walk though for different API usage examples. To select device ensure
// one device type only is uncommented in the #defines above.
// ----------------------------------------------------------------------------
int main(_In_ int argc, _In_ char* argv[])
{
BOOL result;
NTSTATUS nt;
DWORD i, dwPID;
BYTE pbPage1[0x1000], pbPage2[0x1000];
#ifdef _INITIALIZE_FROM_FILE
// Initialize PCILeech DLL with a memory dump file.
printf("------------------------------------------------------------\n");
printf("#01: Initialize from file: \n");
ShowKeyPress();
printf("CALL: VMMDLL_InitializeFile\n");
result = VMMDLL_InitializeFile(_INITIALIZE_FROM_FILE, NULL);
if(result) {
printf("SUCCESS: VMMDLL_InitializeFile\n");
} else {
printf("FAIL: VMMDLL_InitializeFile\n");
return 1;
}
#endif /* _INITIALIZE_FROM_FILE */
#ifdef _INITIALIZE_FROM_TOTALMELTDOWN
// Initialize VMM DLL from a linked PCILeech with the TotalMeltdown exploit.
printf("------------------------------------------------------------\n");
printf("#01: Initialize from TotalMeltdown: \n");
ShowKeyPress();
printf("CALL: VMMDLL_InitializeTotalMeltdown\n");
result = VMMDLL_InitializeTotalMeltdown();
if(result) {
printf("SUCCESS: VMMDLL_InitializeTotalMeltdown\n");
} else {
printf("FAIL: VMMDLL_InitializeTotalMeltdown\n");
return 1;
}
#endif /* _INITIALIZE_FROM_TOTALMELTDOWN */
#ifdef _INITIALIZE_FROM_FPGA
// Initialize VMM DLL from a linked PCILeech with a FPGA hardware device
printf("------------------------------------------------------------\n");
printf("#01: Initialize from FPGA: \n");
ShowKeyPress();
printf("CALL: VMMDLL_InitializeFPGA\n");
result = VMMDLL_InitializeFPGA(NULL, NULL);
if(result) {
printf("SUCCESS: VMMDLL_InitializeFPGA\n");
} else {
printf("FAIL: VMMDLL_InitializeFPGA\n");
return 1;
}
// Retrieve the ID of the FPPA (SP605/PCIeScreamer/AC701 ...) and the bitstream version
ULONG64 qwID, qwVersionMajor, qwVersionMinor;
ShowKeyPress();
printf("CALL: VMMDLL_ConfigGet\n");
result =
VMMDLL_ConfigGet(VMMDLL_OPT_DEVICE_FPGA_FPGA_ID, &qwID) &&
VMMDLL_ConfigGet(VMMDLL_OPT_DEVICE_FPGA_VERSION_MAJOR, &qwVersionMajor) &&
VMMDLL_ConfigGet(VMMDLL_OPT_DEVICE_FPGA_VERSION_MINOR, &qwVersionMinor);
if(result) {
printf("SUCCESS: VMMDLL_ConfigGet\n");
printf(" ID = %lli\n", qwID);
printf(" VERSION = %lli.%lli\n", qwVersionMajor, qwVersionMinor);
} else {
printf("FAIL: VMMDLL_ConfigGet\n");
return 1;
}
// Retrieve the read delay value (in microseconds uS) that is used by the
// FPGA to pause in every read. Sometimes it may be a good idea to adjust
// this (and other related values) to lower versions if the FPGA device
// still works stable without errors. Use PCIleech_DeviceConfigSet to set
// values.
ULONG64 qwReadDelay;
ShowKeyPress();
printf("CALL: VMMDLL_ConfigGet\n");
result = VMMDLL_ConfigGet(VMMDLL_OPT_DEVICE_FPGA_DELAY_READ, &qwReadDelay);
if(result) {
printf("SUCCESS: VMMDLL_ConfigGet\n");
printf(" FPGA Read Delay in microseconds (uS) = %lli\n", qwReadDelay);
} else {
printf("FAIL: VMMDLL_ConfigGet\n");
return 1;
}
#endif /* _INITIALIZE_FROM_FPGA */
// Read physical memory at physical address 0x1000 and display the first
// 0x100 bytes on-screen.
printf("------------------------------------------------------------\n");
printf("#02: Read from physical memory (0x1000 bytes @ 0x1000). \n");
ShowKeyPress();
printf("CALL: VMMDLL_MemRead\n");
result = VMMDLL_MemRead(-1, 0x1000, pbPage1, 0x1000);
if(result) {
printf("SUCCESS: VMMDLL_MemRead\n");
PrintHexAscii(pbPage1, 0x100);
} else {
printf("FAIL: VMMDLL_MemRead\n");
return 1;
}
// Retrieve PID of explorer.exe
// NB! if multiple explorer.exe exists only one will be returned by this
// specific function call. Please see .h file for additional information
// about how to retrieve the complete list of PIDs in the system by using
// the function PCILeech_VmmProcessListPIDs instead.
printf("------------------------------------------------------------\n");
printf("#03: Get PID from the first 'explorer.exe' process found. \n");
ShowKeyPress();
printf("CALL: VMMDLL_PidGetFromName\n");
result = VMMDLL_PidGetFromName("explorer.exe", &dwPID);
if(result) {
printf("SUCCESS: VMMDLL_PidGetFromName\n");
printf(" PID = %i\n", dwPID);
} else {
printf("FAIL: VMMDLL_PidGetFromName\n");
return 1;
}
// Retrieve additional process information such as: name of the process,
// PML4 (PageDirectoryBase) PML4-USER (if exists) and Process State.
printf("------------------------------------------------------------\n");
printf("#04: Get Process Information from 'explorer.exe'. \n");
ShowKeyPress();
VMMDLL_PROCESS_INFORMATION ProcessInformation;
SIZE_T cbProcessInformation = sizeof(VMMDLL_PROCESS_INFORMATION);
ZeroMemory(&ProcessInformation, sizeof(VMMDLL_PROCESS_INFORMATION));
ProcessInformation.magic = VMMDLL_PROCESS_INFORMATION_MAGIC;
ProcessInformation.wVersion = VMMDLL_PROCESS_INFORMATION_VERSION;
printf("CALL: VMMDLL_ProcessGetInformation\n");
result = VMMDLL_ProcessGetInformation(dwPID, &ProcessInformation, &cbProcessInformation);
if(result) {
printf("SUCCESS: VMMDLL_ProcessGetInformation\n");
printf(" Name = %s\n", ProcessInformation.szName);
printf(" PageDirectoryBase = 0x%016llx\n", ProcessInformation.paDTB);
printf(" PageDirectoryBaseUser = 0x%016llx\n", ProcessInformation.paDTB_UserOpt);
printf(" ProcessState = 0x%08x\n", ProcessInformation.dwState);
} else {
printf("FAIL: VMMDLL_ProcessGetInformation\n");
return 1;
}
// Retrieve the memory map from the page table. This function also tries to
// make additional parsing to identify modules and tag the memory map with
// them. This is done by multiple methods internally and may sometimes be
// more resilient against anti-reversing techniques that may be employed in
// some processes.
printf("------------------------------------------------------------\n");
printf("#05: Get Memory Map of 'explorer.exe'. \n");
ShowKeyPress();
ULONG64 cMemMapEntries;
PVMMDLL_MEMMAP_ENTRY pMemMapEntries;
printf("CALL: VMMDLL_ProcessGetMemoryMap #1\n");
result = VMMDLL_ProcessGetMemoryMap(dwPID, NULL, &cMemMapEntries, TRUE);
if(result) {
printf("SUCCESS: VMMDLL_ProcessGetMemoryMap #1\n");
printf(" Count = %lli\n", cMemMapEntries);
} else {
printf("FAIL: VMMDLL_ProcessGetMemoryMap #1\n");
return 1;
}
pMemMapEntries = (PVMMDLL_MEMMAP_ENTRY)LocalAlloc(0, cMemMapEntries * sizeof(VMMDLL_MEMMAP_ENTRY));
if(!pMemMapEntries) {
printf("FAIL: OutOfMemory\n");
return 1;
}
printf("CALL: VMMDLL_ProcessGetMemoryMap #2\n");
result = VMMDLL_ProcessGetMemoryMap(dwPID, pMemMapEntries, &cMemMapEntries, TRUE);
if(result) {
printf("SUCCESS: VMMDLL_ProcessGetMemoryMap #2\n");
printf(" # #PAGES ADRESS_RANGE SRWX\n");
printf(" ====================================================\n");
for(i = 0; i < cMemMapEntries; i++) {
printf(
" %04x %8x %016llx-%016llx %sr%s%s%s%s\n",
i,
(DWORD)pMemMapEntries[i].cPages,
pMemMapEntries[i].AddrBase,
pMemMapEntries[i].AddrBase + (pMemMapEntries[i].cPages << 12) - 1,
pMemMapEntries[i].fPage & VMMDLL_MEMMAP_FLAG_PAGE_NS ? "-" : "s",
pMemMapEntries[i].fPage & VMMDLL_MEMMAP_FLAG_PAGE_W ? "w" : "-",
pMemMapEntries[i].fPage & VMMDLL_MEMMAP_FLAG_PAGE_NX ? "-" : "x",
pMemMapEntries[i].szTag[0] ? (pMemMapEntries[i].fWoW64 ? " 32 " : " ") : "",
pMemMapEntries[i].szTag
);
}
} else {
printf("FAIL: VMMDLL_ProcessGetMemoryMap #2\n");
return 1;
}
// Retrieve the list of loaded DLLs from the process. Please note that this
// list is retrieved by parsing in-process memory structures such as the
// process environment block (PEB) which may be partly destroyed in some
// processes due to obfuscation and anti-reversing. If that is the case the
// memory map may use alternative parsing techniques to list DLLs.
printf("------------------------------------------------------------\n");
printf("#06: Get Module Map of 'explorer.exe'. \n");
ShowKeyPress();
ULONG64 cModules;
PVMMDLL_MODULEMAP_ENTRY pModules;
printf("CALL: VMMDLL_ProcessGetModuleMap #1\n");
result = VMMDLL_ProcessGetModuleMap(dwPID, NULL, &cModules);
if(result) {
printf("SUCCESS: VMMDLL_ProcessGetModuleMap #1\n");
printf(" Count = %lli\n", cModules);
} else {
printf("FAIL: VMMDLL_ProcessGetModuleMap #1\n");
return 1;
}
pModules = (PVMMDLL_MODULEMAP_ENTRY)LocalAlloc(0, cModules * sizeof(VMMDLL_MODULEMAP_ENTRY));
if(!pModules) {
printf("FAIL: OutOfMemory\n");
return 1;
}
printf("CALL: VMMDLL_ProcessGetModuleMap #2\n");
result = VMMDLL_ProcessGetModuleMap(dwPID, pModules, &cModules);
if(result) {
printf("SUCCESS: VMMDLL_ProcessGetModuleMap #2\n");
printf(" MODULE_NAME BASE SIZE ENTRY\n");
printf(" ======================================================================================\n");
for(i = 0; i < cModules; i++) {
printf(
" %-40.40s %i %016llx %08x %016llx\n",
pModules[i].szName,
pModules[i].fWoW64 ? 32 : 64,
pModules[i].BaseAddress,
pModules[i].SizeOfImage,
pModules[i].EntryPoint
);
}
} else {
printf("FAIL: VMMDLL_ProcessGetModuleMap #2\n");
return 1;
}
// Retrieve the module of crypt32.dll by its name. Note it is also possible
// to retrieve it by retrieving the complete module map (list) and iterate
// over it. But if the name of the module is known this is more convenient.
// This required that the PEB and LDR list in-process haven't been tampered
// with ...
printf("------------------------------------------------------------\n");
printf("#07: Get by name 'crypt32.dll' in 'explorer.exe'. \n");
ShowKeyPress();
VMMDLL_MODULEMAP_ENTRY ModuleEntry;
printf("CALL: VMMDLL_ProcessGetModuleFromName\n");
result = VMMDLL_ProcessGetModuleFromName(dwPID, "crypt32.dll", &ModuleEntry);
if(result) {
printf("SUCCESS: VMMDLL_ProcessGetModuleFromName\n");
printf(" MODULE_NAME BASE SIZE ENTRY\n");
printf(" ======================================================================================\n");
printf(
" %-40.40s %i %016llx %08x %016llx\n",
ModuleEntry.szName,
ModuleEntry.fWoW64 ? 32 : 64,
ModuleEntry.BaseAddress,
ModuleEntry.SizeOfImage,
ModuleEntry.EntryPoint
);
} else {
printf("FAIL: VMMDLL_ProcessGetModuleFromName\n");
return 1;
}
// Retrieve the memory at the base of crypt32.dll previously fetched and
// display the first 0x200 bytes of it. This read is fetched from the cache
// by default (if possible). If reads should be forced from the DMA device
// please specify the flag: VMM_FLAG_NOCACHE
printf("------------------------------------------------------------\n");
printf("#08: Read 0x200 bytes of 'crypt32.dll' in 'explorer.exe'. \n");
ShowKeyPress();
DWORD cRead;
printf("CALL: VMMDLL_MemReadEx\n");
result = VMMDLL_MemReadEx(dwPID, ModuleEntry.BaseAddress, pbPage2, 0x1000, &cRead, 0); // standard cached read
//result = VMMDLL_MemReadEx(dwPID, ModuleEntry.BaseAddress, pbPage2, 0x1000, &cRead, VMMDLL_FLAG_NOCACHE); // uncached read
if(result) {
printf("SUCCESS: VMMDLL_MemReadEx\n");
PrintHexAscii(pbPage2, min(cRead, 0x200));
} else {
printf("FAIL: VMMDLL_MemReadEx\n");
return 1;
}
// List the sections from the module of crypt32.dll.
printf("------------------------------------------------------------\n");
printf("#09: List sections of 'crypt32.dll' in 'explorer.exe'. \n");
ShowKeyPress();
DWORD cSections;
PIMAGE_SECTION_HEADER pSectionHeaders;
printf("CALL: VMMDLL_ProcessGetSections #1\n");
result = VMMDLL_ProcessGetSections(dwPID, "crypt32.dll", NULL, 0, &cSections);
if(result) {
printf("SUCCESS: VMMDLL_ProcessGetSections #1\n");
printf(" Count = %lli\n", cModules);
} else {
printf("FAIL: VMMDLL_ProcessGetSections #1\n");
return 1;
}
pSectionHeaders = (PIMAGE_SECTION_HEADER)LocalAlloc(LMEM_ZEROINIT, cSections * sizeof(IMAGE_SECTION_HEADER));
if(!pModules) {
printf("FAIL: OutOfMemory\n");
return 1;
}
printf("CALL: VMMDLL_ProcessGetSections #2\n");
result = VMMDLL_ProcessGetSections(dwPID, "crypt32.dll", pSectionHeaders, cSections, &cSections);
if(result) {
printf("SUCCESS: VMMDLL_ProcessGetSections #2\n");
printf(" # NAME OFFSET SIZE RWX\n");
printf(" =================================\n");
for(i = 0; i < cSections; i++) {
printf(
" %02lx %-8.8s %08x %08x %c%c%c\n",
i,
pSectionHeaders[i].Name,
pSectionHeaders[i].VirtualAddress,
pSectionHeaders[i].Misc.VirtualSize,
(pSectionHeaders[i].Characteristics & IMAGE_SCN_MEM_READ) ? 'r' : '-',
(pSectionHeaders[i].Characteristics & IMAGE_SCN_MEM_WRITE) ? 'w' : '-',
(pSectionHeaders[i].Characteristics & IMAGE_SCN_MEM_EXECUTE) ? 'x' : '-'
);
}
} else {
printf("FAIL: VMMDLL_ProcessGetSections #2\n");
return 1;
}
// Retrieve and display the data directories of crypt32.dll. The number of
// data directories in a PE is always 16 - so this can be used to simplify
// calling the functionality somewhat.
printf("------------------------------------------------------------\n");
printf("#10: List directories of 'crypt32.dll' in 'explorer.exe'. \n");
ShowKeyPress();
LPCSTR DIRECTORIES[16] = { "EXPORT", "IMPORT", "RESOURCE", "EXCEPTION", "SECURITY", "BASERELOC", "DEBUG", "ARCHITECTURE", "GLOBALPTR", "TLS", "LOAD_CONFIG", "BOUND_IMPORT", "IAT", "DELAY_IMPORT", "COM_DESCRIPTOR", "RESERVED" };
DWORD cDirectories;
IMAGE_DATA_DIRECTORY pDirectories[16];
printf("CALL: VMMDLL_ProcessGetDirectories\n");
result = VMMDLL_ProcessGetDirectories(dwPID, "crypt32.dll", pDirectories, 16, &cDirectories);
if(result) {
printf("SUCCESS: PCIleech_VmmProcess_GetDirectories\n");
printf(" # NAME OFFSET SIZE\n");
printf(" =====================================\n");
for(i = 0; i < 16; i++) {
printf(
" %02lx %-16.16s %08x %08x\n",
i,
DIRECTORIES[i],
pDirectories[i].VirtualAddress,
pDirectories[i].Size
);
}
} else {
printf("FAIL: VMMDLL_ProcessGetDirectories\n");
return 1;
}
// Retrieve the export address table (EAT) of crypt32.dll
printf("------------------------------------------------------------\n");
printf("#11: exports of 'crypt32.dll' in 'explorer.exe'. \n");
ShowKeyPress();
DWORD cEATs;
PVMMDLL_EAT_ENTRY pEATs;
printf("CALL: VMMDLL_ProcessGetEAT #1\n");
result = VMMDLL_ProcessGetEAT(dwPID, "crypt32.dll", NULL, 0, &cEATs);
if(result) {
printf("SUCCESS: VMMDLL_ProcessGetEAT #1\n");
printf(" Count = %i\n", cEATs);
} else {
printf("FAIL: VMMDLL_ProcessGetEAT #1\n");
return 1;
}
pEATs = (PVMMDLL_EAT_ENTRY)LocalAlloc(LMEM_ZEROINIT, cEATs * sizeof(VMMDLL_EAT_ENTRY));
if(!pEATs) {
printf("FAIL: OutOfMemory\n");
return 1;
}
printf("CALL: VMMDLL_ProcessGetEAT #2\n");
result = VMMDLL_ProcessGetEAT(dwPID, "crypt32.dll", pEATs, cEATs, &cEATs);
if(result) {
printf("SUCCESS: VMMDLL_ProcessGetEAT #2\n");
printf(" # OFFSET NAME\n");
printf(" =================================\n");
for(i = 0; i < cEATs; i++) {
printf(
" %04lx %08x %s\n",
i,
pEATs[i].vaFunctionOffset,
pEATs[i].szFunction
);
}
} else {
printf("FAIL: VMMDLL_ProcessGetEAT #2\n");
return 1;
}
// Retrieve the import address table (IAT) of crypt32.dll
printf("------------------------------------------------------------\n");
printf("#12: imports of 'crypt32.dll' in 'explorer.exe'. \n");
ShowKeyPress();
DWORD cIATs;
PVMMDLL_IAT_ENTRY pIATs;
printf("CALL: VMMDLL_ProcessGetIAT #1\n");
result = VMMDLL_ProcessGetIAT(dwPID, "crypt32.dll", NULL, 0, &cIATs);
if(result) {
printf("SUCCESS: VMMDLL_ProcessGetIAT #1\n");
printf(" Count = %i\n", cIATs);
} else {
printf("FAIL: VMMDLL_ProcessGetIAT #1\n");
return 1;
}
pIATs = (PVMMDLL_IAT_ENTRY)LocalAlloc(LMEM_ZEROINIT, cIATs * sizeof(VMMDLL_IAT_ENTRY));
if(!pIATs) {
printf("FAIL: OutOfMemory\n");
return 1;
}
printf("CALL: VMMDLL_ProcessGetIAT #2\n");
result = VMMDLL_ProcessGetIAT(dwPID, "crypt32.dll", pIATs, cIATs, &cIATs);
if(result) {
printf("SUCCESS: VMMDLL_ProcessGetIAT #2\n");
printf(" # VIRTUAL_ADDRESS MODULE!NAME\n");
printf(" ===================================\n");
for(i = 0; i < cIATs; i++) {
printf(
" %04lx %016llx %s!%s\n",
i,
pIATs[i].vaFunction,
pIATs[i].szModule,
pIATs[i].szFunction
);
}
} else {
printf("FAIL: VMMDLL_ProcessGetIAT #2\n");
return 1;
}
// The Memory Process File System exists virtually in the form of a virtual
// file system even if it may not be mounted at a mount point or drive.
// It is possible to call the functions 'List', 'Read' and 'Write' by using
// the API.
// Virtual File System: 'List'.
printf("------------------------------------------------------------\n");
printf("#13: call the file system 'List' function on the root dir. \n");
ShowKeyPress();
VMMDLL_VFS_FILELIST VfsFileList;
VfsFileList.h = 0; // your handle passed to the callback functions (not used in example).
VfsFileList.pfnAddDirectory = CallbackList_AddDirectory;
VfsFileList.pfnAddFile = CallbackList_AddFile;
printf("CALL: VMMDLL_VfsList\n");
result = VMMDLL_VfsList(L"\\", &VfsFileList);
if(result) {
printf("SUCCESS: VMMDLL_VfsList\n");
} else {
printf("FAIL: VMMDLL_VfsList\n");
return 1;
}
// Virtual File System: 'Read' of 0x100 bytes from the offset 0x1000
// in the physical memory by reading the /pmem physical memory file.
printf("------------------------------------------------------------\n");
printf("#14: call the file system 'Read' function on the pmem file. \n");
ShowKeyPress();
printf("CALL: VMMDLL_VfsRead\n");
nt = VMMDLL_VfsRead(L"\\pmem", pbPage1, 0x100, &i, 0x1000);
if(nt == VMMDLL_STATUS_SUCCESS) {
printf("SUCCESS: VMMDLL_VfsRead\n");
PrintHexAscii(pbPage1, i);
} else {
printf("FAIL: VMMDLL_VfsRead\n");
return 1;
}
// Initialize plugin manager so that statistics may be read in the
// following read call to the .status built-in module/plugin.
printf("------------------------------------------------------------\n");
printf("#15: initialize virtual file system plugins \n");
printf(" (this is required for following read call) \n");
ShowKeyPress();
printf("CALL: VMMDLL_VfsInitializePlugins\n");
result = VMMDLL_VfsInitializePlugins();
if(result) {
printf("SUCCESS: VMMDLL_VfsInitializePlugins\n");
} else {
printf("FAIL: VMMDLL_VfsInitializePlugins\n");
return 1;
}
// Virtual File System: 'Read' statistics from the .status module/plugin.
printf("------------------------------------------------------------\n");
printf("#16: call file system 'Read' on .status\\statistics \n");
ShowKeyPress();
printf("CALL: VMMDLL_VfsRead\n");
nt = VMMDLL_VfsRead(L"\\.status\\statistics", pbPage1, 0x1000, &i, 0);
if(nt == VMMDLL_STATUS_SUCCESS) {
printf("SUCCESS: VMMDLL_VfsRead\n");
printf("%s", (LPSTR)pbPage1);
} else {
printf("FAIL: VMMDLL_VfsRead\n");
return 1;
}
// Finish everything and exit!
printf("------------------------------------------------------------\n");
printf("#99: FINISHED EXAMPLES! \n");
ShowKeyPress();
printf("FINISHED TEST CASES - EXITING!\n");
return 0;
}