Detours
Detours是经过微软认证的一个开源软件,下载地址,虽然是开源软件,但是它又分成了两个版本,分别是:
- Detours Professional 3.0 is available for commercial use.
- Detours Express 3.0 is available for immediate download under a no-fee license for research, non-commercial, and non-production use. Detours Express may be used to prototype (but not deploy) commercial projects.
这两个版本的代码是一模一样的,所以如果你的公司稍微大一些的话,就不要使用Detours Professional 3.0版本,以免引来不必要的官司。
Detours编译生成对应的库
这个库实际上就做一件事情,那就是注入的事情。
下面我们来看一下如何使用这个开源库。下载回来的文件目录结构如下:
因为3.0版本支持32位和64位同时挂钩,所以我们可以进行32位的编译和64位的编译。首先我们来看如何编译32位库。
首先找到编译工具,如下所示:
打开后界面如下:
编译界面如下:
对于64位编译,找到64位的编译工具,编译完成界面如下所示:
最后就会生成对应的32位库和64位的库,它们的头文件都一样,如下图所示:
Detours库的使用
首先将lib库和include头文件拷贝到工程里面,然后就可以直接使用了。
// EasyHook.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <Windows.h>
#include "../Detours/include/detours.h"
#pragma comment(lib, "../Detours/lib.X86/detours.lib")
static int (WINAPI *OldMessageBoxW)(HWND, LPCWSTR, LPCWSTR, UINT) = MessageBoxW;
int WINAPI NewMessageBoxW(_In_opt_ HWND hWnd, _In_opt_ LPCWSTR lpText, _In_opt_ LPCWSTR lpCaption, _In_ UINT uType)
{
return OldMessageBoxW(hWnd, L"经过修改的MessageBox", lpCaption, uType);
}
void Hook()
{
int error = DetourTransactionBegin();
if (error != NO_ERROR)
{
printf("DetourTransactionBegin Error\r\n");
}
error = DetourUpdateThread(GetCurrentThread());
if (error != NO_ERROR)
{
printf("DetourUpdateThread Error\r\n");
}
error = DetourAttach(&(PVOID&)OldMessageBoxW, NewMessageBoxW);
if (error != NO_ERROR)
{
printf("DetourAttach Error\r\n");
}
error = DetourTransactionCommit();
if (error != NO_ERROR)
{
printf("DetourTransactionCommit Error\r\n");
}
}
int main()
{
MessageBoxW(nullptr, L"Test", L"Tips", MB_OK);
Hook();
MessageBoxW(nullptr, L"Test", L"Tips", MB_OK);
system("pause");
return 0;
}
VS2015下简单的反汇编过程查看
我们说这个库与我们前面实现的挂钩方式不同,那么不同点在哪儿呢?我们使用反汇编调试一下:
从图上可以看出,我们可以看出在调用Hook函数的前后,我们的MessageBoxW函数的地址是没有发生变化的,那么它是怎么实现挂钩呢?
先别急我们再继续往下走,猜想可能的区别就是在于MessageBoxW函数里面的代码不一样了。首先我们来看一下调用Hook函数之前的MessageBoxW的汇编代码是什么样子的!!!如下图所示:
我们再看一下调用Hook函数之后的MessageBoxW函数的汇编代码
细心的朋友会马上发现,这不是第一行代码就直接跳转到我们自己的函数了吗?
下面是我们自己函数的汇编代码:
我们用vs2015就先调试到这儿,先让大家对这个库的原理有一个初步的了解。
x64dbg反汇编软件的反汇编过程查看
下面我们将使用x64dbg反汇编软件进行一步一步的跟踪我们的代码。(对于这个工具使用就不再说明了,感兴趣的朋友可以去了解一下。)
首先,我们找到main函数的反汇编代码,为了简单起见,我们直接在第二个MessageBoxW函数上下一个断点,如下图所示:
其次,我们运行到断点处
进入第二个MessageBoxW函数,如下:
从上图中我们看到第一行代码是一个跳转指令,正好跳转到我们自己的函数,我们就再进入我们自己的函数,如下图所示:
里面有一个call指令,调用OldMessageBoxW函数,我们再进入
上图就是这个Hook库的关键代码了,我们用方框圈起来。我们可以看出,总共有5个字节,这5个字节正好是1个字节的jmp加上4个字节的地址。这就是著名的5字节inline Hook!。
剩下的就不详细说明了。
为了能够更好的查看调用Hook的挂钩过程,我们使用源代码的方式进行跟踪一边。
Hook库源代码的挂钩过程跟踪
使用源码的好处就是我们能够跟踪到源码中,看看源码是怎么实现的。工程结构和代码如下图所示:
实际上我们在了解一个新的库的时候,如果有文档的话,看文档和demo是最好的学习方式,恰好Detours给了我们详细的文档和示例代码。这里我们就不多做说明了。
简单的反调试
很多时候,我们不希望别人对我们的程序进行调试,尤其是修改里面的一些功能,这里我们就以Win32程序为例。
// CheckMe.cpp : 定义应用程序的入口点。
//
#include "stdafx.h"
#include "CheckMe.h"
#include <process.h>
#define MAX_LOADSTRING 100
// 全局变量:
HINSTANCE hInst; // 当前实例
WCHAR szTitle[MAX_LOADSTRING]; // 标题栏文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口类名
// 此代码模块中包含的函数的前向声明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
unsigned int __stdcall CheckMeThread(void *param)
{
while (true)
{
DWORD dwPrevTime = GetTickCount();
Sleep(1000);
if (GetTickCount() - dwPrevTime > 2000)
{
ExitProcess(-1);
}
}
return 0;
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此放置代码。
CloseHandle((HANDLE)_beginthreadex(nullptr, 0, CheckMeThread, nullptr, 0, nullptr));
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_CHECKME, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 执行应用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_CHECKME));
MSG msg;
// 主消息循环:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// 函数: MyRegisterClass()
//
// 目的: 注册窗口类。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_CHECKME));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_CHECKME);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// 函数: InitInstance(HINSTANCE, int)
//
// 目的: 保存实例句柄并创建主窗口
//
// 注释:
//
// 在此函数中,我们在全局变量中保存实例句柄并
// 创建和显示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 将实例句柄存储在全局变量中
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// 函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目的: 处理主窗口的消息。
//
// WM_COMMAND - 处理应用程序菜单
// WM_PAINT - 绘制主窗口
// WM_DESTROY - 发送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜单选择:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此处添加使用 hdc 的任何绘图代码...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// “关于”框的消息处理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
这样对于一个新手来说,他使用OD来调试这个程序就很难达到目的了。
那么难道我们没有其它办法来对这样的程序进行调试吗?答案是有的。
因为我们知道这个程序使用的是GetTickCount函数进行的检查,那么我们就可以将这个函数Hook住,来达到反反调试的目的。
反反调试
利用Hook库进行反反调试的演示。
上图显示了我们要用到的三个工程:
其中CheckMe使我们要反反调试的一个对象,也就是上面的代码,它增加了一个线程来检测是否被调试,如果发现被调试,那么就直接结束进程。
HookTickCount是一个Dll程序,用来挂钩的
HookGetTickCountLoad是用来将HookTickCount的Dll程序注入到CheckMe程序中。下面我们来看HookTickCount的源代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include <Windows.h>
#include "../Detours/include/detours.h"
#pragma comment(lib, "../Detours/lib.X86/detours.lib")
static DWORD (WINAPI *Old_GetTickCount)(VOID) = GetTickCount;
DWORD WINAPI New_GetTickCount(VOID)
{
return 0;
}
extern "C" _declspec(dllexport) void Hook()
{
// 使用Hook库,必须至少有一个导出函数来帮助做一些事情,否则就会注入失败。
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if (DetourIsHelperProcess())
{
return TRUE;
}
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)Old_GetTickCount, New_GetTickCount);
DetourTransactionCommit();
}
else if (ul_reason_for_call == DLL_PROCESS_DETACH)
{
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)Old_GetTickCount, New_GetTickCount);
DetourTransactionCommit();
}
return TRUE;
}
HookGetTickCountLoad程序实现:
//////////////////////////////////////////////////////////////////////////////
//
// Test DetourCreateProcessWithDll function (withdll.cpp).
//
// Microsoft Research Detours Package, Version 3.0.
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#include <stdio.h>
#include <windows.h>
#include "../Detours/include/detours.h"
#pragma comment(lib, "../Detours/lib.X86/detours.lib")
#pragma warning(push)
#if _MSC_VER > 1400
#pragma warning(disable:6102 6103) // /analyze warnings
#endif
#include <strsafe.h>
#pragma warning(pop)
//////////////////////////////////////////////////////////////////////////////
//
void PrintUsage(void)
{
printf("Usage:\n"
" withdll.exe [options] [command line]\n"
"Options:\n"
" /d:file.dll : Start the process with file.dll.\n"
" /v : Verbose, display memory at start.\n"
" /? : This help screen.\n");
}
//////////////////////////////////////////////////////////////////////////////
//
// This code verifies that the named DLL has been configured correctly
// to be imported into the target process. DLLs must export a function with
// ordinal #1 so that the import table touch-up magic works.
//
struct ExportContext
{
BOOL fHasOrdinal1;
ULONG nExports;
};
static BOOL CALLBACK ExportCallback(_In_opt_ PVOID pContext,
_In_ ULONG nOrdinal,
_In_opt_ LPCSTR pszSymbol,
_In_opt_ PVOID pbTarget)
{
(void)pContext;
(void)pbTarget;
(void)pszSymbol;
ExportContext *pec = (ExportContext *)pContext;
if (nOrdinal == 1) {
pec->fHasOrdinal1 = TRUE;
}
pec->nExports++;
return TRUE;
}
//////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////
//
void TypeToString(DWORD Type, char *pszBuffer, size_t cBuffer)
{
if (Type == MEM_IMAGE) {
StringCchPrintfA(pszBuffer, cBuffer, "img");
}
else if (Type == MEM_MAPPED) {
StringCchPrintfA(pszBuffer, cBuffer, "map");
}
else if (Type == MEM_PRIVATE) {
StringCchPrintfA(pszBuffer, cBuffer, "pri");
}
else {
StringCchPrintfA(pszBuffer, cBuffer, "%x", Type);
}
}
void StateToString(DWORD State, char *pszBuffer, size_t cBuffer)
{
if (State == MEM_COMMIT) {
StringCchPrintfA(pszBuffer, cBuffer, "com");
}
else if (State == MEM_FREE) {
StringCchPrintfA(pszBuffer, cBuffer, "fre");
}
else if (State == MEM_RESERVE) {
StringCchPrintfA(pszBuffer, cBuffer, "res");
}
else {
StringCchPrintfA(pszBuffer, cBuffer, "%x", State);
}
}
void ProtectToString(DWORD Protect, char *pszBuffer, size_t cBuffer)
{
if (Protect == 0) {
StringCchPrintfA(pszBuffer, cBuffer, "");
}
else if (Protect == PAGE_EXECUTE) {
StringCchPrintfA(pszBuffer, cBuffer, "--x");
}
else if (Protect == PAGE_EXECUTE_READ) {
StringCchPrintfA(pszBuffer, cBuffer, "r-x");
}
else if (Protect == PAGE_EXECUTE_READWRITE) {
StringCchPrintfA(pszBuffer, cBuffer, "rwx");
}
else if (Protect == PAGE_EXECUTE_WRITECOPY) {
StringCchPrintfA(pszBuffer, cBuffer, "rcx");
}
else if (Protect == PAGE_NOACCESS) {
StringCchPrintfA(pszBuffer, cBuffer, "---");
}
else if (Protect == PAGE_READONLY) {
StringCchPrintfA(pszBuffer, cBuffer, "r--");
}
else if (Protect == PAGE_READWRITE) {
StringCchPrintfA(pszBuffer, cBuffer, "rw-");
}
else if (Protect == PAGE_WRITECOPY) {
StringCchPrintfA(pszBuffer, cBuffer, "rc-");
}
else if (Protect == (PAGE_GUARD | PAGE_EXECUTE)) {
StringCchPrintfA(pszBuffer, cBuffer, "g--x");
}
else if (Protect == (PAGE_GUARD | PAGE_EXECUTE_READ)) {
StringCchPrintfA(pszBuffer, cBuffer, "gr-x");
}
else if (Protect == (PAGE_GUARD | PAGE_EXECUTE_READWRITE)) {
StringCchPrintfA(pszBuffer, cBuffer, "grwx");
}
else if (Protect == (PAGE_GUARD | PAGE_EXECUTE_WRITECOPY)) {
StringCchPrintfA(pszBuffer, cBuffer, "grcx");
}
else if (Protect == (PAGE_GUARD | PAGE_NOACCESS)) {
StringCchPrintfA(pszBuffer, cBuffer, "g---");
}
else if (Protect == (PAGE_GUARD | PAGE_READONLY)) {
StringCchPrintfA(pszBuffer, cBuffer, "gr--");
}
else if (Protect == (PAGE_GUARD | PAGE_READWRITE)) {
StringCchPrintfA(pszBuffer, cBuffer, "grw-");
}
else if (Protect == (PAGE_GUARD | PAGE_WRITECOPY)) {
StringCchPrintfA(pszBuffer, cBuffer, "grc-");
}
else {
StringCchPrintfA(pszBuffer, cBuffer, "%x", Protect);
}
}
static BYTE buffer[65536];
typedef union
{
struct
{
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
} ih;
IMAGE_NT_HEADERS32 ih32;
IMAGE_NT_HEADERS64 ih64;
} IMAGE_NT_HEADER;
struct SECTIONS
{
PBYTE pbBeg;
PBYTE pbEnd;
CHAR szName[16];
} Sections[256];
DWORD SectionCount = 0;
DWORD Bitness = 0;
PCHAR FindSectionName(PBYTE pbBase, PBYTE& pbEnd)
{
for (DWORD n = 0; n < SectionCount; n++) {
if (Sections[n].pbBeg == pbBase) {
pbEnd = Sections[n].pbEnd;
return Sections[n].szName;
}
}
pbEnd = NULL;
return NULL;
}
ULONG PadToPage(ULONG Size)
{
return (Size & 0xfff)
? Size + 0x1000 - (Size & 0xfff)
: Size;
}
BOOL GetSections(HANDLE hp, PBYTE pbBase)
{
DWORD beg = 0;
DWORD cnt = 0;
SIZE_T done;
IMAGE_DOS_HEADER idh;
if (!ReadProcessMemory(hp, pbBase, &idh, sizeof(idh), &done) || done != sizeof(idh)) {
return FALSE;
}
if (idh.e_magic != IMAGE_DOS_SIGNATURE) {
return FALSE;
}
IMAGE_NT_HEADER inh;
if (!ReadProcessMemory(hp, pbBase + idh.e_lfanew, &inh, sizeof(inh), &done) || done != sizeof(inh)) {
printf("No Read\n");
return FALSE;
}
if (inh.ih.Signature != IMAGE_NT_SIGNATURE) {
printf("No NT\n");
return FALSE;
}
beg = idh.e_lfanew
+ FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader )
+ inh.ih.FileHeader.SizeOfOptionalHeader;
cnt = inh.ih.FileHeader.NumberOfSections;
Bitness = (inh.ih32.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) ? 32 : 64;
#if 0
printf("%d %d count=%d\n", beg, Bitness, cnt);
#endif
IMAGE_SECTION_HEADER ish;
for (DWORD n = 0; n < cnt; n++) {
if (!ReadProcessMemory(hp, pbBase + beg + n * sizeof(ish), &ish, sizeof(ish), &done) || done != sizeof(ish)) {
printf("No Read\n");
return FALSE;
}
Sections[n].pbBeg = pbBase + ish.VirtualAddress;
Sections[n].pbEnd = pbBase + ish.VirtualAddress + PadToPage(ish.Misc.VirtualSize);
memcpy(Sections[n].szName, ish.Name, sizeof(ish.Name));
Sections[n].szName[sizeof(ish.Name)] = '\0';
#if 0
printf("--- %p %s\n", Sections[n].pbBeg, Sections[n].szName);
#endif
}
SectionCount = cnt;
return TRUE;
}
BOOL DumpProcess(HANDLE hp)
{
ULONG64 base;
ULONG64 next;
MEMORY_BASIC_INFORMATION mbi;
printf(" %12s %8s %8s: %3s %3s %4s %3s : %8s\n", "Address", "Offset", "Size", "Typ", "Sta", "Prot", "Ini", "Contents");
printf(" %12s %8s %8s: %3s %3s %4s %3s : %8s\n", "------------", "--------", "--------", "---", "---", "----", "---", "-----------------");
for (next = 0;;) {
base = next;
ZeroMemory(&mbi, sizeof(mbi));
if (VirtualQueryEx(hp, (PVOID)base, &mbi, sizeof(mbi)) == 0) {
break;
}
if ((mbi.RegionSize & 0xfff) == 0xfff) {
break;
}
next = (ULONG64)mbi.BaseAddress + mbi.RegionSize;
if (mbi.State == MEM_FREE) {
continue;
}
CHAR szType[16];
TypeToString(mbi.Type, szType, ARRAYSIZE(szType));
CHAR szState[16];
StateToString(mbi.State, szState, ARRAYSIZE(szState));
CHAR szProtect[16];
ProtectToString(mbi.Protect, szProtect, ARRAYSIZE(szProtect));
CHAR szAllocProtect[16];
ProtectToString(mbi.AllocationProtect, szAllocProtect, ARRAYSIZE(szAllocProtect));
CHAR szFile[MAX_PATH];
szFile[0] = '\0';
DWORD cb = 0;
PCHAR pszFile = szFile;
if (base == (ULONG64)mbi.AllocationBase) {
#if 0
cb = pfGetMappedFileName(hp, (PVOID)mbi.AllocationBase, szFile, ARRAYSIZE(szFile));
#endif
if (GetSections(hp, (PBYTE)mbi.AllocationBase)) {
next = base + 0x1000;
StringCchPrintfA(szFile, ARRAYSIZE(szFile), "%d-bit PE", Bitness);
}
}
if (cb > 0) {
for (DWORD c = 0; c < cb; c++) {
szFile[c] = (szFile[c] >= 'a' && szFile[c] <= 'z')
? szFile[c] - 'a' + 'A' : szFile[c];
}
szFile[cb] = '\0';
}
if ((pszFile = strrchr(szFile, '\\')) == NULL) {
pszFile = szFile;
}
else {
pszFile++;
}
PBYTE pbEnd;
PCHAR pszSect = FindSectionName((PBYTE)base, pbEnd);
if (pszSect != NULL) {
pszFile = pszSect;
if (next > (ULONG64)pbEnd) {
next = (ULONG64)pbEnd;
}
}
CHAR szDesc[128];
ZeroMemory(&szDesc, ARRAYSIZE(szDesc));
if (base == (ULONG64)mbi.AllocationBase) {
StringCchPrintfA(szDesc, ARRAYSIZE(szDesc), " %12I64x %8I64x %8I64x: %3s %3s %4s %3s : %s",
(ULONG64)base,
(ULONG64)base - (ULONG64)mbi.AllocationBase,
(ULONG64)next - (ULONG64)base,
szType,
szState,
szProtect,
szAllocProtect,
pszFile);
}
else {
StringCchPrintfA(szDesc, ARRAYSIZE(szDesc), " %12s %8I64x %8I64x: %3s %3s %4s %3s : %s",
"-",
(ULONG64)base - (ULONG64)mbi.AllocationBase,
(ULONG64)next - (ULONG64)base,
szType,
szState,
szProtect,
szAllocProtect,
pszFile);
}
printf("%s\n", szDesc);
}
return TRUE;
}
//////////////////////////////////////////////////////////////////////// main.
//
int CDECL main(int argc, char **argv)
{
BOOLEAN fNeedHelp = FALSE;
BOOLEAN fVerbose = FALSE;
LPCSTR rpszDllsRaw[256];
LPCSTR rpszDllsOut[256];
DWORD nDlls = 0;
for (DWORD n = 0; n < ARRAYSIZE(rpszDllsRaw); n++) {
rpszDllsRaw[n] = NULL;
rpszDllsOut[n] = NULL;
}
int arg = 1;
for (; arg < argc && (argv[arg][0] == '-' || argv[arg][0] == '/'); arg++) {
CHAR *argn = argv[arg] + 1;
CHAR *argp = argn;
while (*argp && *argp != ':' && *argp != '=')
argp++;
if (*argp == ':' || *argp == '=')
*argp++ = '\0';
switch (argn[0]) {
case 'd': // Set DLL Name
case 'D':
if (nDlls < ARRAYSIZE(rpszDllsRaw)) {
rpszDllsRaw[nDlls++] = argp;
}
else {
printf("withdll.exe: Too many DLLs.\n");
fNeedHelp = TRUE;
break;
}
break;
case 'v': // Verbose
case 'V':
fVerbose = TRUE;
break;
case '?': // Help
fNeedHelp = TRUE;
break;
default:
fNeedHelp = TRUE;
printf("withdll.exe: Bad argument: %s\n", argv[arg]);
break;
}
}
if (arg >= argc) {
fNeedHelp = TRUE;
}
if (nDlls == 0) {
fNeedHelp = TRUE;
}
if (fNeedHelp) {
PrintUsage();
return 9001;
}
/////////////////////////////////////////////////////////// Validate DLLs.
//
for (DWORD n = 0; n < nDlls; n++) {
CHAR szDllPath[1024];
PCHAR pszFilePart = NULL;
if (!GetFullPathNameA(rpszDllsRaw[n], ARRAYSIZE(szDllPath), szDllPath, &pszFilePart)) {
printf("withdll.exe: Error: %s is not a valid path name..\n",
rpszDllsRaw[n]);
return 9002;
}
DWORD c = (DWORD)strlen(szDllPath) + 1;
PCHAR psz = new CHAR [c];
StringCchCopyA(psz, c, szDllPath);
rpszDllsOut[n] = psz;
HMODULE hDll = LoadLibraryExA(rpszDllsOut[n], NULL, DONT_RESOLVE_DLL_REFERENCES);
if (hDll == NULL) {
printf("withdll.exe: Error: %s failed to load (error %d).\n",
rpszDllsOut[n],
GetLastError());
return 9003;
}
ExportContext ec;
ec.fHasOrdinal1 = FALSE;
ec.nExports = 0;
DetourEnumerateExports(hDll, &ec, ExportCallback);
FreeLibrary(hDll);
if (!ec.fHasOrdinal1) {
printf("withdll.exe: Error: %s does not export ordinal #1.\n",
rpszDllsOut[n]);
printf(" See help entry DetourCreateProcessWithDllEx in Detours.chm.\n");
return 9004;
}
}
//////////////////////////////////////////////////////////////////////////
STARTUPINFOA si;
PROCESS_INFORMATION pi;
CHAR szCommand[2048];
CHAR szExe[1024];
CHAR szFullExe[1024] = "\0";
PCHAR pszFileExe = NULL;
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(si);
szCommand[0] = L'\0';
StringCchCopyA(szExe, sizeof(szExe), argv[arg]);
for (; arg < argc; arg++) {
if (strchr(argv[arg], ' ') != NULL || strchr(argv[arg], '\t') != NULL) {
StringCchCatA(szCommand, sizeof(szCommand), "\"");
StringCchCatA(szCommand, sizeof(szCommand), argv[arg]);
StringCchCatA(szCommand, sizeof(szCommand), "\"");
}
else {
StringCchCatA(szCommand, sizeof(szCommand), argv[arg]);
}
if (arg + 1 < argc) {
StringCchCatA(szCommand, sizeof(szCommand), " ");
}
}
printf("withdll.exe: Starting: `%s'\n", szCommand);
for (DWORD n = 0; n < nDlls; n++) {
printf("withdll.exe: with `%s'\n", rpszDllsOut[n]);
}
fflush(stdout);
DWORD dwFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED;
SetLastError(0);
SearchPathA(NULL, szExe, ".exe", ARRAYSIZE(szFullExe), szFullExe, &pszFileExe);
if (!DetourCreateProcessWithDllsA(szFullExe[0] ? szFullExe : NULL, szCommand,
NULL, NULL, TRUE, dwFlags, NULL, NULL,
&si, &pi, nDlls, rpszDllsOut, NULL)) {
DWORD dwError = GetLastError();
printf("withdll.exe: DetourCreateProcessWithDllEx failed: %d\n", dwError);
if (dwError == ERROR_INVALID_HANDLE) {
#if DETOURS_64BIT
printf("withdll.exe: Can't detour a 32-bit target process from a 64-bit parent process.\n");
#else
printf("withdll.exe: Can't detour a 64-bit target process from a 32-bit parent process.\n");
#endif
}
ExitProcess(9009);
}
if (fVerbose) {
DumpProcess(pi.hProcess);
}
ResumeThread(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE);
DWORD dwResult = 0;
if (!GetExitCodeProcess(pi.hProcess, &dwResult)) {
printf("withdll.exe: GetExitCodeProcess failed: %d\n", GetLastError());
return 9010;
}
for (DWORD n = 0; n < nDlls; n++) {
if (rpszDllsOut[n] != NULL) {
delete[] rpszDllsOut[n];
rpszDllsOut[n] = NULL;
}
}
return dwResult;
}
//
///////////////////////////////////////////////////////////////// End of File.
最后使用x32od 调试的时候,将已经运行起来的CheckMe程序附加上就可以进行调试了!!!是不是很酷呢???
除了上面的5字节注入外还有一些其他的注入方式
- APC注入
- 输入法注入,在切换输入法的时候进行注入,并且不会被查杀
- 劫持注入,平时的程序都要加载系统Dll,但是有一个加载顺序,先加载自身Dll,那么我就可以写一个同名的Dll让你加载,达到劫持注入的目的。
- WriteProcessMemory注入,在启动进程后会暂停主,然后写入一个LoadLibrary函数,然后再启动一个远线程,然后执行LoadLibrary里面的代码,从而达到注入的目的。
- 还有无Dll注入,也就是注入代码
mhook库
官网下载地址
这里就不多做介绍了,它的代码比较简洁,能够用于商业和非商业。