文章目录
- c++ - 动态载入DLL接口,可以给IDA静态分析增加一点麻烦
- 概述
- 笔记
- 测试工程
- test_load_dll_then_call_api.cpp
- CMyUser32Dll.h
- CMyUser32Dll.cpp
- LateLoad.h
- IDA静态分析
- 引入表中没有PostMessageW
- 字符串查找能找到PostMessageW
- 备注
- 看看CMyUser32Dll.h编译完的样子
- 备注 - debug版不生成调试信息
- debug版即使不生成调试信息,也可能带入一些和工程目录相关的字符串
- 解决方法1
- 解决方法2
- 备注
- END
c++ - 动态载入DLL接口,可以给IDA静态分析增加一点麻烦
概述
工程中用到了PostMessage来压入任务,太扎眼了。在IDA静态分析时,看API引用,很容易看到压入任务的实现。
想动态载入API, 看看静态分析效果如何?
笔记
网上有大神开源了一个动态载入DLL的封装类(http://www.codeproject.com/KB/DLL/LateLoad.aspx)
以前也用过,只是用来方便动态调用DLL接口。
这次用,主要是看看用IDA静态分析时,动态载入的API接口是否容易识别?
测试工程
写个demo(vs2019 vc++ console), 转圈打印字符串. 如果圈数>10, 向控制台程序的窗口post消息,关闭控制台程序的窗口,从而达到关闭控制台程序的目的。
test_load_dll_then_call_api.cpp
//! \file test_load_dll_then_call_api.cpp#include <windows.h>
#include <stdlib.h>
#include <stdio.h>#include "CMyUser32Dll.h"int main()
{int i = 0;CMyUser32Dll myUser32Dll;do {Sleep(500);printf("[%d] runing\n", ++i);if (i > 10){// 发送消息,关掉控制台程序的窗口myUser32Dll.PostMessageW(GetConsoleWindow(), WM_CLOSE, 0, 0);}} while (true);return EXIT_SUCCESS;
}
CMyUser32Dll.h
//! \file CMyLoadDll.h#ifndef __C_MY_LOAD_DLL_H__
#define __C_MY_LOAD_DLL_H__#include "LateLoad.h"// User32.dll
// https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-postmessagea?redirectedfrom=MSDN&devlangs=cpp&f1url=%3FappId%3DDev16IDEF1%26l%3DZH-CN%26k%3Dk(WINUSER%252FPostMessage)%3Bk(PostMessage)%3Bk(DevLang-C%252B%252B)%3Bk(TargetOS-Windows)%26rd%3Dtrue
/*
BOOL PostMessageA([in, optional] HWND hWnd,[in] UINT Msg,[in] WPARAM wParam,[in] LPARAM lParam
);
*/
// https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-postmessagew
/*
BOOL PostMessageW([in, optional] HWND hWnd,[in] UINT Msg,[in] WPARAM wParam,[in] LPARAM lParam
);
*/LATELOAD_BEGIN_CLASS(CMyUser32Dll, TEXT("User32.dll"), FALSE, FALSE)
LATELOAD_FUNC_4(FALSE, BOOL, STDAPICALLTYPE, PostMessageW, HWND, UINT, WPARAM, LPARAM)
LATELOAD_END_CLASS()#endif // #ifndef __C_MY_LOAD_DLL_H__
CMyUser32Dll.cpp
//! \file CMyLoadDll.cpp#include "pch.h"
// #include "memOpt/my_debug_new_define.h"#include "CMyUser32Dll.h"
LateLoad.h
#ifndef __LATE_LOAD__H__
#define __LATE_LOAD__H__#if _MSC_VER > 1000
#pragma once
#endif#include <Windows.h>
#include <assert.h>/*Module : LateLoad.hPurpose: I was tired of constantly re-writing LoadLibrary & GetProcAddress code, over & over & over again. So I created a series of macros that will define & create a wrapper class to do it all for me.This is NOT an end-all-be-all solution for loading DLLs. This is just ahandy lightweight helper when you would use LoadLibrary/GetProcAddress.For more information on DLLs, check out MSDN.This was inspired by my own need, as well as a nice little MSDN article titled "DLLs the Dynamic Way" by "MicHael Galkovsky"http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndllpro/html/dlldynamic.aspUnfortunatly, there was no source to go with the article. So, I wrote this version quite a bit differently.Author : Copyright (c) 2004 by Jason De Arte, All Rights Reserved.Site : http://1001010.com/code/ License: Use it however you like as a compiled portion of your product(s). Don't be a jerk by claiming the it as your own.Usage : The following will declare the class "CUser32Wrapper" that will loadfunction pointer(s) from USER32.DLL. Those function names will become member function(s) of the newly created class.LATELOAD_BEGIN_CLASS(CUser32Wrapper,user32,FALSE)LATELOAD_FUNC_0(NULL,HCURSOR,WINAPI,GetCursor)LATELOAD_FUNC_1(NULL,HANDLE,WINAPI,NonExistantFunction,BOOL)LATELOAD_END_CLASS()// The above statements will auto-generate a class that functionaly equivalent to...class CUser32Wrapper : public CLateLoadBase{public:BOOL Is_GetCursor();HCURSOR WINAPI GetCursor();BOOL Is_NonExistantFunction();HANDLE WINAPI NonExistantFunction();};// And usage is even easierCUser32Wrapper uw;HCURSOR h = uw.GetCursor();// And for DLLs missing a function export, you can still use the member function!uw.NonExistantFunction(true);// It will just return the default non-existant value in the definition// To determine if the funtion was imported, use the "BOOL Is_<FunctionName>();" memberIs_NonExistantFunction();// If it exists, it will return TRUE. False if it does not exist-------------------------------------------Let's look at the various defines that are used...LATELOAD_BEGIN_CLASS(CUser32Wrapper,user32,FALSE)CUser32Wrapper - the class that will be createduser32 - the library that will be loaded FALSE - determines if "FreeLibrary()" will be called in the destructor.LATELOAD_FUNC_0(NULL,HCURSOR,WINAPI,GetCursor)NULL - if the ProcAddress was not loaded, this value is returned in the call to that funcHCURSOR - the return type for the functionWINAPI - function calling conventionGetCursor - the exported functionLATELOAD_FUNC_1 (up to _9)Identical to LATELOAD_FUNC_0, except it allows 1-9 parameters to the functionLATELOAD_FUNC_0_ (up to _9)Same as before, but for use with NULL return typesLATELOAD_END_CLASS()finalizes the class declaration-------------------------------------------History:2004.Feb.29.JED - Created
*///
// Used for keeping track
//
enum ImportedProcState
{ipsUnknown = 0, // No attempts to load the Proc have been made yetipsMissing, // GetProcAddress failed to find the exported functionipsAvailable, // the Proc is Ready & Available for useips_MAX
};#ifndef NO_VTABLE
# if _MSC_VER > 1000
# define NO_VTABLE __declspec(novtable)
# else
// 2004.Feb.29.JED - Note to self, find out what is appropriate under non-msvc compilers
//# define NO_VTABLE
# endif //_MSC_VER
#endif//
// Declaration for the utility base class, not intended for non-inherited use.
//
class NO_VTABLE CLateLoadBase
{//// Why no v-table?// 1) not needed// 2) reduces code size// 3) LATELOAD_BEGIN_CLASS calls ZeroMemory to blank out all the derived function // pointers & ImportedProcState memeber variables - in the process the vtable// will get blanked out.// 4) This class is not intended to be instantiated on its own.// 5) Makes it so that using this class on its own will cause an Access Violation//
protected:HMODULE m_module; // Handle to the DLLDWORD m_dwLoadLibError; // If there was an error from LoadLibraryBOOL m_bManaged; // FreeLibrary in the destructor?LPTSTR m_pszModule; // The name of the module, handy for first-use loading/****************************************************************************** NAME: * CLateLoadBase::dll_LoadLibrary* * DESCRIPTION: * Loads the dll. * *******************************************************************************/
protected:BOOL dll_LoadLibrary(LPCTSTR pszLibrary, BOOL bDoItNow){//// Make a record of the DLL name//if( !m_pszModule ){if( pszLibrary && *pszLibrary){int nLen = lstrlen(pszLibrary); m_pszModule = new TCHAR[nLen+2]; if( m_pszModule ) { ZeroMemory( m_pszModule, sizeof(TCHAR)*(nLen+2) ); lstrcpy(m_pszModule,pszLibrary); } else { assert(!"CLateLoadBase::dll_LoadLibrary - Unable to allocate memory for a string!"); }} else { assert(!"CLateLoadBase::dll_LoadLibrary - We need a valid pszLibrary string"); } }// Nothing to do?if( m_module )return TRUE;// Should we do this later?if( !bDoItNow )return FALSE;// Load the DLLm_dwLoadLibError = 0;m_module = ::LoadLibrary(pszLibrary);if( m_module )return TRUE;// Unable to load it, find out whym_dwLoadLibError = GetLastError();// We rely on the last error being not equal to zero on LoadLibrary failure.// So, in the off chance it IS zero, we set it to a non-zero value. // Tip: According to MSDN, Bit 29 in Get/SetLastError logic is reserved for Application // defined error codes. No system level error codes should have this bit set.if( !m_dwLoadLibError ){assert(!"Wow, that should NOT have happened! Could you tell JED how you did it?");m_dwLoadLibError = 0x20000000;}return FALSE;}/****************************************************************************** NAME: * CLateLoadBase::dll_GetProcAddress* * DESCRIPTION: * Loads the function pointer from the DLL* *******************************************************************************/
protected:FARPROC dll_GetProcAddress(LPCSTR pszFunctionName, ImportedProcState& ips){FARPROC pfn = NULL;ips = ipsUnknown;// Does the DLL still need to be loaded?if( !m_module && m_pszModule && *m_pszModule && 0 == m_dwLoadLibError // There is no reason to attempt to load the DLL more than once){dll_LoadLibrary(m_pszModule,TRUE);}if( m_module ){ pfn = ::GetProcAddress(m_module,pszFunctionName);if( pfn )ips = ipsAvailable;elseips = ipsMissing;}return pfn; }/****************************************************************************** NAME: * CLateLoadBase::~CLateLoadBase* * DESCRIPTION: * Description goes here...* *******************************************************************************/
public:~CLateLoadBase(){if( m_bManaged && m_module )::FreeLibrary(m_module);m_module = NULL;m_bManaged = FALSE;if( m_pszModule )delete m_pszModule;m_pszModule = NULL;}/****************************************************************************** NAME: * CLateLoadBase::dll_GetLoadError* * DESCRIPTION: * If LoadLibrary failed, returns the error code.* If there was no error, returns zero* *******************************************************************************/
public:DWORD dll_GetLoadError() {return m_dwLoadLibError; }/****************************************************************************** NAME: * CLateLoadBase::dll_IsLoaded* * DESCRIPTION: * Is the DLL loaded?* *******************************************************************************/
public:BOOL dll_IsLoaded() { return NULL!=m_module; }/****************************************************************************** NAME: * CLateLoadBase::dll_GetModuleName* * DESCRIPTION: * return the name of the module loaded* *******************************************************************************/
public:LPCTSTR dll_GetModuleName() { return (LPCTSTR)m_pszModule; }
};//--------------------------------------------------------------------------------------
//
// Start of macros
//
//--------------------------------------------------------------------------------------//
// Start, Declares the name of the class
//
// ClassName = the name of the class
// ModuleName = the DLL name
// bLoadDllNow = if true, the DLL will be loaded in the constructor.
// if false, it will ONLY be loaded when any bound function is first used
// bManaged = if true, FreeLibrary will be called in the destructor
//
#define LATELOAD_BEGIN_CLASS(ClassName,ModuleName,bLoadDllNow,bManaged) \
class ClassName : public CLateLoadBase \
{ \
public:\ClassName()\{\/*Automagicaly blank out all the function pointers and ImportedProcState member vars*/ \/*that will be declared in following LATELOAD_FUNC_* declarations, very handy. */ \ZeroMemory(static_cast<ClassName*>(this),sizeof(ClassName)); \m_bManaged = bManaged; \/*and load the DLL*/ \dll_LoadLibrary(ModuleName,bLoadDllNow); \}//
// Function Declaration, Zero Parameters, returns a value
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType, type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName, Name of the function
//
// A function prototype that looked like...
// typedef BOOL (CALLBACK* SOMETHING)();
// or
// BOOL CALLBACK Something();
//
// Would be changed to...
// LATELOAD_FUNC_0(0,BOOL,CALLBACK,Something)
//
// If "Something" could not be loaded, and it was called - it would return 0
//
#define LATELOAD_FUNC_0(ErrorResult,ReturnType,CallingConv,FuncName) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName() \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(); \}//
// Function Declaration, One Parameter, returns a value
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType, type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
// A function prototype that looked like...
// typedef BOOL (CALLBACK* SOMETHING)(BOOL);
// or
// BOOL CALLBACK Something(BOOL bEnable);
//
// Would be changed to...
// LATELOAD_FUNC_1(0,BOOL,CALLBACK,Something,BOOL)
//
// If "Something" could not be loaded, and it was called - it would return 0
//
#define LATELOAD_FUNC_1(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1); \}//
// Function Declaration, Two Parameters, returns a value
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType, type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
// A function prototype that looked like...
// typedef BOOL (CALLBACK* SOMETHING)(BOOL,INT);
// or
// BOOL CALLBACK Something(BOOL bEnable, INT nNum);
//
// Would be changed to...
// LATELOAD_FUNC_2(0,BOOL,CALLBACK,Something,BOOL,INT)
//
// If "Something" could not be loaded, and it was called - it would return 0
//
#define LATELOAD_FUNC_2(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2); \}//
// Function Declaration, Three Parameters, returns a value
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType, type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
#define LATELOAD_FUNC_3(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2,ParamType3) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2,p3); \}//
// Function Declaration, Four Parameters, returns a value
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType, type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
#define LATELOAD_FUNC_4(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2,p3,p4); \}//
// Function Declaration, Five Parameters, returns a value
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType, type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
#define LATELOAD_FUNC_5(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2,p3,p4,p5); \}//
// Function Declaration, Six Parameters, returns a value
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType, type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
#define LATELOAD_FUNC_6(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2,p3,p4,p5,p6); \}//
// Function Declaration, Seven Parameters, returns a value
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType, type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
#define LATELOAD_FUNC_7(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6,ParamType7 p7) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2,p3,p4,p5,p6,p7); \}//
// Function Declaration, Eight Parameters, returns a value
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType, type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
#define LATELOAD_FUNC_8(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6,ParamType7 p7,ParamType8 p8) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2,p3,p4,p5,p6,p7,p8); \}//
// Function Declaration, Nine Parameters, returns a value
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType, type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
#define LATELOAD_FUNC_9(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8,ParamType9) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8,ParamType9); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6,ParamType7 p7,ParamType8 p8,ParamType9 p9) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2,p3,p4,p5,p6,p7,p8,p9); \}//
// Function Declaration, Zero Parameters, returns nothing
//
// CallingConv, Calling convention of the function
// FuncName, Name of the function
//
// A function prototype that looked like...
// typedef VOID (CALLBACK* SOMETHING)();
// or
// VOID CALLBACK Something();
//
// Would be changed to...
// LATELOAD_FUNC_0_VOID(CALLBACK,Something)
//
#define LATELOAD_FUNC_0_VOID(CallingConv,FuncName) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName() \{\if( Is_##FuncName() ) \m_pf##FuncName(); \}//
// Function Declaration, One Parameters, returns nothing
//
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
// A function prototype that looked like...
// typedef VOID (CALLBACK* SOMETHING)(BOOL);
// or
// VOID CALLBACK Something(BOOL bEnable);
//
// Would be changed to...
// LATELOAD_FUNC_1_VOID(CALLBACK,Something,BOOL)
//
#define LATELOAD_FUNC_1_VOID(CallingConv,FuncName,ParamType1) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1); \}//
// Function Declaration, Two Parameters, returns nothing
//
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
// A function prototype that looked like...
// typedef VOID (CALLBACK* SOMETHING)(BOOL,INT);
// or
// VOID CALLBACK Something(BOOL bEnable, INT nNumber);
//
// Would be changed to...
// LATELOAD_FUNC_2_VOID(CALLBACK,Something,BOOL,INT)
//
#define LATELOAD_FUNC_2_VOID(CallingConv,FuncName,ParamType1,ParamType2) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2); \}//
// Function Declaration, Three Parameters, returns nothing
//
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
#define LATELOAD_FUNC_3_VOID(CallingConv,FuncName,ParamType1,ParamType2,ParamType3) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2,p3); \}//
// Function Declaration, Four Parameters, returns nothing
//
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
#define LATELOAD_FUNC_4_VOID(CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2,p3,p4); \}//
// Function Declaration, Five Parameters, returns nothing
//
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
#define LATELOAD_FUNC_5_VOID(CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2,p3,p4,p5); \}//
// Function Declaration, Six Parameters, returns nothing
//
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
#define LATELOAD_FUNC_6_VOID(CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2,p3,p4,p5,p6); \}//
// Function Declaration, Seven Parameters, returns nothing
//
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
#define LATELOAD_FUNC_7_VOID(CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6,ParamType7 p7) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2,p3,p4,p5,p6,p7); \}//
// Function Declaration, Eight Parameters, returns nothing
//
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
#define LATELOAD_FUNC_8_VOID(CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6,ParamType7 p7,ParamType8 p8) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2,p3,p4,p5,p6,p7,p8); \}//
// Function Declaration, Nine Parameters, returns nothing
//
// CallingConv, Calling convention of the function
// FuncName, Name of the function
// ParamN types of the function parameters
//
#define LATELOAD_FUNC_9_VOID(CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8,ParamType9) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8,ParamType9); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6,ParamType7 p7,ParamType8 p8,ParamType9) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2,p3,p4,p5,p6,p7,p8,p9); \}//
// End of the class
//
#define LATELOAD_END_CLASS() };#endif //__LATE_LOAD__H__
IDA静态分析
引入表中没有PostMessageW
字符串查找能找到PostMessageW
可以顺着字符串,找到动态载入API的实现。
rcx是类指针this
[rcx + 0x18]就是保存PostMessageW函数地址的指针变量
在IDA中搜索文本18h, 就能找到动态调用postMessageW的调用点。
如果程序大了,想知道API的动态调用点,应该是挺不好找的。
如果将字符串编码后,需要用到的时候,再解码来用,那么调试者就只能动态调试才能找到思路了。
如果这样的话,唯一不能隐藏的就是LoadLibary和GetProcAddress。剩下2这个API无所谓,因为壳程序人家也是留下这2个API的,如果人家愿意调试你的程序,也挡不住人家。
备注
如果在程序中动态载入API, 可以给IDA静态分析增加一点麻烦。
虽然麻烦增加的不多,但是有点用。
看看CMyUser32Dll.h编译完的样子
CMyUser32Dll.h是包含在main.cpp中,进行宏展的。
编译main.cpp, 就能看到CMyUser32Dll.h中宏展开的样子。
整理 test_load_dll_then_call_api.i
// test_load_dll_then_call_api.cpp的宏展开的整理enum ImportedProcState
{ipsUnknown = 0, ipsMissing, ipsAvailable, ips_MAX
};class __declspec(novtable) CLateLoadBase
{
protected:HMODULE m_module; DWORD m_dwLoadLibError; BOOL m_bManaged; LPTSTR m_pszModule; protected:BOOL dll_LoadLibrary(LPCTSTR pszLibrary, BOOL bDoItNow){if( !m_pszModule ){if( pszLibrary && *pszLibrary){int nLen = lstrlenW(pszLibrary); m_pszModule = new TCHAR[nLen+2]; if( m_pszModule ) { memset((m_pszModule),0,(sizeof(TCHAR)*(nLen+2))); lstrcpyW(m_pszModule,pszLibrary); } else { assert(false);}} else { assert(false);} }if( m_module )return 1;if( !bDoItNow )return 0;m_dwLoadLibError = 0;m_module = ::LoadLibraryW(pszLibrary);if( m_module )return 1;m_dwLoadLibError = GetLastError();if( !m_dwLoadLibError ){assert(false);m_dwLoadLibError = 0x20000000;}return 0;}protected:FARPROC dll_GetProcAddress(LPCSTR pszFunctionName, ImportedProcState& ips){FARPROC pfn = 0;ips = ipsUnknown;if( !m_module && m_pszModule && *m_pszModule && 0 == m_dwLoadLibError ){dll_LoadLibrary(m_pszModule,1);}if( m_module ){ pfn = ::GetProcAddress(m_module,pszFunctionName);if( pfn )ips = ipsAvailable;elseips = ipsMissing;}return pfn; }
public:~CLateLoadBase(){if( m_bManaged && m_module )::FreeLibrary(m_module);m_module = 0;m_bManaged = 0;if( m_pszModule )delete m_pszModule;m_pszModule = 0;}public:DWORD dll_GetLoadError() {return m_dwLoadLibError; }public:BOOL dll_IsLoaded() { return 0!=m_module; }public:LPCTSTR dll_GetModuleName() { return (LPCTSTR)m_pszModule; }
};class CMyUser32Dll : public CLateLoadBase
{
public: CMyUser32Dll() { memset((static_cast<CMyUser32Dll*>(this)),0,(sizeof(CMyUser32Dll))); m_bManaged = 0; dll_LoadLibrary(L"User32.dll",0); }protected: typedef BOOL(__stdcall * TYPE_PostMessageW)(HWND,UINT,WPARAM,LPARAM); TYPE_PostMessageW m_pfPostMessageW; ImportedProcState m_ipsPostMessageW;
public: BOOL Is_PostMessageW(){ if(ipsUnknown == m_ipsPostMessageW) m_pfPostMessageW = (TYPE_PostMessageW)dll_GetProcAddress("PostMessageW", m_ipsPostMessageW); return(ipsAvailable == m_ipsPostMessageW); } BOOL PostMessageW(HWND p1,UINT p2,WPARAM p3,LPARAM p4) { if( !Is_PostMessageW() )return 0; return m_pfPostMessageW(p1,p2,p3,p4); }
};int main()
{int i = 0;CMyUser32Dll myUser32Dll;do {Sleep(500);printf("[%d] runing\n", ++i);if (i > 10){myUser32Dll.PostMessageW(GetConsoleWindow(), 0x0010, 0, 0);}} while (true);return 0;
}
备注 - debug版不生成调试信息
VS2019工程的debug版默认是生成.pdb的, 如果到了发行阶段,又不想生成release版(以前听说过,有的软件发行时,如果生成release版就会报错,厂家又解决不了,只能以debug版来发行),那么可以在编译选项中去掉调试信息,这样就不会生成pdb了。
debug版即使不生成调试信息,也可能带入一些和工程目录相关的字符串
这些和工程目录相关的字符串的引入,看IDA的字符串引用,是和debug版的new, assert等用到了__FILE__, __LINE__有关系。
解决方法1
将工程目录模拟成磁盘,在磁盘根目录下生成发布版(即使是debug), 这样,在工程中留下的字符串,就比较短。也不那么敏感。只和工程本身相关。如果在客户那里出了问题,就能看到断言,用户截个图就大概知道问题在哪里。
解决方法2
release版,但是不优化。
如果真的是程序有问题,只能在debug版下运行不报错,只要release版不优化,这样大概也是不会出问题的。
如果程序真的有问题,还是将bug从根上搞掉才是解决问题的态度。
release版默认虽然没有DEBUG预处理宏,但是还是会生成调试信息,所有也要去掉调试信息才靠谱。
看release版不优化(也不带调试信息)的EXE, 已经看不到和工程相关的路径和文件信息了。
看到有个和工程所在盘符相关的x:字符串,去看了一下,并没有引用关系。
备注
用动态载入DLL接口的方法,将IsDebuggerPresent动态来用,能隐蔽一些。