reading PE32+ section's raw data - windows

I'm trying to read the .pdata section of a x64 exe.
I'm mapping the file to the memory, finding the .pdata section, and then I use it's PointerToRawData to get to the actual data of the section...
But then my "pdata" pointer points at a illegal address :(
This is what I do:
void* mappingHandle = CreateFileMapping(fileHandle,
NULL,
PAGE_READONLY,
0,
1,
NULL);
char* fileMemory = (char*)MapViewOfFile(mappingHandle, FILE_MAP_READ, 0, 0, 1);
IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)fileMemory;
IMAGE_SECTION_HEADER* pdataSectionHeader = NULL;
if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) // "MZ" signature
{
IMAGE_NT_HEADERS* ntHeaders = (IMAGE_NT_HEADERS*)(fileMemory + dosHeader->e_lfanew);
if (ntHeaders->Signature == IMAGE_NT_SIGNATURE) // Supposed to be "PE"
{
unsigned int sectionCount = ntHeaders->FileHeader.NumberOfSections;
IMAGE_SECTION_HEADER* sectionHeaders = IMAGE_FIRST_SECTION(ntHeaders);
pdataSectionHeader = sectionHeaders + 3; // Going to .pdata section.
}
}
unsigned long pdataSize = pdataSectionHeader->SizeOfRawData;
char* pdata = fileMemory + pdataSectionHeader->PointerToRawData;
can anybody tell me what I doing wrong?

The problem was in the way I mapped the file to the memory.
I should have done it this way:
void* mappingHandle = CreateFileMapping(fileHandle,
NULL,
PAGE_READONLY,
0,
0, //Here: 0 instead of 1
NULL);

Related

Why might DeviceCapabilities() return 4294967295 for DC_BINS?

