Hello,
well I'm using the DeviceIoControl
to send the request of course and GetQueuedCompletionStatus
to trace the port for any response, I can give you the thread code here: (just ignore that much of printing :))
DWORD WINAPI ThreadStartRoutine(
LPVOID lpThreadParameter
)
{
BOOL getQueueReturn, isSuccess;
DWORD numofBytestTransfered = 0, threadId = 0;
ULONG_PTR completionKey;
OVERLAPPED *lpOverLapped = NULL;
OVERLAPPED overLapped;
PTHREAD_PARAMETER_CONTEXT lpThreadParameterContext;
PIO_CONTEXT ioContext;
DATA_TRANSFERE_FROM_KERNEL bufDataFromKernel = { 0 };
HANDLE hFile;
KLEDR_PE_ANALYSIS_RESULT peAanlyzeResult;
lpThreadParameterContext = (PTHREAD_PARAMETER_CONTEXT)lpThreadParameter;
threadId = GetCurrentThreadId();
// wcscpy_s(bufDataFromKernel.binPath, 12, L"TestMessage");
for(;;) {
ZeroMemory(&overLapped, sizeof(OVERLAPPED));
ZeroMemory(&peAanlyzeResult, sizeof(KLEDR_PE_ANALYSIS_RESULT));
DeviceIoControl(
lpThreadParameterContext->hDevice,
KLEDR_CTL,
nullptr,
0,
&bufDataFromKernel,
sizeof(DATA_TRANSFERE_FROM_KERNEL),
&numofBytestTransfered,
&overLapped
);
if ((GetLastError()) == ERROR_IO_PENDING)
std::cout << "[+] the IO control is sent successfully and in pending state, from a thread number " << threadId << std::endl;
else
std::cout << "[-] ERROR while sending the IOCTL, not in pending state, \n\t thread ID : " << threadId << "\n\t error code : " << GetLastError() << std::endl;
getQueueReturn = GetQueuedCompletionStatus(lpThreadParameterContext->hCompletionPort, &numofBytestTransfered, &completionKey, &lpOverLapped, INFINITE);
if (lpOverLapped == NULL)
{
std::cout << "[+++] WE RECIEVED THE REQUEST TO SHUT US DOWN, SHUTTING DOWN (THREAD ID: " << threadId << ")\n.";
// the main thread should cancel all pending requests. So ignore and return.
return 0;
}
if (!getQueueReturn)
{
// IT'S THE CLEAN-UP REQUEST NOWWWW..
std::cout << "[---] ERROR FROM THE COMPLETION ROUTINE, error code: " << GetLastError() << " (THREAD ID : " << threadId << ")\n.";
return 0;
}
std::cout << "[+] success, we recieved the a complition packet, let's now create another request..\n";
std::cout << "[**] NOW IN TID: " << threadId << ", dealing with OVERLAPPED STRUCTURE: " << lpOverLapped << std::endl;
// CREATE ANOTHER REQUEST..
// ioContext = CONTAINING_RECORD(lpOverLapped, IO_CONTEXT, ov);
// now let's print the data came from the kernel..
std::wcout << "[*****] THE DATA CAME FROM THE KERNEL LAND IS: " << bufDataFromKernel.binPath << std::endl;
// printf("[*****] THE DATA CAME FROM THE KERNEL LAND IS: %ls, while its address : %p, and struct address : %p\n", ioContext->DataFromKernel.binPath, bufDataFromKernel.binPath, &bufDataFromKernel);
// let's check if the file is signed or not.
std::cout << "checking if the file is signed ...\n";
KlEdrCheckSigned(bufDataFromKernel.binPath, &isSuccess);
std::cout << "is the file signed ? => " << isSuccess << std::endl;
// now check for the APIs and the string.
std::cout << "checking the string and APIs...\n";
peAanlyzeResult = KlEdrAnalyzePeFile(bufDataFromKernel.binPath);
std::cout << "RESULT..\nString found: " << peAanlyzeResult.StringFound << \
"\nWriteProcessMemory Found: " << peAanlyzeResult.WriteProcessMemoryFlag << \
"\nCreateRemoteThread Found: " << peAanlyzeResult.CreateRemoteThreadFlag << \
"\nVirtualAllocEx Found: " << peAanlyzeResult.VirtualAllocExFlag << \
"\nOpenProcess Found: " << peAanlyzeResult.OpenProcessFlag << std::endl;
}
}
and this is code in the main thread that opens a handle to the driver and the completion port:
hDevice = CreateFileA(
"\\\\.\\symKLEDR",
GENERIC_ALL,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, NULL
);
if (hDevice == INVALID_HANDLE_VALUE)
{
std::cout << "[-]ERROR: couldn't open a handle to the device, error code : " << GetLastError() << std::endl;
return -1;
}
// let's make the IOCP
hCompletionPort = CreateIoCompletionPort(hDevice, NULL, 0, 0);
if (hCompletionPort == NULL)
{
std::cout << "[-]ERROR: while creating the IOCP, error code: " << GetLastError() << std::endl;
return -1;
}
threadParamContext->hCompletionPort = hCompletionPort;
threadParamContext->hDevice = hDevice;
And from the driver side, yah I'm pretty sure I have set the Irp->IoStatus.inforamtion to the size of the response, and it works fine with other IRPs but only the first IRPs never give any data when it's completed. (knowing that the same code runs for all IRPs whether it's the first or any, but only the first don't hold any data sent by the driver).
and regarding the "thing I've tried" section, I haven't described what I have done very well, so I run the user app under a debugging session and set a break point just after it receives the first request from kernel, then I kept it paused until the driver completed all the other IRPs, then I continued the execution of that user mode app and then it worked very well. But I have just realized now that I have only paused a single thread
from the whole process, so again sorry ignore that test 