I've basically been trying to get this working for the last couple of days, but all of my attempts and all the examples/suggestions found online have failed. I'm trying to use Microsoft's "setupapi.dll" methods in implementing my own .dll to access a peripheral device I have created.
Right now I'm simply trying to use the method "SetupDiGetClassDevs" found in "setupapi.dll" to retrieve a list of attached HID devices on my computer. I've tried everything from "AfxLoadLibrary" to "__declspec(dllimport)" and pretty much everything else I've found online to no avail.
I've found working examples in C# but have not found anything that even compiles in C++. I am running Microsoft visual C++ 2010 express on Windows 7 64-bit if that makes a difference (ideally I'd want it to be OS independent - at least across more recent versions of windows). Any code samples that can successfully import/use this method would be greatly appreciated. (Also don't forget to mention any configuration settings/resource files/etc as I need to figure out a holistic process to make this work.)
UPDATE!!!:
so i finally got my code to compile using a combination of suggestions from the responses given here + some more googling. my current code is as follows (and fyi the main issue here was that an "L" had to be prefixed to the .dll in quotes):
GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, 0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30};
HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
PSP_DEVICE_INTERFACE_DATA InterfaceDataStructure = new SP_DEVICE_INTERFACE_DATA;
SP_DEVINFO_DATA DevInfoData;
DWORD InterfaceIndex = 0;
DWORD StatusLastError = 0;
DWORD dwRegType;
DWORD dwRegSize;
DWORD StructureSize = 0;
PBYTE PropertyValueBuffer;
bool MatchFound = false;
DWORD ErrorStatus;
BOOL BoolStatus = FALSE;
DWORD LoopCounter = 0;
HINSTANCE dllHandle = LoadLibrary(L"setupapi.dll");
if(dllHandle)
{
typedef HDEVINFO (WINAPI *pFUNC)(LPGUID, PCTSTR, HWND, DWORD);
pFUNC SetupDiGetClassDevs = (pFUNC) GetProcAddress(dllHandle,
#ifdef UNICODE
"SetupDiGetClassDevsW"
#else
"SetupDiGetClassDevsA"
#endif
);
if(SetupDiGetClassDevs)
hDevInfo = SetupDiGetClassDevsW(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
FreeLibrary(dllHandle);
unfortunately i wasn't able to get this working using the "safeloadlibrary" function remy mentioned. and my impression from other forums is that the standard "loadlibrary" function is non-ideal. so i'd like to know what is needed to implement it (header files, etc) or if there is another better way to do this. apparently there are also some issues that may arise using "loadlibrary" in a DLL (particularly in DLLMain entry point) but given my lack of experience using .dll's i'm not really sure what they are.
Your code fails because you are passing the wrong function name to GetProcAddress(). Like most API functions that have string parameters, SetupDiGetClassDevs() has separate Ansi and Unicode flavors available (SetupDiGetClassDevsA() and SetupDiGetClassDevsW(), respectively), and the API header files transparently hide this detail from you. So you need to adjust your string value according to which flavor you actually want to use, eg:
HINSTANCE dllHandle = SafeLoadLibrary("setupapi.dll");
if (dllHandle)
{
typedef HDEVINFO WINAPI (*pFUNC)(LPGUID, PCTSTR, HWND, DWORD);
pFUNC SetupDiGetClassDevs = (pFUNC) GetProcAddress(dllHandle,
#ifdef UNICODE
"SetupDiGetClassDevsW"
#else
"SetupDiGetClassDevsA"
#endif
);
if (SetupDiGetClassDevs)
hDevInfo = SetupDiGetClassDevs(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
FreeLibrary(dllHandle);
}
Here is a simply trick to make the mapping easier to work with if you have multiple functions to load dynamically:
#if defined(UNICODE)
#define _MAP_WINNAME_STR(n) n "W"
#else
#define _MAP_WINNAME_STR(n) n "A"
#endif
pFUNC SetupDiGetClassDevs = (pFUNC) GetProcAddress(dllHandle, _MAP_WINNAME_STR("SetupDiGetClassDevs"));
Related
Below is the main code, the problems encountered, and how they were resolved
***.h
std::list<DISPLAY_DEVICE> m_vDisplayDevice_list;
std::list<DEVMODE> m_vDevmode_list;
int m_nDisplayScreen;
***.cpp
std::list<DISPLAY_DEVICE> devices;
std::list<DEVMODE> modes;
int devId = 0;
BOOL ret = false; // bool ret = false;
bool isPrimary = false;
//list all DisplayDevices (Monitors)
do
{
DISPLAY_DEVICE displayDevice;
ZeroMemory(&displayDevice, sizeof(DISPLAY_DEVICE));
displayDevice.cb = sizeof(displayDevice);
ret = EnumDisplayDevices(NULL, devId, &displayDevice, 0);
if (ret != 0) // reinterpret_cast
{
// 有‘DISPLAY_DEVICE_ATTACHED_TO_DESKTOP’标志的显示设备
if ((displayDevice.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == DISPLAY_DEVICE_ATTACHED_TO_DESKTOP)
{
devices.push_back(displayDevice);
isPrimary = ((displayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) == DISPLAY_DEVICE_PRIMARY_DEVICE);
}
}
devId++;
} while (ret);
m_vDisplayDevice_list = devices;
std::list<DISPLAY_DEVICE>::iterator it;
for (it = m_vDisplayDevice_list.begin(); it != m_vDisplayDevice_list.end(); it++)
{
DEVMODE deviceMode;
deviceMode.dmSize = sizeof(DEVMODE);
deviceMode.dmFields = DM_PELSWIDTH | // dmPelsWidth
DM_PELSHEIGHT | //dmPelsHeight
DM_BITSPERPEL |
DM_POSITION |
DM_DISPLAYFREQUENCY |
DM_DISPLAYFLAGS; // | DM_DISPLAYORIENTATION;
EnumDisplaySettings((const char*)(it->DeviceName), (int)ENUM_REGISTRY_SETTINGS, &deviceMode);
modes.push_back(deviceMode);
}
m_vDevmode_list = modes;
I used this function to open the Windows desktop shortcut:
ShellExecute(NULL,
NULL,
_T("C:\\Users\\Administrator\\Desktop\\BasePointV - ***.lnk"),
NULL,
NULL,
SW_SHOWNORMAL);
I have a problem:
--------Configuration: Display - Win32 Release--------
Linking...
***.obj : error LNK2001: unresolved external symbol __imp__EnumDisplayDevicesA#16
Debug/***.exe : fatal error LNK1120: 1 unresolved externals
***.exe - 1 error(s), 0 warning(s)
The project I built is MFC AppWinzard(exe);
Environment is:Windows10 VC 6.0
Online solutions include:
Define WINVER 0x0500
Add user32.DLL
There are many good solutions, but the linking problem is not solved yet!
Your linkage error concerning _EnumDisplayDevices says it all.
My psychic powers suggests that since Visual Studio 6.0 (released in 1998) predates the availability of EnumDisplayDevices (Windows 2000), you are trying to pre-declare the API yourself. You probably predeclared EnumDisplayDevices manually yourself. Something like this:
BOOL EnumDisplayDevices(
LPCSTR lpDevice,
DWORD iDevNum,
PDISPLAY_DEVICEA lpDisplayDevice,
DWORD dwFlags
);
There's two problems with this approach.
First, there's no API called EnumDisplayDevices. There is however, two APIs called EnumDisplayDevicesA and EnumDisplayDevicesW for both Unicode and ANSI builds. The Windows SDK will use a macro to map either one to the thing you can to invoke:
#ifdef UNICODE
#define EnumDisplayDevices EnumDisplayDevicesW
#else
#define EnumDisplayDevices EnumDisplayDevicesA
#endif // !UNICODE
Second, the actual declaration of EnumDisplayDevicesA and EnumDisplayDevicesW will are declared as stdcall calling type, like most Win32 APIs. Your declaration is likely missing this detail.
Hence, you want to declare this:
BOOL __stdcall EnumDisplayDevicesW(
LPCWSTR lpDevice,
DWORD iDevNum,
PDISPLAY_DEVICEW lpDisplayDevice,
DWORD dwFlags);
And this:
BOOL __stdcall EnumDisplayDevicesA(
LPCSTR lpDevice,
DWORD iDevNum,
PDISPLAY_DEVICEA lpDisplayDevice,
DWORD dwFlags);
If you manually declared DISPLAY_DEVICE and PDISPLAY_DEVICE yourself, you may also need to fixup your declaration as well. That's addressed in the sample code below.
Finally, even after you fix this, you still won't have a lib to link with since your version of user32.lib doesn't know anything about this API that came later.
You could find a newer version of the Windows SDK that still works with VC 6.0. But a simpler approach might to LoadLibrary the API directly at runtime. So putting it all together, here's a complete solution in which we'll dynamically load the EnumDisplayDevicesW and EnumDisplayDevicesA functions at runtime. Sample invocation as well:
#include <windows.h>
// BORROWED THIS FROM THE WINDOWS SDK - uncomment it if you need it
#if 0
typedef struct _DISPLAY_DEVICEA {
DWORD cb;
CHAR DeviceName[32];
CHAR DeviceString[128];
DWORD StateFlags;
CHAR DeviceID[128];
CHAR DeviceKey[128];
} DISPLAY_DEVICEA, *PDISPLAY_DEVICEA, *LPDISPLAY_DEVICEA;
typedef struct _DISPLAY_DEVICEW {
DWORD cb;
WCHAR DeviceName[32];
WCHAR DeviceString[128];
DWORD StateFlags;
WCHAR DeviceID[128];
WCHAR DeviceKey[128];
} DISPLAY_DEVICEW, *PDISPLAY_DEVICEW, *LPDISPLAY_DEVICEW;
#ifdef UNICODE
typedef DISPLAY_DEVICEW DISPLAY_DEVICE;
typedef PDISPLAY_DEVICEW PDISPLAY_DEVICE;
typedef LPDISPLAY_DEVICEW LPDISPLAY_DEVICE;
#else
typedef DISPLAY_DEVICEA DISPLAY_DEVICE;
typedef PDISPLAY_DEVICEA PDISPLAY_DEVICE;
typedef LPDISPLAY_DEVICEA LPDISPLAY_DEVICE;
#endif // UNICODE
#endif // if 0
// Declare the function types for EnumDisplayDevices
typedef BOOL(__stdcall* FN_EDD_W)(LPCWSTR, DWORD, PDISPLAY_DEVICEW, DWORD);
typedef BOOL(__stdcall* FN_EDD_A)(LPCSTR, DWORD, PDISPLAY_DEVICEA, DWORD);
int main()
{
FN_EDD_W fnEnumDisplayDevicesW;
FN_EDD_A fnEnumDisplayDevicesA;
// Dynamically load EnumDisplayDevices
HMODULE hMod = LoadLibraryW(L"user32.dll");
fnEnumDisplayDevicesW = (FN_EDD_W)GetProcAddress(hMod, "EnumDisplayDevicesW");
fnEnumDisplayDevicesA = (FN_EDD_A)GetProcAddress(hMod, "EnumDisplayDevicesA");
// now invoke the loaded API function
DISPLAY_DEVICEW device = {};
device.cb = sizeof(device);
fnEnumDisplayDevicesW(NULL, 0, &device, 0); // equivalent to EnumDisplayDevicesW
}
Disclaimer: This questions seems to get downvoted because I should use the normal Win32 API (CreateProcess, ShellExecute). I know about these APIs and I'm aware that RtlCreateUserProcess is not supposed to be called directly. However, the native API is a very relevant topic regarding security, that's why I am researching it.
I'm trying to run programs on Windows using the function RtlCreateUserProcess, exported from ntdll.dll. My code works to run calc.exe, however, after trying to run notepad.exe, I receive an error message that reads The ordinal 345 could not be located in dynamic link library "C:\Windows\SysWOW64\notepad.exe". When trying to run other programs it displays various similar messages, always related to some ordinals or DLLs missing.
My example code looks like this:
#include <windows.h>
#include <iostream>
#include <winternl.h>
typedef struct _SECTION_IMAGE_INFORMATION {
PVOID EntryPoint;
ULONG StackZeroBits;
ULONG StackReserved;
ULONG StackCommit;
ULONG ImageSubsystem;
WORD SubSystemVersionLow;
WORD SubSystemVersionHigh;
ULONG Unknown1;
ULONG ImageCharacteristics;
ULONG ImageMachineType;
ULONG Unknown2[3];
} SECTION_IMAGE_INFORMATION, * PSECTION_IMAGE_INFORMATION;
typedef struct _RTL_USER_PROCESS_INFORMATION {
ULONG Size;
HANDLE ProcessHandle;
HANDLE ThreadHandle;
CLIENT_ID ClientId;
SECTION_IMAGE_INFORMATION ImageInformation;
} RTL_USER_PROCESS_INFORMATION, * PRTL_USER_PROCESS_INFORMATION;
typedef VOID(NTAPI* Func1)(PUNICODE_STRING DestinationString, __drv_aliasesMem PCWSTR SourceString);
typedef NTSTATUS(NTAPI* Func2)(OUT PRTL_USER_PROCESS_PARAMETERS* pProcessParameters, IN PUNICODE_STRING ImagePathName, IN PUNICODE_STRING DllPath OPTIONAL, IN PUNICODE_STRING CurrentDirectory OPTIONAL, IN PUNICODE_STRING CommandLine OPTIONAL, IN PVOID Environment OPTIONAL, IN PUNICODE_STRING WindowTitle OPTIONAL, IN PUNICODE_STRING DesktopInfo OPTIONAL, IN PUNICODE_STRING ShellInfo OPTIONAL, IN PUNICODE_STRING RuntimeData OPTIONAL);
typedef NTSTATUS(NTAPI* Func3)(PUNICODE_STRING NtImagePathName, ULONG Attributes, PRTL_USER_PROCESS_PARAMETERS ProcessParameters, PSECURITY_DESCRIPTOR ProcessSecurityDescriptor, PSECURITY_DESCRIPTOR ThreadSecurityDescriptor, HANDLE ParentProcess, BOOLEAN InheritHandles, HANDLE DebugPort, HANDLE ExceptionPort, PRTL_USER_PROCESS_INFORMATION ProcessInformation);
int main()
{
UNICODE_STRING str;
PRTL_USER_PROCESS_PARAMETERS processparameters;
RTL_USER_PROCESS_INFORMATION processinformation = { 0 };
Func1 RtlInitUnicodeString = (Func1)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlInitUnicodeString");
Func2 RtlCreateProcessParameters = (Func2)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlCreateProcessParameters");
Func3 RtlCreateUserProcess = (Func3)GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlCreateUserProcess");
RtlInitUnicodeString(&str, L"\\??\\C:\\Windows\\SysWOW64\\notepad.exe"); //Starting calc.exe works, notepad.exe does not.
RtlCreateProcessParameters(&processparameters, &str, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
NTSTATUS works = RtlCreateUserProcess(&str, OBJ_CASE_INSENSITIVE, processparameters, NULL, NULL, NULL, FALSE, NULL, NULL, &processinformation);
if (NT_SUCCESS(works)) {
ResumeThread(processinformation.ThreadHandle);
//Started application crashes at this point + the error message gets shown
}
else {
std::cout << "Failed" << std::endl;
}
return 0;
}
Unfortunately, there is not much information available about using this function, so I would appreciate any answers on how to use this function correctly.
CreateProcess after create new process do much more job, in particular it create activation context for new process based on exe manifest (BasepConstructSxsCreateProcessMessage + CsrClientCallServer) as result new process have initial activation context, stored in PEB (SystemDefaultActivationContextData and ActivationContextData) but in process created with pure call to RtlCreateUserProcess this fields is empty (0). as result your process loaded ComCtl32.dll from system32 (version 5.82 ) and notepad with activation context - 6+ version.
The ordinal 345 could not be located in dynamic link library
really in ComCtl32.DLL pre 6 version (5.82). 345 - this is TaskDialogIndirect api which exist only in ComCtl32.DLL version 6+. but your process load 5.82.. - calling TaskDialogIndirect loader says ordinal 345 not found
so CreateProcess not a thin shell over RtlCreateUserProcess or NtCreateUserProcess, but big and complex api. at it functional very hard if possible at all implement direct
I always used this solution to enumerate files and folders recursively. The code works very fine showing correct file/folder names to all found. But exists a trouble related to recognition of symbolic link files, making the linked solution fail, for example:
Like you can see on image above, these 3 files are symbolic link files pointing to dll files in some place. Then, executing the code mentioned will prints:
api-ms-win-core-console-l1-1-0.dll
instead of
api-ms-win-core-console-l1-1-0.symlink
Another similar case with some use of IoCreateFile() function. If this receive a wrong filename or objectattributes (still referring to code of linked answer), also will fail with a ntstatus error STATUS_OBJECT_PATH_NOT_FOUND or some other related to this trouble.
Then my question is:
Is threre some solution to recognize symbolic link files where the linked code that i had used could work (and also why not any other function like ZwOpenFile etc)?
Thanks in advance by any suggestion.
Edition:
Here is a code where IoCreateFile() function fails when a simbolic link is passed as filename:
#include <ntifs.h>
#include <ntddk.h>
HANDLE
MyIoOpenFile(
IN PCWSTR FileName,
IN ACCESS_MASK DesiredAccess,
IN ULONG ShareAccess)
{
NTSTATUS ntStatus;
UNICODE_STRING uniFileName;
OBJECT_ATTRIBUTES objectAttributes;
HANDLE ntFileHandle;
IO_STATUS_BLOCK ioStatus;
if (KeGetCurrentIrql() > PASSIVE_LEVEL) {
DbgPrint("KeGetCurrentIrql() > PASSIVE_LEVEL\n");
return 0;
}
RtlInitUnicodeString(&uniFileName, FileName);
InitializeObjectAttributes(&objectAttributes, &uniFileName,
OBJ_KERNEL_HANDLE | OBJ_CASE_INSENSITIVE, NULL, NULL);
ntStatus = IoCreateFile(&ntFileHandle,
DesiredAccess,
&objectAttributes,
&ioStatus,
0,
FILE_ATTRIBUTE_NORMAL,
ShareAccess,
FILE_OPEN,
0,
NULL,
0,
0,
NULL,
IO_NO_PARAMETER_CHECKING);
if (!NT_SUCCESS(ntStatus)) {
DbgPrint("IoCreateFile() error - 0x%X \n", ntStatus);
return 0;
}
return ntFileHandle;
}
//---------------------------------------------------------------------------
HANDLE hFileHandle = MyIoOpenFile(L"\\??\\C:\\Full-Path-FileName-Here",
FILE_READ_ATTRIBUTES,
FILE_SHARE_READ);
if (hFileHandle != 0) {
DbgPrint("hFileHandle: %08X\n", hFileHandle);
ZwClose(hFileHandle);
}
From Microsoft docs:
FILE_ATTRIBUTE_REPARSE_POINT - A file or directory that has an associated reparse point, or a file that is a symbolic link.
So you can use GetFileAttributes to detect symbolic links:
LPCWSTR lpszFileName = /* get file name */;
if (GetFileAttributes(lpszFileName) & FILE_ATTRIBUTE_REPARSE_POINT)
{
// Symbolic link
}
See comments for further information about error checks and kernel mode API
I have been attempting to run a 64-bit DLL purely in a processes virtual memory without 'manually mapping' it (i.e. manually resolving relocations/imports).
The plan was to inject code into the target application and load the module via conventional means, such as LoadLibrary.
I was under the assumption LoadLibrary would fix the module relocations/imports on it's own, as that's what it is designed to do.
After loading the module, the injected code would obtain information regarding the module with GetModuleInformation, transfer it to a temporary memory buffer, free the module, allocate memory at the same address it was originally loaded at, write it back, and execute the entry point.
That last step is where I believe the error is occurring.
In order to test this theory, I have hard-coding entry point addresses, debugged the remote application via Visual Studio's 'Attach to Process' feature, emulated a similar environment to correct bad pointer arithmetic, all in order to gain a bit more information on what the error might be.
Here is some general information which may or may not be useful:
Both applications (the injector, and DLL) are compiled to run in 64-bit architectures
The test application I have been using to test the injection method is the windows update applicaiton (wuauclt.exe - located in /System32/), it is of course compiled to run as a 64-bit PE
Host machine: Windows 7 Home Premium (system type: 64-bit operating system)
As far as information relating directly to the injector goes:
The primary code injection method works (as far as I can tell), and I have proven this via caveman debugging with MessageBoxA
The project is using a multi-byte character set with code optimizations disabled. The code was compiled using VS 2013 Ultimate (both projects built for Release x64)
SDL checks are off since unsafe functions are used (strcpy and friends)
The injector is debugged with elevated privileges (as high as SE_DEBUG_PRIVILEGES) every time its ran.
Code Preface:
The code exhibited below is not in any which way meant to look pretty or exhibit good programming practices. Keep this in mind when viewing the code. It was specifically designed to test a code-injection method to verify it works. If you have issues with the program layout, structure, etc, feel free to correct them and/or restructure them on your own. It's not the reason I'm here. Unless it is what resulted in the error, then it is entirely the reason I'm here :)
The code for the injector: http://pastebin.com/FF5G9nnR
/*
Some of the code was truncated (functions not pertaining to the injection), but
I have verified the code compiles and works correctly with it's injeteme.dll counterpart
*/
#include <Windows.h>
#include <Psapi.h>
#define TARGET_PID 1124
typedef BOOL(WINAPI* pFreeLibrary)(HMODULE);
typedef HMODULE(WINAPI* pLoadLibraryA)(LPCSTR);
typedef HANDLE(WINAPI* pGetCurrentProcess)(void);
typedef BOOL(WINAPI* DLL_MAIN)(HMODULE, DWORD, LPVOID);
typedef HANDLE(WINAPI* pOpenProcess)(DWORD, BOOL, DWORD);
typedef BOOL(WINAPI* pVirtualFree)(LPVOID, SIZE_T, DWORD);
typedef int(__stdcall* pMessageBoxA)(HWND, LPCSTR, LPCSTR, UINT);
typedef LPVOID(WINAPI* pVirtualAlloc)(LPVOID, SIZE_T, DWORD, DWORD);
typedef BOOL(WINAPI* pGetModuleInformation)(HANDLE, HMODULE, LPMODULEINFO, DWORD);
typedef BOOL(WINAPI* pWriteProcessMemory)(HANDLE, LPVOID, LPCVOID, SIZE_T, SIZE_T*);
//////////////////////////////////////////////////////////////////
struct IINFO
{
LPVOID stubAddr;
LPVOID retStatusPtr;
char fullModulePath[MAX_PATH];
DWORD pId, sizeOfCurrStruct;
// DEBUG
pMessageBoxA messageBox;
pOpenProcess openProcess;
pVirtualFree virtualFree;
pFreeLibrary freeLibrary;
pLoadLibraryA loadLibrary;
pVirtualAlloc virtualAlloc;
pGetCurrentProcess getCurrProc;
pWriteProcessMemory writeMemory;
pGetModuleInformation getModInfo;
};
static DWORD WINAPI stub(IINFO *iInfo)
{
HMODULE hMod;
MODULEINFO mInfo;
DLL_MAIN dllMain;
LPVOID lpNewMod, lpTempModBuff;
PIMAGE_DOS_HEADER pIDH;
PIMAGE_NT_HEADERS pINH;
iInfo->messageBox(NULL, iInfo->fullModulePath, NULL, 0);
hMod = iInfo->loadLibrary(iInfo->fullModulePath);
if (!hMod)
return 0;
if (!iInfo->getModInfo(iInfo->getCurrProc(), hMod, &mInfo, sizeof(MODULEINFO)))
return 0;
lpTempModBuff = iInfo->virtualAlloc(NULL, mInfo.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!lpTempModBuff)
return 0;
if (!iInfo->writeMemory(iInfo->getCurrProc(), lpTempModBuff, mInfo.lpBaseOfDll, mInfo.SizeOfImage, NULL))
return 0;
if (!iInfo->freeLibrary(hMod))
return 0;
lpNewMod = iInfo->virtualAlloc(mInfo.lpBaseOfDll, mInfo.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!lpNewMod)
return 0;
// using wpm since we have already acquired the function
if (!iInfo->writeMemory(iInfo->getCurrProc(), lpNewMod, lpTempModBuff, mInfo.SizeOfImage, NULL))
return 0;
if (!iInfo->virtualFree(lpTempModBuff, 0, MEM_RELEASE))
return 0;
/*if (!iInfo->virtualFree(iInfo, 0, MEM_RELEASE))
return 0;
iInfo->messageBox(NULL, NULL, NULL, 0); */
pIDH = (PIMAGE_DOS_HEADER)lpNewMod;
if (!pIDH)
return 0;
pINH = (PIMAGE_NT_HEADERS)((LPBYTE)lpNewMod + pIDH->e_lfanew);
if (!pINH)
return 0;
dllMain = (DLL_MAIN)((LPBYTE)lpNewMod + pINH->OptionalHeader.AddressOfEntryPoint);
if (!dllMain)
return 0;
iInfo->messageBox(NULL, NULL, NULL, 0);
dllMain((HINSTANCE)lpNewMod, DLL_PROCESS_ATTACH, NULL);
return 1;
}
static DWORD WINAPI stubEnd(){ return 0; }
//////////////////////////////////////////////////////////////////
int main()
{
HANDLE hThread = 0;
DWORD dwStubSize = 0;
int sucResp = 0, count = 0;
HMODULE hUser32 = 0, hNtdll = 0;
char fullPathName[] = "C:\\injectme.dll";
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, TARGET_PID);
if (!hProc || hProc == INVALID_HANDLE_VALUE)
return 0;
__int64 SizeOfStub = (LPBYTE)stubEnd - (LPBYTE)stub;
LPVOID lpStub = VirtualAllocEx(hProc, NULL, SizeOfStub, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!lpStub)
return 0;
hUser32 = LoadLibraryA("user32.dll");
if (!hUser32)
return 0;
hNtdll = LoadLibraryA("kernel32.dll");
if (!hNtdll)
return 0;
IINFO iInfo = {};
iInfo.retStatusPtr = &sucResp;
strcpy(iInfo.fullModulePath, fullPathName);
iInfo.sizeOfCurrStruct = sizeof(IINFO);
iInfo.stubAddr = lpStub;
iInfo.pId = GetCurrentProcessId();
iInfo.messageBox = (pMessageBoxA)GetProcAddress(hUser32, "MessageBoxA");
iInfo.openProcess = (pOpenProcess)GetProcAddress(hNtdll, "OpenProcess");
iInfo.virtualFree = (pVirtualFree)GetProcAddress(hNtdll, "VirtualFree");
iInfo.freeLibrary = (pFreeLibrary)GetProcAddress(hNtdll, "FreeLibrary");
iInfo.loadLibrary = (pLoadLibraryA)GetProcAddress(hNtdll, "LoadLibraryA");
iInfo.virtualAlloc = (pVirtualAlloc)GetProcAddress(hNtdll, "VirtualAlloc");
iInfo.getCurrProc = (pGetCurrentProcess)GetProcAddress(hNtdll, "GetCurrentProcess");
iInfo.writeMemory = (pWriteProcessMemory)GetProcAddress(hNtdll, "WriteProcessMemory");
iInfo.getModInfo = (pGetModuleInformation)GetProcAddress(hNtdll, "K32GetModuleInformation");
LPVOID lpStubInfo = VirtualAllocEx(hProc, NULL, sizeof(IINFO), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (!lpStubInfo)
return 0;
if (!WriteProcessMemory(hProc, lpStub, stub, SizeOfStub, NULL))
return 0;
if (!WriteProcessMemory(hProc, lpStubInfo, &iInfo, sizeof(iInfo), NULL))
return 0;
hThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)lpStub, lpStubInfo, 0, NULL);
if (!hThread || hThread == INVALID_HANDLE_VALUE)
return 0;
WaitForSingleObject(hThread, INFINITE);
return 1;
}
The code for the DLL to be injected: http://pastebin.com/8WXxcpu1
#include <Windows.h>
BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD fdwReason, LPVOID lpParam)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
{
MessageBoxA(NULL, "Hello from injectme.dll!", "", MB_OK | MB_ICONINFORMATION);
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
The error when running the code above verbatim (assuming you also applied the settings above and have a similar environment) in VS2013's debugger is as follows:
"Unhandled exception at 0x000007FEEA5125D4 in wuauclt.exe: 0xC0000005: Access violation executing location 0x000007FEEA5125D4."
Upon viewing the process "wuauclt.exe" in Process Hacker, I can clearly see the module was allocated originally (upon being loaded via LoadLibrary) at 0x7fef67c0000. This is shown in the context menu->under miscellaneous->unloaded modules.
Once double-clicking "wuauclt.exe", you can browse over the application's virtual memory to ensure everything is working as it should be. I can confirm for this current session, an RWX memory buffer has been allocated at 0x7fef67c0000 with the exact size of the unloaded module, containing the injectme.dll module. When digging into injectme.dll with CFF Explorer, then entry point RVA seems to be 0x132C, which does not add up, considering the error is much further away in memory. Additionally, I can verify two more RWX memory buffers containing the code injection stub, and information structure. Looking back the information structure probably doesn't need RWX. Anyway, I can't for the life of me figure out the error.
I'm hoping one you may be able to assist me. I am extremely grateful for your time.
My gut feeling is that you're lacking the fundamental understanding for such a challenging project. You're mixing concepts from rather distinct realms.
Windows itself cares very, very little about the programming language you used in development. Either you get CLR code (.Net) or native code. In this case it's x64. But Windows really doesn't care about strcpy or SDL checks. That's for the compiler to deal with, not the OS. Chances are strcpy wouldn't even survive, when its code is fully inlined. But you apparently have optimizations turned off, for some strange reason - again a compiler versus OS confusion.
However, Windows does care about other concepts that you don't mention. Chiefly those would be ASLR and DEP - Address Space Layout Randomization and Data Execution Prevention. They're techniques to keep hackers out, and you're hacking. So that's not really a surprise.
I'm not sure if by "RWX" you mean Read Write Execute" because you should know that's asking for problems. DEP is inspired by the more aptly named W^X, Write XOR eXecute.
The more likely culprit is ASLR, though. Windows by design tries to load DLL's at unpredicatble addresses, as that eliminates an entire class of hacks. It appears you're assuming a load address, while Windows really is using another address.
A final mistake might be that you're failing to understand where the relocations are done. To improve the amount of shareable pages, relocations are done on the Import Address Table, not the code itself. The IAT is a trampoline table, and therefore executable. Your failure might also be a missing IAT.
Is there a way to progammatically detect when a module - specifically a DLL - has been unloaded from a process?
I don't have the DLL source, so I can't change it's DLL entry point. Nor can I poll if the DLL is currently loaded because the DLL may be unloaded and then reloaded between polling.
RESULTS:
I ended up using jimharks solution of detouring the dll entry point and catching DLL_PROCESS_DETACH. I found detouring FreeLibrary() to work as well but code must be added to detect when the module is actually unloaded or if the reference count is just being decreased. Necrolis' link about finding the reference count was handy for on method of doing so.
I should note that I had problems with MSDetours not actually unloading the module from memory if a detour existed within it.
One very bad way(which was used by starcraft 2), is to make your program attach to itsself then monitor for the dll unload debug event(http://msdn.microsoft.com/en-us/library/ms679302(VS.85).aspx), else you'd either need to IAT hook FreeLibrary and FreeLibraryEx in the process or hotpatch the functions in kernel32 them monitor the names being passed and the global reference counts.
Try using LdrRegisterDllNotification if you're on Vista or above. It does require using GetProcAddress to find the function address from ntdll.dll, but it's the proper way of doing it.
Maybe a less bad way then Necrolis's would be to use Microsoft Research's Detours package to hook the dll's entry point to watch for DLL_PROCESS_DETACH notifications.
You can find the entry point given an HMODULE (as returned by LoadLibrary) using this function:
#include <windows.h>
#include <DelayImp.h>
PVOID GetAddressOfEntryPoint(HMODULE hmod)
{
PIMAGE_DOS_HEADER pidh = (PIMAGE_DOS_HEADER)hmod;
PIMAGE_NT_HEADERS pinth = (PIMAGE_NT_HEADERS)((PBYTE)hmod + pidh->e_lfanew);
PVOID pvEntry = (PBYTE)hmod + pinth->OptionalHeader.AddressOfEntryPoint;
return pvEntry;
}
Your entrypoint replacement could take direct action or increment a counter that you check for in your main loop or where it's important to you. (And should almost certainly call the original entrypoint.)
UPDATE: Thanks to #LeoDavidson for pointing this out in the comments below. Detours 4.0 is now licensed using the liberal MIT License.
I hope this helps.
#Necrolis, your link to “The covert way to find the Reference Count of DLL” was just too intriguing for me to ignore because it contains the technical details I needed to implement this alternate solution (that I thought of yesterday, but was lacking the Windows Internals). Thanks. I voted for your answer because of the link you shared.
The linked article shows how to get to the internal LDR_MODULE:
struct _LDR_MODULE
{
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID BaseAddress;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;
Right here we have EntryPoint, Window's internal pointer to the module’s entry point. For a dll that’s DllMain (or the language run time function that eventually calls DllMain). What if we just change that? I wrote a test and it seems to work, at least on XP. The DllMain hook gets called with reason DLL_PROCESS_DETACH just before the DLL unloads.
The BaseAddress is the same value as an HMODULE and is useful for finding the right LDR_MODULE. The LoadCount is here so we can track that. And finally FullDllName is helpful for debugging and makes it possible to search for DLL name instead of HMODULE.
This is all Windows internals. It’s (mostly) documented, but the MSDN documentation warns “ZwQueryInformationProcess may be altered or unavailable in future versions of Windows.”
Here’s a full example (but without full error checking). It seems to work but hasn’t seen much testing.
// HookDllEntryPoint.cpp by Jim Harkins (jimhark), Nov 2010
#include "stdafx.h"
#include <stdio.h>
#include <winternl.h>
#include <process.h> // for _beginthread, only needed for testing
typedef NTSTATUS(WINAPI *pfnZwQueryInformationProcess)(
__in HANDLE ProcessHandle,
__in PROCESSINFOCLASS ProcessInformationClass,
__out PVOID ProcessInformation,
__in ULONG ProcessInformationLength,
__out_opt PULONG ReturnLength);
HMODULE hmodNtdll = LoadLibrary(_T("ntdll.dll"));
// Should test pZwQueryInformationProcess for NULL if you
// might ever run in an environment where this function
// is not available (like future version of Windows).
pfnZwQueryInformationProcess pZwQueryInformationProcess =
(pfnZwQueryInformationProcess)GetProcAddress(
hmodNtdll,
"ZwQueryInformationProcess");
typedef BOOL(WINAPI *PDLLMAIN) (
__in HINSTANCE hinstDLL,
__in DWORD fdwReason,
__in LPVOID lpvReserved);
// Note: It's possible for pDllMainNew to be called before
// HookDllEntryPoint returns. If pDllMainNew calls the old
// function, it should pass a pointer to the variable used
// so we can set it here before we hook.
VOID HookDllEntryPoint(
HMODULE hmod, PDLLMAIN pDllMainNew, PDLLMAIN *ppDllMainOld)
{
PROCESS_BASIC_INFORMATION pbi = {0};
ULONG ulcbpbi = 0;
NTSTATUS nts = (*pZwQueryInformationProcess)(
GetCurrentProcess(),
ProcessBasicInformation,
&pbi,
sizeof(pbi),
&ulcbpbi);
BOOL fFoundMod = FALSE;
PLIST_ENTRY pcurModule =
pbi.PebBaseAddress->Ldr->InMemoryOrderModuleList.Flink;
while (!fFoundMod && pcurModule !=
&pbi.PebBaseAddress->Ldr->InMemoryOrderModuleList)
{
PLDR_DATA_TABLE_ENTRY pldte = (PLDR_DATA_TABLE_ENTRY)
(CONTAINING_RECORD(
pcurModule, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks));
// Note: pldte->FullDllName.Buffer is Unicode full DLL name
// *(PUSHORT)&pldte->Reserved5[1] is LoadCount
if (pldte->DllBase == hmod)
{
fFoundMod = TRUE;
*ppDllMainOld = (PDLLMAIN)pldte->Reserved3[0];
pldte->Reserved3[0] = pDllMainNew;
}
pcurModule = pcurModule->Flink;
}
return;
}
PDLLMAIN pDllMain_advapi32 = NULL;
BOOL WINAPI DllMain_advapi32(
__in HINSTANCE hinstDLL,
__in DWORD fdwReason,
__in LPVOID lpvReserved)
{
char *pszReason;
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
pszReason = "DLL_PROCESS_ATTACH";
break;
case DLL_PROCESS_DETACH:
pszReason = "DLL_PROCESS_DETACH";
break;
case DLL_THREAD_ATTACH:
pszReason = "DLL_THREAD_ATTACH";
break;
case DLL_THREAD_DETACH:
pszReason = "DLL_THREAD_DETACH";
break;
default:
pszReason = "*UNKNOWN*";
break;
}
printf("\n");
printf("DllMain(0x%.8X, %s, 0x%.8X)\n",
(int)hinstDLL, pszReason, (int)lpvReserved);
printf("\n");
if (NULL == pDllMain_advapi32)
{
return FALSE;
}
else
{
return (*pDllMain_advapi32)(
hinstDLL,
fdwReason,
lpvReserved);
}
}
void TestThread(void *)
{
// Do nothing
}
// Test HookDllEntryPoint
int _tmain(int argc, _TCHAR* argv[])
{
HMODULE hmodAdvapi = LoadLibrary(L"advapi32.dll");
printf("advapi32.dll Base Addr: 0x%.8X\n", (int)hmodAdvapi);
HookDllEntryPoint(
hmodAdvapi, DllMain_advapi32, &pDllMain_advapi32);
_beginthread(TestThread, 0, NULL);
Sleep(1000);
return 0;
}