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.
Related
I have a client and a server communicating using a named pipe.
I'm trying to pass the address stored by an LPCWSTR variable from the client to the server.
To do this, I first write the address onto a wchar_t buffer, then I send the server the size of that buffer (as a DWORD), so now the server knows how many bytes it has to read. I managed to send the buffer size successfully, I'm unable to send the complete string though.
Even though the server says it has read the required number of bytes, the buffer on the server side doesn't have the entire string.
Client:
wchar_t msgBuffer[1024];
LPCWSTR lpName = L"NameString";
_swprintf(msgBuffer, _T("%p\0"), lpName); //Write data to the buffer
DWORD nBytesToWrite = wcslen(msgBuffer); //Number of bytes to be written
bWriteFile = WriteFile( //Send the buffer size
hCreateFile,
&nBytesToWrite,
(DWORD)sizeof(nBytesToWrite),
&dwNoBytesWritten,
NULL
);
bWriteFile = WriteFile( //Send the data
hCreateFile,
msgBuffer,
(DWORD)wcslen(msgBuffer),
&dwNoBytesWritten,
NULL
);
Server:
DWORD dwBytesToRead = 0;
bReadFile = ReadFile( //Read the size of the next message
hCreateNamedPipe,
&dwBytesToRead,
sizeof(DWORD),
&dwNoBytesRead,
NULL);
std::cout << "\nBytes to be read: " << dwBytesToRead;
wchar_t msg[] = L"";
bReadFile = ReadFile( //Read the data
hCreateNamedPipe,
&msg,
dwBytesToRead,
&dwNoBytesRead,
NULL);
std::cout << "\nBytes Read: " << dwNoBytesRead;// << '\n' << msg;
wprintf(L"\nMessage: %s\nSize: %zu", msg, wcslen(msg));
This is what the output on the server side is:
Bytes to be read: 9
Bytes Read: 9
Message: 78E7
Size: 5
The address is 78E7325C on the client side, but my server only prints 78E7
Even though the server says to have read 9 bytes, the size of the resultant wchar_t is just 5, why is this?
EDIT: I've checked the buffer on the client side, it has the correct address stored. And is it okay to be sending the DWORD variable using the address-of (&) operator in WriteFile()?
The Solution
Changed (DWORD)wcslen(nBytesToWrite) to (DWORD)sizeof(nBytesToWrite)
wcslen gives the number of characters, whereas sizeof gives the number of bytes, and these aren't the same.
C-style strings are represented as pointers to a character array, with an implied length. The length is the number of characters in the array up to the first NUL character. When you interpret binary data as a C-style string (which your call to wprintf does), it stops writing characters once it finds the first character with a value of zero.
You are indeed able to read the entire message. The bug is that your code to verify this condition is based on a wrong assumption. You'll have to output dwNoBytesRead bytes in a loop, and cannot take advantage of the built-in string facilities of wprintf.
Besides that, you are reading into unallocated memory. wchar_t msg[] = L"" allocates an array of exactly one character, but you are reading into it, as if it were able to grow. That's not how things work in C. You'll need to familiarize yourself with the basics of the programming language you are using.
In addition, you are sending only half of your payload. WriteFile expects the number of bytes to write, but you are passing the return value of wcslen, i.e. the number of characters. On Windows, a wchar_t is 2 bytes wide.
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.
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!
In some code I use the Win32 RegGetValue() API to read a string from the registry.
I call the aforementioned API twice:
The purpose of the first call is to get the proper size to allocate a destination buffer for the string.
The second call reads the string from the registry into that buffer.
What is odd is that I found that RegGetValue() returns different size values between the two calls.
In particular, the size value returned in the second call is two bytes (equivalent to one wchar_t) less than the first call.
It's worth noting that the size value compatible with the actual string length is the value returned by the second call (this corresponds to the actual string length, including the terminating NUL).
But I don't understand why the first call returns a size two bytes (one wchar_t) bigger than that.
A screenshot of program output and Win32 C++ compilable repro code are attached.
Repro Source Code
#include <windows.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void PrintSize(const char* const message, const DWORD sizeBytes)
{
cout << message << ": " << sizeBytes << " bytes ("
<< (sizeBytes/sizeof(wchar_t)) << " wchar_t's)\n";
}
int main()
{
const HKEY key = HKEY_LOCAL_MACHINE;
const wchar_t* const subKey = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion";
const wchar_t* const valueName = L"CommonFilesDir";
//
// Get string size
//
DWORD keyType = 0;
DWORD dataSize = 0;
const DWORD flags = RRF_RT_REG_SZ;
LONG result = ::RegGetValue(
key,
subKey,
valueName,
flags,
&keyType,
nullptr,
&dataSize);
if (result != ERROR_SUCCESS)
{
cout << "Error: " << result << '\n';
return 1;
}
PrintSize("1st call size", dataSize);
const DWORD dataSize1 = dataSize; // store for later use
//
// Allocate buffer and read string into it
//
vector<wchar_t> buffer(dataSize / sizeof(wchar_t));
result = ::RegGetValue(
key,
subKey,
valueName,
flags,
nullptr,
&buffer[0],
&dataSize);
if (result != ERROR_SUCCESS)
{
cout << "Error: " << result << '\n';
return 1;
}
PrintSize("2nd call size", dataSize);
const wstring text(buffer.data());
cout << "Read string:\n";
wcout << text << '\n';
wcout << wstring(dataSize/sizeof(wchar_t), L'*') << " <-- 2nd call size\n";
wcout << wstring(dataSize1/sizeof(wchar_t), L'-') << " <-- 1st call size\n";
}
Operating System: Windows 7 64-bit with SP1
EDIT
Some confusion seems to be arisen by the particular registry key I happened to read in the sample repro code.
So, let me clarify that I read that key from the registry just as a test. This is not production code, and I'm not interested in that particular key. Feel free to add a simple test key to the registry with some test string value.
Sorry for the confusion.
RegGetValue() is safer than RegQueryValueEx() because it artificially adds a null terminator to the output of a string value if it does not already have a null terminator.
The first call returns the data size plus room for an extra null terminator in case the actual data is not already null terminated. I suspect RegGetValue() does not look at the real data at this stage, it just does an unconditional data size + sizeof(wchar_t) to be safe.
(36 * sizeof(wchar_t)) + (1 * sizeof(wchar_t)) = 74
The second call returns the real size of the actual data that was read. That size would include the extra null terminator only if one had to be artificially added. In this case, your data has 35 characters in the path, and a real null terminator present (which well-behaved apps are supposed to do), thus the extra null terminator did not need to be added.
((35+1) * sizeof(wchar_t)) + (0 * sizeof(wchar_t)) = 72
Now, with that said, you really should not be reading from the Registry directly to get the CommonFilesDir path (or any other system path) in the first place. You should be using SHGetFolderPath(CSIDL_PROGRAM_FILES_COMMON) or SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon) instead. Let the Shell deal with the Registry for you. This is consistent across Windows versions, as Registry settings are subject to be moved around from one version to another, as well as accounting for per-user paths vs system-global paths. These are the main reasons why the CSIDL API was introduced in the first place.
void fn(HINTERNET hfile,...){
char *url=new char[3000];
DWORD *len = new DWORD;
if(InternetQueryOption(hFile, INTERNET_OPTION_URL, url, len)==FALSE){
char* tmp=new char[3000];
sprintf(temp, "InternetQueryOption failed!\nError: %d\nRequired bytes: %d\n", GetLastError(),*len);
MessageBox(NULL, tmp, _T("myBHO !!!"), MB_OK | MB_ICONINFORMATION);
delete [] tmp;
}
//....
delete [] url;
delete len;
}
In this function I am using InternetQueryOption() with option INTERNET_OPTION_URL to find URL of handle hfile. Result would be returned in url[] and length in len.
But the function always fails and GetLastError() always returns 122 (ERROR_INSUFFICIENT_BUFFER).
According to this, maximum URL length possible in IE is 2083 while I am using buffer of length 3000. So how is this error possible? I even made url[] global, still the same error.
Now, according to IntenetQueryOption() documentation, if GetLastError() returns ERROR_INSUFFICIENT_BUFFER, then variable len points to the number of bytes required to hold the requested information (in this case the information is URL). And this value is always less than 100. So how can the URL not fit in ulr[]?
What could be the reason for this strange behaviour?
Please help.
According to this, maximum URL length possible in IE is 2083 while I am using buffer of length 3000. So how is this error possible? I even made url[] global, still the same error.
Browse your code, you declare a buffer big enough but you do not communicate to InternetQueryOption its size, that's why it always returns ERROR_INSUFFICIENT_BUFFER.
And this value is always less than 100.
After you call that function, check its value BEFORE.
With this line:
DWORD *len = new DWORD;
You allocate a pointer to DWORD but value in the pointed memory is uninitialized (may be 0, may be 338, may be 1234567, may crash your app). You may keep this code but you need to initialize memory to right value:
DWORD *len = new DWORD;
*len = sizeof(url);
IMO here you don't need any dynamic allocation and you may simply change it to:
DWORD len = sizeof(url);
Now you can pass its address to InternetQueryOption, like this:
if (InternetQueryOption(hFile, INTERNET_OPTION_URL, url, &len) == FALSE) {
Don't forget to remove delete len;.