I was trying to inject a shared library in another process and I managed to get it working on x64. Though, when I tried using it for 32bits, something weird is happening: ptrace is not being able to execute properly due to an Input/Output error (errno 5). I don't know what to do, since this same code worked for x64.
Then, I tried to make a smaller example using a function that I called test_ptrace. Surprisingly, the error doesn't happen there, though it is doing essentially the same thing (allocate memory on target process, inject a payload, set registers to match the payload, run the payload). When I saw the error was not happening, I tried again injecting the shared library with ptrace using a function called load_library. But unfortunately, there the error was again.
//this is the function that is NOT working, 'load_library'
void* load_library(pid_t pid, std::string path, int mode)
{
int status;
struct user_regs_struct old_regs, regs;
void* dlopen_ex = (void*)0xf7c29700; //I disabled ASLR, so this address does not change
void* handle_ex = (void*)-1;
unsigned char inj_buf[] =
{
0x51, //push ecx
0x53, //push ebx
0xFF, 0xD0, //call eax
0xCC, //int3 (SIGTRAP)
};
size_t path_size = path.size();
size_t inj_size = sizeof(inj_buf) + path_size;
void* inj_addr = allocate_memory(pid, inj_size, PROT_EXEC | PROT_READ | PROT_WRITE);
void* path_addr = (void*)((uintptr_t)inj_addr + sizeof(inj_buf));
write_memory(pid, inj_addr, (void*)inj_buf, sizeof(inj_buf));
write_memory(pid, path_addr, (void*)path.c_str(), path_size);
if(ptrace(PTRACE_ATTACH, pid, NULL, NULL))
{
perror("PTRACE_ATTACH");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
wait(&status);
if(ptrace(PTRACE_GETREGS, pid, NULL, &old_regs) == -1)
{
perror("PTRACE_GETREGS");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
regs.eax = (unsigned long)dlopen_ex;
regs.ebx = (unsigned long)path_addr;
regs.ecx = (unsigned long)mode;
regs.eip = (unsigned long)inj_addr;
if(ptrace(PTRACE_SETREGS, pid, NULL, ®s) == -1)
{
perror("PTRACE_SETREGS");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
if(ptrace(PTRACE_CONT, pid, NULL, NULL) == -1)
{
perror("PTRACE_CONT");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
waitpid(pid, &status, WSTOPPED);
if(ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1)
{
perror("PTRACE_GETREGS");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
handle_ex = (void*)old_regs.eax;
if(ptrace(PTRACE_SETREGS, pid, NULL, &old_regs) == -1)
{
perror("PTRACE_SETREGS");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
if(ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1)
{
perror("PTRACE_DETACH");
std::cout << "Errno: " << errno << std::endl;
return handle_ex;
}
deallocate_memory(pid, inj_addr, inj_size);
return handle_ex;
}
//this one, though, is working, but it is very similar to the function
//above (except it doesn't restore the execution, but the code of the
//other function doesn't even get there anyway.
void test_ptrace(pid_t pid)
{
int status;
struct user_regs_struct regs;
unsigned char inj_buf[] =
{
0xCD, 0x80, //int80 (syscall)
0xCC, //int3 (SIGTRAP)
};
void* inj_addr = allocate_memory(pid, sizeof(inj_buf), PROT_EXEC | PROT_READ | PROT_WRITE);
write_memory(pid, inj_addr, inj_buf, sizeof(inj_buf));
std::cout << "--ptrace test started--" << std::endl;
if(ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1)
{
perror("PTRACE_ATTACH");
std::cout << "Errno: " << errno << std::endl;
return;
}
wait(&status);
if(ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1)
{
perror("PTRACE_GETREGS");
std::cout << "Errno: " << errno << std::endl;
return;
}
regs.eax = __NR_exit;
regs.ebx = 222;
regs.eip = (unsigned long)inj_addr;
if(ptrace(PTRACE_SETREGS, pid, NULL, ®s) == -1)
{
perror("PTRACE_SETREGS");
std::cout << "Errno: " << errno << std::endl;
return;
}
if(ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1)
{
perror("PTRACE_DETACH");
std::cout << "Errno: " << errno << std::endl;
return;
}
std::cout << "--ptrace test ended--" << std::endl;
}
Program entry:
int main()
{
pid_t pid = get_process_id("target");
std::cout << "PID: " << pid << std::endl;
std::string lib_path = "<my_path>/ptrace-test/libtest.so";
load_library(pid, lib_path, RTLD_LAZY);
return 0;
}
Output:
PID: 2383
PTRACE_SETREGS: Input/output error
Errno: 5
If you need the whole project as a 'minimal' reproducible example, here you go: https://github.com/rdbo/ptrace-test
The PID is correct, I'm running as root, both the tracer and the tracee are compiled with G++ on 32 bits. Running up-to-date Manjaro. Any ideas?
I fixed it. Don't ask me how, though, I have no clue. I followed the exact same logic and out of sudden it worked. I used the working test_ptrace and kept putting the load_library code on it line by line to see what could be causing the problem. Turns out, I got it fixed and still don't know what it was. Anyways, here's the code (it is a bit messed up, because I didn't expect it to work):
void load_library(pid_t pid, std::string lib_path)
{
int status;
struct user_regs_struct old_regs, regs;
unsigned char inj_buf[] =
{
0x51, //push ecx
0x53, //push ebx
0xFF, 0xD0, //call eax
0xCC, //int3 (SIGTRAP)
};
size_t inj_size = sizeof(inj_buf) + lib_path.size();
void* inj_addr = allocate_memory(pid, inj_size, PROT_EXEC | PROT_READ | PROT_WRITE);
void* path_addr = (void*)((uintptr_t)inj_addr + sizeof(inj_buf));
write_memory(pid, inj_addr, inj_buf, sizeof(inj_buf));
write_memory(pid, path_addr, (void*)lib_path.c_str(), lib_path.size());
std::cout << "--ptrace test started--" << std::endl;
if(ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1)
{
perror("PTRACE_ATTACH");
std::cout << "Errno: " << errno << std::endl;
return;
}
wait(&status);
if(ptrace(PTRACE_GETREGS, pid, NULL, &old_regs) == -1)
{
perror("PTRACE_GETREGS");
std::cout << "Errno: " << errno << std::endl;
return;
}
regs = old_regs;
long dlopen_ex = 0xf7c28700;
regs.eax = dlopen_ex;
regs.ebx = (long)path_addr;
regs.ecx = RTLD_LAZY;
regs.eip = (unsigned long)inj_addr;
if(ptrace(PTRACE_SETREGS, pid, NULL, ®s) == -1)
{
perror("PTRACE_SETREGS");
std::cout << "Errno: " << errno << std::endl;
return;
}
ptrace(PTRACE_CONT, pid, NULL, NULL);
waitpid(pid, &status, WSTOPPED);
ptrace(PTRACE_SETREGS, pid, NULL, &old_regs);
if(ptrace(PTRACE_DETACH, pid, NULL, NULL) == -1)
{
perror("PTRACE_DETACH");
std::cout << "Errno: " << errno << std::endl;
return;
}
deallocate_memory(pid, inj_addr, inj_size);
std::cout << "--ptrace test ended--" << std::endl;
}
Output:
PID: 24615
--ptrace test started--
--ptrace test ended--
Target Process:
PID: 24615
dlopen: 0xf7c28700
Waiting...
Injected!
Waiting...
Waiting...
Waiting...
Waiting...
EDIT:
I just remembered I ran the following command as root (could've been it, not sure though):
echo 0 > /proc/sys/kernel/yama/ptrace_scope
EDIT2: It was not it, the code is still working after a reboot. Also, I improved the code a bit on the GitHub repository.
Related
I wrote a simple inter-process communication with a memory-mapped file. The code works relatively well, but I have a problem with the buffer that I'll explain shortly. Here is the code (C++, Windows):
#define UNICODE
#define _UNICODE
#include <iostream>
#include <tchar.h>
#include <Windows.h>
int wmain(int argc, wchar_t** argv)
{
if (argc != 2)
{
std::cout << "Usage: `win32mmap w` for writing, or `win32mmap r` for reading.\n";
return -1;
}
HANDLE hMapFile;
HANDLE hEvent;
HANDLE isOpened = CreateEvent(NULL, true, false, L"IsOpened"); // To check if a `win32mmap w` runs
if (wcscmp(argv[1], L"w") == 0)
{
SetEvent(isOpened);
hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, L"mmapFile");
if (hMapFile == NULL)
{
std::cout << "CreateFileMapping() Error: " << GetLastError() << "\n";
return GetLastError();
}
hEvent = CreateEvent(NULL, true, false, L"mmapEvent");
if (hEvent == INVALID_HANDLE_VALUE || hEvent == NULL)
{
std::cout << "CreateEvent() Error: " << GetLastError() << "\n";
return GetLastError();
}
char* buff = (char*)MapViewOfFile(hMapFile, FILE_MAP_WRITE, 0, 0, 0);
if (!buff)
{
std::cout << "MapViewOfFile() Error: " << GetLastError() << "\n";
return GetLastError();
}
while (buff[0] != L'.')
{
std::cin >> buff;
SetEvent(hEvent);
}
UnmapViewOfFile(buff);
}
else if (wcscmp(argv[1], L"r") == 0)
{
if (WaitForSingleObject(isOpened, 0) == WAIT_TIMEOUT)
{
std::cout << "Waiting for `win32mmap w`...";
WaitForSingleObject(isOpened, INFINITE);
std::cout << "\n";
}
hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, L"mmapFile");
if (hMapFile == NULL)
{
std::cout << "CreateFileMapping() Error: " << GetLastError() << "\n";
return GetLastError();
}
hEvent = OpenEvent(EVENT_ALL_ACCESS, false, L"mmapEvent");
if (hEvent == INVALID_HANDLE_VALUE || hEvent == NULL)
{
std::cout << "CreateFile() Error: " << GetLastError() << "\n";
return GetLastError();
}
char* buff = (char*)MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0);
if (!buff)
{
std::cout << "MapViewOfFile() Error: " << GetLastError() << "\n";
return GetLastError();
}
if (!buff)
{
std::cout << "MapViewOfFile() Error: " << GetLastError() << "\n";
return GetLastError();
}
while (true)
{
WaitForSingleObject(hEvent, INFINITE);
ResetEvent(hEvent);
if (buff[0] == '.')
{
break;
}
std::cout << buff << "\n";
}
UnmapViewOfFile(buff);
}
else
{
std::cout << "Usage: `win32mmap w` for writing, or `win32mmap r` for reading.\n";
return -1;
}
CloseHandle(hMapFile);
return 0;
}
The program is a simple inter-process communication "chat" that relies on memory-mapped files. To use the program, you need to make two executable instance of the program: win32mmap w and win32mmap r. The first instance is used to type text that is displayed in the second instance. When you type . in the first instance, both of them are terminated.
My problem is when I run the 2 instances of the program, and I type the world Hello in the first instance (win32mmap w), the second instance shows Hello as expected. But when I type Hello World in the first instance, the second instance shows only the word World instead of Hello World. How can I fix the code that the buffer will get the whole text?
Your writer is not waiting for the reader to consume the data before overwriting it with new data.
You need 2 events - one for the reader to wait on signaling when the buffer has data to read, and one for the writer to wait on signaling when the buffer needs data.
Try this instead:
#define UNICODE
#define _UNICODE
#include <iostream>
#include <tchar.h>
#include <Windows.h>
const DWORD BufSize = 1024;
int wmain(int argc, wchar_t** argv)
{
if (argc != 2)
{
std::cout << "Usage: `win32mmap w` for writing, or `win32mmap r` for reading.\n";
return -1;
}
HANDLE hMapFile;
char* buff;
HANDLE hNeedDataEvent;
HANDLE hHasDataEvent;
DWORD dwError;
HANDLE isOpened = CreateEvent(NULL, TRUE, FALSE, L"IsOpened"); // To check if a `win32mmap w` runs
if (isOpened == NULL)
{
dwError = GetLastError();
std::cout << "CreateEvent() Error: " << dwError << "\n";
return dwError;
}
if (wcscmp(argv[1], L"w") == 0)
{
hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, BufSize, L"mmapFile");
if (hMapFile == NULL)
{
dwError = GetLastError();
std::cout << "CreateFileMapping() Error: " << dwError << "\n";
SetEvent(isOpened);
return dwError;
}
buff = (char*) MapViewOfFile(hMapFile, FILE_MAP_WRITE, 0, 0, BufSize);
if (!buff)
{
dwError = GetLastError();
std::cout << "MapViewOfFile() Error: " << dwError << "\n";
SetEvent(isOpened);
return dwError;
}
hNeedDataEvent = CreateEvent(NULL, TRUE, TRUE, L"mmapNeedDataEvent");
if (hNeedDataEvent == NULL)
{
dwError = GetLastError();
std::cout << "CreateEvent() Error: " << dwError << "\n";
SetEvent(isOpened);
return dwError;
}
hHasDataEvent = CreateEvent(NULL, TRUE, FALSE, L"mmapHasDataEvent");
if (hHasDataEvent == NULL)
{
dwError = GetLastError();
std::cout << "CreateEvent() Error: " << dwError << "\n";
SetEvent(isOpened);
return dwError;
}
SetEvent(isOpened);
while (WaitForSingleObject(hNeedDataEvent, INFINITE) == WAIT_OBJECT_0)
{
std::cin.get(buff, BufSize);
ResetEvent(hNeedDataEvent);
SetEvent(hHasDataEvent);
if (buff[0] == L'.') break;
}
}
else if (wcscmp(argv[1], L"r") == 0)
{
if (WaitForSingleObject(isOpened, 0) == WAIT_TIMEOUT)
{
std::cout << "Waiting for `win32mmap w`...";
WaitForSingleObject(isOpened, INFINITE);
std::cout << "\n";
}
hMapFile = OpenFileMapping(FILE_MAP_READ, FALSE, L"mmapFile");
if (hMapFile == NULL)
{
dwError = GetLastError();
std::cout << "CreateFileMapping() Error: " << dwError << "\n";
return dwError;
}
char* buff = (char*) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, BufSize);
if (!buff)
{
dwError = GetLastError();
std::cout << "MapViewOfFile() Error: " << dwError << "\n";
return dwError;
}
hNeedDataEvent = OpenEvent(SYNCHRONIZE, FALSE, L"mmapNeedDataEvent");
if (hNeedDataEvent == NULL)
{
dwError = GetLastError();
std::cout << "OpenEvent() Error: " << dwError << "\n";
return dwError;
}
hHasDataEvent = OpenEvent(SYNCHRONIZE, FALSE, L"mmapHasDataEvent");
if (hHasDataEvent == NULL)
{
dwError = GetLastError();
std::cout << "OpenEvent() Error: " << dwError << "\n";
return dwError;
}
do
{
SetEvent(hNeedDataEvent);
if (WaitForSingleObject(hHasDataEvent, INFINITE) != WAIT_OBJECT_0)
break;
std::cout << buff << "\n";
ResetEvent(hHasDataEvent);
}
while (buff[0] != '.');
}
else
{
std::cout << "Usage: `win32mmap w` for writing, or `win32mmap r` for reading.\n";
return -1;
}
UnmapViewOfFile(buff);
CloseHandle(hMapFile);
CloseHandle(hNeedDataEvent);
CloseHandle(hHasDataEvent);
CloseHandle(isOpened);
return 0;
}
I am trying out VirtualDisk APIs,
So far I am able to Open a VHDX file, get some of the properties by using GetVirtualDiskInformation.
But I am not able to get RCT information and ChangedAreas.
The first call to GetVirtualDiskInformation is successful.
The second call to GetVirtualDiskInformation fails with insufficient buffer ERROR_INSUFFICIENT_BUFFER(122).
Call to QueryChangesVirtualDisk fails with ERROR_INVALID_HANDLE(6).
The RCT ID hardcoded in the code is proper, I am able to get the ChangedAreas using WMI Explorer.
Attached screenshot of the same.
If it is invalid handle, then GetVirtualDiskInformation should also throw the same error?
#include "pch.h"
#include <iostream>
#include <string>
#include <cstdlib>
#define WINVER _WIN32_WINNT_WIN10
#include <windows.h>
#include <winioctl.h>
#include <virtdisk.h>
#include <initguid.h>
#pragma comment(lib, "virtdisk.lib")
DEFINE_GUID(VIRTUAL_STORAGE_TYPE_VENDOR_MICROSOFT, 0xec984aec, 0xa0f9, 0x47e9, 0x90, 0x1f, 0x71, 0x41, 0x5a, 0x66, 0x34, 0x5b);
DEFINE_GUID(VIRTUAL_STORAGE_TYPE_VENDOR_UNKNOWN, 0x00000000, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00);
int main()
{
HANDLE vhdHandle;
_VIRTUAL_STORAGE_TYPE storageType;
storageType.DeviceId = VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN;
storageType.VendorId = VIRTUAL_STORAGE_TYPE_VENDOR_UNKNOWN;
wchar_t path[] = L"C:\\Hyper-V\\Virtual Hard Disks\\Lacazette\\Windows2016.vhdx";
VIRTUAL_DISK_ACCESS_MASK mask = VIRTUAL_DISK_ACCESS_GET_INFO;
PGET_VIRTUAL_DISK_INFO diskInfo;
ULONG diskInfoSize = sizeof(GET_VIRTUAL_DISK_INFO);
std::wcout << "size of diskinfo structure " << diskInfoSize << std::endl;
diskInfo = (PGET_VIRTUAL_DISK_INFO)malloc(diskInfoSize);
if (diskInfo == NULL)
{
std::cout << "Failed to malloc disk info, ret=" << std::endl;
return 0;
}
std::wcout << "Opening Virtual disk " << path << std::endl;
DWORD res = OpenVirtualDisk(&storageType, path,
VIRTUAL_DISK_ACCESS_GET_INFO,
OPEN_VIRTUAL_DISK_FLAG_NO_PARENTS,
NULL,
&vhdHandle);
if (res != ERROR_SUCCESS)
{
std::cout << "Failed to open disk, ret=" << res << std::endl;
return 0;
}
diskInfo->Version = GET_VIRTUAL_DISK_INFO_SIZE;
res = GetVirtualDiskInformation(vhdHandle, &diskInfoSize, diskInfo, NULL);
if (res != ERROR_SUCCESS)
{
std::cout << "Failed to GET_VIRTUAL_DISK_INFO_SIZE, ret=" << res << std::endl;
}
long physicalSize = diskInfo->Size.PhysicalSize;
long virtualSize = diskInfo->Size.VirtualSize;
long sectorSize = diskInfo->Size.SectorSize;
long blockSize = diskInfo->Size.BlockSize;
std::wcout << "physicalSize :" << physicalSize << std::endl;
std::wcout << "virtualSize :" << virtualSize << std::endl;
std::wcout << "sectorSize :" << sectorSize << std::endl;
std::wcout << "blockSize :" << blockSize << std::endl;
diskInfo->Version = GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE;
res = GetVirtualDiskInformation(vhdHandle, &diskInfoSize, diskInfo, NULL);
if (res != ERROR_SUCCESS)
{
std::cout << "Failed to GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE, ret=" << res << std::endl;
}
std::cout << "\nrct id:" << diskInfo->ChangeTrackingState.MostRecentId << std::endl;
std::cout << "\nQuerying for changed disk areas...\n" << std::endl;
wchar_t rctId[] = L"rctX:c2eb01d9:ccb1:405d:acb6:f0e76d055906:00000001";
ULONG64 byteOffset = 0L;
ULONG64 byteLength = 19327352832;
QUERY_CHANGES_VIRTUAL_DISK_RANGE* changedAreas = NULL;
ULONG rangeCount = 0L;
ULONG64 processedLength = 0L;
res = QueryChangesVirtualDisk(&vhdHandle, rctId, byteOffset, byteLength,
QUERY_CHANGES_VIRTUAL_DISK_FLAG_NONE,
changedAreas, &rangeCount, &processedLength);
if (res != ERROR_SUCCESS)
{
std::cout << "Failed to get chanegd areas, ret=" << res << std::endl;
if (vhdHandle != NULL)
{
CloseHandle(vhdHandle);
std::cout << "closing handle!" <<std::endl;
}
return 0;
}
std::cout << "Total changed areas:" << rangeCount << std::endl;
std::cout << "Total processed length:" << processedLength << std::endl;
if (vhdHandle != NULL)
{
CloseHandle(vhdHandle);
}
return 0;
}
Output of the program:
Screenshot of wmi explorer output.
Here's something that more or less works.. the issue was with the format of the rctid which I was passing to the QueryChangesVirtualDisk API. Should be as in the below sample. I was enclosing it inside the braces {} which was not correct. The below code returns RCT changed blocks successfully
Another observation is that it seems the ChangeTrackingState.MostRecentId which we get from GetVirtualDiskInformation for a offline vhdx is one more than what we initially created while the VM was up.. For example, I created a reference point with rctid: rctX:4abdf273:7e45:4748:85af:78ec4af82ebf:00000000 Now when the vhdx is offline and if I query the ChangeTrackingState.MostRecentId it is giving me this : rctX:4abdf273:7e45:4748:85af:78ec4af82ebf:00000001 which fails the QueryChangesVirtualDisk with invalid rctid error. The below function expects the user to pass the correct rctid externally. Apparently, the Get-VmReferencePoints still lists only the earlier 00000 reference point.. This one (00001) seems to be returned only by the GetVirtualDiskInformation API
Finally, for the other issue of getting access denied with a live VM, it seems we have to set the VIRTUAL_DISK_ACCESS_MASK in OpenVirtualDisk to 0 along with version 3 of OPEN_VIRTUAL_DISK_PARAMETERS as shown below. With this we we are able to query RCT CBT from a live checkpointed VM's base disk. Got clarified from https://github.com/cloudbase/rct-service/issues/1
#include <windows.h>
#include <virtdisk.h>
#include <iostream>
bool QueryRCTData(const std::wstring& file, const std::wstring& rctid)
{
DWORD fRet;
HANDLE hVHD;
VIRTUAL_STORAGE_TYPE vst =
{
VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN,
{0xEC984AEC, 0xA0F9, 0x47e9, { 0x90, 0x1F, 0x71, 0x41, 0x5A, 0x66, 0x34, 0x5B }}
};
OPEN_VIRTUAL_DISK_PARAMETERS params = { 0 };
params.Version = OPEN_VIRTUAL_DISK_VERSION_3;
DWORD fRet = OpenVirtualDisk(
&vst,
file.c_str(),
(VIRTUAL_DISK_ACCESS_MASK) 0, //VIRTUAL_DISK_ACCESS_ALL, //VIRTUAL_DISK_ACCESS_GET_INFO,
OPEN_VIRTUAL_DISK_FLAG_NONE,
¶ms,
&hvhd);
if (fRet != ERROR_SUCCESS)
{
std::cout << "OpenVirtualDisk failed " << fRet << "\n";
return false;
}
//query rctid
WCHAR vdinfo[2048];
ZeroMemory(vdinfo, sizeof(vdinfo));
((GET_VIRTUAL_DISK_INFO *)vdinfo)->Version = GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE;
ULONG vdinfoSize = sizeof(vdinfo);
fRet = GetVirtualDiskInformation(
hVHD,
&vdinfoSize,
(PGET_VIRTUAL_DISK_INFO)vdinfo,
NULL);
if (fRet != ERROR_SUCCESS)
{
std::cout << "GetVirtualDiskInformation GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE failed " << fRet << "\n";
return false;
}
std::wcout << "RCT ID : " << std::wstring(((GET_VIRTUAL_DISK_INFO *)vdinfo)->ChangeTrackingState.MostRecentId) << "\n";
//query disk length
ZeroMemory(vdinfo, sizeof(vdinfo));
((GET_VIRTUAL_DISK_INFO *)vdinfo)->Version = GET_VIRTUAL_DISK_INFO_SIZE;
vdinfoSize = sizeof(vdinfo);
fRet = GetVirtualDiskInformation(
hVHD,
&vdinfoSize,
(PGET_VIRTUAL_DISK_INFO)vdinfo,
NULL);
if (fRet != ERROR_SUCCESS)
{
std::cout << "GetVirtualDiskInformation GET_VIRTUAL_DISK_INFO_SIZE failed " << fRet << "\n";
return false;
}
std::cout << "Disk length : " << ((GET_VIRTUAL_DISK_INFO *)vdinfo)->Size.VirtualSize << "\n";
ULONG RangeCount = 1024;
ULONG64 ProcessedLength = 0;
QUERY_CHANGES_VIRTUAL_DISK_RANGE Ranges[1024] = { 0 };
fRet = QueryChangesVirtualDisk(
hVHD,
rctid.c_str(), //((GET_VIRTUAL_DISK_INFO *)vdinfo)->ChangeTrackingState.MostRecentId,
0,
((GET_VIRTUAL_DISK_INFO *)vdinfo)->Size.VirtualSize,
QUERY_CHANGES_VIRTUAL_DISK_FLAG_NONE,
Ranges,
&RangeCount,
&ProcessedLength);
if (fRet != ERROR_SUCCESS)
{
std::cout << "QueryChangesVirtualDisk failed " << fRet << "\n";
return false;
}
std::cout << "RangeCount : " << RangeCount << "\n";
std::cout << "ProcessedLength : " << ProcessedLength << "\n";
for (int i = 0; i < RangeCount; i++)
{
std::cout << "Range offset : " << Ranges[i].ByteOffset << "\n";
std::cout << "Range length : " << Ranges[i].ByteLength << "\n";
}
CloseHandle(hVHD);
return true;
}
int main(void)
{
QueryRCTData(L"D:\\BACKUP\\INCR\\Virtual Hard Disks\\2019.vhdx", L"rctX:4abdf273:7e45:4748:85af:78ec4af82ebf:00000000");
return 0;
}
#pragma comment(lib, "virtdisk.lib")
Here is my working code for getting changed blocks from offline VHDX.
(most credit goes to Neelabh Mam who posted above)
Hope it might help someone struggling to make RCT work with C++.
BOOLEAN VHDWRAP_CONVENTION CloseVhd(HANDLE hDisk)
{
return CloseHandle(hDisk) == TRUE;
}
HANDLE VHDWRAP_CONVENTION OpenVhdx(PCWSTR path, unsigned mask)
{
DWORD fRet;
HANDLE hVHD;
VIRTUAL_STORAGE_TYPE vst =
{
VIRTUAL_STORAGE_TYPE_DEVICE_UNKNOWN,
{0xEC984AEC, 0xA0F9, 0x47e9, { 0x90, 0x1F, 0x71, 0x41, 0x5A, 0x66, 0x34, 0x5B }}
};
OPEN_VIRTUAL_DISK_PARAMETERS params = { 0 };
params.Version = OPEN_VIRTUAL_DISK_VERSION_3;
fRet = OpenVirtualDisk(
&vst,
path, (VIRTUAL_DISK_ACCESS_MASK)mask,
OPEN_VIRTUAL_DISK_FLAG_NONE,
¶ms,
&hVHD);
g_dwLastError = fRet;
return hVHD;
}
VHDWRAP_API BOOLEAN VHDWRAP_CONVENTION EnableVhdRct(HANDLE hVHD)
{
SET_VIRTUAL_DISK_INFO vdinfo;
vdinfo.Version = SET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE;
vdinfo.ChangeTrackingEnabled = TRUE;
DWORD fRet = SetVirtualDiskInformation(hVHD, &vdinfo);
g_dwLastError = fRet;
return fRet == 0;
}
VHDWRAP_API BOOLEAN VHDWRAP_CONVENTION DisableVhdRct(HANDLE hVHD)
{
SET_VIRTUAL_DISK_INFO vdinfo;
vdinfo.Version = SET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE;
vdinfo.ChangeTrackingEnabled = FALSE;
DWORD fRet = SetVirtualDiskInformation(hVHD, &vdinfo);
g_dwLastError = fRet;
return fRet == 0;
}
VHDWRAP_API BOOLEAN VHDWRAP_CONVENTION QueryVhdRct(HANDLE hVHD, LPCWSTR szRctId)
{
//query rctid
WCHAR vdinfo[1024];
ZeroMemory(vdinfo, sizeof(vdinfo));
((GET_VIRTUAL_DISK_INFO*)vdinfo)->Version = GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE;
ULONG vdinfoSize = sizeof(vdinfo);
DWORD fRet = GetVirtualDiskInformation(
hVHD,
&vdinfoSize,
(PGET_VIRTUAL_DISK_INFO)vdinfo,
NULL);
g_dwLastError = fRet;
if (fRet != ERROR_SUCCESS)
{
std::cout << "GetVirtualDiskInformation GET_VIRTUAL_DISK_INFO_CHANGE_TRACKING_STATE failed " << fRet << "\n";
return false;
}
std::wcout << L"RCT status : " << ((((GET_VIRTUAL_DISK_INFO*)vdinfo)->ChangeTrackingState.Enabled) ? L"enabled" : L"disabled") << L"\n";
std::wcout << L"RCT ID : " << std::wstring(((GET_VIRTUAL_DISK_INFO*)vdinfo)->ChangeTrackingState.MostRecentId) << "\n";
std::wcout << L"RCT changes : " << ((((GET_VIRTUAL_DISK_INFO*)vdinfo)->ChangeTrackingState.NewerChanges) ? L"yes": L"no") << "\n";
//query disk length
_GET_VIRTUAL_DISK_INFO sizeInfo;
sizeInfo.Version = GET_VIRTUAL_DISK_INFO_SIZE;
vdinfoSize = sizeof(sizeInfo);
fRet = GetVirtualDiskInformation(
hVHD,
&vdinfoSize,
(PGET_VIRTUAL_DISK_INFO)&sizeInfo,
NULL);
g_dwLastError = fRet;
if (fRet != ERROR_SUCCESS)
{
std::cout << "GetVirtualDiskInformation GET_VIRTUAL_DISK_INFO_SIZE failed " << fRet << "\n";
return FALSE;
}
std::cout << "Disk length : " << sizeInfo.Size.VirtualSize << "\n";
const ULONG MaxRangeCount = 4096*1024;
ULONG RangeCount = MaxRangeCount;
ULONG64 ProcessedLength = 0;
QUERY_CHANGES_VIRTUAL_DISK_RANGE* Ranges = new QUERY_CHANGES_VIRTUAL_DISK_RANGE[MaxRangeCount];
fRet = QueryChangesVirtualDisk(
hVHD,
szRctId?szRctId : ((GET_VIRTUAL_DISK_INFO *)vdinfo)->ChangeTrackingState.MostRecentId,
0,
sizeInfo.Size.VirtualSize,
QUERY_CHANGES_VIRTUAL_DISK_FLAG_NONE,
Ranges,
&RangeCount,
&ProcessedLength);
g_dwLastError = fRet;
if (fRet != ERROR_SUCCESS)
{
delete[] Ranges;
std::cout << "QueryChangesVirtualDisk failed " << fRet << " ranges:" << RangeCount << " len " << ProcessedLength << "\n";
return FALSE;
}
if (MaxRangeCount <= RangeCount || ProcessedLength < sizeInfo.Size.VirtualSize)
{
delete[] Ranges;
std::cout << "Not enough resources to capture all the changes \n";
return FALSE;
}
std::cout << "RangeCount : " << RangeCount << "\n";
std::cout << "ProcessedLength : " << ProcessedLength << "\n";
for (int i = 0; i < RangeCount; i++)
{
std::cout << "Range offset : " << Ranges[i].ByteOffset << "\n";
std::cout << "Range length : " << Ranges[i].ByteLength << "\n";
}
delete[] Ranges;
return TRUE;
}
The task of getting the PID of the process that I'm starting, CreateProcess() ProcessInformation.dwProcessId does a great job of this, but in my case, the process that I start opens the child processes and then closes, and I need to get all the PIDs that creates the process I am opening.
I found this code, it receives the child PIDs but they do not match the final Firefox window, what am I doing wrong
Source:
CreateProcess returns handle different than launched Chrome.exe
Update 1
After Drake Wu - MSFT comment, I used the following code
int test(const wchar_t* programPath) {
HANDLE Job = CreateJobObject(nullptr, nullptr);
if (!Job) {
std::cout << "CreateJobObject, error " << GetLastError() << std::endl;
return 0;
}
HANDLE IOPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1);
if (!IOPort) {
std::cout << "CreateIoCompletionPort, error " << GetLastError() << std::endl;
return 0;
}
JOBOBJECT_ASSOCIATE_COMPLETION_PORT Port;
Port.CompletionKey = Job;
Port.CompletionPort = IOPort;
if (!SetInformationJobObject(Job,
JobObjectAssociateCompletionPortInformation,
&Port, sizeof(Port))) {
std::cout << "SetInformation, error " << GetLastError() << std::endl;
return 0;
}
PROCESS_INFORMATION ProcessInformation;
STARTUPINFOW StartupInfo = { sizeof(StartupInfo) };
LPWSTR szCmdline = const_cast<LPWSTR>(programPath);
if (!CreateProcessW(
programPath,
nullptr,
nullptr,
nullptr,
FALSE,
CREATE_SUSPENDED,
nullptr,
nullptr,
&StartupInfo,
&ProcessInformation))
{
std::cout << "CreateProcess, error " << GetLastError() << std::endl;
return 0;
}
std::cout << "PID: " << ProcessInformation.dwProcessId << std::endl;
if (!AssignProcessToJobObject(Job, ProcessInformation.hProcess)) {
std::cout << "Assign, error " << GetLastError() << std::endl;
return 0;
}
ResumeThread(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hProcess);
DWORD CompletionCode;
ULONG_PTR CompletionKey;
LPOVERLAPPED Overlapped;
while (GetQueuedCompletionStatus(IOPort, &CompletionCode, &CompletionKey, &Overlapped, INFINITE))
{
switch (CompletionCode)
{
case JOB_OBJECT_MSG_NEW_PROCESS:
std::cout << "New PID: " << (int)Overlapped << std::endl;
break;
case JOB_OBJECT_MSG_EXIT_PROCESS:
std::cout << "Exit PID: " << (int)Overlapped << std::endl;
break;
case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
std::cout << "JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO" << std::endl;
break;
default:
break;
}
if (CompletionCode == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO)
break;
}
std::cout << "All done" << std::endl;
}
and I got the following results:
standart Firefox
test(L"C:\\Program Files\\Mozilla Firefox\\firefox.exe");
portable edition Firefox
test(L"D:\\FirefoxPortable\\FirefoxPortable.exe");
As before, PIDs are incorrectly returned. In the case of the portable version, the process hangs on the while loop, in the case of the standard version of firefox, GetQueuedCompletionStatus() returns JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO. Why am I getting the wrong result?
Update 2
I ran Visual Studio as an administrator and, but on standard startup everything displays correctly
I tested that the process of Firefox is not new and exit in order(the pid obtained by CreateProcess will exit), and your code will not receive the new Firefox process if there is any new process created later.
You could use swtich-case statement, the following sample work for me:
int openProgram(const wchar_t* programPath) {
HANDLE Job = CreateJobObject(nullptr, nullptr);
if (!Job) {
std::cout << "CreateJobObject, error " << GetLastError() << std::endl;
return 0;
}
HANDLE IOPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1);
if (!IOPort) {
std::cout << "CreateIoCompletionPort, error " << GetLastError() << std::endl;
return 0;
}
JOBOBJECT_ASSOCIATE_COMPLETION_PORT Port;
Port.CompletionKey = Job;
Port.CompletionPort = IOPort;
if (!SetInformationJobObject(Job,
JobObjectAssociateCompletionPortInformation,
&Port, sizeof(Port))) {
std::cout << "SetInformation, error " << GetLastError() << std::endl;
return 0;
}
PROCESS_INFORMATION ProcessInformation;
STARTUPINFO StartupInfo = { sizeof(StartupInfo) };
LPTSTR szCmdline = _tcsdup(programPath);
if (!CreateProcessW(
nullptr,
szCmdline,
nullptr,
nullptr,
FALSE,
CREATE_SUSPENDED,
nullptr,
nullptr,
&StartupInfo,
&ProcessInformation))
{
std::cout << "CreateProcess, error " << GetLastError() << std::endl;
return 0;
}
std::cout << "PID: " << ProcessInformation.dwProcessId << std::endl;
if (!AssignProcessToJobObject(Job, ProcessInformation.hProcess)) {
std::cout << "Assign, error " << GetLastError() << std::endl;
return 0;
}
ResumeThread(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hProcess);
DWORD CompletionCode;
ULONG_PTR CompletionKey;
LPOVERLAPPED Overlapped;
while (GetQueuedCompletionStatus(IOPort, &CompletionCode, &CompletionKey, &Overlapped, INFINITE))
{
switch (CompletionCode)
{
case JOB_OBJECT_MSG_NEW_PROCESS:
std::cout << "New PID: " << (int)Overlapped << std::endl;
break;
case JOB_OBJECT_MSG_EXIT_PROCESS:
std::cout << "Exit PID: " << (int)Overlapped << std::endl;
break;
case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
std::cout << "JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO" << std::endl;
break;
default:
break;
}
if (CompletionCode == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO)
break;
}
std::cout << "All done" << std::endl;
}
Result:
I'm trying to read the contents of a DLL from memory for some academic research. Specifically, the NTDSA.DLL library for the purpose of mutating specific instructions to simulate programming errors to force the system to fail. The failure will then be recorded to train machine learning algorithms to predict future failures (this is an attempt to generalize previously published research seen here).
I'm getting what I believe to be the base address in virtual memory of the lsass.exe process (which loads the target DLL) through the process outlined here. I'm then calling ReadProcessMemory with an allocated buffer and the handle to lsass obtained by calling OpenProcess with 'PROCESS_ALL_ACCESS' set. The ReadProcessMemory returns with error code 299 80% of the time (partial read) with zero bytes read. My assumption is that the area I'm trying to access is in use when the call is made. Fortunately, it will occasionally return the number of bytes I'm requesting. Unfortunately, the data returned does not match what is on disk when compared to the static DLL in the System32 directory.
So the question is, is ReadProcessMemory doing something funny with the address that I give it, or is my virtual address wrong? Is there another way to figure out where that DLL gets loaded into memory? Any thoughts? Any help or suggestions would be greatly appreciated.
Adding Code:
// FaultInjection.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <windows.h>
#include <psapi.h>
#include <string>
#include <iostream>
#include <fstream>
#include <stdio.h>
#include <io.h>
#include <tchar.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[]) {
// Declarations
int pid = 0;
__int64* start_addr;
DWORD size_of_ntdsa;
DWORD aProcesses[1024], cbNeeded, cProcesses;
TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");
HMODULE hmods[1024];
unsigned int i;
// Get All pids
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)){
cout << "Failed to get all PIDs: " << GetLastError() << endl;
return -1;
}
// Find pid for lsass.exe
cProcesses = cbNeeded / sizeof(DWORD);
for (i = 0; i < cProcesses; i++) {
if (aProcesses[i] != 0) {
HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, aProcesses[i]);
if (hProc != NULL) {
HMODULE hMod;
DWORD cbNeededMod;
if (EnumProcessModules(hProc, &hMod, sizeof(hMod), &cbNeededMod)) {
GetModuleBaseName(hProc, hMod, szProcessName, sizeof(szProcessName) / sizeof(TCHAR));
}
if (wstring(szProcessName).find(L"lsass.exe") != string::npos) {
pid = aProcesses[i];
}
CloseHandle(hProc);
}
}
}
cout << "lsass pid: " << pid << endl;
HANDLE h_lsass = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (!h_lsass) {
cout << "Failed to open process (are you root?): " << GetLastError() << endl;
return -1;
}
// Get Process Image File Name
char filename[MAX_PATH];
if (GetProcessImageFileName(h_lsass, (LPTSTR)&filename, MAX_PATH) == 0) {
cout << "Failed to get image file name: " << GetLastError() << endl;
CloseHandle(h_lsass);
return -1;
}
// Enumerate modules within process
if (EnumProcessModules(h_lsass, hmods, sizeof(hmods), &cbNeeded)) {
for (i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
TCHAR szModName[MAX_PATH];
if (GetModuleFileNameEx(h_lsass, hmods[i], szModName, sizeof(szModName) / sizeof(TCHAR))) {
if (wstring(szModName).find(L"NTDSA.dll") != string::npos) {
_tprintf(TEXT("%s\n"), szModName);
MODULEINFO lModInfo = { 0 };
if (GetModuleInformation(h_lsass, hmods[i], &lModInfo, sizeof(lModInfo))){
cout << "\t Base Addr: " << lModInfo.lpBaseOfDll << endl;
cout << "\t Entry Point: " << lModInfo.EntryPoint << endl;
cout << "\t Size of image: " << lModInfo.SizeOfImage << endl;
start_addr = (__int64*)lModInfo.lpBaseOfDll;
size_of_ntdsa = lModInfo.SizeOfImage;
}
else {
cout << "Failed to Print enumerated list of modules: " << GetLastError() << endl;
}
}
} else {
cout << "Failed to Print enumerated list of modules: " << GetLastError() << endl;
}
}
}
else {
cout << "Failed to enum the modules: " << GetLastError() << endl;
}
// Ready to continue?
string cont = "";
cout << "Continue? [Y|n]: ";
getline(cin, cont);
if (cont.find("n") != string::npos || cont.find("N") != string::npos) {
CloseHandle(h_lsass);
return 0;
}
void* buf = malloc(size_of_ntdsa);
if (!buf) {
cout << "Failed to allocate space for memory contents: " << GetLastError() << endl;
CloseHandle(h_lsass);
return -1;
}
SIZE_T num_bytes_read = 0;
int count = 0;
if (ReadProcessMemory(h_lsass, &start_addr, buf, size_of_ntdsa, &num_bytes_read) != 0) {
cout << "Read success. Got " << num_bytes_read << " bytes: " << endl;
} else {
int error_code = GetLastError();
if (error_code == 299) {
cout << "Partial read. Got " << num_bytes_read << " bytes: " << endl;
} else {
cout << "Failed to read memory: " << GetLastError() << endl;
CloseHandle(h_lsass);
free(buf);
return -1;
}
}
if (num_bytes_read > 0) {
FILE *fp;
fopen_s(&fp, "C:\\ntdsa_new.dll", "w");
SIZE_T bytes_written = 0;
while (bytes_written < num_bytes_read) {
bytes_written += fwrite(buf, 1, num_bytes_read, fp);
}
fclose(fp);
cout << "Wrote " << bytes_written << " bytes." << endl;
}
CloseHandle(h_lsass);
free(buf);
return 0;
}
Code works as described minus my amateur mistake of sending the address of the variable I was using to store the address of the location in virtual memory of the target application. In above code, changed:
if (ReadProcessMemory(h_lsass, &start_addr, buf, size_of_ntdsa, &num_bytes_read) != 0) {
to
if (ReadProcessMemory(h_lsass, start_addr, buf, size_of_ntdsa, &num_bytes_read) != 0) {
Works like a charm. Thank you ssbssa for pointing out mistake, sorry for wasting anyone's time.
I have a program that creates semaphore. But when i try to use SETALL, i get the error in errno as
Numerical result out of range
if((temp_semid = semget(IPC_PRIVATE, 250, 0666 | IPC_CREAT)) != -1)
{
semun arg;
ushort array[100];
memset(array,0, 100);
arg.array = array;
if(semctl(temp_semid, 0, SETALL, arg) == -1){
std::cout << " failed to setall semaphore" << std::endl;
std::cout << strerror(errno) << std::endl;
break;
}
}
What is wrong?
This worked.
if((temp_semid = semget(IPC_PRIVATE, 250, 0666 | IPC_CREAT)) != -1)
{
semun arg;
ushort array[100] = { 0 };
arg.array = array;
if(semctl(temp_semid, 0, SETALL, arg) == -1){
std::cout << " failed to setall semaphore" << std::endl;
std::cout << strerror(errno) << std::endl;
break;
}
}
i was using memset in the wrong way. thanks to hmjd for pointing that out.