I try to use the following code from kernel mode in a driver:
NTSTATUS NTAPI MmCopyVirtualMemory
(
PEPROCESS SourceProcess,
PVOID SourceAddress,
PEPROCESS TargetProcess,
PVOID TargetAddress,
SIZE_T BufferSize,
KPROCESSOR_MODE PreviousMode,
PSIZE_T ReturnSize
);
I use it in the following way:
PEPROCESS process;
NTSTATUS status;
unsigned int readValue;
// get notepad.exe process -> Notepad is opened already and this is the ID from Task Mgr
status = PsLookupProcessByProcessId((HANDLE)7252, &process);
if (!NT_SUCCESS(status))
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "\n\n ## Lookup By Id failed. ##\n\n");
if (status == STATUS_INVALID_CID)
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "\n\n ## Id could not be found. ##\n\n");
}
goto Exit;
}
SIZE_T cbBytesReturned;
status = MmCopyVirtualMemory(process, 0x00, PsGetCurrentProcess(), &readValue, sizeof(unsigned int), KernelMode, &cbBytesReturned);
if (!NT_SUCCESS(status))
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "\n\n ## MemCopy failed. ##\n\n");
}
else
{
DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "\n\n ## MemCopy DONE ##\n\n");
}
ObfDereferenceObject(process);
Currently this fails. I assumed that 0x00 points to the first byte of memory of the process I am reading from. A I wrong or is that relative which means process + 0x00 is the first memory location ?
I am not sure why you think your code is going to work, your input values for MmCopyVirtualMemory are not correct.
You're passing a NULL pointer for the second parameter. How is the Windows Kernel supposed to know where the memory you'd like to copy from the SourceProcess is present if you don't give it a valid address?
You're passing the pointer address to a local variable as the fourth parameter, yet the fourth parameter is supposed to be a pointer address which is valid under the process you're targeting (the third parameter). The pointer address used for the fourth parameter is supposed to be where you'd like to put the memory copied from the SourceAddress (within the virtual memory of SourceProcess) within the TargetProcess.
For the fifth parameter, you're passing the size of an unsigned int? This is also incorrect.
I believe the fifth parameter (BufferSize) should be the length of how much memory you'd like to copy from SourceAddress into TargetAddress. If this is the case, make sure that there is enough room at TargetAddress - take this with a grain of salt.
I suggest you take a look at the function prototype which you shared in your original post and re-check your code, and try again, taking on-board my comments here and after some more research on the routine.
Remember though, MmCopyVirtualMemory is not officially documented and you'll be taking a risk by using it in any production-level source code. I recommend strongly that you re-consider your options if this isn't just an educational experiment, because stable and documented code is generally an important thing.
Related
I'm somewhat puzzled by SetFilePointerEx, ReadFile and WriteFile APIs. Say, if I want to move file pointer to a new position, is it sufficient to do:
if(SetFilePointerEx(hFile, liPtr, NULL, FILE_BEGIN))
{
//Success, moved file pointer to liPtr position
}
Or, do I need to check the value returned in lpNewFilePointer too, as such?
LARGE_INTEGER liSetTo = {0};
if(SetFilePointerEx(hFile, liPtr, &liSetTo, FILE_BEGIN) &&
liPtr.QuadPart == liSetTo.QuadPart)
{
//Success
}
The same applies to ReadFile and WriteFile. For instance:
if(WriteFile(hFile, buffer, numberBytesToWrite, NULL, NULL))
{
//Success writing numberBytesToWrite into file
}
Or do I need to do this to make sure that all my data was written successfully:
DWORD numberBytesWritten = 0;
if(WriteFile(hFile, buffer, numberBytesToWrite, &numberBytesWritten, NULL) &&
numberBytesWritten == numberBytesToWrite)
{
//Success writing numberBytesToWrite into file
}
In other words, what's the point to have those return sizes and offsets? I mean, if I want it to write 1024 bytes into a file, can it just write 1000 instead. :) If it doesn't write all the data that I requested it to, wouldn't it constitute an error, or FALSE to be returned from the API?
Anyway, I'd appreciate if someone could clarify.
For SetFilePointerEx, you should only check it's return value. It contains whether the operation was a SUCCESS.
For a synchronous ReadFile/WriteFile, if it returns false, then the operation has completely failed (no bytes read/written at all). If it returns true, then you should check lpNumberOfBytesRead/ lpNumberOfBytesWritten, as it can be lower then the number you specified. For example, for read, if EOF reached, then you'll get a short read. For write, if disk becomes full during write, you may get a short write.
There could be other various reasons for short read/write, so your code should handle these cases.
I'm having a very strange problem whilst trying to download a file from the internet inside a C++ application written for Windows Compact 2013.
BOOL WWW::Read(char* buffer, DWORD buffer_size)
{
memset(buffer, 0, buffer_size);
m_dwBytesRead = 0;
BOOL bResult = InternetReadFile(m_handle, buffer, buffer_size, &m_dwBytesRead);
if (!bResult)
{
DWORD dwLastError = GetLastError();
TCHAR *err;
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, dwLastError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
(LPTSTR)&err, 0, NULL))
{
LOGMSG(1, (TEXT("InternetReadFile failed at - (%u) %s\r\n"), dwLastError, err));
LocalFree(err);
}
}
// FUDGE
if (m_dwBytesRead == 0)
{
DWORD dwZeros = countZeros(buffer, buffer_size);
if (dwZeros < buffer_size)
{
m_dwBytesRead = buffer_size;
}
}
// END OF FUDGE
return bResult;
}
I repeatedly call the above function from another member function as follows
DWORD dwWritten;
while (!(Read(buffer, DOWNLOAD_BUFFER_SIZE) && m_dwBytesRead == 0))
{
WriteFile(m_hDownload, buffer, m_dwBytesRead, &dwWritten, NULL);
m_dwActualSize += dwWritten;
++m_dwChunks;
if (m_dwBytesRead > 0)
m_dwInactivity = 0;
else if (++m_dwInactivity > INACTIVITY_LIMIT)
return WDS_INACTIVITY;
}
Without the FUDGE, this function fails the first time through, and works correctly on subsequent calls. The error that I get on the first pass through this function call is
InternetReadFile failed at - (112) There is not enough space on the disk.
I don't understand why I should be getting a "not enough space on disk" error during a READ operation. I have checked that that the buffer is allocated, and available, and matches the expected size. In fact when I inspect the contents of the buffer, I find that it HAS been filled with the expected number of bytes, however the contents of the m_dwBytesRead variable is still set to 0.
As you can see, I have tried to code around this specific case by inspecting the contents of the buffer to see if it has been filled, and then fudging the m_dwBytesRead variable, but this is only a temporary work around to get me past this error, I really need to understand why this problem is occurring.
The consequences of this error (without my fudge), is that the data is thrown away, and I end up with a file that is missing the first block but otherwise fully correct. Consequently MD5 checks fail, and I am missing the first part of the file.
I just happen to know that the file will always be larger than the block size that I am using, so my fudge will work, but I don't like having these horrible workarounds in the code when they shouldn't be needed.
If anyone can shed any light upon what is causing the problem, it would be greatly appreciated.
I'm using Visual Studio 2013 C++ (native Windows app, not MFC), the target is 32-bit and Unicode, running on Windows Compact 2013.
Many thanks,
Andrew
Is the machine actually running out of disk space? InternetReadFile will write to disk behind your back by default:
To ensure all data is retrieved, an application must continue to call the InternetReadFile function until the function returns TRUE and the lpdwNumberOfBytesRead parameter equals zero. This is especially important if the requested data is written to the cache, because otherwise the cache will not be properly updated and the file downloaded will not be committed to the cache. Note that caching happens automatically unless the original request to open the data stream set the INTERNET_FLAG_NO_CACHE_WRITE flag.
Blackbone is located here:
https://github.com/DarthTon/Blackbone
Now what I would like to try to achieve is to read process memory using the aforementioned library.
I have used the pattern scan method to obtain an address in memory which I will later use in the reading process, however I'm getting a bit stuck on whether how I should read the data stored at the Output buffer that the read method is using.
The Read method is structured here:
https://github.com/DarthTon/Blackbone/blob/master/src/BlackBone/Process/ProcessMemory.h#L57
And I'm following exactly the same approach taken in the patternscan example provided at:
https://github.com/DarthTon/Blackbone/blob/master/src/TestApp/PatternTest.cpp
Just with a little slightly difference:
void FindKeys()
{
Process p;
std::vector<DWORD> procs;
std::vector<ptr_t> results;
ptr_t dwAddress;
size_t dwSize;
PVOID pResult;
bool handleHoles = true;
Process::EnumByName(L"Notepad++.exe", procs);
if (!procs.empty())
{
p.Attach(procs.front());
auto pMainMod = p.modules().GetMainModule();
// Initialize patterns
PatternSearch ps1{ 0x33, 0xC5, 0x89 };
// Scan all allocated process memory
std::wcout << L"[+] Searching for Pattern Scan...\n";
ps1.SearchRemoteWhole(p, false, 0, results);
std::wcout << L"[+] Found at Address: [0x";
std::wcout << std::hex << results[0];
std::wcout << L"]!\n";
dwAddress = results[0];
dwSize = 0x10;
p.memory().Read(dwAddress, dwSize, pResult, handleHoles);
results.clear();
}
else
std::wcout << L"Can't find Notepad++.exe, aborting\n\n";
}
My question comes from failing to read pResult, how could I successfully read from it, specifically, I would like to read it as an array of bytes.
Thanks in advance for any further help you guys provide me, it will be deeply appreciated!
Alright guys, here is it, fairly simple! and I was over complicating myself.
What I did was to create another variable type BYTE (unsigned char byte) named pResultBytes and then use memcpy(&pResultBytes, pResult, dwSize); where pResult is the output buffer from the Read() method and dwSize is the number of bytes to copy to the new variable.
Next I just started iterating through the pResultBytes variable byte by byte and doing the relative conversions to make a hex string and ultimately read it.
Woops almost for got to mention, I also initialized pResult to a new UCHAR[dwSize]; as well, so it eventually has the proper space required for the memcpy.
That's it!
Cheers!
I thought I read the documentation correctly over at MSDN but apparently I didn't? I am not entirely sure what the heck I'm doing wrong and I'm ready to pull my hair out.
The documentation for the EVENTLOGRECORD structure provides the offset to the SID
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363646(v=vs.85).aspx
UserSidOffset
The offset of the security identifier (SID) within this event log record. To obtain the user name for this SID, use the LookupAccountSid function.
Then to convert this sid, we use the LookupAccountSid() API.
https://msdn.microsoft.com/en-us/library/windows/desktop/aa379166(v=vs.85).aspx
lpSid [in]
A pointer to the SID to look up.
I've searched endlessly and found examples that look very similar to my implementation but gave me the same result. I'm rusty with the Windows API, so I wouldn't be surprised if I am overlooking the issue.
And finally, here is my code:
size_t BytesRemaining = 0;
while (BytesRemaining < BytesInBuffer)
{
EVENTLOGRECORD *Record = reinterpret_cast<EVENTLOGRECORD *>(buffer + BytesRemaining);
char UsernameBuffer[256], DomainBuffer[256];
DWORD UsernameBufferSize = 256, DomainBufferSize = 256;
SID_NAME_USE SidType;
PSID SID = (PSID)((LPBYTE)Record + Record->UserSidOffset);
if (!LookupAccountSid(NULL, SID, UsernameBuffer, &UsernameBufferSize, DomainBuffer, &DomainBufferSize, &SidType))
{
std::cout << "Failed reading SID (" << SID << "): " << GetLastErrorMessage().c_str();
}
else {
std::cout << "I didn't shit on the SID.\n";
}
BytesRemaining += Record->Length;
}
bytesInBuffer = 0;
Edit
After doing some debugging, I discovered that the StringsOffset and UserSidOffset contain the same value. So it looks like the offsets are pointing incorrectly... Which is why I am not able to pass a valid SID to the API.
Anyone?
cchName [in, out]
On input, specifies the size, in TCHARs, of the lpName buffer. If the function fails because the buffer is too small or if cchName is zero, cchName receives the required buffer size, including the terminating null character.
cchReferencedDomainName [in, out]
On input, specifies the size, in TCHARs, of the lpReferencedDomainName buffer. If the function fails because the buffer is too small or if cchReferencedDomainName is zero, cchReferencedDomainName receives the required buffer size, including the terminating null character.
You need to assign the array sizes to UsernameBufferSize and DomainBufferSize before calling LookupAccountSid(). Either that, or set them to zero and then dynamically allocate UsernameBuffer and DomainBuffer, then call LookupAccountSid() again.
The following piece of code seems to unreliably execute and after and undeterministic time it will fail with error code 234 at the RegEnumValue function.
I have not written this code, I am merely trying to debug it. I know there is an issue with doing RegEnumValue and then deleting keys in the while loop.
I am trying to figure out first, why it is throwing this 234 error at seemingly random points, as in, it is never after a consistent number of loop iterations or anything like that.
From what I have seen it fails to fill its name buffer, but this buffer is by no means too small for its purpose, so I don't understand how it could fail??
Could someone please advice on getting rid of this 234 error thrown by the RegEnumValue funciton?
HKEY key;
DWORD dw;
int idx;
char name[8192];
DWORD namesize=4096;
std::string m_path = "SOFTWARE\\Company\\Server 4.0";
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,m_path.c_str(),0,KEY_ALL_ACCESS,&key) == ERROR_SUCCESS)
{
bool error=false;
idx=0;
long result;
long delresult;
while (true)
{
result = RegEnumValue(key,idx,(char*)name,&namesize,NULL,NULL,NULL,NULL);
if (result == ERROR_SUCCESS && !error){
delresult = RegDeleteValue(key,name);
if (delresult != ERROR_SUCCESS)
error = true;
idx++;
}
else
{
break;
}
}
RegCloseKey(key);
}
There are some errors in your code:
The 4-th parameter of RegEnumValue (the namesize) is in-out parameter. So you have to reset namesize to sizeof(name)/sizeof(name[0]) (in case of the usage char type it is just sizeof(name)) inside the while loop before every call of RegEnumValue. It's the main error in your program.
If you don't want to have ERROR_MORE_DATA error any time you have the buffer having 32,767 characters. It is the maximum size of name the the regitry value (see documentation of RegEnumValue).
It is not good to use KEY_ALL_ACCESS in the RegOpenKeyEx. I'll recomend you to change it to KEY_QUERY_VALUE | KEY_SET_VALUE. It is not a real error, but depends on your environment it could be.
It if better to use UNICODE version of all this functions to speed-up a little the code.
UPDATED: Only small comment about the usage of the UNICODE version. Intern Windows work with UNICODE characters. So usage of non-Unicode version of RegEnumValue si more slow because at the evry call a new UICODE memeory block will be allocated and converted to ANSI/Multi-byte. Moreover if you will has a value name written in a language which can't be converted in you Windows ANSI code page (Chinese, Japanese and so on) and some characters will be replaced to '?' (see WC_DEFAULTCHAR flag of WideCharToMultiByte), then it can be that the function RegDeleteValue will fail with the error code like "the value with the name is not exist".
just change the value of your fourth parameter i.e namesize from 4096 to 8192 .Always MakeSure that it should be always equal to buffer size.
The answer is at the bottom of that page:
http://msdn.microsoft.com/en-us/library/ms724865(VS.85).aspx
Please read the answer of "ERROR_MORE_DATA: lpData too small, or lpValueName too small?" question.