I'm trying to build a PE viewer in C++ and it seems to crash if i try to output the names of the libraries in the Import Directory Table. It seems that I am not getting the correct pointers for the DLLs that are used by the program.
HANDLE handle = CreateFile("messagebox.exe",GENERIC_READ,0,0,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,0);
DWORD size = GetFileSize(handle,NULL);
PVOID virtualpointer = VirtualAlloc(NULL,size,MEM_COMMIT,PAGE_READWRITE);
state = ReadFile(handle,virtualpointer,size,&byteread,NULL);
CloseHandle(handle);
PIMAGE_NT_HEADERS ntheaders = PIMAGE_NT_HEADERS(PCHAR(vpointer) +
PIMAGE_DOS_HEADER(vpointer)->e_lfanew);
handle = GetCurrentProcess();
DWORD EntryAddr = ntheaders->OptionalHeader.ImageBase +
ntheaders->OptionalHeader.AddressOfEntryPoint;
DWORD importdir =
(DWORD) &(ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]);
DWORD va = (DWORD)(ntheaders->OptionalHeader.ImageBase) +
((PIMAGE_DATA_DIRECTORY)dwValueB)->VirtualAddress;
LPSTR libname[128];
int i =0;
while(((PIMAGE_IMPORT_DESCRIPTOR)dwValueC)->Name)
{
// get DLL name
libname[i] = (LPSTR)(nt->OptionalHeader.ImageBase +
((PIMAGE_IMPORT_DESCRIPTOR)dwValueC)->Name);
i++;
}
To read the names of the libraries in the Import Directory Table, you can do the following:
Get the file's memory-mapped base address.
Get pointer to IMAGE_NT_HEADERS structure.
Get pointer to IMAGE_SECTION_HEADER structure.
DataDirectory is the final 128 bytes of OptionalHeader, which in turn is the final member of the PE header IMAGE_NT_HEADERS.
The structure has 2 members which contain the location and size of the data structure.
If you want to look up information about the dll names, you first find the RVA (Relative Virtual Address) of the Import Directory from the Data Directory, find that address in the raw section data and now you have an array of IMAGE_IMPORT_DESCRIPTOR. Get the member of this array that relates to mapped image by inspecting the strings pointed to by the Name fields.
I will not describe structure of Portable Executable File Format, but you can look at the following links:
Peering Inside the PE
Microsoft Systems Journal
Some variables in your code are not declared and this is confusing, but sticking to your skeleton code I wrote it so that it meets to your question.
DWORD Rva2Offset(DWORD rva,PIMAGE_SECTION_HEADER psh,PIMAGE_NT_HEADERS pnt);
int _tmain(int argc, _TCHAR* argv[])
{
LPCWSTR fNmae=L"C:\\Windows\\system32\\notepad.exe";
HANDLE handle=CreateFile(fNmae/*"messagebox.exe"*/, GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
DWORD byteread,size=GetFileSize(handle, NULL);
PVOID virtualpointer=VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE);
ReadFile(handle, virtualpointer, size, &byteread, NULL);
CloseHandle(handle);
// Get pointer to NT header
PIMAGE_NT_HEADERS ntheaders=(PIMAGE_NT_HEADERS)(PCHAR(virtualpointer) + PIMAGE_DOS_HEADER(virtualpointer)-> e_lfanew);
PIMAGE_SECTION_HEADER pSech=IMAGE_FIRST_SECTION(ntheaders);//Pointer to first section header
PIMAGE_IMPORT_DESCRIPTOR pImportDescriptor; //Pointer to import descriptor
__try
{
if(ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size != 0)/*if size of the table is 0 - Import Table does not exist */
{
pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD_PTR)virtualpointer +\
Rva2Offset(ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress,pSech,ntheaders));
LPSTR libname[256];
size_t i=0;
// Walk until you reached an empty IMAGE_IMPORT_DESCRIPTOR
while(pImportDescriptor->Name != NULL)
{
printf("Library Name :");
//Get the name of each DLL
libname[i]=(PCHAR)((DWORD_PTR)virtualpointer + Rva2Offset(pImportDescriptor->Name,pSech,ntheaders));
printf("%s\n", libname[i]);
pImportDescriptor++; //advance to next IMAGE_IMPORT_DESCRIPTOR
i++;
}
}
else
{
printf("No Import Table!\n");
return 1;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
if(EXCEPTION_ACCESS_VIOLATION == GetExceptionCode())
{
printf("Exception: EXCEPTION_ACCESS_VIOLATION\n");
return 1;
}
}
if(virtualpointer)
VirtualFree(virtualpointer, size, MEM_DECOMMIT);
return 0;
}
/*Convert Virtual Address to File Offset */
DWORD Rva2Offset(DWORD rva,PIMAGE_SECTION_HEADER psh,PIMAGE_NT_HEADERS pnt)
{
size_t i = 0;
PIMAGE_SECTION_HEADER pSeh;
if(rva == 0)
{
return (rva);
}
pSeh = psh;
for(i = 0; i < pnt->FileHeader.NumberOfSections; i++)
{
if(rva >= pSeh->VirtualAddress && rva < pSeh->VirtualAddress +
pSeh->Misc.VirtualSize)
{
break;
}
pSeh++;
}
return (rva - pSeh->VirtualAddress + pSeh->PointerToRawData);
}
Related
I'm trying to get the last opened directory by an open file dialog, and it seems that we can get it by first retrieving the key's name that contains the path using the first BYTE of the MRUListEx key, then the path can be obtained by reading the value of this key's name.
MRUListEx key can be find at: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU.
The problem is that I don't know how to properly fill the "SHITEMID" structure. (it doesn't let me access the index, and it throws a Memory access violation). I don't even know if the code below is valid in any points.
Sorry, the code is very dirty for the moment but I'll revamps it when I finally find what causes these errors.
void MyClass::OnDocumentSave(bool showSaveDlg)
{
// Do something that is not relevant here...
try {
HKEY hKey = NULL;
std::wstring regPath = LR"(Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU\)";
LSTATUS statusCode = RegOpenKeyExW(HKEY_CURRENT_USER, regPath.data(), 0, KEY_QUERY_VALUE, &hKey);
if (statusCode != ERROR_SUCCESS)
throw std::exception(std::string("Unable to open the specified registry key, sys err code: ") + std::to_string(statusCode));
BYTE data[MAX_PATH];
DWORD bufferSize = MAX_PATH;
statusCode = RegGetValueW(hKey, L"", L"MRUListEx", RRF_RT_REG_BINARY, NULL, &data, &bufferSize);
if (statusCode != ERROR_SUCCESS)
throw std::runtime_error(std::string("Failed at RegGetValue() Sys error code: ") + std::to_string(statusCode));
// Please note that the buffer has intentionally a fixed size here and everything is
// simplified for readability, but it uses dynamic memory allocation in the real code to
// handle errors such as ERROR_MORE_DATA
BYTE* pathData[512];
bufferSize = 512;
DWORD type = 0; // In case it matters, the returned value is 3
statusCode = RegGetValueW(hKey, L"", std::to_wstring(data[0]).c_str(), RRF_RT_REG_BINARY, &type, &pathData, &bufferSize);
if (statusCode != ERROR_SUCCESS)
throw std::runtime_error(std::string("Failed at RegGetValue() Sys error code: ") + std::to_string(statusCode));
// I don't know how to fill this structure, the documentation is very minimal,
// and I don't understand it.
int offset = sizeof(APPNAME);
SHITEMID shellIDList[2]{
// Throw a memory access violation at 0x*****, the debugger can't seems to
// get anything in pathData.
{ sizeof(USHORT) + sizeof(pathData), *pathData[0 + offset] },
{ 0, 0 } };
ITEMIDLIST idl{ shellIDList[0] };
// This is supposed give me the last path that was opened by a File Picker.
SHGetPathFromIDListW(&idl, initialDir.data());
}
catch (std::exception& e) {
// Silently set the initial directory to a hard-coded path instead of getting the registry value.
}
}
You don't seem to understand how types and simple arrays work! BYTE* pathData[512]; is not a 512 byte buffer. Use BYTE pathData[512];.
After reading into pathData, call SHGetPathFromIDList((PCIDLIST_ABSOLUTE) pathData, ...);.
That being said, that ComDlg32 key is undocumented and I don't think it stores a pidl so even if your code is corrected it is not going to work.
EnumMRUListW is a documented function you can call but it is not going to help you decode the data.
It looks to me like the location might be prefixed with the name of the .exe so as a minimum you would have to skip (lstrlenW(pathData)+1)*2 bytes before you find the real data...
If you're going for undocumented stuff, here is a code that can dump an MRU list like the LastVisitedPidlMRU one:
// the Shell object that can read MRU lists
static GUID CLSID_MruLongList = { 0x53bd6b4e,0x3780,0x4693,{0xaf,0xc3,0x71,0x61,0xc2,0xf3,0xee,0x9c} };
typedef enum MRULISTF
{
MRULISTF_USE_MEMCMP = 0x0,
MRULISTF_USE_STRCMPIW = 0x1,
MRULISTF_USE_STRCMPW = 0x2,
MRULISTF_USE_ILISEQUAL = 0x3,
};
typedef int (__stdcall *MRUDATALISTCOMPARE)(const BYTE*, const BYTE*, int);
MIDL_INTERFACE("00000000-0000-0000-0000-000000000000") // unknown guid but we don't care
IMruDataCompare : public IUnknown
{
public:
virtual HRESULT CompareItems(const BYTE*, int, const BYTE*, int) = 0;
};
MIDL_INTERFACE("d2c22919-91f5-4284-8807-58a2d64e561c")
IMruDataList2 : public IUnknown
{
public:
virtual HRESULT InitData(UINT uMax, MRULISTF flags, HKEY hKey, LPCWSTR pszSubKey, MRUDATALISTCOMPARE pfnCompare) = 0;
virtual HRESULT AddData(const BYTE* pData, DWORD cbData, DWORD* pdwSlot) = 0;
virtual HRESULT InsertData(const BYTE*, DWORD cbData, int* piIndex, DWORD* pdwSlot) = 0;
virtual HRESULT FindData(const BYTE* pData, DWORD cbData, int* piIndex) = 0;
virtual HRESULT GetData(int iIndex, BYTE* pData, DWORD cbData) = 0;
virtual HRESULT QueryInfo(int iIndex, DWORD* pdwSlot, DWORD* pcbData) = 0;
virtual HRESULT Delete(int iIndex) = 0;
virtual HRESULT InitData2(UINT uMax, MRULISTF flags, HKEY hKey, LPCWSTR pszSubKey, IMruDataCompare* pfnCompare) = 0;
};
int main()
{
CoInitialize(NULL);
{
CComPtr<IMruDataList2> mru;
if (SUCCEEDED(mru.CoCreateInstance(CLSID_MruLongList)))
{
const int max = 100; // get max 100 entries
mru->InitData(max, MRULISTF_USE_MEMCMP, HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ComDlg32\\LastVisitedPidlMRU\\", nullptr);
for (auto i = 0; i < max; i++)
{
DWORD slot;
DWORD size;
// get size
if (FAILED(mru->QueryInfo(i, &slot, &size)))
continue;
// get data
// note beginning is a LPWSTR containing exe data
auto data = (LPBYTE)_alloca(size);
if (FAILED(mru->GetData(i, data, size)))
continue;
// the rest is a PIDL
auto pidl = (LPCITEMIDLIST)(data + (lstrlen((LPWSTR)data) + 1) * 2);
// get the shell item
CComPtr<IShellItem> item;
if (SUCCEEDED(SHCreateItemFromIDList(pidl, IID_PPV_ARGS(&item))))
{
// get its path
CComHeapPtr<WCHAR> path;
item->GetDisplayName(SIGDN::SIGDN_DESKTOPABSOLUTEPARSING, &path);
wprintf(L"Executable: %s LastVisited: %s\n", data, path);
}
}
}
}
CoUninitialize();
}
PS: I'm using ATL's smart classes
I finally figured out how to create an ITEMIDLIST structure, and it all works well.
Explanations:
After some test, I've concluded that this value is not necessarily updated. The application that uses a file picker needs to write to the registry to reflect changes.
For example, VS Community 2022 / VS Code updates this value, but the new Microsoft Notepad does not (not sure for the old one). Apparently, .NET applications seem to be updated automatically, though.
First, we obviously must open the key at Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU.
The stored data value we need is on one of the values, which has a number as a name.
The first byte of the MRUListEx value lies the last path.
HKEY hKey = NULL;
std::wstring regMRUPath = LR"(Software\Microsoft\Windows\CurrentVersion\Explorer\ComDlg32\LastVisitedPidlMRU\)";
LSTATUS statusCode = RegOpenKeyExW(HKEY_CURRENT_USER, regMRUPath.data(), 0, KEY_QUERY_VALUE, &hKey);
if (statusCode != ERROR_SUCCESS)
// Handle error
BYTE data[MAX_PATH];
DWORD bufferSize = MAX_PATH;
statusCode = RegGetValueW(hKey, L"", L"MRUListEx", RRF_RT_REG_BINARY, NULL, &data, &bufferSize);
if (statusCode != ERROR_SUCCESS)
// Handle error
std::wstring keyName = std::to_wstring(data[0]);
Now that we have the value, we can get its data.
DWORD reqBuffSize = 0;
statusCode = RegQueryValueExW(hKey, keyName.data(), NULL, NULL, NULL, &reqBuffSize);
if (statusCode != ERROR_SUCCESS)
// maybe the specified key is invalid or missing.
BYTE pathData[reqBuffSize];
statusCode = RegGetValueW(hKey, L"", keyName.data(), RRF_RT_REG_BINARY, NULL, &pathData, &reqBuffSize);
if (statusCode != ERROR_SUCCESS)
// maybe the specified key is invalid or missing.
We have another problem: The path is not directly accessible. The executable name is prefixed from the real path, so we must remove it.
Actually, it's very easy. The delimiter between the executable name and the real path is \0\0\0.
const size_t offset = (lstrlenW(reinterpret_cast<LPCWSTR>(pathData)) * sizeof(wchar_t))
+ (2 * sizeof(wchar_t));
The obtained value is not human-readable, but it actually stores a PIDL, so we can get it with SHGetPathFromIDList. The ITEMIDList is not very well documented on the Microsoft's website, but here's a way to create one of these things:
IMalloc* pMalloc;
if (FAILED(SHGetMalloc(&pMalloc)))
// handle error, failed to get a pointer to the IMalloc interface.
PIDLIST_ABSOLUTE pIdl = (PIDLIST_ABSOLUTE)pMalloc->Alloc((reqBuffSize - offset) + 1);
pIdl->mkid.cb = (sizeof(USHORT) + (reqBuffSize - offset));
memcpy_s(pIdl->mkid.abID, (reqBuffSize - offset) + 1, pathData + offset, (reqBuffSize - offset) + 1);
PIDLIST_ABSOLUTE pNext = ILGetNext(pIdl);
if (!pNext)
// handle error, failed to get a pointer to the next item
pNext->mkid.cb = 0;
initialDir.reserve(MAX_PATH * 2);
The ITEMIDList structure has two members: cb and abID. cb is the size of a USHORT + the size of the data, and abID is a pointer to the data itself.
The last ITEMIDList struct must have cb set to zero to mark the end. ILGetNext gives us a pointer to the next element.
// Finally, return the path!
std::wstring retValue;
retValue.reserve(MAX_PATH);
if (FALSE == SHGetPathFromIDList(pIdl, initialDir.data())) {
// oh no! free the allocated memory and throw an exception
}
pMalloc->Free(pIdl);
It's not as hard as it seems to be at the beginning, but if you want to get this value for use with a file picker, it's recommended to use COM instead since it's easier and less error-prone. Thank tou all for tour response and have a good day!
If you want to get the path from a specified extension, see the OpenSavePidlMRU key. It works the same, but it requires some little adjustments.
A Few days back I have started with Windows System Programming using c++. Hence, thought of building a utility to grab the PE Headers of an exe file.
And now I am stuck with the Image_Import_descriptor structure.
What I want is to get the names of the DLL files(modules) imported by the exe. And below is the code I am using to get those names:
DWORD Image_Import_Descriptor_addr = (DWORD)ntHeader + (DWORD)sizeof(ntHeader->FileHeader) + (DWORD)ntHeader->FileHeader.SizeOfOptionalHeader + (DWORD)ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
importImageDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)Image_Import_Descriptor_addr;
To get the RVA of the name:
DWORD name = importImageDescriptor->Name;
printf("\n\n\n\t\t (((Module Name)): %X", name);
Gives an output:
4778B00
Hope untill now everything was fine technically.
However, my motive is to print the DLL names(like kernel32.dll).
Can anyone help me out how to get the names of the DLL ?
My workarounds:
LPCSTR snames = (LPCSTR)name;
printf("\n\n\n\t\t (((Module Name)): %s", *snames);
But this is giving me an error: Access Violation
I am getting confused with the pointers and Datatype conversions. A help is much appreciated.
Got the technique from 'ired' security blogs. Below is the working code.
HANDLE h_File = CreateFile(L"testing.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (!h_File) {
printf("\nERROR : Could not open the file specified\n");
}
//Mapping Given EXE file to Memory
HANDLE hMapObject = CreateFileMapping(h_File, NULL, PAGE_READONLY, 0, 0, NULL);
LPVOID basepointer = (char*)MapViewOfFile(hMapObject, FILE_MAP_READ, 0, 0, 0);
//PIMAGE_DOS_HEADER dos_header;
PIMAGE_DOS_HEADER dos_header = (PIMAGE_DOS_HEADER)basepointer;
printf("Magic number - %X\n", dos_header->e_magic);
printf("DOS HEADER: IMAGE NT HEADER offset(Relative Address) - %X\n", dos_header->e_lfanew); //DOS header working fine...
//PIMAGE_NT_HEADERS ntHeader;
PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)((DWORD)basepointer + dos_header->e_lfanew);
printf("NT HEADER: Signature %x\n", nt_header->Signature);
PIMAGE_FILE_HEADER file_header = (PIMAGE_FILE_HEADER)((DWORD)basepointer + dos_header->e_lfanew + sizeof(nt_header->Signature));
printf("FILE HEADER: Machine %x\n", file_header->Machine);
PIMAGE_OPTIONAL_HEADER optional_header = (PIMAGE_OPTIONAL_HEADER)((DWORD)basepointer + dos_header->e_lfanew + sizeof(nt_header->Signature) + sizeof(nt_header->FileHeader));
printf("OPTIONAL HEADER: Image Base %x\n", optional_header->ImageBase);
PIMAGE_SECTION_HEADER section_header = (PIMAGE_SECTION_HEADER)((DWORD)basepointer + dos_header->e_lfanew + sizeof(nt_header->Signature) + sizeof(nt_header->FileHeader) + sizeof(nt_header->OptionalHeader));
DWORD numberofsections = file_header->NumberOfSections;
printf("Section Header: Number of Sections %x\n", file_header->NumberOfSections);
for (int j = 0; j < optional_header->NumberOfRvaAndSizes;j++) {
printf("Data Directory: Virtual Address: %x\t\n", optional_header->DataDirectory[j].VirtualAddress);
}
DWORD RVAimport_directory = nt_header->OptionalHeader.DataDirectory[1].VirtualAddress;
//printf("RVAimport_directory %x", RVAimport_directory);
PIMAGE_SECTION_HEADER import_section = {};
for (int i = 1; i <= numberofsections; i++, section_header++) {
printf("Section Header: Section Name %s\n", section_header->Name);
if (RVAimport_directory >= section_header->VirtualAddress && RVAimport_directory < section_header->VirtualAddress + section_header->Misc.VirtualSize) {
import_section = section_header;
}
//section_header += (DWORD)sizeof(PIMAGE_SECTION_HEADER);
}
DWORD import_table_offset = (DWORD)basepointer + import_section->PointerToRawData;
//imageBaseAddress + pointerToRawDataOfTheSectionContainingRVAofInterest + (RVAofInterest - SectionContainingRVAofInterest.VirtualAddress
importImageDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(import_table_offset + (nt_header->OptionalHeader.DataDirectory[1].VirtualAddress - import_section->VirtualAddress));
//DLL Imports
for (;importImageDescriptor->Name != 0 ; importImageDescriptor++) {
DWORD Imported_DLL = import_table_offset + (importImageDescriptor->Name - import_section->VirtualAddress);
printf("Imported DLLs: %s\n", Imported_DLL);
I have used In-Memory Execution of an Executable as Release-x86 with test_x86.exe as the file and worked properly:
/* In memory execution example */
/*
Author: Amit Malik
http://www.securityxploded.com
Compile in Dev C++
*/
#include
#include
#include
#define DEREF_32( name )*(DWORD *)(name)
int main()
{
char file[20];
HANDLE handle;
PVOID vpointer;
HINSTANCE laddress;
LPSTR libname;
DWORD size;
DWORD EntryAddr;
int state;
DWORD byteread;
PIMAGE_NT_HEADERS nt;
PIMAGE_SECTION_HEADER section;
DWORD dwValueA;
DWORD dwValueB;
DWORD dwValueC;
DWORD dwValueD;
printf("Enter file name: ");
scanf("%s",&file);
// read the file
printf("Reading file..\n");
handle = CreateFile(file,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
// get the file size
size = GetFileSize(handle,NULL);
// Allocate the space
vpointer = VirtualAlloc(NULL,size,MEM_COMMIT,PAGE_READWRITE);
// read file on the allocated space
state = ReadFile(handle,vpointer,size,&byteread,NULL);
CloseHandle(handle);
printf("You can delete the file now!\n");
system("pause");
// read NT header of the file
nt = PIMAGE_NT_HEADERS(PCHAR(vpointer) + PIMAGE_DOS_HEADER(vpointer)->e_lfanew);
handle = GetCurrentProcess();
// get VA of entry point
EntryAddr = nt->OptionalHeader.ImageBase + nt->OptionalHeader.AddressOfEntryPoint;
// Allocate the space with Imagebase as a desired address allocation request
PVOID memalloc = VirtualAllocEx(
handle,
PVOID(nt->OptionalHeader.ImageBase),
nt->OptionalHeader.SizeOfImage,
MEM_RESERVE | MEM_COMMIT,
PAGE_EXECUTE_READWRITE
);
// Write headers on the allocated space
WriteProcessMemory(handle,
memalloc,
vpointer,
nt->OptionalHeader.SizeOfHeaders,
0
);
// write sections on the allocated space
section = IMAGE_FIRST_SECTION(nt);
for (ULONG i = 0; i < nt->FileHeader.NumberOfSections; i++)
{
WriteProcessMemory(
handle,
PCHAR(memalloc) + section[i].VirtualAddress,
PCHAR(vpointer) + section[i].PointerToRawData,
section[i].SizeOfRawData,
0
);
}
// read import dirctory
dwValueB = (DWORD) &(nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]);
// get the VA
dwValueC = (DWORD)(nt->OptionalHeader.ImageBase) +
((PIMAGE_DATA_DIRECTORY)dwValueB)->VirtualAddress;
while(((PIMAGE_IMPORT_DESCRIPTOR)dwValueC)->Name)
{
// get DLL name
libname = (LPSTR)(nt->OptionalHeader.ImageBase +
((PIMAGE_IMPORT_DESCRIPTOR)dwValueC)->Name);
// Load dll
laddress = LoadLibrary(libname);
// get first thunk, it will become our IAT
dwValueA = nt->OptionalHeader.ImageBase +
((PIMAGE_IMPORT_DESCRIPTOR)dwValueC)->FirstThunk;
// resolve function addresses
while(DEREF_32(dwValueA))
{
dwValueD = nt->OptionalHeader.ImageBase + DEREF_32(dwValueA);
// get function name
LPSTR Fname = (LPSTR)((PIMAGE_IMPORT_BY_NAME)dwValueD)->Name;
// get function addresses
DEREF_32(dwValueA) = (DWORD)GetProcAddress(laddress,Fname);
dwValueA += 4;
}
dwValueC += sizeof( IMAGE_IMPORT_DESCRIPTOR );
}
// call the entry point :: here we assume that everything is ok.
((void(*)(void))EntryAddr)();
}
But when I use this code as Release-x64 with test_x64.exe as the file I get access violation in this line:
// get the VA
dwValueC = (DWORD)(nt->OptionalHeader.ImageBase) + ((PIMAGE_DATA_DIRECTORY)dwValueB)->VirtualAddress;
I don`t know why.
That might be that on x64 pointer not DWORD size:
DWORD dwValueB;
...
dwValueB = (DWORD) &(nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]);
In my minwindef.h i have:
typedef unsigned long DWORD;
And sizeof(DWORD) gives me 4, but sizeof(void*) gives 8.
The question is fairly straight forward, what I'm trying to do is restore my process' detoured functions.
When I say detoured I mean the usual jmp instruction to an unknown location.
For example, when the ntdll.dll export NtOpenProcess() is not detoured, the first 5 bytes of the instruction of the function are along the lines of mov eax, *.
(The * offset depending on the OS version.)
When it gets detoured, that mov eax, * turns into a jmp.
What I'm trying to do is restore their bytes to what they were originally before any memory modifications.
My idea was to try and read the information I need from the disk, not from memory, however I do not know how to do that as I'm just a beginner.
Any help or explanation is greatly welcomed, if I did not explain my problem correctly please tell me!
I ended up figuring it out.
Example on NtOpenProcess.
Instead of restoring the bytes I decided to jump over them instead.
First we have to define the base of ntdll.
/* locate ntdll */
#define NTDLL _GetModuleHandleA("ntdll.dll")
Once we've done that, we're good to go. GetOffsetFromRva will calculate the offset of the file based on the address and module header passed to it.
DWORD GetOffsetFromRva(IMAGE_NT_HEADERS * nth, DWORD RVA)
{
PIMAGE_SECTION_HEADER sectionHeader = IMAGE_FIRST_SECTION(nth);
for (unsigned i = 0, sections = nth->FileHeader.NumberOfSections; i < sections; i++, sectionHeader++)
{
if (sectionHeader->VirtualAddress <= RVA)
{
if ((sectionHeader->VirtualAddress + sectionHeader->Misc.VirtualSize) > RVA)
{
RVA -= sectionHeader->VirtualAddress;
RVA += sectionHeader->PointerToRawData;
return RVA;
}
}
}
return 0;
}
We call this to get us the file offset that we need in order to find the original bytes of the function.
DWORD GetExportPhysicalAddress(HMODULE hmModule, char* szExportName)
{
if (!hmModule)
{
return 0;
}
DWORD dwModuleBaseAddress = (DWORD)hmModule;
IMAGE_DOS_HEADER* pHeaderDOS = (IMAGE_DOS_HEADER *)hmModule;
if (pHeaderDOS->e_magic != IMAGE_DOS_SIGNATURE)
{
return 0;
}
IMAGE_NT_HEADERS * pHeaderNT = (IMAGE_NT_HEADERS *)(dwModuleBaseAddress + pHeaderDOS->e_lfanew);
if (pHeaderNT->Signature != IMAGE_NT_SIGNATURE)
{
return 0;
}
/* get the export virtual address through a custom GetProcAddress function. */
void* pExportRVA = GetProcedureAddress(hmModule, szExportName);
if (pExportRVA)
{
/* convert the VA to RVA... */
DWORD dwExportRVA = (DWORD)pExportRVA - dwModuleBaseAddress;
/* get the file offset and return */
return GetOffsetFromRva(pHeaderNT, dwExportRVA);
}
return 0;
}
Using the function that gets us the file offset, we can now read the original export bytes.
size_t ReadExportFunctionBytes(HMODULE hmModule, char* szExportName, BYTE* lpBuffer, size_t t_Count)
{
/* get the offset */
DWORD dwFileOffset = GetExportPhysicalAddress(hmModule, szExportName);
if (!dwFileOffset)
{
return 0;
}
/* get the path of the targetted module */
char szModuleFilePath[MAX_PATH];
GetModuleFileNameA(hmModule, szModuleFilePath, MAX_PATH);
if (strnull(szModuleFilePath))
{
return 0;
}
/* try to open the file off the disk */
FILE *fModule = fopen(szModuleFilePath, "rb");
if (!fModule)
{
/* we couldn't open the file */
return 0;
}
/* go to the offset and read it */
fseek(fModule, dwFileOffset, SEEK_SET);
size_t t_Read = 0;
if ((t_Read = fread(lpBuffer, t_Count, 1, fModule)) == 0)
{
/* we didn't read anything */
return 0;
}
/* close file and return */
fclose(fModule);
return t_Read;
}
And we can retrieve the syscall index from the mov instruction originally placed in the first 5 bytes of the export on x86.
DWORD GetSyscallIndex(char* szFunctionName)
{
BYTE buffer[5];
ReadExportFunctionBytes(NTDLL, szFunctionName, buffer, 5);
if (!buffer)
{
return 0;
}
return BytesToDword(buffer + 1);
}
Get the NtOpenProcess address and add 5 to trampoline over it.
DWORD _ptrNtOpenProcess = (DWORD) GetProcAddress(NTDLL, "NtOpenProcess") + 5;
DWORD _oNtOpenProcess = GetSyscallIndex("NtOpenProcess");
The recovered/reconstructed NtOpenProcess.
__declspec(naked) NTSTATUS NTAPI _NtOpenProcess
(
_Out_ PHANDLE ProcessHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes,
_In_opt_ PCLIENT_ID ClientId
) {
__asm
{
mov eax, [_oNtOpenProcess]
jmp dword ptr ds : [_ptrNtOpenProcess]
}
}
Let's call it.
int main()
{
printf("NtOpenProcess %x index: %x\n", _ptrNtOpenProcess, _oNtOpenProcess);
uint32_t pId = 0;
do
{
pId = GetProcessByName("notepad.exe");
Sleep(200);
} while (pId == 0);
OBJECT_ATTRIBUTES oa;
CLIENT_ID cid;
cid.UniqueProcess = (HANDLE)pId;
cid.UniqueThread = 0;
InitializeObjectAttributes(&oa, NULL, 0, NULL, NULL);
HANDLE hProcess;
NTSTATUS ntStat;
ntStat = _NtOpenProcess(&hProcess, PROCESS_ALL_ACCESS, &oa, &cid);
if (!NT_SUCCESS(ntStat))
{
printf("Couldn't open the process. NTSTATUS: %d", ntStat);
return 0;
}
printf("Successfully opened the process.");
/* clean up. */
NtClose(hProcess);
getchar();
return 0;
}
I have a Win32 application that gets the HANDLE of a MFC application. My goal is to force the MFC program not to display ASSERT error message box.
Basically, I have made a prototype that allows my Win32 application to force the MFC application to show a message box, just to check if the idea is possible. Now I need to force the MFC application not to display such ASSERT error message boxes.
Is that possible?
You can do this by intercepting the MessageBoxA/MessageBoxW function call. At a usermode level, this is typically done in one of three places:
Call site - There may be more than one call to MessageBox in your executable. You need to find the one that you want to disable. Then you can overwrite the call with code that does nothing (i.e. overwrite with nop instructions).
IAT - The Import Address Table; a table of function pointers filled in by the PE loader. Execution often (but not always) flows through here and replacing the function pointer for MessageBox can allow the MessageBox call to be redirected to some routine that does nothing.
Function entry point - The start of the MessageBox function. This can be located by GetProcAddress and the first instruction replaced with a ret.
The manipulation is done either at runtime (dynamically) or statically (binary rewriting/executable editing) with the first option being far more common. A library which can help you achieve runtime detouring is Microsoft Detours.
This is not a comprehensive list of all the possibilities, but rather the most common methods of execution redirection and detouring.
To my great regret I missed that code. However you still can do it by hands.
Download and install CFF explorer
Open your exe-file with it
Select import directory in sections explorer.
Select USER32.dll in the imported dll list
Select MessageBoxA or MessageBoxW. Edit OFT column. Write there OFT of some "harmless" function. I used GetWindowRect as example.
If you still want an appliaction to do this, I have a code with very similar functionality. It just embeds your dll into import table. You may both edit it to reach wanted result or to use it to redirect MessageBoxW call to your handler.
#include <windows.h>
#include <tchar.h>
#include "stdafx.h"
#include <stdio.h>
DWORD MapFile(HANDLE &FileMapping, LPVOID &FileBegin, const _TCHAR *exeName) {
HANDLE File = CreateFile(exeName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (File == INVALID_HANDLE_VALUE) {
return GetLastError();
}
FileMapping = CreateFileMapping(File, NULL, PAGE_READWRITE, 0, 0, NULL);
CloseHandle(File);
if (!FileMapping) {
return GetLastError();
}
FileBegin = MapViewOfFile(FileMapping, FILE_MAP_WRITE, 0, 0, 0);
if (!FileBegin) {
CloseHandle(FileMapping);
return GetLastError();
}
return 0;
}
DWORD RewriteImportTable(const HANDLE FileMapping, const LPVOID FileBegin, const _TCHAR *dllName, const _TCHAR *funcName, DWORD &finalResult) {
IMAGE_DOS_HEADER* dos_header;
IMAGE_FILE_HEADER* file_header;
IMAGE_OPTIONAL_HEADER* optional_header;
IMAGE_SECTION_HEADER* section_header;
// Counting PE-header offset
dos_header = (IMAGE_DOS_HEADER*) FileBegin;
DWORD PEOffset = dos_header->e_lfanew;
file_header = (IMAGE_FILE_HEADER*) ((DWORD)FileBegin + PEOffset); // file_header must reference "PE\0"
// Checking if we work with PE
_TCHAR* PEString = "PE\0";
if (_tcscmp(PEString, (const _TCHAR*) file_header) != 0) {
printf("This file is not Portable Executable!\n");
return 666;
}
file_header = (IMAGE_FILE_HEADER *)((DWORD)file_header + sizeof(DWORD)); // Ignoring PE
optional_header = (IMAGE_OPTIONAL_HEADER *)((DWORD)file_header + sizeof(IMAGE_FILE_HEADER));
// Finding import section
DWORD ImportRVA = optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
int sectNum = -1;
// Finding import table
section_header = (IMAGE_SECTION_HEADER*) ((DWORD) optional_header + sizeof(IMAGE_OPTIONAL_HEADER));
for (int i = 0; i < (file_header->NumberOfSections); i++) {
if (ImportRVA < (section_header->VirtualAddress)) {
section_header--;
sectNum = i-1;
break;
}
section_header++;
}
if (sectNum == -1) {
printf("This program uses no external libraries! (strange)\n");
return 666;
}
// Getting address of section folowing import section
section_header++;
DWORD SectionNextToImportBegin = (DWORD)FileBegin + section_header->PointerToRawData;
section_header--;
// Getting the address of the import table
LPVOID ImportSectionBegin = (LPVOID) ((DWORD)FileBegin + section_header->PointerToRawData);
// Counting the import table offset in the import section
LPVOID ImportTable = (LPVOID)((DWORD)ImportSectionBegin + (ImportRVA - section_header->VirtualAddress));
IMAGE_IMPORT_DESCRIPTOR *DLLInfo = (IMAGE_IMPORT_DESCRIPTOR*) ImportTable;
LPVOID DLLName;
DWORD DLLCounter = 0;
while (DLLInfo->Name != NULL) {
DLLCounter++;
DLLName = (LPVOID) ((DWORD)ImportSectionBegin + ((DWORD)DLLInfo->Name - section_header->VirtualAddress));
DLLInfo++;
}
printf("Number of imported libraries: %d\n", DLLCounter);
// Counting the size of the future import table
DWORD newImportTableSize = sizeof(IMAGE_IMPORT_DESCRIPTOR) * (DLLCounter + 2);
// Finding the end of the import section
LPVOID pos = (LPVOID) (SectionNextToImportBegin - 1);
DWORD maxFree = 0;
DWORD prevPtr;
LPVOID freePtr = NULL;
// Searching for the free place
while (pos >= ImportSectionBegin) {
if (*(BYTE*)pos == 0) {
prevPtr = (DWORD) pos;
while (*(BYTE*)pos == 0) {
pos = (LPVOID) ((DWORD)pos - 1);
}
if (((DWORD)prevPtr - (DWORD)pos) > maxFree) {
maxFree = ((DWORD)prevPtr - (DWORD)pos);
freePtr = (LPVOID) ((DWORD)pos + 1);
}
}
pos = (LPVOID) ((DWORD)pos - 1);
}
// Modifying pointer: it can refer the tailing zero of some stucture
freePtr = (LPVOID) ((LPDWORD)freePtr + 1);
maxFree -= 4;
// Checking if we have enough space in the import section
if (maxFree < newImportTableSize) {
printf("Not enough free space in Import Section\n");
return 666;
}
printf("Injecting new library...\n");
// Copying old import table on the new place
memcpy(freePtr, ImportTable, sizeof(IMAGE_IMPORT_DESCRIPTOR) * DLLCounter);
// Saving everithing we need on the old place
typedef struct {
DWORD ZeroDword;
DWORD IAT;
DWORD IATEnd;
} MeanStruct;
MeanStruct patch;
patch.ZeroDword = NULL; // this is \0 for dll name
patch.IAT = ImportRVA + _tcslen(dllName) + sizeof(MeanStruct); // RVA to where list of functions begins
patch.IATEnd = NULL;
WORD Hint = 0;
IMAGE_IMPORT_BY_NAME myName;
myName.Hint = 0x00;
myName.Name[0] = 0x00;
LPDWORD zeroPtr = (LPDWORD) ImportTable;
memcpy(zeroPtr, dllName, _tcslen(dllName));
zeroPtr = (LPDWORD) ((DWORD)zeroPtr + strlen(dllName));
memcpy(zeroPtr, &patch, sizeof(patch));
zeroPtr = (LPDWORD) ((DWORD)zeroPtr + sizeof(patch));
finalResult = (DWORD)zeroPtr - (DWORD)ImportSectionBegin + section_header->VirtualAddress;
memcpy(zeroPtr, &Hint, sizeof(WORD));
zeroPtr = (LPDWORD) ((DWORD)zeroPtr + sizeof(WORD));
memcpy(zeroPtr, funcName, strlen(funcName) + 1); // we have no need to write \0 into the end - this is already free space
zeroPtr = (LPDWORD) ((DWORD)zeroPtr + strlen(funcName) + 1);
memcpy(zeroPtr, &myName, sizeof(IMAGE_IMPORT_BY_NAME));
// filling info about dll
IMAGE_IMPORT_DESCRIPTOR myDLL;
// counting RVA for IMAGE_IMPORT_BY_NAME:
DWORD IIBN_Table = ImportRVA + strlen(dllName) + sizeof(DWORD);
// function name pointer
myDLL.Characteristics = IIBN_Table;
myDLL.TimeDateStamp = NULL;
myDLL.ForwarderChain = NULL;
// dll name pointer
myDLL.Name = ImportRVA;
myDLL.FirstThunk = IIBN_Table;
// writting dll info into the new import table
LPVOID oldFreePtr = freePtr;
freePtr = (LPVOID) ((DWORD)freePtr + sizeof(IMAGE_IMPORT_DESCRIPTOR) * DLLCounter);
memcpy(freePtr, &myDLL, sizeof(IMAGE_IMPORT_DESCRIPTOR));
// creating list tail
myDLL.Characteristics = NULL;
myDLL.TimeDateStamp = NULL;
myDLL.ForwarderChain = NULL;
myDLL.Name = NULL;
myDLL.FirstThunk = NULL;
// writing list tail
freePtr = (LPVOID) ((DWORD)freePtr + sizeof(IMAGE_IMPORT_DESCRIPTOR));
memcpy(freePtr, &myDLL, sizeof(IMAGE_IMPORT_DESCRIPTOR));
// setting new import table rva
DWORD newImportTableRVA = (DWORD)oldFreePtr - (DWORD)ImportSectionBegin + section_header->VirtualAddress;
// changing DataDirectory
optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = newImportTableRVA;
optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = (DLLCounter + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR);
// clearing non-actual values
optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress = 0;
optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size = 0;
return 0;
}
int _tmain(int argc, _TCHAR *argv[]) {
if (argc != 4) {
printf("Invalid arguments number!!!\n");
return 0;
}
HANDLE FileMapping;
LPVOID FileBegin;
DWORD FileMappingResult = MapFile(FileMapping, FileBegin, argv[1]);
if (0 != FileMappingResult) {
printf("Error of file mapping (%d)\n", FileMappingResult);
if (NULL != FileMapping) CloseHandle(FileMapping);
return FileMappingResult;
}
DWORD functionAddr;
DWORD RewriteImportTableResult = RewriteImportTable(FileMapping, FileBegin, argv[2], argv[3], functionAddr);
if (0 != RewriteImportTableResult) {
UnmapViewOfFile(FileBegin);
CloseHandle(FileMapping);
return 666;
}
printf("Library successfully injected!\n");
printf("Address of injected function: %X", functionAddr);
UnmapViewOfFile(FileBegin);
CloseHandle(FileMapping);
return 0;
}