C运行时库

MS提供的C运行时库实在是多,今天又遇到了一个crtdll.dll竟然也是C运行时库,于是各种查资料,这里整理一下。

参考资料一:C Run-Time Libraries
1、libc(d).lib单线程的静态库,已经不可用了。
2、libcmt(d).lib多线程的静态库,编译选项MT(d),影响预定义_MT(_MT,_DEBUG)。
3、msvcrt(d).lib多线程的动态库,编译选项MD(d),影响预定义_MT,_DLL(_MT,_DLL,_DEBUG)。动态链接库名一般形如msvcr80.dll,其中80代表版本号,不过也看到过msvcrt.dll,估计是历史产物。
4、msvcmrt.lib用于托管代码和本地代码混合使用。
5、msvcurt.lib纯MSIL code编译而成。
6、crtdll.lib支持多线程,并且支持Win32s,动态链接库名为crtdll.dll,估计也是历史产物了。参考资料二:How To Use the C Run-Time

一个模块静态链接C运行时库意味着该C运行时库信息的存储就在这个模块里,这会导致同一进程内的不同模块的C运行时库函数不能相互影响。例如在一个静态链接CRT的DLL中调用_set_se_translator只能捕获该模块产生的异常。

How To Share All Data in a DLL

error C2440: ‘initializing’ : cannot convert from ‘PCTSTR’ to ‘ATL::CStringT’

CAtlString是可以在ANSI和UNICODE之间转换的,例如CAtlStringA strSomething = lpszSomething,这里的lpszSomething是wchar_t字符串类型。

可是你会发现这种语句有时候可以编过,有时候又不可以编过,非要用CAtlStringA strSomething(lpszSomething):
error C2440: 'initializing' : cannot convert from 'PCTSTR' to 'ATL::CStringT<BaseType,StringTraits>'
with
[
BaseType=char,
StringTraits=ATL::StrTraitATL<char,ATL::ChTraitsCRT<char>>
]
Constructor for class 'ATL::CStringT<BaseType,StringTraits>' is declared 'explicit'
with
[
BaseType=char,
StringTraits=ATL::StrTraitATL<char,ATL::ChTraitsCRT<char>>
]

遇到这种问题真是抑郁,明知是2B的问题却不明真相,每次遇到只能退缩改为显式构造。

这次深究了一下,CAtlString的实现会落在CStringT上,CStringT的构造函数对于YCHAR(即另一种字符类型,CStringT把自身的字符类型定义为XCHAR)作参数如下:
CSTRING_EXPLICIT CStringT( __in_z_opt const YCHAR* pszSrc )

注意这里有个CSTRING_EXPLICIT宏,它被定义为:
#ifdef _ATL_CSTRING_EXPLICIT_CONSTRUCTORS
#define CSTRING_EXPLICIT explicit
#else
#define CSTRING_EXPLICIT
#endif

原来是有_ATL_CSTRING_EXPLICIT_CONSTRUCTORS宏定义的时候不同类型的字符串转换需要显式调用构造函数,看看stdafx.h里还真有这个宏定义:
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // some CString constructors will be explicit

删掉这个宏定义就能使用CAtlStringA strSomething = lpszSomething这类用法了。为什么需要这么一个宏去控制这类构造函数需要显式调用?

MS说得很清楚了,防止无心的字符串转换。_ATL_CSTRING_EXPLICIT_CONSTRUCTORS

Inline Hook

Inline Hook也是光听说过没练过,今天自己动手还是遇到了不少问题,主要是替换后的函数如何调用原函数和平衡其调用堆栈。Demo里没有实现如何先执行被Inline Hook覆盖的代码后跳转到原函数的流程,以后实现后补充。

#include <Windows.h>
#include <tchar.h>