I'm fetching the selected printer tray from a WIN32 call to PrintDlgEx(). This seems to work successfully most of the time, but recently I added a new printer to my machine (a DYMO LabelWriter 450) and it caused my simple software to fail.
Upon investigation, the call to DeviceCapabilities() for DC_BINS is returning 4294967295, while all of the other printers I've tested so far return single digit bin counts.
My first inclination is to omit the bin name when the bin count is greater than a given threshold (say... 20?), but I don't love this solution.
Is there a known reason that a printer would return the max UNSIGNED INT value for this? Is it just poorly written drivers, or is there an alternate meaning? Or perhaps I totally misunderstand the intended value.
If I have to write an arbitrary cap I will, but I'd like to better understand why this situation exists. Clearly, this printer doesn't have billions of different printer trays.
Here's an MRE:
HINSTANCE hinst = GetModuleHandle(NULL);
HRESULT hResult;
PRINTDLGEX pdx = {0};
LPPRINTPAGERANGE pPageRanges = NULL;
HWND hWndOwner = GetForegroundWindow();
if(!hWndOwner){
hWndOwner = GetDesktopWindow();
}
// Allocate an array of PRINTPAGERANGE structures.
pPageRanges = (LPPRINTPAGERANGE) GlobalAlloc(GPTR, 10 * sizeof(PRINTPAGERANGE));
if(!pPageRanges){
return wprintf(L"{\"error\": \"%s\"}", GetLastError()); // "Your computer does not have enough memory to complete this operation:"
}
// Initialize the PRINTDLGEX structure.
pdx.lStructSize = sizeof(PRINTDLGEX);
pdx.hwndOwner = hWndOwner;
pdx.hDevMode = NULL;
pdx.hDevNames = NULL;
pdx.hDC = NULL;
pdx.Flags = PD_RETURNDC | PD_COLLATE;
pdx.Flags2 = 0;
pdx.ExclusionFlags = 0;
pdx.nPageRanges = 0;
pdx.nMaxPageRanges = 10;
pdx.lpPageRanges = pPageRanges;
pdx.nMinPage = 1;
pdx.nMaxPage = 1000;
pdx.nCopies = 1;
pdx.hInstance = 0;
pdx.lpPrintTemplateName = NULL;
pdx.lpCallback = NULL;
pdx.nPropertyPages = 0;
pdx.lphPropertyPages = NULL;
pdx.nStartPage = START_PAGE_GENERAL;
pdx.dwResultAction = 0;
// Invoke the Print property sheet.
hResult = PrintDlgEx(&pdx);
DEVMODE * myDevMode = (DEVMODE *)GlobalLock(pdx.hDevMode);
DWORD binCount = DeviceCapabilities((CHAR*)myDevMode->dmDeviceName, nullptr, DC_BINS, nullptr, nullptr);
DWORD binNameCount = DeviceCapabilities((CHAR*)myDevMode->dmDeviceName, nullptr, DC_BINNAMES, nullptr, nullptr);
wprintf(L"\"binCount\":\"%lu\",", binCount);
wprintf(L"\"binNameCount\":\"%lu\",", binNameCount);
DeviceCapabilities() returns a signed int, not an unsigned DWORD.
The unsigned value 4294967295 is hex 0xFFFFFFFF, which is the same numeric value as a signed -1.
Per the DeviceCapabilities() documentation:
Return value
If the function succeeds, the return value depends on the setting of the fwCapability parameter. A return value of zero generally indicates that, while the function completed successfully, there was some type of failure, such as a capability that is not supported. For more details, see the descriptions for the fwCapability values.
If the function returns -1, this may mean either that the capability is not supported or there was a general function failure.
You are not accounting for the possibility of DeviceCapabilities() failing (or PrintDlgEx(), either).
Try this:
HWND hWndOwner = GetForegroundWindow();
if (!hWndOwner){
hWndOwner = GetDesktopWindow();
}
// Allocate an array of PRINTPAGERANGE structures.
LPPRINTPAGERANGE pPageRanges = (LPPRINTPAGERANGE) GlobalAlloc(GPTR, 10 * sizeof(PRINTPAGERANGE));
if (!pPageRanges){
// NOTE: GetLastError() returns DWORD, not TCHAR*! So, if you
// want to translate the error code in a human-readable string,
// use FormatMessage() instead...
return wprintf(L"{\"error\": %lu}", GetLastError());
}
// Initialize the PRINTDLGEX structure.
PRINTDLGEX pdx = {0};
pdx.lStructSize = sizeof(PRINTDLGEX);
pdx.hwndOwner = hWndOwner;
pdx.Flags = PD_RETURNDC | PD_COLLATE;
pdx.nMaxPageRanges = 10;
pdx.lpPageRanges = pPageRanges;
pdx.nMinPage = 1;
pdx.nMaxPage = 1000;
pdx.nCopies = 1;
pdx.nStartPage = START_PAGE_GENERAL;
HRESULT hResult = PrintDlgEx(&pdx);
if (hResult != S_OK)
{
GlobalFree(reinterpret_cast<HGLOBAL>(pPageRanges));
return wprintf(L"{\"error\": %d}", hResult);
}
if (pdx.dwResultAction == PD_RESULT_CANCEL)
{
GlobalFree(reinterpret_cast<HGLOBAL>(pPageRanges));
return wprintf(L"{\"error\": \"cancelled\"}");
}
DEVMODE *myDevMode = (DEVMODE*) GlobalLock(pdx.hDevMode);
int binCount = DeviceCapabilities(reinterpret_cast<TCHAR*>(myDevMode->dmDeviceName), nullptr, DC_BINS, nullptr, nullptr);
wprintf(L"\"binCount\":%d,", binCount);
int binNameCount = DeviceCapabilities(reinterpret_cast<TCHAR*>(myDevMode->dmDeviceName),
nullptr, DC_BINNAMES, nullptr, nullptr);
wprintf(L"\"binNameCount\":%d,", binNameCount);
if (binCount == -1)
{
...
}
if (binNameCount == -1)
{
...
}
...
GlobalUnlock(pdx.hDevMode);
GlobalFree(reinterpret_cast<HGLOBAL>(pPageRanges));
return ...;

