I am attempting to read ntdl.dll from disk using the NtReadFile native call. A handle has already been acquired through the NtCreateFile native call. However, when I attempt to read it using NtReadFIle, it neither fails nor succeeds. I try to print out the NtStatus, but there's nothing, as it looks like the call never actually completes. It does seem to work with the ReadFile Win API call, so not quite sure why the native call isn't working.
In the debugger, it seems like an unhandled exception is raised when transitioning back to userland after making the syscall, so not sure what is going on. Below is a snippet of the code.
#define OBJ_CASE_INSENSITIVE 0x00000040
#define FILE_OPEN 0x00000001
#define FILE_DIRECTORY_FILE 0x00000001
#define FILE_NON_DIRECTORY_FILE 0x00000040
#define FILE_RANDOM_ACCESS 0x00000800
#define FILE_OPEN_BY_FILE_ID 0x00002000
#define FILE_SYNCHRONOUS_IO_NONALERT 0x00000020
SIZE_T bytesWritten = 0;
DWORD oldProtection = 0;
//HANDLE file = NULL;
HANDLE file = NULL;
ULONG fileSize = NULL;
LPDWORD bytesRead = NULL;
PVOID fileData = NULL;
OBJECT_ATTRIBUTES oa;
UNICODE_STRING f;
IO_STATUS_BLOCK IoStatusBlock;
WCHAR ntdl[100] = L"\\?\\c:\\windows\\system32\\ntdll.dll";
WCHAR filepath[100] = L"\\??\\c:\\windows\\system32";
RtlInitUnicodeString(&f, ntdl);
InitializeObjectAttributes(&oa, &f, OBJ_CASE_INSENSITIVE, NULL, NULL);
NTSTATUS stat = NtCreateFile(&file, FILE_GENERIC_READ, &oa, &IoStatusBlock, 0, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, 0, 0);
fileSize = GetFileSize(file, NULL);
fileData = HeapAlloc(GetProcessHeap(), 0, fileSize);
NTSTATUS stat1 = NtReadFile(file, NULL, NULL, NULL, &IoStatusBlock, fileData, sizeof(fileData), 0, NULL);
if (stat1 != 0)
{
printf("failed: %X\n", stat1);
}
Instead of sizeof(fileData) pass the fileSize and initialize + pass the ByteOffset value of 0.
LARGE_INTEGER liBytes = { 0 };
status = SysNtReadFile(file, NULL, NULL, NULL, &statusBlock, fileData, fileSize, &liBytes, NULL);
And, In code line
fileData = HeapAlloc(GetProcessHeap(), 0, fileSize);
the third parameter should be set HEAP_ZERO_MEMORY, not 0. See nf-heapapi-heapalloc
Related
I am writing a WinDBG extension to debug a device driver, and need to call an external binary to debug the device's firmware. I would like to show the output of this binary in the WinDBG console.
My initial idea was to simply pipe the output of the binary to a buffer and print that buffer with ControlledOutput. However, I get a 'broken pipe' error when I try to read from the pipe in my extension.
Here is how I create the external process in my extension:
SECURITY_ATTRIBUTES sAttr;
HANDLE childOutRead = NULL;
HANDLE childOutWrite = NULL;
PROCESS_INFORMATION childProcInfo;
STARTUPINFO childStartInfo;
char buf[4096];
sAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
sAttr.bInheritHandle = TRUE;
sAttr.lpSecurityDescriptor = NULL;
CreatePipe(&childOutRead, &childOutWrite, &sAttr, 0);
// don't inherit read end
SetHandleInformation(childOutRead, HANDLE_FLAG_INHERIT, 0);
ZeroMemory(&childProcInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&childStartInfo, sizeof(STARTUPINFO));
childStartInfo.cb = sizeof(STARTUPINFO);
childStartInfo.hStdError = childOutWrite;
childStartInfo.hStdOut = childOutWrite;
childStartInfo.hStdIn = GetStdHandle(STD_INPUT_HANDLE);
childStartInfo.dwFlags |= STARTF_USESTDHANDLES;
CreateProcessA(NULL, "myBinary.exe someArgs",
NULL, NULL, TRUE, 0, NULL, NULL,
&childStartInfo, &childProcInfo);
// close the handle not used in parent
CloseHandle(childOutWrite);
// read output
while (1) {
DWORD read;
BOOL r;
DWORD error;
r = ReadFile(childOutRead, buf, sizeof(buf), &read, NULL);
if (!r) {
error = GetLastError();
windbgPrintf("got error 0x%x\n", error);
break;
}
if (read == 0) break;
windbgPrint(buf, read);
}
ReadFile fails with error 0x6D, BROKEN_PIPE. This makes me suspect that the pipe is somehow not being inherited.
I have nearly identical code working in a test outside of WinDBG, so it must be doing something differently. How do I get pipes working in this way inside WinDBG?
I am using pipes to get redirected stdout output from a command line executable. Unfortunately I don't get any output until the process has completed. The executable outputs progress status as it runs and this is what I would like to parse.
BOOL RunCmd( char *pCmd,
char *pWorkingDir,
int nWaitSecs,
BOOL fRegImport,
DWORD *pdwExitCode )
{
BOOL fSuccess = TRUE;
STARTUPINFO si;
PROCESS_INFORMATION pi;
SECURITY_ATTRIBUTES sFileSecurity;
ZeroMemory( &sFileSecurity, sizeof( sFileSecurity ) );
sFileSecurity.nLength = sizeof( sFileSecurity );
sFileSecurity.bInheritHandle = TRUE;
HANDLE hReadPipe = NULL;
HANDLE hWritePipe = NULL;
fSuccess = CreatePipe( &hReadPipe, &hWritePipe, &sFileSecurity, 0 );
SetHandleInformation( hReadPipe, HANDLE_FLAG_INHERIT, 0 );
ZeroMemory( &si, sizeof(si) );
ZeroMemory( &pi, sizeof(pi) );
si.cb = sizeof( si );
si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
si.hStdOutput = hWritePipe;
si.hStdError = hWritePipe;
si.wShowWindow = SW_HIDE;
int rc;
// Start the child process.
rc = CreateProcess( NULL, // No module name (use command line).
pCmd, // Command line.
NULL, // Process handle not inheritable.
NULL, // Thread handle not inheritable.
TRUE,
CREATE_NO_WINDOW,
NULL, // Use parent's environment block.
pWorkingDir, // Working folder
&si, // Pointer to STARTUPINFO structure.
&pi ); // Pointer to PROCESS_INFORMATION structure.
if( ! rc )
return FALSE;
// Wait until child process exits.
DWORD dwWaitResult;
DWORD dwTimeStart = ::GetTickCount();
DWORD dwTimeNow;
#define BUFSIZE 4096
DWORD dwRead = 0;
DWORD dwAvail;
CHAR chBuf[ BUFSIZE ];
BOOL bSuccess = TRUE;
for( ;; )
{
dwTimeNow = ::GetTickCount();
dwWaitResult = ::WaitForSingleObject( pi.hProcess, ONE_SECOND );
dwRead = 0;
for( dwAvail = 0; PeekNamedPipe( hReadPipe, 0, 0, 0, &dwAvail, 0 ) && dwAvail; dwAvail = 0 )
{
dwRead = 0;
ReadFile( hReadPipe, chBuf, min( BUFSIZE, dwAvail ), &dwRead, NULL );
if( dwRead > 0 )
{
FILE *op = fopen( "c:\\REDIR.OUT", "a" );
if( op )
{
fwrite( chBuf, 1, dwRead, op );
fclose( op );
}
}
}
if( dwWaitResult == WAIT_OBJECT_0 )
{
DWORD dwExitCode;
GetExitCodeProcess( pi.hProcess, &dwExitCode );
if( pdwExitCode )
(*pdwExitCode) = dwExitCode;
break;
}
if( dwWaitResult == WAIT_TIMEOUT )
{
if( dwTimeNow - dwTimeStart < (DWORD)( ONE_SECOND * nWaitSecs ) )
continue;
else
{
fSuccess = FALSE;
break;
}
}
fSuccess = FALSE;
break;
}
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
CloseHandle( hReadPipe );
CloseHandle( hWritePipe );
return fSuccess;
}
The PeekNamedPipe() call is called every second and dwAvail is zero every time until the process completes.
How do I get output from the process sooner? When running the process from the console, I see progress output as it goes. The process will be using "\r" in it's output to display a percentage at the start of the same line.
Note: My answer only deals with executables compiled using MSVC.
The buffering policy is coded inside Microsoft C Runtime (CRT) Library. You can learn the details here. This article suggests using console handles and manipulate console buffers to receive unbuffered output.
However, there's an undocumented feature inside Microsoft C Runtime to inherit file handles with some internal flags directly from its parent process using lpReserved2 and cbReserved2 fields of STARTUPINFO structure. You can find the details in the crt source code provided by Microsoft Visual Studio. Or search for something like posfhnd on GitHub.
We can exploit this undocumented feature to provide a pipe handle and specify FOPEN | FDEV flags to the child process, to fool the child process treat that pipe handle the same way as a FILE_TYPE_CHAR handle.
I have a working Python3 script to demonstrate this method.
A simplified C/C++ version of the answer by #youfu.
STARTUPINFO si;
int nh = 2;
si.cbReserved2 = (WORD)(sizeof(int) + (nh *
(sizeof(char) + sizeof(HANDLE))));
si.lpReserved2 = (LPBYTE) calloc(si.cbReserved2, 1);
*((UNALIGNED int *)(si.lpReserved2)) = nh;
unsigned char* posfile = (unsigned char *)(si.lpReserved2 + sizeof(int));
UNALIGNED HANDLE* posfhnd = (UNALIGNED HANDLE *)(si.lpReserved2 + sizeof(int) +
(nh * sizeof(unsigned char)));
unsigned char FOPEN = 0x01;
unsigned char FDEV = 0x40;
*posfile = FOPEN | FDEV;
posfile++;
*posfile = FOPEN | FDEV;
*posfhnd = child_stdin_rd;
posfhnd++;
*posfhnd = child_stdout_wr;
-Suraj
GetWindowThreadProcessId(hwndFoundWindow, &dwTrayProcessID);
HANDLE hTrayProc = OpenProcess(PROCESS_ALL_ACCESS, 0, dwTrayProcessID);
int iButtonsCount = SendMessage(hwndFoundWindow, TB_BUTTONCOUNT, 0, 0);
LPVOID lpData = VirtualAllocEx(hTrayProc, NULL, sizeof(TBBUTTON), MEM_COMMIT, PAGE_READWRITE);
int iButton;
DWORD dwBytesRead;
TBBUTTON buttonData;
dwBytesRead = -1;
int chk_data = (int)SendMessage(hwndFoundWindow, TB_GETBUTTON, iButton, (LPARAM)lpData);
ReadProcessMemory(hTrayProc, lpData, &buttonData, sizeof(TBBUTTON), &dwBytesRead);
int len_text = (int)SendMessage(hwndFoundWindow, TB_GETBUTTONTEXTW, buttonData.idCommand, (LPARAM)lpData);
till now, i know the length of button's text but i also need to get the text to display on console.
my problem is i do not really know how to get that text from the button. please kindly help.
what i am trying is ... trying to access to lpData to get the string inside, but could not do that.
My first comment is that you need to add error checking to your code. As far as I can see, you perform no checking of return values. Any of the API functions you call could fail. If you don't check return values for errors then you have no way of diagnosing where you went wrong.
For instance, starting with GetWindowThreadProcessId, you need to write it like this:
if (GetWindowThreadProcessId(hwndFoundWindow, &dwTrayProcessID) == 0)
{
// handle error
}
And so on for all the other functions. Consult MSDN carefully to understand how each function signals failure.
Now to the main part of the question. I believe that it is the TB_GETBUTTONTEXTW message that is giving you trouble. You need to write it like this:
LRESULT len = SendMessage(hwndFoundWindow, TB_GETBUTTONTEXTW,
buttonData.idCommand, NULL);
if (len == -1)
{
// handle error
}
size_t size = sizeof(wchar_t)*(len+1);
LPVOID lpData = VirtualAllocEx(hTrayProc, NULL, size, MEM_COMMIT, PAGE_READWRITE);
if (lpData == NULL)
{
// handle error
}
len = SendMessage(hwndFoundWindow, TB_GETBUTTONTEXTW,
buttonData.idCommand, (LPARAM)lpData);
if (len == -1)
{
// handle error
}
wchar_t* str = new wchar_t[len+1];
if (!ReadProcessMemory(hTrayProc, lpData, (LPVOID)str, size, NULL))
{
// handle error
}
// the text is now in str, as a null-terminated UTF-16 string
delete[] str;
You need this: (see documentation of TB_GETBUTTONTEXTW).
WCHAR *buffer ;
int len_text = (int)SendMessage(hwndFoundWindow, TB_GETBUTTONTEXTW,
buttonData.idCommand, (LPARAM)NULL);
buffer = (WCHAR*)malloc(sizeof(WCHAR) * (len_text + 1)) ;
SendMessage(hwndFoundWindow, TB_GETBUTTONTEXTW,
buttonData.idCommand, (LPARAM)buffer);
....
free(buffer) ;
I am trying to access the ListView control (located within an Dialog) in another application, and get the data from within the control. Here is the Win32 code (with appropiate comments) that I am writing :
HWND hListView32 = hRoot; //HANDLE to the ListView control within the Dialog, having class name - "SysListView32"
int cnt = (int) ::SendMessage(hListView32, LVM_GETITEMCOUNT, 0, 0L); //returns CORRECT item count of the ListView Control
int nItem=0,nRes;
for(int nItem=0;nItem<cnt;nItem++)
{
LVITEM LvItem; // ListView Item struct
char Text[255]={0};
char Temp[255]={0};
char Temp1[255]={0};
memset(&LvItem,0,sizeof(LvItem));
LvItem.mask=LVIF_TEXT;
LvItem.iSubItem=1; //Trying to get the 2nd Colomn text
LvItem.pszText=Text; //Does not returns any Text, after the below SendMessage is executed???
LvItem.cchTextMax=256;
LvItem.iItem=nItem;
nRes = (int)::SendMessage(hListView32,LVM_GETITEMTEXT, nItem, (LPARAM)&LvItem);
DWORD dd = ::GetLastError(); //returns 0
}
Though the code is executing, I am not getting any data from within the control. However, I am able to retrieve the correct Item count from within the control, but no data.
Another approach maybe would be to use an MSAA hook to get the data. But that would be a very long and cumbersome process. Running out of ideas here. Pls help.
Thanks,
There a few possibilities.
DLL Injection Using windows hooks. Pros: simple and straight forward. Cons: many processes get this dll loaded.
DLL Injection Making process to load library by opening it for debugging, allocating a chunc of virtual memory using VallocEx in the context of this process, writing it's memory with WriteProcessMemory and creating a remote thread with start address of LoadLibrary function. Pros: a single process is affected. Cons: A bit more complex than hooks solution.
Read of process memory. Same as option 2 but instead of writing this memory and executing the code remotely, send the message LVM_GETITEMTEXT to the window in question providing a valid known memory location and then read that location with ReadProcessMemory.
ListView messages that pass around buffers only work within the address space of the process that owns the ListView. You will have to use VirtualAllocEx() to allocate a memory block within that same process, then you can write to it with WriteProcessMemory() and have the ListView fill it as needed, then you can read it with ReadProcessMemory() and deallocate it with VirtualFreeEx().
Try this (error handling omitted for brevity):
HWND hListView32 = hRoot;
int cnt = (int) ::SendMessage(hListView32, LVM_GETITEMCOUNT, 0, 0);
if (cnt > 0)
{
DWORD dwProcessId;
GetWindowThreadProcessId(hListView32, &dwProcessId);
HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwProcessId);
LVITEM *pLvItem = (LVITEM*) VirtualAllocEx(hProcess, NULL, sizeof(LVITEM), MEM_COMMIT, PAGE_READWRITE);
LPTSTR pText = (LPTSTR) VirtualAllocEx(hProcess, NULL, sizeof(TCHAR)*256, MEM_COMMIT, PAGE_READWRITE);
for(int nItem = 0; nItem < cnt; ++nItem)
{
TCHAR Text[256] = {0};
LVITEM LvItem = {0};
LvItem.mask = LVIF_TEXT;
LvItem.iSubItem = 1;
LvItem.pszText = pText;
LvItem.cchTextMax = 256;
LvItem.iItem = nItem;
WriteProcessMemory(hProcess, pLvItem, &LvItem, sizeof(LVITEM), NULL);
int nRes = (int) ::SendMessage(hListView32, LVM_GETITEMTEXT, nItem, (LPARAM)pLvItem);
if (nRes > 0)
ReadProcessMemory(hProcess, pText, &Text[0], sizeof(TCHAR)*nRes, NULL);
// use Text as needed...
}
VirtualFreeEx(hProcess, pText, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, pLvItem, 0, MEM_RELEASE);
CloseHandle(hProcess);
}
I copypasted code by Remy Lebeau, but it is working very strange in my particular case. The number of elements is retreived correctly via SendMessage(listview, LVM_GETITEMCOUNT, 0, 0)
, but the cycle reads jast the same element every time! It is not the first or last element, not the selected, but seems to be random. Here is my code:
HWND win=FindWindowEx(NULL, NULL, _("TEventLogView"), NULL);
HWND listview=FindWindowEx(win, NULL, _("TListView"), NULL);
int cnt = (int) ::SendMessage(listview, LVM_GETITEMCOUNT, 0, 0);
if (cnt > 0)
{
DWORD dwProcessId;
GetWindowThreadProcessId(listview, &dwProcessId);
int n = grdEvents->GetNumberRows();
HANDLE hProcess = OpenProcess(PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwProcessId);
LVITEM *pLvItem = (LVITEM*) VirtualAllocEx(hProcess, NULL, sizeof(LVITEM), MEM_COMMIT, PAGE_READWRITE);
LPTSTR pText = (LPTSTR) VirtualAllocEx(hProcess, NULL, sizeof(TCHAR)*255, MEM_COMMIT, PAGE_READWRITE);
for(int nItem = 0; nItem < cnt; ++nItem)
{
// need to read 1 - 3 subitems
for (int j = 1; j < 4; j++)
{
TCHAR Text[255] = {0};
LVITEM LvItem = {0};
LvItem.mask = LVIF_STATE | LVIF_TEXT;
LvItem.pszText = pText;
LvItem.cchTextMax = sizeof(TCHAR)*255;
LvItem.iItem = nItem;
LvItem.iSubItem = j;
int nRes1 = WriteProcessMemory(hProcess, pLvItem, &LvItem, sizeof(LVITEM), NULL);
int nRes2 = (int) ::SendMessage(listview, LVM_GETITEMTEXT, (WPARAM)nItem, (LPARAM)pLvItem);
if (nRes2 > 0)
{
ReadProcessMemory(hProcess, pText, &Text[0], sizeof(TCHAR)*nRes2, NULL);
// insert into wxWidgets grid
grdEvents->SetCellValue(nItem, j - 1, Text);
}
}
VirtualFreeEx(hProcess, pText, 0, MEM_RELEASE);
VirtualFreeEx(hProcess, pLvItem, 0, MEM_RELEASE);
CloseHandle(hProcess);
}
}
My aim is to detect Solid State Drives in systems with raid configuration. Using smartmontools' following command I observe bit 434 (217) shows value 1 for SSD: smartctl -i -r ataioctl,2 /dev/csmi0,0
Attempting to read the same 512 bytes of data I am trying to send IDENTIFY DEVICE commands in following 2 ways:
Method 1 fails with DeviceIoControl() set GetLastError() as 87 (ERROR_INVALID_PARAMETER), Can you help me in understanding what could be wrong and am I on the right track?
Method 2 has info->IoctlHeader.ReturnCode = 3 which means CSMI_SAS_STATUS_INVALID_PARAMETER.(CSMI buffer provided is too small)
///////// Method 1 /////////
handle = CreateFile(L"\\\\.\\PhysicalDrive0",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
NULL,
NULL);
if (handle == INVALID_HANDLE_VALUE)
{
printf("Unable to open handle to device , Error %u\n", GetLastError());
return 1;
}
//Allocate memory for ATA_PASS_THROUGH_EX and clear the contents
pATAData = (PATA_PASS_THROUGH_EX) VirtualAlloc(NULL, dataSize, MEM_COMMIT, PAGE_READWRITE);
ZeroMemory(pATAData,dataSize);
//Fill in the IDENTIFY DEVICE query data
pATAData->Length = sizeof(ATA_PASS_THROUGH_EX);
pATAData->DataBufferOffset = sizeof(ATA_PASS_THROUGH_EX);
pATAData->DataTransferLength = 512;
pATAData->AtaFlags = ATA_FLAGS_DATA_IN;
pATAData->TimeOutValue = 10; //Seconds
pATAData->CurrentTaskFile[6] = 0xEC; /* send the command*/
status = DeviceIoControl( handle,
IOCTL_ATA_PASS_THROUGH,
pATAData,
dataSize, /* input buffer and size */
pATAData,
dataSize, /* output buffer and size */
&bytescopied, /* bytes copied to output buffer*/
NULL ); /* no overlapping */
//////// Method 2 //////////
BYTE portNumber = 0;
_stprintf_s(DeviceName, (sizeof(char)*128),_T("\\\\.\\Scsi%u:"),portNumber);
// get a handle to the miniport driver
handle = CreateFile(DeviceName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
NULL,
NULL);
if (handle == INVALID_HANDLE_VALUE)
{
printf("Unable to open handle to device , Error %u\n", GetLastError());
return;
}
CSMI_SAS_STP_PASSTHRU_BUFFER* info = (CSMI_SAS_STP_PASSTHRU_BUFFER *) calloc(1, sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER));
info->IoctlHeader.HeaderLength = sizeof(IOCTL_HEADER);
info->IoctlHeader.Timeout = CSMI_SAS_TIMEOUT;
info->IoctlHeader.ControlCode = CC_CSMI_SAS_STP_PASSTHRU;
info->IoctlHeader.Length = sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER) - sizeof(IOCTL_HEADER);
info->IoctlHeader.ReturnCode = 0;
memcpy(&info->IoctlHeader.Signature, CSMI_SAS_SIGNATURE, sizeof(CSMI_SAS_SIGNATURE));
bSuccess = DeviceIoControl(handle, IOCTL_SCSI_MINIPORT, info, sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER),
info, sizeof(CSMI_SAS_STP_PASSTHRU_BUFFER),
&bytesReturned, NULL);