Obtain Thread Info from tls callback - c++

So I have a tls callback implemented in a separate dll and I want to retrieve the thread information on thread attach like the entry point,
parameters etc
void NTAPI on_tls_callback(LPVOID dll, DWORD reason, LPVOID reserved)
{
if (reason == DLL_THREAD_ATTACH)
{
//obtain thread info
}
}
#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:__xl_b")
#pragma data_seg(".CRT$XLB")
EXTERN_C
PIMAGE_TLS_CALLBACK _xl_b = on_tls_callback;
#pragma data_seg()
I read here that it's possible
How can DLL injection be detected?

Okay after a bit of further research I found a way here
Thread EntryPoint in TLS callback as AntiDebug technique
typedef NTSTATUS
(NTAPI*NtQueryInformationThread_t)(
IN HANDLE ThreadHandle,
IN THREADINFOCLASS ThreadInformationClass,
OUT PVOID ThreadInformation,
IN ULONG ThreadInformationLength,
OUT PULONG ReturnLength OPTIONAL
);
void NTAPI on_tls_callback(LPVOID dll, DWORD reason, LPVOID reserved)
{
if (reason == DLL_THREAD_ATTACH)
{
DWORD startAddress = 0;
NtQueryInformationThread_t pNtQueryInformationThread =
reinterpret_cast<NtQueryInformationThread_t>(GetProcAddress(
GetModuleHandle("ntdll.dll"), "NtQueryInformationThread"));
pNtQueryInformationThread(GetCurrentThread(), THREADINFOCLASS(9),
&startAddress, sizeof(startAddress), nullptr);
}
}
#pragma comment (linker, "/INCLUDE:__tls_used")
#pragma comment (linker, "/INCLUDE:__xl_b")
#pragma data_seg(".CRT$XLB")
EXTERN_C
PIMAGE_TLS_CALLBACK _xl_b = on_tls_callback;
#pragma data_seg()

Related

CEN XFS Exception when returning from WFPOpen