How to enumerate the volumes on a phyiscal drive using Windows API?

I'm trying to create a list of partitions and their volumes of all the (fixed) disks in the system (Something like: PhyiscalDrive0, Partition 1, C:\; PhyiscalDrive0, Partition 2, D:\; ...) . I already got the list of installed disks via SetupApi and IOCTL_STORAGE_GET_DEVICE_NUMBER, as well as the number of partition.
The problem I'm having is how to find out the drive letters that are mounted to a partiticular partition (in meaning of partition 1 of Physical drive is C:\, for example)?
Thanks in before for any help,
Willi K.
The API you probably want to use is the Storage Management API. This provides a way to query just about anything you want. In particular, the drive letters are part of the MSFT_Volume class. The API isn't the friendliest for C++, which is why I'm not providing a sample query, but there's a sample here that shows how to write queries.
Once you have obtained the drives through the setup api you need to obtain information about partitions (through IOCTL_DISK_GET_DRIVE_LAYOUT_EX) and especially their starting offset and length.
Here is the code I use inside my library libwindevblk (libwindevblk):
BOOL FindVolume(int diskno, PSMI_DEVBLK_ENTRY pDevBlkEntry)
{
HANDLE vol;
BOOL success;
TCHAR szNextVolName[MAX_PATH+1];
TCHAR szNextVolNameNoBSlash[MAX_PATH+1];
vol = FindFirstVolume(szNextVolName, MAX_PATH);
success = (vol != INVALID_HANDLE_VALUE);
while (success)
{
//We are now enumerating volumes. In order for this function to work, we need to get partitions that compose this volume
HANDLE volH;
PVOLUME_DISK_EXTENTS vde;
DWORD bret;
//For this CreateFile, volume must be without trailing backslash
StringCchCopy(szNextVolNameNoBSlash, MAX_PATH, szNextVolName);
szNextVolNameNoBSlash[_tcslen(szNextVolNameNoBSlash) - 1] = _T('\0');
volH = CreateFile(szNextVolNameNoBSlash,
FILE_READ_ATTRIBUTES | SYNCHRONIZE | FILE_TRAVERSE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, 0);
if (volH != INVALID_HANDLE_VALUE)
{
bret = sizeof(VOLUME_DISK_EXTENTS) + 256 * sizeof(DISK_EXTENT);
vde = (PVOLUME_DISK_EXTENTS)malloc(bret);
if (DeviceIoControl(volH, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, (void *)vde, bret, &bret, NULL))
{
for (unsigned i = 0; i < vde->NumberOfDiskExtents; i++)
{
if (vde->Extents[i].DiskNumber == diskno &&
vde->Extents[i].StartingOffset.QuadPart == pDevBlkEntry->StartingOffset.QuadPart &&
vde->Extents[i].ExtentLength.QuadPart == pDevBlkEntry->PartitionLength.QuadPart)
{
CHAR szVolumeName[MAX_PATH + 1] = { 0 };
CHAR szFileSystemName[MAX_PATH + 1] = { 0 };
DWORD dwSerialNumber = 0;
DWORD dwMaxFileNameLength = 0;
DWORD dwFileSystemFlags = 0;
if (GetVolumeInformation(szNextVolName,
szVolumeName, _countof(szVolumeName),
&dwSerialNumber,
&dwMaxFileNameLength,
&dwFileSystemFlags,
szFileSystemName,
_countof(szFileSystemName)))
{
_tcsncpy(pDevBlkEntry->szRootPathName, szNextVolName, MAX_PATH);
_tcsncpy(pDevBlkEntry->szVolumeName, szVolumeName, MAX_PATH);
_tcsncpy(pDevBlkEntry->szFileSystemName, szFileSystemName, MAX_PATH);
pDevBlkEntry->dwSerialNumber = dwSerialNumber;
pDevBlkEntry->dwFileSystemFlags = dwFileSystemFlags;
}
else
{
DWORD dwErr = GetLastError();
printf("%lu", dwErr);
}
DWORD length = 0;
TCHAR pathnames[MAX_PATH + 1] = { 0 };
if (GetVolumePathNamesForVolumeName(szNextVolName, (LPTSTR)pathnames, MAX_PATH, &length))
{
_tcsncpy(pDevBlkEntry->szVolumePathName, pathnames, MAX_PATH);
}
free(vde);
CloseHandle(volH);
FindVolumeClose(vol);
return TRUE;
}
}//for
}
free(vde);
CloseHandle(volH);
}
success = FindNextVolume(vol, szNextVolName, MAX_PATH) != 0;
}
FindVolumeClose(vol);
return FALSE;
}