//////////////////////////////////////////////////////////////////////////
// Inline Hook
#define JMP_CODE_SIZE 5
#pragma warning(push)
#pragma warning(disable:4311)
BOOL InlineHook(void* lpOldFunction, void* lpNewFunction)
{
BOOL bRet = FALSE;
byte btCode[JMP_CODE_SIZE] = {0xE9}; // 5字节相对跳转指令
do
{
if(NULL == lpOldFunction || NULL == lpNewFunction)
{
break;
}

DWORD* pdwAddr = (DWORD*)&btCode[1];
*pdwAddr = (DWORD)lpNewFunction – (DWORD)lpOldFunction – JMP_CODE_SIZE; // 计算相对跳转地址

DWORD dwProtect = 0;
if(VirtualProtect(lpOldFunction, JMP_CODE_SIZE, PAGE_READWRITE, &dwProtect))
{
memcpy(lpOldFunction, btCode, JMP_CODE_SIZE);
VirtualProtect(lpOldFunction, JMP_CODE_SIZE, dwProtect, &dwProtect);
bRet = TRUE;
}
} while (0);
return bRet;
}
#pragma warning(pop)

// 用于恢复Inline Hook
void* g_lpMessageBoxA = NULL;
byte g_btCode[JMP_CODE_SIZE] = {0};

//////////////////////////////////////////////////////////////////////////
// 真正实现MessageBoxA, MyMessageBoxA只是一个马甲
int WINAPI InternalMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
MessageBoxW(NULL, L”In MyMessageBoxA”, L”InlineHook”, MB_OK);

// 还原Inline Hook
DWORD dwProtect = 0;
if(VirtualProtect(g_lpMessageBoxA, JMP_CODE_SIZE, PAGE_READWRITE, &dwProtect))
{
memcpy(g_lpMessageBoxA, g_btCode, JMP_CODE_SIZE);
VirtualProtect(g_lpMessageBoxA, JMP_CODE_SIZE, dwProtect, &dwProtect);
}

// 调回真正的MessageBoxA
return MessageBoxA(hWnd, lpText, lpCaption, uType);
}

//////////////////////////////////////////////////////////////////////////
// Inline Hook用MyMessageBoxA替换MessageBoxA
// 编译器生成代码时会在函数前后增加一些堆栈平衡的代码, 导致直接ret 10h堆栈并不会平衡
// 因此需要使用_declspec(naked)修饰, 但函数内需要自己控制寄存器
_declspec(naked) int WINAPI MyMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType)
{
// 堆栈平衡
_asm
{
push ebp
mov ebp, esp
push dword ptr[ebp+0x14]
push dword ptr[ebp+0x10]
push dword ptr[ebp+0xC]
push dword ptr[ebp+0x8]
call InternalMessageBoxA
pop ebp
ret 10h ; 由于没有jmp回被Inline Hook的函数, 因此需要平衡堆栈.
}
}

int _tmain(int argc, TCHAR* argv[])
{
HMODULE hLib = LoadLibrary(_T(“user32.dll”));
if(hLib != NULL)
{
g_lpMessageBoxA = GetProcAddress(hLib, “MessageBoxA”);
memcpy(g_btCode, g_lpMessageBoxA, JMP_CODE_SIZE); // 保存即将覆盖的5字节代码
InlineHook(g_lpMessageBoxA, MyMessageBoxA); // Inline Hook MessageBoxA到MyMessageBoxA
MessageBoxA(NULL, “Real MessageBoxA”, “InlineHook”, MB_OK);
FreeLibrary(hLib);
}
return 0;
}

IAT Hook

IAT Hook早了解过了一直没有自己动手实现过,写了个demo却也花了不少时间,记下来备用。

#include <Windows.h>
#include <tchar.h>