I'm writing a test DLL for an IDC service provider. I also written an mfc testing application that loads the XFS Manager.
I'm encountering some problems calling the function WFSOpen.
The manager correctly loads the SPI dll and calls its WFPOpen function. The WFPOpen executes correctly, reaching the return statement and returning WFS_SUCCESS to the caller. Then in the XFS Manager dll something bad happens, an exception is thrown and the WFSOpen function returns WFS_ERR_INTERNAL_ERROR to my testing application.
I always get this message:
First-chance exception at 0x1000C285 (msxfs.dll) in TestXFSIDC.exe: 0xC0000005: Access violation writing location 0x011E3987.
(Date)01.07.2015 (Time)09:29.39,570
Exception occurred
The XFS log file reports this:
09:29:39.554 APICALL: WFSAsyncOpen
actLogicalName: <MyCardReader>
hApp: 398257252 0X17BCEC64
actszAppId: <TestXFSIDC>
actTraceLevel: 10 0X0000000A
actTimeOut: 0 0X00000000
actpHService: 8321216 0X007EF8C0
actHWnd: 461622 0X00070B36
dwSrvcVersionsRequired: 201731 0X00031403
lpSrvcVersion: 8315892 0X007EE3F4
lpSPIVersion: 8315364 0X007EE1E4
actpRequestId: 8315352 0X007EE1D8
09:29:39.554 OpenKey( HKEY_CURRENT_USER,WOSA_CURRENT_USER_KEY_NAME,0,KEY_READ | KEY_WRITE,&hKeyWosaRoot) failed (reason=2,file=d:\buffington\development\cen xfs manager\source\xfs_conf.cpp,line=2168)
09:29:39.570 WFPCALL: WFPOpen
tmpServiceHandle: 1 0X00000001
actLogicalName: <MyCardReader>
hApp: 398257252 0X17BCEC64
actszAppId: <TestXFSIDC>
actTraceLevel: 10 0X0000000A
actTimeOut: 0 0X00000000
actHWnd: 461622 0X00070B36
tmpRqId: 1 0X00000001
pUser->getServiceProviderHandle(): 1807786008 0X6BC0A018
dwSPIVersionsRequired: 16908035 0X0101FF03
lpSPIVersion: 8315364 0X007EE1E4
dwSrvcVersionsRequired: 201731 0X00031403
lpSrvcVersion: 8315892 0X007EE3F4
09:29:39.570
(Date)01.07.2015 (Time)09:29.39,570
Called WFPOpen
09:29:39.570
(Date)01.07.2015 (Time)09:29.39,570
Exiting WFPOpen
09:29:39.554 RegOpenKeyEx( x80000003, .DEFAULT\XFS )) failed (reason=5,file=d:\buffington\development\cen xfs manager\source\xfs_conf.cpp,line=384)
09:29:39.570
(Date)01.07.2015 (Time)09:29.39,570
Exception occurred
I'm using Visual Studio 2012 in Windows 8.1 64bit
I've managed to solve the exception in this way:
#pragma comment(linker, "/EXPORT:WFPCancelAsyncRequest=_WFPCancelAsyncRequest#8")
#pragma comment(linker, "/EXPORT:WFPClose=_WFPClose#12")
#pragma comment(linker, "/EXPORT:WFPDeregister=_WFPDeregister#20")
#pragma comment(linker, "/EXPORT:WFPExecute=_WFPExecute#24")
#pragma comment(linker, "/EXPORT:WFPGetInfo=_WFPGetInfo#24")
#pragma comment(linker, "/EXPORT:WFPLock=_WFPLock#16")
#pragma comment(linker, "/EXPORT:WFPOpen=_WFPOpen#52")
#pragma comment(linker, "/EXPORT:WFPRegister=_WFPRegister#20")
#pragma comment(linker, "/EXPORT:WFPSetTraceLevel=_WFPSetTraceLevel#8")
#pragma comment(linker, "/EXPORT:WFPUnloadService=_WFPUnloadService#0")
#pragma comment(linker, "/EXPORT:WFPUnlock=_WFPUnlock#12")
extern "C"{
DllExport HRESULT WINAPI WFPCancelAsyncRequest(HSERVICE hService, REQUESTID RequestID);
DllExport HRESULT WINAPI WFPClose(HSERVICE hService, HWND hWnd, REQUESTID ReqID);
DllExport HRESULT WINAPI WFPDeregister(HSERVICE hService, DWORD dwEventClass, HWND hWndReg, HWND hWnd, REQUESTID ReqID);
DllExport HRESULT WINAPI WFPExecute(HSERVICE hService, DWORD dwCommand, LPVOID lpCmdData, DWORD dwTimeOut, HWND hWnd, REQUESTID ReqID);
DllExport HRESULT WINAPI WFPGetInfo(HSERVICE hService, DWORD dwCategory, LPVOID lpQueryDetails, DWORD dwTimeOut, HWND hWnd, REQUESTID ReqID);
DllExport HRESULT WINAPI WFPLock(HSERVICE hService, DWORD dwTimeOut, HWND hWnd, REQUESTID ReqID);
DllExport HRESULT WINAPI WFPOpen(HSERVICE hService, LPSTR lpszLogicalName, HAPP hApp, LPSTR lpszAppID, DWORD dwTraceLevel, DWORD dwTimeOut, HWND hWnd, REQUESTID ReqID, HPROVIDER hProvider, DWORD dwSPIVersionsRequired, LPWFSVERSION lpSPIVersion, DWORD dwSrvcVersionsRequired, LPWFSVERSION lpSrvcVersion);
DllExport HRESULT WINAPI WFPRegister(HSERVICE hService, DWORD dwEventClass, HWND hWndReg, HWND hWnd, REQUESTID ReqID);
DllExport HRESULT WINAPI WFPSetTraceLevel(HSERVICE hService, DWORD dwTraceLevel);
DllExport HRESULT WINAPI WFPUnloadService();
DllExport HRESULT WINAPI WFPUnlock(HSERVICE hService, HWND hWnd, REQUESTID ReqID);
};
When exporting the WFPxxx function using WINAPI the dll produced has the _WFPxxx#yyy names, that are not readable by the msxfs.dll, which gives the error WFS_ERR_INVALID_SERVPROV.
The solution was to specify the name recognizable by the msxfs.dll using the
#pragma comment(linker, "/EXPORT:WFPxxx=_WFPxxx#yyy")

NtOpenKey fails with 0xC0000034 - how to fix this?