How to get string from pointer

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) ;

Win32- Getting ListView Control content from another application

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);
}
}

Extract Eventlog messages in a specific language

I need some help regarding the extraction of eventlog data under Windows 7.
What I try to achieve:
A computer has Windows 7 German (or any other language) installed. I want to extract the eventlog messages in Englisch to transport them to another computer where I want to store and analyze the eventlog.
This should be done somehow programatically (C# or C++).
I have tried different ways. Write a C# programm to extract the messages result always in getting the messages not in englisch but the configured language of the computer. I also tried it in C++ but also with the same result.
The other approach was then to extract the eventlog in a evtx-File and transport it to another computer with an englisch operating system. But the problem with that solution is that I also need non Windows eventlog messages (e.g. from the installed programs) which cannot be viewed on the other computer where the program and the message dlls are not installed.
Does anybody have an idea how to extract eventlog messages in English independent from the language of the operating system?
Thanks a lot,
Ulli
Here is the complete code for C++ to extract special eventlog messages in a specific language (Thanks to "Apokal" and MSDN). You can change the definitions for
Provider Name (this is the key in the registry)
Resource dll (this is the path to the message dll referenced in the registry)
Message language (this is the language code - Note: Seems the complete code is needed "DE" is not working "DE-de" works ...)
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <strsafe.h>
#define PROVIDER_NAME L"SceCli"
#define RESOURCE_DLL L"C:\\Windows\\System32\\scecli.dll"
#define MESSAGE_LANGUAGE 0x0409 // En-Us
#define MAX_TIMESTAMP_LEN 23 + 1 // mm/dd/yyyy hh:mm:ss.mmm
#define MAX_RECORD_BUFFER_SIZE 0x10000 // 64K
HANDLE GetMessageResources();
DWORD DumpRecordsInBuffer(PBYTE pBuffer, DWORD dwBytesRead);
DWORD GetEventTypeName(DWORD EventType);
LPWSTR GetMessageString(DWORD Id, DWORD argc, LPWSTR args);
void GetTimestamp(const DWORD Time, WCHAR DisplayString[]);
DWORD ApplyParameterStringsToMessage(CONST LPCWSTR pMessage, LPWSTR & pFinalMessage);
CONST LPWSTR pEventTypeNames[] = {L"Error", L"Warning", L"Informational", L"Audit Success", L"Audit Failure"};
HANDLE g_hResources = NULL;
void wmain(void)
{
HANDLE hEventLog = NULL;
DWORD status = ERROR_SUCCESS;
DWORD dwBytesToRead = 0;
DWORD dwBytesRead = 0;
DWORD dwMinimumBytesToRead = 0;
PBYTE pBuffer = NULL;
PBYTE pTemp = NULL;
// The source name (provider) must exist as a subkey of Application.
hEventLog = OpenEventLog(NULL, PROVIDER_NAME);
if (NULL == hEventLog)
{
wprintf(L"OpenEventLog failed with 0x%x.\n", GetLastError());
goto cleanup;
}
// Get the DLL that contains the string resources for the provider.
g_hResources = GetMessageResources();
if (NULL == g_hResources)
{
wprintf(L"GetMessageResources failed.\n");
goto cleanup;
}
// Allocate an initial block of memory used to read event records. The number
// of records read into the buffer will vary depending on the size of each event.
// The size of each event will vary based on the size of the user-defined
// data included with each event, the number and length of insertion
// strings, and other data appended to the end of the event record.
dwBytesToRead = MAX_RECORD_BUFFER_SIZE;
pBuffer = (PBYTE)malloc(dwBytesToRead);
if (NULL == pBuffer)
{
wprintf(L"Failed to allocate the initial memory for the record buffer.\n");
goto cleanup;
}
// Read blocks of records until you reach the end of the log or an
// error occurs. The records are read from newest to oldest. If the buffer
// is not big enough to hold a complete event record, reallocate the buffer.
while (ERROR_SUCCESS == status)
{
if (!ReadEventLog(hEventLog,
EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ,
0,
pBuffer,
dwBytesToRead,
&dwBytesRead,
&dwMinimumBytesToRead))
{
status = GetLastError();
if (ERROR_INSUFFICIENT_BUFFER == status)
{
status = ERROR_SUCCESS;
pTemp = (PBYTE)realloc(pBuffer, dwMinimumBytesToRead);
if (NULL == pTemp)
{
wprintf(L"Failed to reallocate the memory for the record buffer (%d bytes).\n", dwMinimumBytesToRead);
goto cleanup;
}
pBuffer = pTemp;
dwBytesToRead = dwMinimumBytesToRead;
}
else
{
if (ERROR_HANDLE_EOF != status)
{
wprintf(L"ReadEventLog failed with %lu.\n", status);
goto cleanup;
}
}
}
else
{
// Print the contents of each record in the buffer.
DumpRecordsInBuffer(pBuffer, dwBytesRead);
}
}
getchar();
cleanup:
if (hEventLog)
CloseEventLog(hEventLog);
if (pBuffer)
free(pBuffer);
}
// Get the provider DLL that contains the string resources for the
// category strings, event message strings, and parameter insert strings.
// For this example, the path to the DLL is hardcoded but typically,
// you would read the CategoryMessageFile, EventMessageFile, and
// ParameterMessageFile registry values under the source's registry key located
// under \SYSTEM\CurrentControlSet\Services\Eventlog\Application in
// the HKLM registry hive. In this example, all resources are included in
// the same resource-only DLL.
HANDLE GetMessageResources()
{
HANDLE hResources = NULL;
hResources = LoadLibraryEx(RESOURCE_DLL, NULL, LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE);
if (NULL == hResources)
{
wprintf(L"LoadLibrary failed with %lu.\n", GetLastError());
}
return hResources;
}
// Loop through the buffer and print the contents of each record
// in the buffer.
DWORD DumpRecordsInBuffer(PBYTE pBuffer, DWORD dwBytesRead)
{
DWORD status = ERROR_SUCCESS;
PBYTE pRecord = pBuffer;
PBYTE pEndOfRecords = pBuffer + dwBytesRead;
LPWSTR pMessage = NULL;
LPWSTR pFinalMessage = NULL;
WCHAR TimeStamp[MAX_TIMESTAMP_LEN];
while (pRecord < pEndOfRecords)
{
// If the event was written by our provider, write the contents of the event.
if (0 == wcscmp(PROVIDER_NAME, (LPWSTR)(pRecord + sizeof(EVENTLOGRECORD))))
{
GetTimestamp(((PEVENTLOGRECORD)pRecord)->TimeGenerated, TimeStamp);
wprintf(L"Time stamp: %s\n", TimeStamp);
wprintf(L"record number: %lu\n", ((PEVENTLOGRECORD)pRecord)->RecordNumber);
wprintf(L"status code: %d\n", ((PEVENTLOGRECORD)pRecord)->EventID & 0xFFFF);
wprintf(L"event type: %s\n", pEventTypeNames[GetEventTypeName(((PEVENTLOGRECORD)pRecord)->EventType)]);
pMessage = GetMessageString(((PEVENTLOGRECORD)pRecord)->EventCategory, 0, NULL);
if (pMessage)
{
wprintf(L"event category: %s", pMessage);
LocalFree(pMessage);
pMessage = NULL;
}
pMessage = GetMessageString(((PEVENTLOGRECORD)pRecord)->EventID,
((PEVENTLOGRECORD)pRecord)->NumStrings, (LPWSTR)(pRecord + ((PEVENTLOGRECORD)pRecord)->StringOffset));
if (pMessage)
{
status = ApplyParameterStringsToMessage(pMessage, pFinalMessage);
wprintf(L"event message: %s", (pFinalMessage) ? pFinalMessage : pMessage);
LocalFree(pMessage);
pMessage = NULL;
if (pFinalMessage)
{
free(pFinalMessage);
pFinalMessage = NULL;
}
}
// To write the event data, you need to know the format of the data. In
// this example, we know that the event data is a null-terminated string.
if (((PEVENTLOGRECORD)pRecord)->DataLength > 0)
{
wprintf(L"event data: %s\n", (LPWSTR)(pRecord + ((PEVENTLOGRECORD)pRecord)->DataOffset));
}
wprintf(L"\n");
}
pRecord += ((PEVENTLOGRECORD)pRecord)->Length;
}
return status;
}
// Get an index value to the pEventTypeNames array based on
// the event type value.
DWORD GetEventTypeName(DWORD EventType)
{
DWORD index = 0;
switch (EventType)
{
case EVENTLOG_ERROR_TYPE:
index = 0;
break;
case EVENTLOG_WARNING_TYPE:
index = 1;
break;
case EVENTLOG_INFORMATION_TYPE:
index = 2;
break;
case EVENTLOG_AUDIT_SUCCESS:
index = 3;
break;
case EVENTLOG_AUDIT_FAILURE:
index = 4;
break;
}
return index;
}
// Formats the specified message. If the message uses inserts, build
// the argument list to pass to FormatMessage.
LPWSTR GetMessageString(DWORD MessageId, DWORD argc, LPWSTR argv)
{
LPWSTR pMessage = NULL;
DWORD dwFormatFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER;
DWORD_PTR* pArgs = NULL;
LPWSTR pString = argv;
// The insertion strings appended to the end of the event record
// are an array of strings; however, FormatMessage requires
// an array of addresses. Create an array of DWORD_PTRs based on
// the count of strings. Assign the address of each string
// to an element in the array (maintaining the same order).
if (argc > 0)
{
pArgs = (DWORD_PTR*)malloc(sizeof(DWORD_PTR) * argc);
if (pArgs)
{
dwFormatFlags |= FORMAT_MESSAGE_ARGUMENT_ARRAY;
for (DWORD i = 0; i < argc; i++)
{
pArgs[i] = (DWORD_PTR)pString;
pString += wcslen(pString) + 1;
}
}
else
{
dwFormatFlags |= FORMAT_MESSAGE_IGNORE_INSERTS;
wprintf(L"Failed to allocate memory for the insert string array.\n");
}
}
if (!FormatMessage(dwFormatFlags,
g_hResources,
MessageId,
MESSAGE_LANGUAGE,
(LPWSTR)&pMessage,
0,
(va_list*)pArgs))
{
wprintf(L"Format message failed with %lu\n", GetLastError());
}
if (pArgs)
free(pArgs);
return pMessage;
}
// If the message string contains parameter insertion strings (for example, %%4096),
// you must perform the parameter substitution yourself. To get the parameter message
// string, call FormatMessage with the message identifier found in the parameter insertion
// string (for example, 4096 is the message identifier if the parameter insertion string
// is %%4096). You then substitute the parameter insertion string in the message
// string with the actual parameter message string.
DWORD ApplyParameterStringsToMessage(CONST LPCWSTR pMessage, LPWSTR & pFinalMessage)
{
DWORD status = ERROR_SUCCESS;
DWORD dwParameterCount = 0; // Number of insertion strings found in pMessage
size_t cbBuffer = 0; // Size of the buffer in bytes
size_t cchBuffer = 0; // Size of the buffer in characters
size_t cchParameters = 0; // Number of characters in all the parameter strings
size_t cch = 0;
DWORD i = 0;
LPWSTR* pStartingAddresses = NULL; // Array of pointers to the beginning of each parameter string in pMessage
LPWSTR* pEndingAddresses = NULL; // Array of pointers to the end of each parameter string in pMessage
DWORD* pParameterIDs = NULL; // Array of parameter identifiers found in pMessage
LPWSTR* pParameters = NULL; // Array of the actual parameter strings
LPWSTR pTempMessage = (LPWSTR)pMessage;
LPWSTR pTempFinalMessage = NULL;
// Determine the number of parameter insertion strings in pMessage.
while (pTempMessage = wcschr(pTempMessage, L'%'))
{
dwParameterCount++;
pTempMessage++;
}
// If there are no parameter insertion strings in pMessage, return.
if (0 == dwParameterCount)
{
pFinalMessage = NULL;
goto cleanup;
}
// Allocate an array of pointers that will contain the beginning address
// of each parameter insertion string.
cbBuffer = sizeof(LPWSTR) * dwParameterCount;
pStartingAddresses = (LPWSTR*)malloc(cbBuffer);
if (NULL == pStartingAddresses)
{
wprintf(L"Failed to allocate memory for pStartingAddresses.\n");
status = ERROR_OUTOFMEMORY;
goto cleanup;
}
RtlZeroMemory(pStartingAddresses, cbBuffer);
// Allocate an array of pointers that will contain the ending address (one
// character past the of the identifier) of the each parameter insertion string.
pEndingAddresses = (LPWSTR*)malloc(cbBuffer);
if (NULL == pEndingAddresses)
{
wprintf(L"Failed to allocate memory for pEndingAddresses.\n");
status = ERROR_OUTOFMEMORY;
goto cleanup;
}
RtlZeroMemory(pEndingAddresses, cbBuffer);
// Allocate an array of pointers that will contain pointers to the actual
// parameter strings.
pParameters = (LPWSTR*)malloc(cbBuffer);
if (NULL == pParameters)
{
wprintf(L"Failed to allocate memory for pEndingAddresses.\n");
status = ERROR_OUTOFMEMORY;
goto cleanup;
}
RtlZeroMemory(pParameters, cbBuffer);
// Allocate an array of DWORDs that will contain the message identifier
// for each parameter.
pParameterIDs = (DWORD*)malloc(cbBuffer);
if (NULL == pParameterIDs)
{
wprintf(L"Failed to allocate memory for pParameterIDs.\n");
status = ERROR_OUTOFMEMORY;
goto cleanup;
}
RtlZeroMemory(pParameterIDs, cbBuffer);
// Find each parameter in pMessage and get the pointer to the
// beginning of the insertion string, the end of the insertion string,
// and the message identifier of the parameter.
pTempMessage = (LPWSTR)pMessage;
while (pTempMessage = wcschr(pTempMessage, L'%'))
{
if (isdigit(*(pTempMessage+1)))
{
pStartingAddresses[i] = pTempMessage;
pTempMessage++;
pParameterIDs[i] = (DWORD)_wtoi(pTempMessage);
while (isdigit(*++pTempMessage))
;
pEndingAddresses[i] = pTempMessage;
i++;
}
}
// For each parameter, use the message identifier to get the
// actual parameter string.
for (DWORD i = 0; i < dwParameterCount; i++)
{
pParameters[i] = GetMessageString(pParameterIDs[i], 0, NULL);
if (NULL == pParameters[i])
{
wprintf(L"GetMessageString could not find parameter string for insert %lu.\n", i);
status = ERROR_INVALID_PARAMETER;
goto cleanup;
}
cchParameters += wcslen(pParameters[i]);
}
// Allocate enough memory for pFinalMessage based on the length of pMessage
// and the length of each parameter string. The pFinalMessage buffer will contain
// the completed parameter substitution.
pTempMessage = (LPWSTR)pMessage;
cbBuffer = (wcslen(pMessage) + cchParameters + 1) * sizeof(WCHAR);
pFinalMessage = (LPWSTR)malloc(cbBuffer);
if (NULL == pFinalMessage)
{
wprintf(L"Failed to allocate memory for pFinalMessage.\n");
status = ERROR_OUTOFMEMORY;
goto cleanup;
}
RtlZeroMemory(pFinalMessage, cbBuffer);
cchBuffer = cbBuffer / sizeof(WCHAR);
pTempFinalMessage = pFinalMessage;
// Build the final message string.
for (DWORD i = 0; i < dwParameterCount; i++)
{
// Append the segment from pMessage. In the first iteration, this is "8 " and in the
// second iteration, this is " = 2 ".
wcsncpy_s(pTempFinalMessage, cchBuffer, pTempMessage, cch = (pStartingAddresses[i] - pTempMessage));
pTempMessage = pEndingAddresses[i];
cchBuffer -= cch;
// Append the parameter string. In the first iteration, this is "quarts" and in the
// second iteration, this is "gallons"
pTempFinalMessage += cch;
wcscpy_s(pTempFinalMessage, cchBuffer, pParameters[i]);
cchBuffer -= cch = wcslen(pParameters[i]);
pTempFinalMessage += cch;
}
// Append the last segment from pMessage, which is ".".
wcscpy_s(pTempFinalMessage, cchBuffer, pTempMessage);
cleanup:
if (ERROR_SUCCESS != status)
pFinalMessage = (LPWSTR)pMessage;
if (pStartingAddresses)
free(pStartingAddresses);
if (pEndingAddresses)
free(pEndingAddresses);
if (pParameterIDs)
free(pParameterIDs);
for (DWORD i = 0; i < dwParameterCount; i++)
{
if (pParameters[i])
LocalFree(pParameters[i]);
}
return status;
}
// Get a string that contains the time stamp of when the event
// was generated.
void GetTimestamp(const DWORD Time, WCHAR DisplayString[])
{
ULONGLONG ullTimeStamp = 0;
ULONGLONG SecsTo1970 = 116444736000000000;
SYSTEMTIME st;
FILETIME ft, ftLocal;
ullTimeStamp = Int32x32To64(Time, 10000000) + SecsTo1970;
ft.dwHighDateTime = (DWORD)((ullTimeStamp >> 32) & 0xFFFFFFFF);
ft.dwLowDateTime = (DWORD)(ullTimeStamp & 0xFFFFFFFF);
FileTimeToLocalFileTime(&ft, &ftLocal);
FileTimeToSystemTime(&ftLocal, &st);
StringCchPrintf(DisplayString, MAX_TIMESTAMP_LEN, L"%d/%d/%d %.2d:%.2d:%.2d",
st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond);
}
It's impossible to do in full way.
Here is why:
Each program that writes events to EventLog has an appropriate EventSource registered under HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog. And an EventMessagFile value under EventSource key provides a path to a file that contain's event messages. So if, for example, some custom program provides only german event messages in that file, where do you get an english event messages from? The answer is from nowhere, because developers simply could not shipped event messages for other languages.
And for Windows, if you've got a german windows, but no english language pack (Microsoft's MUI) where does Windows have to get translations from? Nowhere.

Resources