//////////////////////////////////////////////////////////////////////////
// 用lpNewFunction替换hModule中导入的lpImageName的lpOldFunction
BOOL IATHook(HMODULE hModule, char* lpImageName, void* lpOldFunction, void* lpNewFunction)
{
BOOL bRet = FALSE;
do
{
if(NULL == hModule || NULL == lpImageName || NULL == lpOldFunction || NULL == lpNewFunction)
{
break;
}

PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
if(pDosHeader->e_magic != IMAGE_DOS_SIGNATURE || pDosHeader->e_lfanew <= 0)
{
break;
}

PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + (DWORD_PTR)hModule);
if(pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
{
break;
}

// 从导入表中找到lpImageName的IMAGE_THUNK_DATA
PIMAGE_IMPORT_DESCRIPTOR pDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)(pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + (DWORD_PTR)hModule);
PIMAGE_THUNK_DATA pIATThunk = NULL;
while(pDescriptor->Characteristics != 0)
{
char* lpszName = (char*)(pDescriptor->Name + (DWORD_PTR)hModule);
if(0 == _stricmp(lpszName, lpImageName))
{
pIATThunk = (PIMAGE_THUNK_DATA)(pDescriptor->FirstThunk + (DWORD_PTR)hModule);
break;
}
++pDescriptor;
}

// 从IMAGE_THUNK_DATA找到需要替换的具体函数
if(NULL == pIATThunk)
{
break;
}
while(pIATThunk->u1.Function != 0)
{
if(pIATThunk->u1.Function == (DWORD_PTR)lpOldFunction)
{
DWORD dwProtect = 0;
if(VirtualProtect(&(pIATThunk->u1.Function), sizeof(DWORD), PAGE_READWRITE, &dwProtect))
{
bRet = WriteProcessMemory((HANDLE)-1, &(pIATThunk->u1.Function), &lpNewFunction, sizeof(DWORD), NULL);
VirtualProtect(&(pIATThunk->u1.Function), sizeof(DWORD), dwProtect, &dwProtect);
}
break;
}
++pIATThunk;
}
} while (0);
return bRet;
}

//////////////////////////////////////////////////////////////////////////
// 用MyMessageBox替换MessageBoxW
typedef int (WINAPI *MESSAGE_BOX)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType);
MESSAGE_BOX RealMessageBox = NULL;
int WINAPI MyMessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType)
{
int nRet = 0;
if(RealMessageBox != NULL)
{
RealMessageBox(NULL, _T(“In MyMessageBox”), _T(“IATHook”), MB_OK);
nRet = RealMessageBox(hWnd, lpText, lpCaption, uType);
}
return nRet;
}

int _tmain(int argc, TCHAR* argv[])
{
HMODULE hLib = LoadLibrary(_T(“user32.dll”));
if(hLib != NULL)
{
RealMessageBox = (MESSAGE_BOX)GetProcAddress(hLib, “MessageBoxW”);            // 需要IAT Hook的函数地址, 用于在IAT中查找并替换
IATHook(GetModuleHandle(NULL), “user32.dll”, RealMessageBox, MyMessageBox);    // IAT Hook本exe模块中的MessageBoxW
MessageBox(NULL, _T(“Real MessageBox”), _T(“IATHook”), MB_OK);
FreeLibrary(hLib);
}
return 0;
}

再见,fly。

周五例会听到fly不再回来的消息心里猛然一沉,今天又看到fly的邮件心里又是一阵难过,一种从此天涯难相见的难过。

再见,fly。或许我们永远不会再见,但感谢你曾出现在我的生命里,带给我一段关于你的记忆,带给我一些关于你的情绪。

在你身上我似乎看到了另一个我,善良本分的性格,谦卑的态度以及任劳任怨的工作。开会、讨论时你总在一旁默默地听,吃饭路上你总走在最后,却在工作中不断地“收拾烂摊子”和“救火”。看到你就像看到当初初进社会的我。

犹记得一起上沟通与性格的课,我们性格上的相似让我对此记忆深刻,以至于后来多次和你提起。

记得吃饭时你的笑话、你的故事和你的猫。

记得你的一句“不能让好人吃亏”硬生生地把我请你们吃KFC的钱塞还给我。我不要一是那真是我请大家,二是其实我想说“你也是好人”。好在最后一顿饭钱我没忘了给你,但遗憾那天晚上没陪你们回公司游戏。我以为我们还能相见,或许只能期待重逢。

记得欠你一个使命召唤OL内测号,希望你能记得找我要。还有很多……

听说你会远离IT这个行业,回到家乡,回到父母身边,过着平淡的日子,真心为你感到幸福。或许某一天你会记起这段在TX的日子,希望你感受到的不是工作的鸭梨山大,而是经历过的淡淡温馨,也希望你能记得V5的我。

再见,fly。保重,fly。

继续阅读“再见,fly。”