I want to read a file from disk using a kernelmode driver. When I try to convert the char buffer to a PCWSTR it either returns "/" or ???????? or some other random characters. I took https://support.microsoft.com/de-at/help/891805/how-to-open-a-file-from-a-kernel-mode-device-driver-and-how-to-read-fr as reference.
I tried the following:
void ZwReadFromFile(PCWSTR FileName, char* outStr)
{
UNICODE_STRING UnicodeName;
OBJECT_ATTRIBUTES ObjectAttribute;
HANDLE FileHandle;
NTSTATUS NTStatus;
IO_STATUS_BLOCK IOStatusBlock;
char FileBuffer[BUFFER_SIZE];
LARGE_INTEGER ByteOffset;
RtlInitUnicodeString(&UnicodeName, FileName);
InitializeObjectAttributes(&ObjectAttribute, &UnicodeName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL, NULL);
NTStatus = ZwCreateFile(&FileHandle,
GENERIC_READ,
&ObjectAttribute, &IOStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT,
NULL, 0);
if (NT_SUCCESS(NTStatus))
{
ByteOffset.LowPart = ByteOffset.HighPart = 0;
NTStatus = ZwReadFile(FileHandle, NULL, NULL, NULL, &IOStatusBlock,
FileBuffer, BUFFER_SIZE, &ByteOffset, NULL);
if (NT_SUCCESS(NTStatus))
{
FileBuffer[BUFFER_SIZE - 1] = '\0';
DbgPrint("%s\n", FileBuffer);
for (int i = 0; i < BUFFER_SIZE; ++i)
{
outStr[i] = FileBuffer[i];
}
}
ZwClose(FileHandle);
}
}
DebugView using DbgPrint("%s\n", buffer); shows the correct output of my textfile, however when I try to convert it like:
char myStr[BUFFER_SIZE];
ZwReadFromFile(L"\\SystemRoot\\test.txt", myStr);
DbgPrint("%s\n", myStr);
It starts to act weird again.
The Reason why I have to convert the char buffer to a PCWSTR is cause I am forced to use: RtlInitUnicodeString(&myUnicode, myStr /*L"TEST"*/);
Im struggling 3 days for it now, did research on google and stackoverflow but nothing really helped me to solve this Issue on my own. I appreciate any help I can get, sorry for my english im not a native speaker.
Related
I am trying to download an image onto the user's desktop from a URL using Win32. I have taken care of all the HTTP request stuff and know for a fact that it is all working well. When I go to call CreateFile() the Visual Studios debugger just says "Exception: Application.exe has triggered a breakpoint" and that it will resume on the CreateFile() line. Also there is an error code "Critical error detected c0000374"
Here is my code:
VARIANT varResponse;
VariantInit(&varResponse);
...
hr = pIWinHttpRequest->get_ResponseBody(&varResponse);
...
if (SUCCEEDED(hr)) {
long upperBounds;
long lowerBounds;
unsigned char* buff;
//Make sure that varResponse is an array of unsigned bytes
if (varResponse.vt == (VT_ARRAY | VT_UI1)) {
long Dims = SafeArrayGetDim(varResponse.parray);
//It should only have one dimension
if (Dims == 1) {
//Get Array lower and upper bounds
SafeArrayGetLBound(varResponse.parray, 1, &lowerBounds);
SafeArrayGetUBound(varResponse.parray, 1, &upperBounds);
upperBounds++;
SafeArrayAccessData(varResponse.parray, (void**)&buff);
HANDLE hFile;
DWORD dwBytesWritten;
PWSTR filepath[MAX_PATH];
HRESULT hr = SHGetKnownFolderPath(FOLDERID_Desktop, 0, NULL, &*filepath);
if (SUCCEEDED(hr)) {
//PathCombine(filepathForImage, filepathToDesktop, L"\\todaysDailyImage.jpg");
PathAppend(*filepath, L"todaysDailyImage.jpg");
MessageBox(NULL, *filepath, L"Check if filepath works", MB_OK);
}
hFile = CreateFile(*filepath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
//File failed
}
else {
WriteFile(hFile, buff, upperBounds - lowerBounds, &dwBytesWritten, NULL);
//File was written
}
CloseHandle(hFile);
CoTaskMemFree(filepath);
SafeArrayUnaccessData(varResponse.parray);
MessageBox(NULL, L"Everything was cleaned up", L"Update:", MB_OK);
}
}
}
Am I doing anything wrong?
The way you are using filepath is all wrong.
You are declaring it as an array of MAX_PATH (260) number of PWSTR pointers.
When you refer to an array by its name alone, you end up with a pointer to the 1st element of the array. So, &*filepath is the same as &*(&filepath[0]), which is effectively &filepath[0]. And *filepath is the same as *(&filepath[0]), which is effectively filepath[0]. So, as far as SHGetKnownFolderPath() and MessageBox() are concerned, they are only operating on the 1st PWSTR pointer in the array, and the other 259 array elements are ignored. That part is ok, but wasteful.
However, PathAppend() requires a destination buffer that is an array of MAX_PATH number of WCHAR elements. You are appending to the WCHAR[] array that SHGetKnownFolderPath() allocates as its output, which is not large enough to hold the filename you are trying to append to it. So, you are triggering errors because you are trying to modify memory that hasn’t been allocated to hold that modification.
You don’t need the PWSTR array at all. Try something more like this instead:
PWSTR folderpath;
HRESULT hr = SHGetKnownFolderPath(FOLDERID_Desktop, 0, NULL, &folderpath);
if (FAILED(hr)) {
// ...
}
else {
PWSTR filepath;
hr = PathAllocCombine(folderpath, L"todaysDailyImage.jpg", 0, &filepath);
if (FAIlED(hr)) {
// ...
}
else {
MessageBoxW(NULL, filepath, L"Check if filepath works", MB_OK);
hFile = CreateFileW(filepath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
//File failed
}
else {
WriteFile(hFile, buff, upperBounds - lowerBounds, &dwBytesWritten, NULL);
//File was written
CloseHandle(hFile);
}
LocalFree(filepath);
}
CoTaskMemFree(folderpath);
}
I am trying to write the content of unsigned short and unsigned char array content to an .img file. I am using WriteFile method to do the same. it seems like WriteFile function successfully writing the array content to the file but the main problem is i am not able to view that file in file system. Following are the two methods I am using to Write the data to the file.
void createImageFile(unsigned short* src,int srcLength,const char* fileName)
{
DWORD dwBytesWritten = 0;
unsigned short *dest = new unsigned short[srcLength];
if(is_file_exist(fileName))
{
remove(fileName);
}
HANDLE hFile = CreateFile(LPCWSTR(fileName), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD e = GetLastError();
if(hFile)
{
memcpy(dest,src,srcLength*sizeof(unsigned short));
bool b = WriteFile(hFile,dest,srcLength,&dwBytesWritten,NULL);
if(!b)
{
DWORD e = GetLastError();
} CloseHandle(hFile);
}
if(dest)
{
delete[] dest;
dest = NULL;
}
}
void createImageFile(unsigned char* src,int srcLength,const char* fileName)
{
DWORD dwBytesWritten = 0;
unsigned short *dest = new unsigned short[srcLength];
if(is_file_exist(fileName))
{
remove(fileName);
}
HANDLE hFile = CreateFile(LPCWSTR(fileName), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
DWORD e = GetLastError();
if(hFile)
{
memcpy(dest,src,srcLength*sizeof(unsigned short));
bool b = WriteFile(hFile,dest,srcLength,&dwBytesWritten,NULL);
if(!b)
{
DWORD e = GetLastError();
} CloseHandle(hFile);
}
if(dest)
{
delete[] dest;
dest = NULL;
}
}
I am not sure what exactly I am doing wrong. I am not able to view those files on the specified path. Can somebody please help me in this?
Another thing I want to highlight is, the above code is the part of unmanaged code and supposed to be reside in the dll.
You cannot just cast fileName to a wide string.
Don't forget to close the file.
CreateFile returns INVALID_HANDLE_VALUE not zero on failure. So, your error checking condition is incorrect.
Copying src to dest is quite unnecessary. Nor do you need to set the pointer to NULL after deletion.
Also, you have a race condition between remove(fileName) and CreateFile. You don't need to remove — setting dwCreationDisposition is sufficient.
The whole function could be written as:
void createImageFile(unsigned short* src, int srcLength, const char* fileName)
{
using namespace std;
ofstream stream(fileName, ios_base::binary | ios_base::trunc);
stream.write(src, srcLength * sizeof(unsigned short));
}
Here is the code I am using:
std::wstring GetPathFromFileReference (DWORDLONG frn)
{
if (frn != 0)
{
HANDLE handle = NULL;
wchar_t file_buffer[2048] = { NULL };
wchar_t unicode_buffer[8] = { NULL };
UNICODE_STRING unicodeString;
unicodeString.Length = 8;
unicodeString.MaximumLength = 8;
unicodeString.Buffer = unicode_buffer;
OBJECT_ATTRIBUTES objAttributes = { NULL };
InitializeObjectAttributes(&objAttributes, &unicodeString, OBJ_CASE_INSENSITIVE, _root, NULL);
IO_STATUS_BLOCK ioStatusBlock = { NULL };
LARGE_INTEGER allocSize = { NULL };
int _result = NtCreateFile(&handle, GENERIC_ALL /*FILE_TRAVERSE*/ /* FILE_READ_DATA */, &objAttributes, &ioStatusBlock, /*&allocSize*/ NULL , NULL, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, FILE_OPEN_BY_FILE_ID | FILE_NON_DIRECTORY_FILE /*FILE_OPEN_FOR_BACKUP_INTENT*/, NULL, NULL);
if (_result == S_OK)
{
typedef NTSTATUS (NTAPI *LPFN_NtQueryInformationFile) (HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, int);
LPFN_NtQueryInformationFile pfnNtQueryInformationFile = (LPFN_NtQueryInformationFile)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationFile");
_result = pfnNtQueryInformationFile(handle, &ioStatusBlock, file_buffer, 4096, 9);
if (_result == S_OK)
{
return std::wstring(file_buffer + 2);
}
}
}
return L"";
}
The call to NtCreateFile is failing with STATUS_INVALID_PARAMETER. The commented out bits of code in that call show other things I have tried.
The RootDirectory handle is definitely getting set in objAttributes (to 0x30). I am using this same handle elsewhere in the code, and it is working perfectly. And the frn looks good too.
I have no idea what else to try or how to narrow the cause down any further :( Any help would be greatly appreciated.
EDIT: I forgot to mention what I am trying to achieve here. Sorry! The frn comes from the USN Change Journal. I have successfully read the Change Journal using my root handle (hence I believe it is correct), and for each entry I have got an frn and parent_frn. I want to get the full path to the file, and the code below is how I am trying to convert frn to path. Both frn and parent_frn give the same STATUS_INVALID_PARAMETER return code.
A comment on the documentation for NtCreateFile says that:
If using FILE_OPEN_BY_FILE_ID, the ObjectAttributes.RootDirectory handle MUST be filled in with a handle to the volume, otherwise you will get STATUS_INVALID_PARAMETER.
To open a handle to a volume you can use CreateFile using a path of \\.\X: where X is the drive letter. Note there is no trailing backslash. The CreateFile documentation has info about it (look for the string "Opens the C: volume.")
I have a problem with using DeviceIOControl to put 128 byte buffer to my driver, i use this code:
int Initialize(unsigned char* public_signature, int size)
{
int ret = DeviceIoControl(
DeviceFileHandle,
2236440,
public_signature,
size,
NULL,
0,
NULL,
NULL);
if(ret != 0)
return 0;
wprintf(L"Format message failed with 0x%x\n", GetLastError()); // always error 0x6!
return 1;
}
I always get 0x6 error, what i'm doing wrong?
upd
My handle creating function:
int CreateFileHandle()
{
DeviceFileHandle = CreateFile( L"\Device\test",
GENERIC_WRITE,
GENERIC_READ | GENERIC_WRITE,
NULL,
OPEN_EXISTING,
0,
0);
if(DeviceFileHandle)
return 0;
return 1;
}
The error is in the 1st parameter of CreateFile. In your example, it would try to open a file, not a device. In addition, you didn't escape backslashes in the string. \t and similar are interpreted as special characters in C++.
The device name should be "\\\\.\\Device\\test".
Ok so I've been following this tutorial: http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=4422&lngWId=3
And so far I've gotten everything to work, up until I need the program to load in a .raw audio file.
Here's the relevant code:
LPSTR loadAudioBlock(const char* filename, DWORD* blockSize)
{
HANDLE hFile = INVALID_HANDLE_VALUE;
DWORD size = 0;
DWORD readBytes = 0;
void* block = NULL;
//open the file
if((hFile = CreateFile((LPCWSTR)filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
return NULL;
// get it's size, allocate memory, and then read it into memory
size = GetFileSize(hFile, NULL);
block = HeapAlloc(GetProcessHeap(), 0, size);
ReadFile(hFile, block, size, &readBytes, NULL);
CloseHandle(hFile);
*blockSize = size;
return (LPSTR)block;
}
And then my main function which calls it:
int _tmain(int argc, _TCHAR* argv[])
{
HWAVEOUT hWaveOut; //device handle
WAVEFORMATEX wfx; //struct for format info
MMRESULT result; // for waveOut return values
LPSTR block;
DWORD blockSize;
// first let's set up the wfx format struct
wfx.nSamplesPerSec = 44100; // rate of the sample
wfx.wBitsPerSample = 16; //sample size
wfx.nChannels = 2; // 2 channels = stereo
wfx.cbSize = 0; // no extra info
wfx.wFormatTag = WAVE_FORMAT_PCM; //PCM format
wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
// then let's open the device
if(waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR)
{
fprintf(stderr, "unable to open Wave Mapper device.\n");
Sleep(1000);
ExitProcess(1);
}
// if no errors then close it
printf("The Wave Mapper device was opened successfully!\n");
//load and play file
if((block = loadAudioBlock("ding.raw", &blockSize)) == NULL)
{
fprintf(stderr, "Unable to load file\n");
Sleep(1000);
ExitProcess(1);
}
writeAudioBlock(hWaveOut, block, blockSize);
Sleep(1000);
waveOutClose(hWaveOut);
return 0;
}
Everytime I run the program I get the: "Unable to load file" output. I've got the "ding.raw" file in the same directory as my exe. I've also tried doing the full path as "C://path" and "C:/path" but then the compiler just gives me more errors about being unable to load a pdb file.
Any ideas? I'm using the Visual Studio 2012 Professional IDE and compiler.
Instead of using the standard char you should be using e.g. _TCHAR and LPCTSTR everywhere. This will make all string and string pointers you pass around be correct.
Look at the argv argument to _tmain and you will see that it uses _TCHAR instead of char. This is because Windows support both normal characters and Unicode characters depending on a couple of macros. See e.g. here for some more information.
So to solve what is likely your problem (since you don't get the actual error code, see my comment about GetLastError) you should change the function like this:
void *loadAudioBlock(LPCTSTR filename, DWORD* blockSize)
{
// ...
if((hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
return NULL;
// ...
}
And call it like this:
// ...
void *block;
if((block = loadAudioBlock(_T("C:\\path\\ding.raw"), &blockSize)) == NULL)
{
fprintf(stderr, "unable to open Wave Mapper device, error code %ld.\n", GetLastError());
Sleep(1000);
ExitProcess(1);
}
// ...
As you can see I also changed the return type, as the file is binary and won't have any readable text.
LPSTR loadAudioBlock(const char* filename, DWORD* blockSize)
{
if((hFile = CreateFile(CA2T(filename), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE)
return NULL;
}
See ATL conversion macros: http://msdn.microsoft.com/en-us/library/87zae4a3%28v=vs.80%29.aspx Just casting const char* LPCWSTR doesn't work.