I'm creating a user-mode CMD app using VS 2013 in C++ and I'm trying to use the native registry editing functions in it. I'm trying to open certain key with 'NtOpenKey' but it always fails with 'STATUS_OBJECT_NAME_NOT_FOUND' and I'm sure that the 'object' is in it's place so the reason must be somewhere else. I want to use the native registry API's because they can handle 'Hidden Registry Keys' - look here for more info. Here is a snippet of my code:
#include <Windows.h>
#include <tchar.h>
#include <wininet.h>
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "Nt_Funcs_declr.h" //here I have manually included the native api declarations as normally I'm denied to use them but they're exported in ntdll.dll so basically it is possible
#include <zlib.h>
//Obitain Steam folder path
wchar_t *GetSteamPathBuffer()
{
//Open the Sofware Steam registry
OBJECT_ATTRIBUTES objAttrs;
objAttrs.Length = sizeof(OBJECT_ATTRIBUTES);
objAttrs.RootDirectory = NULL;
wchar_t strRegSteam [] = L"\\Registry\\Machine\\SOFTWARE\\Valve";
UNICODE_STRING uStrTmp = { sizeof(strRegSteam), sizeof(strRegSteam), strRegSteam };
objAttrs.ObjectName = &uStrTmp;
objAttrs.Attributes = OBJ_CASE_INSENSITIVE; //
objAttrs.SecurityDescriptor = NULL;
objAttrs.SecurityQualityOfService = NULL;
HANDLE pKey;
ULONG tmmp = NtOpenKey(&pKey, GENERIC_READ, &objAttrs); //here it fails with 'STATUS_OBJECT_NAME_NOT_FOUND'
if(tmmp)
{
cout << "Error: " << GetLastError();
return NULL;
}
//....
}
And Nt_Funcs_declr.h:
#pragma once
//NTDLL import declarations
#define STATUS_BUFFER_TOO_SMALL ((NTSTATUS)0xC0000023L)
//
// Unicode strings are counted 16-bit character strings. If they are
// NULL terminated, Length does not include trailing NULL.
//
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
#ifdef MIDL_PASS
[size_is(MaximumLength / 2), length_is((Length) / 2)] USHORT * Buffer;
#else // MIDL_PASS
_Field_size_bytes_part_(MaximumLength, Length) PWCH Buffer;
#endif // MIDL_PASS
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
typedef const UNICODE_STRING *PCUNICODE_STRING;
//
// Valid values for the Attributes field
//
#define OBJ_INHERIT 0x00000002L
#define OBJ_PERMANENT 0x00000010L
#define OBJ_EXCLUSIVE 0x00000020L
#define OBJ_CASE_INSENSITIVE 0x00000040L
#define OBJ_OPENIF 0x00000080L
#define OBJ_OPENLINK 0x00000100L
#define OBJ_KERNEL_HANDLE 0x00000200L
#define OBJ_FORCE_ACCESS_CHECK 0x00000400L
#define OBJ_VALID_ATTRIBUTES 0x000007F2L
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor; // Points to type SECURITY_DESCRIPTOR
PVOID SecurityQualityOfService; // Points to type SECURITY_QUALITY_OF_SERVICE
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
typedef CONST OBJECT_ATTRIBUTES *PCOBJECT_ATTRIBUTES;
extern "C"
NTSYSAPI
NTSTATUS
NTAPI
NtOpenKey(
_Out_ PHANDLE KeyHandle,
_In_ ACCESS_MASK DesiredAccess,
_In_ POBJECT_ATTRIBUTES ObjectAttributes
);
typedef enum _KEY_INFORMATION_CLASS {
KeyBasicInformation,
KeyNodeInformation,
KeyFullInformation,
KeyNameInformation,
KeyCachedInformation,
KeyFlagsInformation,
KeyVirtualizationInformation,
KeyHandleTagsInformation,
KeyTrustInformation,
MaxKeyInfoClass // MaxKeyInfoClass should always be the last enum
} KEY_INFORMATION_CLASS;
extern "C"
NTSYSAPI
NTSTATUS
NTAPI
NtQueryKey(
_In_ HANDLE KeyHandle,
_In_ KEY_INFORMATION_CLASS KeyInformationClass,
_Out_writes_bytes_opt_(Length) PVOID KeyInformation,
_In_ ULONG Length,
_Out_ PULONG ResultLength
);
typedef enum _KEY_VALUE_INFORMATION_CLASS {
KeyValueBasicInformation,
KeyValueFullInformation,
KeyValuePartialInformation,
KeyValueFullInformationAlign64,
KeyValuePartialInformationAlign64,
MaxKeyValueInfoClass // MaxKeyValueInfoClass should always be the last enum
} KEY_VALUE_INFORMATION_CLASS;
typedef struct _KEY_VALUE_PARTIAL_INFORMATION {
ULONG TitleIndex;
ULONG Type;
ULONG DataLength;
_Field_size_bytes_(DataLength) UCHAR Data[1]; // Variable size
} KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION;
extern "C"
NTSYSAPI
NTSTATUS
NTAPI
NtQueryValueKey(
_In_ HANDLE KeyHandle,
_In_ PUNICODE_STRING ValueName,
_In_ KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
_Out_writes_bytes_opt_(Length) PVOID KeyValueInformation,
_In_ ULONG Length,
_Out_ PULONG ResultLength
);
NOTE: It's for educational prupose so please don't ask me why I don't use WIN32 API.
NB: using the kernel API from user mode is unsupported. I strongly recommend against doing so, unless there is a compelling reason why it is necessary.
Here's the problem:
UNICODE_STRING uStrTmp = { sizeof(strRegSteam), sizeof(strRegSteam), strRegSteam };
From the documentation for UNICODE_STRING:
If the string is null-terminated, Length does not include the trailing null character.
So you should be saying something like
UNICODE_STRING uStrTmp = { sizeof(strRegSteam) - sizeof(wchar_t),
sizeof(strRegSteam),
strRegSteam };
As written, your code was attempting to open a key named L"Valve\0" instead of the key named L"Valve".
Addendum: it has been disputed whether so-called "hidden" keys (an unfortunate name IMO; the keys are visible to Win32 code, they simply can't be manipulated) are actually possible, so here's working code to create one:
#include <Windows.h>
#include <stdio.h>
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWCH Buffer;
} UNICODE_STRING;
typedef UNICODE_STRING *PUNICODE_STRING;
typedef const UNICODE_STRING *PCUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES {
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES;
typedef OBJECT_ATTRIBUTES *POBJECT_ATTRIBUTES;
typedef CONST OBJECT_ATTRIBUTES *PCOBJECT_ATTRIBUTES;
#define OBJ_CASE_INSENSITIVE 0x00000040L
#pragma comment(lib, "ntdll.lib")
__declspec(dllimport) NTSTATUS NTAPI NtCreateKey(
__out PHANDLE KeyHandle,
__in ACCESS_MASK DesiredAccess,
__in POBJECT_ATTRIBUTES ObjectAttributes,
__reserved ULONG TitleIndex,
__in_opt PUNICODE_STRING Class,
__in ULONG CreateOptions,
__out_opt PULONG Disposition
);
NTSYSAPI NTSTATUS NTAPI NtOpenKey(
__out PHANDLE KeyHandle,
__in ACCESS_MASK DesiredAccess,
__in POBJECT_ATTRIBUTES ObjectAttributes
);
NTSYSAPI NTSTATUS NTAPI NtDeleteKey(
__out HANDLE KeyHandle
);
int main(int argc, char ** argv)
{
HANDLE pKey;
NTSTATUS result;
OBJECT_ATTRIBUTES objAttrs;
wchar_t strSoftwareKey [] = L"\\Registry\\Machine\\SOFTWARE\\Test\0Key";
// If you use this string instead, the key functions normally, proving that the
// issue isn't because we're using UTF-16 rather than ANSI strings:
//
// wchar_t strSoftwareKey [] = L"\\Registry\\Machine\\SOFTWARE\\Test\u2D80Key";
UNICODE_STRING uStrSoftwareKey = {
sizeof(strSoftwareKey) - sizeof(wchar_t),
sizeof(strSoftwareKey),
strSoftwareKey };
objAttrs.Length = sizeof(OBJECT_ATTRIBUTES);
objAttrs.RootDirectory = NULL;
objAttrs.ObjectName = &uStrSoftwareKey;
objAttrs.Attributes = OBJ_CASE_INSENSITIVE;
objAttrs.SecurityDescriptor = NULL;
objAttrs.SecurityQualityOfService = NULL;
result = NtCreateKey(&pKey, GENERIC_ALL, &objAttrs, 0, NULL, 0, NULL);
if(result)
{
printf("NtCreateKey: %x\n", result);
return NULL;
}
#if 0 // enable this section to delete the key
// you won't be able to use regedit!
result = NtDeleteKey(pKey);
if(result)
{
printf("NtDeleteKey: %x\n", result);
return NULL;
}
#endif
}
As of Windows 7, at least, this still works. (You'll need a copy of ntdll.lib, available from the DDK/WDK, in order to build this code.)
Please do not do this in production code or on other people's machines.

Creating Custom Simple Mapi DLL, fails when executing

i need to create small dll to intercept simple mapi calls and send files via:
file->Send to as attachment (in excel, word, acrobat reader...)
or via explorer->rightclickmenu->Send to->Mail recipient
to attach to gmail.
after reading this:
Mapi32.dll Stub Registry Settings (Windows)
Mapi32 Stub Library (Windows)
and searching here on stackoverflow and other webs i have created one very small dll using code::blocks that shows me information for investigating the process.
I have installed it on registry correctly like indicated in previous links on HKEY_LOCAL_MACHINE\SOFTWARE\Clients\Mail\SimpMapi.
I have selected on my win xp pro in control panel->Internet options my SimpMapi client.
when i open excel write some cells and go to File->Send like attachment dll shows me that first calls MapiLogon, second calls MapiSendMail and third when i close excel it calls MapiLogoff, but then it throws an error and closes excel.exe.
If i try it from explorer window right clicking one file and SendTo->Mail recipient it calls directly MapiSendMail and when i click on ok in MsgBox it throws an error and closes explorer.exe
Could you help me to find out what is wrong.
This is the mail.h file:
#ifndef __MAIN_H__
#define __MAIN_H__
#include <windows.h>
// Todo lo necesario a incluir por mapi.h
#define SUCCESS_SUCCESS 0
#define MAPI_E_USER_ABORT 1
#define MAPI_E_LOGIN_FAILURE 3
typedef unsigned long FLAGS;
typedef unsigned long LHANDLE;
typedef unsigned long FAR *LPLHANDLE, FAR *LPULONG;
typedef struct {
ULONG ulReserved;
ULONG ulRecipClass;
LPSTR lpszName;
LPSTR lpszAddress;
ULONG ulEIDSize;
LPVOID lpEntryID;
} MapiRecipDesc, *lpMapiRecipDesc;
typedef struct {
ULONG ulReserved;
ULONG flFlags;
ULONG nPosition;
LPSTR lpszPathName;
LPSTR lpszFileName;
LPVOID lpFileType;
} MapiFileDesc, *lpMapiFileDesc;
typedef struct {
ULONG ulReserved;
LPSTR lpszSubject;
LPSTR lpszNoteText;
LPSTR lpszMessageType;
LPSTR lpszDateReceived;
LPSTR lpszConversationID;
FLAGS flFlags;
lpMapiRecipDesc lpOriginator;
ULONG nRecipCount;
lpMapiRecipDesc lpRecips;
ULONG nFileCount;
lpMapiFileDesc lpFiles;
} MapiMessage, *lpMapiMessage;
/* To use this exported function of dll, include this header
* in your project.
*/
#ifdef BUILD_DLL
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif
extern "C" void DLL_EXPORT SomeFunction(const LPCSTR sometext);
extern "C" ULONG DLL_EXPORT MAPILogon(ULONG_PTR ulUIParam,LPSTR lpszProfileName,LPSTR lpszPassword,FLAGS flFlags,ULONG ulReserved,LPLHANDLE lplhSession);
extern "C" ULONG DLL_EXPORT MAPILogoff(LHANDLE lhSession,ULONG_PTR ulUIParam,FLAGS flFlags,ULONG ulReserved);
extern "C" ULONG DLL_EXPORT MAPISendDocuments(ULONG_PTR ulUIParam,LPSTR lpszDelimChar,LPSTR lpszFilePaths,LPSTR lpszFileNames,ULONG ulReserved);
extern "C" ULONG DLL_EXPORT MAPISendMail(LHANDLE lhSession,ULONG_PTR ulUIParam,lpMapiMessage lpMessage,FLAGS flFlags,ULONG ulReserved);
#endif
This is the main.cpp file:
#include "main.h"
// a sample exported function
void DLL_EXPORT SomeFunction(const LPCSTR sometext)
{
MessageBoxA(0, sometext, "DLL Message", MB_OK | MB_ICONINFORMATION);
}
ULONG DLL_EXPORT MAPILogon(ULONG_PTR ulUIParam,LPSTR lpszProfileName,LPSTR lpszPassword,FLAGS flFlags,ULONG ulReserved,LPLHANDLE lplhSession)
{
MessageBoxA(0, "MAPILogon", "MAPILogon", MB_OK | MB_ICONINFORMATION);
return SUCCESS_SUCCESS;
}
ULONG DLL_EXPORT MAPILogoff(LHANDLE lhSession,ULONG_PTR ulUIParam,FLAGS flFlags,ULONG ulReserved)
{
MessageBoxA(0, "MAPILogoff", "MAPILogoff", MB_OK | MB_ICONINFORMATION);
return SUCCESS_SUCCESS;
}
ULONG DLL_EXPORT MAPISendDocuments(ULONG_PTR ulUIParam,LPSTR lpszDelimChar,LPSTR lpszFilePaths,LPSTR lpszFileNames,ULONG ulReserved)
{
MessageBoxA(0, "MAPISendDocuments", "MAPISendDocuments", MB_OK | MB_ICONINFORMATION);
return SUCCESS_SUCCESS;
}
ULONG DLL_EXPORT MAPISendMail(LHANDLE lhSession,ULONG_PTR ulUIParam,lpMapiMessage lpMessage,FLAGS flFlags,ULONG ulReserved)
{
MessageBoxA(0, "MAPISendMail", "MAPISendMail", MB_OK | MB_ICONINFORMATION);
return SUCCESS_SUCCESS;
}
extern "C" DLL_EXPORT BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
// attach to process
// return FALSE to fail DLL load
break;
case DLL_PROCESS_DETACH:
// detach from process
break;
case DLL_THREAD_ATTACH:
// attach to thread
break;
case DLL_THREAD_DETACH:
// detach from thread
break;
}
return TRUE; // succesful
}
Thanks in advance.
Jorge
In link: Creating Custom Simple Mapi DLL, throws error when executing
Andy helped me to solve the problem that was incorrect calling convention.
Adding WINAPI to all my Simple MAPI function declarations and definitions it worked, example:
extern "C" ULONG DLL_EXPORT WINAPI MAPILogon( // etc
Then the problem was that my exported functions were exported with declarations.
To correct this in code::blocks you have to add in:
Main menu: Project -> Build options -> GNU GCC Compiler -> Linker settings -> Other linker options: -Wl,--kill-at
Thanks!

Using GUIDFromString requries including Shell32.dll: How do I do that

I am attempting to use the WinAPI function GUIDFromString() but it requires some finagling to include it in my project.
According to the msdn documentation:
This function is not declared in a header or exported by name from a
.dll file. It must be loaded from Shell32.dll as ordinal 703 for
GUIDFromStringA and ordinal 704 for GUIDFromStringW.
It can also be accessed from Shlwapi.dll as ordinal 269 for
GUIDFromStringA and ordinal 270 for GUIDFromStringW.
I have never loaded a DLL before so I am not sure what I should do and I am unsure if loading the DLL is enough, do I also have to include an 'ordinal' with the number 703? Would anyone be able to provide any advice on what I need to do to use this function and even an example?
My attempt below does not work(I am using VC++ 2010 Express):
#pragma comment(lib, "shell32.lib") // if I am including the dll do I need to include the lib aswell?
// I've heard that the dll location differs across versions of windows
// Does anyone know of a Cross-Windows-Version way to include Shell32.dll no matter where it is? Maybe use a keyword like "%SYSTEM%/Shell32.dll"
HINSTANCE shell32DLL = LoadLibary("C:/System/Shell32.dll");
// Now do I include 'Ordinal 703' as described in msdn? And how do I do that?
If you read the documentation for GUIDFromString(), it says:
GUIDFromString is available through Windows XP with Service Pack 2 (SP2) or Windows Vista. It might be altered or unavailable in subsequent versions. Applications should use CLSIDFromString or IIDFromString in place of this function.
CLSIDFromString() and IIDFromString() are both exported by name from Ole32.dll, so you can use them like you would any other DLL function.
With that said, if you still want to use GUIDFromString() then use LoadLibrary() to load shell32.dll and then use GetProcAddress() to access the function. MSDN documentation demonstrates how to do that. To load a function by ordinal, you can use the MAKEINTRESOURCE() macro when calling GetProcAddress().
So, for example:
// MAKEINTRESOURCE() returns an LPTSTR, but GetProcAddress()
// expects LPSTR even in UNICODE, so using MAKEINTRESOURCEA()...
#ifdef UNICODE
#define MAKEINTRESOURCEA_T(a, u) MAKEINTRESOURCEA(u)
#else
#define MAKEINTRESOURCEA_T(a, u) MAKEINTRESOURCEA(a)
#endif
BOOL myGUIDFromString(LPCTSTR psz, LPGUID pguid)
{
BOOL bRet = FALSE;
typedef BOOL (WINAPI *LPFN_GUIDFromString)(LPCTSTR, LPGUID);
LPFN_GUIDFromString pGUIDFromString = NULL;
HINSTANCE hInst = LoadLibrary(TEXT("shell32.dll"));
if (hInst)
{
pGUIDFromString = (LPFN_GUIDFromString) GetProcAddress(hInst, MAKEINTRESOURCEA_T(703, 704));
if (pGUIDFromString)
bRet = pGUIDFromString(psz, pguid);
FreeLibrary(hInst);
}
if (!pGUIDFromString)
{
hInst = LoadLibrary(TEXT("Shlwapi.dll"));
if (hInst)
{
pGUIDFromString = (LPFN_GUIDFromString) GetProcAddress(hInst, MAKEINTRESOURCEA_T(269, 270));
if (pGUIDFromString)
bRet = pGUIDFromString(psz, pguid);
FreeLibrary(hInst);
}
}
return bRet;
}
Save the following lines as SHLWAPIX.DEF:
LIBRARY SHLWAPI
VERSION 6.0
EXPORTS
GUIDFromStringA #269
GUIDFromStringW #270
Save the following lines as SHLWAPIX.C:
// https://msdn.microsoft.com/en-us/library/bb776431.aspx
__declspec(dllexport)
int __stdcall GUIDFromStringA(void *_1, void *_2)
{ return 0; }
__declspec(dllexport)
int __stdcall GUIDFromStringW(void *_1, void *_2)
{ return 0; }
Run
CL.EXE /LD /Zl SHLWAPIX.C /link /DEF:SHLWAPIX.DEF /NOENTRY
to create the import library SHLWAPIX.LIB, then delete SHLWAPIX.OBJ, SHLWAPIX.EXP and SHLWAPIX.DLL
Save the following lines as SHLWAPIX.H:
#pragma once
#pragma comment(linker, "/DEFAULTLIB:SHLWAPIX.LIB")
__declspec(dllimport)
BOOL WINAPI GUIDFromStringA(LPCSTR psz, LPGUID pguid);
__declspec(dllimport)
BOOL WINAPI GUIDFromStringW(LPCWSTR psz, LPGUID pguid);
Finally save the following lines as SHLWAPIX.C:
#pragma comment(lib, "SHLWAPIX.LIB")
#pragma comment(lib, "USER32.LIB")
#pragma comment(linker, "/ENTRY:wWinMainCRTStartup")
#pragma comment(linker, "/SUBSYSTEM:WINDOWS,5.0")
#pragma comment(linker, "/VERSION:1.0")
#define STRICT
#define UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "shlwapix.h"
VOID wWinMainCRTStartup()
{
GUID guid = {0};
WCHAR szBuffer[1025] = L"";
if (GUIDFromStringA("{00112233-4455-6677-8899-AABBCCDDEEFF}", &guid))
if (wsprintf(szBuffer, L"GUID = {%08lX-%04hX-%04hX-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",`
guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]) > 0)
MessageBox((HWND) NULL, szBuffer, L"GUIDFromStringA()", MB_OK);
if (GUIDFromStringW(L"{FFEEDDCC-BBAA-9988-7766-554433221100}", &guid))
if (wsprintf(szBuffer, L"GUID = {%08lX-%04hX-%04hX-%02X%02X-%02X%02X%02X%02X%02X%02X}\n",
guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]) > 0)
MessageBox((HWND) NULL, szBuffer, L"GUIDFromStringW()", MB_OK);
}
Finally run CL.EXE /GS- SHLWAPIX.C to create SHLWAPIX.EXE, then run the latter.
This gives your an error "syntax error '('":
typedef BOOL WINAPI (*LPFN_GUIDFromString)(LPCTSTR, LPGUID);
The proper version is:
typedef BOOL (WINAPI *LPFN_GUIDFromString)(LPCTSTR, LPGUID);

Detect when a Module (DLL) is unloaded

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