在自己品读了kanxue大哥的加密解密之后,不禁想在论坛上写篇这样的文章.小弟新手,高手前辈请别取笑小弟。
大伙都知道,在windows下当一个可执行文件运行时,Windows加载器将可执行模块映射到进程的地址空间中,加载器分析可执行模块的输入表,并设法找出任何需要的DLL,并将它们映射到进程的地址空间中。由于输入表是根据DLL名来进行查找,首先是查找当前目录下有没这文件,没有话在去查找系统目录C:\\Windows\\system32有没这文件,所以我们可以完全趁这个机会去劫持DLL,把他劫持下来后就可以在里面进行我们要做的事情。例如:
1.//补丁前,程序运行方式如下:
2..//补丁后,程序运行方式如下
为了让我们自己开发的补丁DLL有导出函数,我们可以用 #parma comment(..),如:
#pragma comment(linker, "/EXPORT:testadd=_TESTDLLLIB_testadd,@1")
我写了个DLL,分别是补丁前和补丁后,我把代码贴出来。
1.补丁前DLL。
//tstDll.dll 补丁前
//By symanli
#include "stdafx.h"
#include "dll.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
//原始函数
int testadd(int a,int b)
{
::MessageBox(NULL,"这是补丁前的DLL","TODO",MB_OK);
return a+b;
}
2.补丁后
//这是补丁代码。by symanli
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 头文件
#include <Windows.h>
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 宏定义
#define EXTERNC extern "C"
#define NAKED __declspec(naked)
#define EXPORT __declspec(dllexport)
#define ALCPP EXPORT NAKED
#define ALSTD EXTERNC EXPORT NAKED void __stdcall
#define ALCFAST EXTERNC EXPORT NAKED void __fastcall
#define ALCDECL EXTERNC NAKED void __cdecl
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TESTDLLLIB 命名空间
namespace TESTDLLLIB
{
HMODULE m_hModule = NULL; // 原始模块句柄
DWORD m_dwReturn[1] = {0}; // 原始函数返回地址
// 加载原始模块
inline BOOL WINAPI Load()
{
TCHAR tzPath[MAX_PATH];
TCHAR tzTemp[MAX_PATH * 2];
//将原始DLL名改成其他的名字,我们用LoadLibrary去加载他。
lstrcpy(tzPath, TEXT("tstDllold.dll"));
m_hModule = LoadLibrary(tzPath);
if (m_hModule == NULL)
{
wsprintf(tzTemp, TEXT("无法加载 %s,程序无法正常运行。"), tzPath);
MessageBox(NULL, tzTemp, TEXT("AheadLib"), MB_ICONSTOP);
}
return (m_hModule != NULL);
}
// 释放原始模块
inline VOID WINAPI Free()
{
if (m_hModule)
{
FreeLibrary(m_hModule);
}
}
// 获取原始函数地址
FARPROC WINAPI GetAddress(PCSTR pszProcName)
{
FARPROC fpAddress;
CHAR szProcName[16];
TCHAR tzTemp[MAX_PATH];
fpAddress = GetProcAddress(m_hModule, pszProcName);
if (fpAddress == NULL)
{
if (HIWORD(pszProcName) == 0)
{
wsprintf(szProcName, "%d", pszProcName);
pszProcName = szProcName;
}
wsprintf(tzTemp, TEXT("无法找到函数 %hs,程序无法正常运行。"), pszProcName);
MessageBox(NULL, tzTemp, TEXT("AheadLib"), MB_ICONSTOP);
ExitProcess(-2);
}
return fpAddress;
}
}
using namespace TESTDLLLIB;
// 入口函数
BOOL WINAPI DllMain(HMODULE hModule, DWORD dwReason, PVOID pvReserved)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
DisableThreadLibraryCalls(hModule);
for (INT i = 0; i < sizeof(m_dwReturn) / sizeof(DWORD); i++)
{
m_dwReturn[i] = TlsAlloc();
}
return Load();
}
else if (dwReason == DLL_PROCESS_DETACH)
{
for (INT i = 0; i < sizeof(m_dwReturn) / sizeof(DWORD); i++)
{
TlsFree(m_dwReturn[i]);
}
Free();
}
return TRUE;
}
// 导出函数
ALCDECL TESTDLLLIB_testadd(void)
{
::MessageBox(NULL,"这是补丁后的DLL","TODO",MB_OK);
// 保存返回地址到 TLS
//__asm PUSH m_dwReturn[0 * TYPE long];
//__asm CALL DWORD PTR [TlsSetValue];
// 调用原始函数
GetAddress("testadd");
__asm JMP EAX;
// 获取返回地址并返回
// __asm PUSH EAX;
// __asm PUSH m_dwReturn[0 * TYPE long];
// __asm CALL DWORD PTR [TlsGetValue];
// __asm XCHG EAX, [ESP];
// __asm RET;
}
#pragma comment(linker, "/EXPORT:testadd=_TESTDLLLIB_testadd,@1")
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
3.exe调用代码
//exe调用代码
#pragma comment(lib,"tstDll.lib")//导入先前DLL的lib,就是最原始的LIB
void CTstExeDlg::OnButton1()
{
// TODO: Add your control notification handler code here
int _sum =testadd(10,20);
CString szSum;
szSum.Format("结果是:[%d]",_sum);
AfxMessageBox(szSum);
}
4.看看结果
1.补丁前:
2.补丁后:
别的我就不多了。我写了个例子程序,可以下载来研究下。大家通过这种原理可以做出类似劫持其他应用程序的程序
点击下载例子:
DLL.rar
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)