Windows程序设计学习笔记——hook和服务

1.Dll注入补充

在使用远程线程注入将dll注入到目标进程时,我们可以为其添加菜单,替换掉原有的过程处理函数便可以使用我们自己的菜单

LONG SetWindowLong(          
    HWND hWnd,    
    int nIndex,
    LONG dwNewLong    //替换值
);
//函数成功返回原有值

该函数用于修改窗口属性,其中第二个参数是一个索引值,表示要修改的属性,见下表:

如果是win64话,需要使用SetWindowLongPtr:

LONG_PTR SetWindowLongPtr(          HWND hWnd,
    int nIndex,
    LONG_PTR dwNewLong
);

第二个参数有所不同,其他参数与SetWindowLong相同: 

过程处理函数:

LRESULT CALLBACK WindowProc(          HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
);

由于除了我们点击自己菜单所发出的消息要处理外,还有各种消息需要处理,所以原来的消息处理函数要保存,并且在测试中将dll注入进MFC工程发现自己的回调函数会出现问题(可能是因为MFC消息处理机制与win32api不同),另外如果原来程序有菜单那么注入后会阻塞

当我们成功替换了消息处理函数后,我们可以开始处理WM_COMMAND消息了,最后卸载dll时候不能直接调用Freelibrary,因为Freelibrary释放dll后剩下的代码也消失了所以会引发崩溃,而FreeLibraryAndExitThread则会退出当前线程(主线程),所以要卸载dll我们需要创建一个线程再调用FreeLibraryAndExitThread

示例如下:

#include "pch.h"
#include<Windows.h>
#include"resource.h"
#include<WinUser.h>

WNDPROC oldfun = NULL;  //原来的过程处理函数要保留
HINSTANCE diahs = NULL;
HWND diahw = NULL;
HMODULE g_module = NULL;

DWORD WINAPI ThreadProc(__in  LPVOID lpParameter)
{
    Sleep(1);   //等待主线程处理剩下工作
    FreeLibraryAndExitThread(g_module, 0);
}

INT_PTR CALLBACK DialogProc(HWND hwndDlg,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
)
{
    //对话框消息处理函数
    if (uMsg == WM_CLOSE)
    {
        DestroyWindow(hwndDlg);
    }
    return FALSE;
}

LRESULT CALLBACK NewWindowProc(HWND hWnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
)
{
    //消息处理函数
    if (uMsg == WM_COMMAND)
    {
        switch (LOWORD(wParam))
        {
        case ID_MENU_1:
        {
            //MessageBox(hWnd, "this is 1", NULL, MB_OK);
            diahw =CreateDialog(diahs, MAKEINTRESOURCE(IDD_DIALOG1), hWnd, DialogProc);
            ShowWindow(diahw, SW_SHOW);
            break;
        }
        case ID_MENU_2:
        {
            //MessageBox(hWnd, "this is 2", NULL, MB_OK);
            DestroyWindow(diahw);
            break;
        }
        case ID_MENU_3:
        {
            SetWindowLongPtr(hWnd, GWLP_WNDPROC,(LONG_PTR)oldfun);  //归还过程函数
            SetMenu(FindWindow(NULL, "Dialog"), NULL);      //卸载菜单
            CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
            break;
        }
        default:
            break;
        }
    }
    else
    {
        return oldfun(hWnd, uMsg, wParam, lParam);
    }
}

BOOL init(HMODULE hModule)
{
    //获取窗体句柄,设置菜单
    HMENU hm = LoadMenu(hModule, MAKEINTRESOURCE(IDR_MENU1));
    HWND hw = FindWindow(NULL, "Dialog");
    diahs = hModule;
    DWORD error = GetLastError();
    SetMenu(hw, hm);
    if (hm == NULL || hw == NULL)
    {
        return FALSE;
    }

    //替换原有过程函数(x64要用SetWindowLongPtr)
    oldfun = (WNDPROC)SetWindowLongPtr(hw, GWLP_WNDPROC,(LONG_PTR)NewWindowProc);
    error = GetLastError();
    return TRUE;
}

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    {
        g_module = hModule;
        if (init(hModule))
        {
            MessageBox(FindWindow(NULL, "Dialog"), "finish!", "notice", MB_OK);
        }
        else
        {
            MessageBox(FindWindow(NULL, "Dialog"), "Fail!", "notice", MB_OK);
        }
    }
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

2.Windows Hook

Hook意为钩子,用于监视指定窗口的某种类型的消息,当操作系统发送消息时,钩子会先捕获消息然后再传递消息。钩子的种类有很多,参加MSDN:

 按照作用范围划分,钩子又可以分为全局钩子和局部钩子,局部钩子是针对某个线程的,而全局钩子是作用于整个系统

安装(全局\局部)钩子:

HHOOK SetWindowsHookEx(          int idHook,
    HOOKPROC lpfn,
    HINSTANCE hMod,
    DWORD dwThreadId
);

第一个参数是钩子类型,如下:

 第二个参数是钩子的消息处理函数

第三个参数如果是局部钩子,则填NULL,如果是全局钩子,则填钩子函数所在的Dll模块句柄

第四个参数是目标窗口主线程id,如果是全局钩子则填0

该函数返回值是钩子的句柄

卸载钩子:

BOOL UnhookWindowsHookEx(          
        HHOOK hhk    //钩子句柄
);

这里我们以键盘消息为例,以下是键盘钩子的回调函数

LRESULT CALLBACK KeyboardProc(          
int code,
    WPARAM wParam,
    LPARAM lParam
);

wParam和lParam是键盘消息的两个参数,而第一个参数code是Hook代码,钩子函数使用该参数以明确任务,所以该参数依赖于钩子类型,注意:由于系统中可能有多个钩子存在,所以我们需要调用如下函数把消息传给下一个钩子函数:

LRESULT CallNextHookEx(          
    HHOOK hhk,
    int nCode,
    WPARAM wParam,
    LPARAM lParam
);

另外,msdn有说明,如果code<0,则不应处理消息并使用CallNextHookEx返回

安装局部钩子示例:

//MFC
HHOOK hook1 = NULL;
LRESULT CALLBACK KeyboardProc(int code,
	WPARAM wParam,
	LPARAM lParam
)
{
	if (code < 0)
	{
		return CallNextHookEx(hook1, code, wParam, lParam);
	}
	//处理
	CString key;
	key.Format("%c", wParam);
	SetDlgItemText(FindWindow(NULL, "HOOK"), IDC_EDIT1, key);
	//传给下一个钩子
	return CallNextHookEx(hook1, code, wParam, lParam);
}
void CTestHookDlg::OnBnClickedButton1()
{	
	//创建键盘钩子
	hook1 = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, GetCurrentThreadId());
}


void CTestHookDlg::OnBnClickedButton2()
{
	//卸载钩子
	UnhookWindowsHookEx(hook1);
}

对于全局钩子来说,全局钩子的函数必须放在dll中,因为进程的地址空间是相互隔离的,所以一个进程不能调用其他进程地址空间的钩子函数,如果函数在dll里,则系统会将这个dll插入到发生指定事件的进程的地址空间,使得进程能够调用钩子函数

全局钩子示例:

//钩子函数所在dll
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"

HHOOK hook1 = NULL;
__declspec(dllexport) char buffer[MAXBYTE] = {};
LRESULT CALLBACK KeyboardProc(int code,
	WPARAM wParam,
	LPARAM lParam
)
{
	if (code < 0)
	{
		return CallNextHookEx(hook1, code, wParam, lParam);
	}
	//处理
	wsprintf(buffer, "%c", wParam);
    MessageBox(FindWindow(NULL, "HOOK"), buffer, NULL, MB_OK);
	//传给下一个钩子
	return CallNextHookEx(hook1, code, wParam, lParam);
}

__declspec(dllexport) BOOL install()
{
    hook1 = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, GetModuleHandle("Dll_hook"), 0);
    return TRUE;

}

__declspec(dllexport) BOOL unload()
{
    UnhookWindowsHookEx(hook1);
    return TRUE;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}
//cpp(MFC)
#pragma comment(lib,"Dll_hook")
BOOL install();
BOOL unload();
extern char __declspec(dllimport) buffer[MAXBYTE];
void CTestHookDlg::OnBnClickedButton3()
{
	install();	//创建全局钩子
}

void CTestHookDlg::OnBnClickedButton4()
{
	unload();	//卸载全局钩子
}

3.服务

服务也是一种进程,但是服务是非用户进程,拥有比管理员更高的权限,此外,服务会长期驻留在windows系统中。Windows服务包含3个组件:服务应用程序,服务控制程序(SCP)和服务控制管理器(SCM),服务应用程序是执行服务的进程,服务控制程序用于控制该服务启动、停止等,服务控制管理器则是媒介,服务控制程序通过管理器来控制服务

首先先来编写服务程序:

每个服务都有自己的main函数:

VOID WINAPI ServiceMain(
  __in  DWORD dwArgc,
  __in  LPTSTR* lpszArgv
);

我们需要先调用以下函数将服务的main函数与SCM关联起来,才能管理我们的服务(告诉SCM服务入口在哪里):

BOOL WINAPI StartServiceCtrlDispatcher(
  __in  const SERVICE_TABLE_ENTRY* lpServiceTable    //该参数是一个以空结尾的指针数组(因为服务可以有多个)
);

typedef struct _SERVICE_TABLE_ENTRY {  
LPTSTR lpServiceName;      //服务名称
LPSERVICE_MAIN_FUNCTION lpServiceProc;    //服务主函数地址
} SERVICE_TABLE_ENTRY,  *LPSERVICE_TABLE_ENTRY;

然后注册服务回调函数:

SERVICE_STATUS_HANDLE WINAPI RegisterServiceCtrlHandlerEx(
  __in      LPCTSTR lpServiceName,    //服务名称
  __in      LPHANDLER_FUNCTION_EX lpHandlerProc,    //回调函数地址
  __in_opt  LPVOID lpContext        //用户自定义参数
);
//失败返回0,成功返回服务状态句柄

第一个参数指定服务名字,第二个参数是服务回调函数的地址,如下:

DWORD WINAPI HandlerEx(
  __in  DWORD dwControl,
  __in  DWORD dwEventType,
  __in  LPVOID lpEventData,
  __in  LPVOID lpContext
);

前三个参数参加MSDN手册或Windows服务框架与服务的编写_11080813的技术博客_51CTO博客

第四个参数表示从RegisterServiceCtrlHandlerEx传递的用户定义数据。当多个服务共享一个进程时,lpContext参数可以帮助标识该服务

当回调函数注册完成后,我们需要通知SCM服务开始运行,使用如下api

BOOL WINAPI SetServiceStatus(
  __in  SERVICE_STATUS_HANDLE hServiceStatus,    //RegisterServiceCtrlHandlerEx返回的句柄
  __in  LPSERVICE_STATUS lpServiceStatus    //结构体指针,指向如下结构体
);


typedef struct _SERVICE_STATUS {  
DWORD dwServiceType;  
DWORD dwCurrentState;  
DWORD dwControlsAccepted;  
DWORD dwWin32ExitCode;  
DWORD dwServiceSpecificExitCode;  
DWORD dwCheckPoint;  
DWORD dwWaitHint;
} SERVICE_STATUS,  *LPSERVICE_STATUS;

结构体成员介绍参见SERVICE_STATUS结构各成员解析 - 知己而已 - 博客园

接着我们要在服务回调函数中处理服务的各种状态(暂停、停止,继续等),处理完状态后也要通知SCM服务发生改变

服务程序示例:

#include<Windows.h>
#include<iostream>
using std::cout;
using std::endl;
#define SERVICENAME "my_person_service"
SERVICE_STATUS_HANDLE ssh = NULL;

DWORD WINAPI HandlerEx(
	__in  DWORD dwControl,
	__in  DWORD dwEventType,
	__in  LPVOID lpEventData,
	__in  LPVOID lpContext
)
{	//服务回调函数
	SERVICE_STATUS ss = {};
	ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	ss.dwCurrentState = SERVICE_RUNNING;
	ss.dwControlsAccepted = SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
	ss.dwWin32ExitCode = NO_ERROR;
	switch (dwControl)
	{
	case SERVICE_CONTROL_PAUSE:
		ss.dwCurrentState = SERVICE_PAUSED;
		break;
	case SERVICE_CONTROL_CONTINUE:
		ss.dwCurrentState = SERVICE_RUNNING;
		break;
	case SERVICE_CONTROL_SHUTDOWN:
		ss.dwCurrentState = SERVICE_STOP_PENDING;
		break;
	case SERVICE_CONTROL_STOP:
		ss.dwCurrentState = SERVICE_STOPPED;
		break;
	default:
		break;
	}
	BOOL flag = SetServiceStatus(ssh, &ss);
	if (flag != 0)
	{
		//system("echo 服务状态改变,通知SCM成功 ");
	}
	else
	{
		//system("echo 服务回调函数通知SCM失败");
	}
	return NO_ERROR;
}

VOID WINAPI ServiceMain(
	__in  DWORD dwArgc,
	__in  LPTSTR* lpszArgv
)
{
	//服务主线程函数
	 ssh= RegisterServiceCtrlHandlerEx(SERVICENAME, HandlerEx, NULL);	//注册回调函数
	if (ssh != 0 && ssh!=NULL)
	{
		//system("echo 服务回调函数创建成功");
	}
	//通知SCM服务开始运行
	SERVICE_STATUS ss = {};
	ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
	ss.dwCurrentState = SERVICE_RUNNING;
	ss.dwControlsAccepted = SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
	ss.dwWin32ExitCode = NO_ERROR;
	BOOL error = SetServiceStatus(ssh, &ss);
	if (error != 0)
	{
		//system("echo 通知SCM完毕!");
	}


}

int main()
{
	SERVICE_TABLE_ENTRY entry[2] = { {(LPSTR)SERVICENAME,ServiceMain},{NULL,NULL} };
	StartServiceCtrlDispatcher(entry);	//将主函数与SCM关联
	return 0;
}

完成如上步骤,我们的服务程序就ok了,我们要把程序编译成release版本的才能让服务顺利安装

接下来开始编写服务控制程序:

首先是安装我们的服务程序,我们要先与SCM建立连接,使用如下api打开SCM数据库

//头文件Winsvc.h
SC_HANDLE WINAPI OpenSCManager(
  __in_opt  LPCTSTR lpMachineName,    //目标计算机名,填NULL则表示连接到本地
  __in_opt  LPCTSTR lpDatabaseName,    //要打开的SCM数据库,一般填NULL,打开SERVICES_ACTIVE_DATABASE 数据库
  __in      DWORD dwDesiredAccess    //数据库访问权限
);

SCM数据库访问权限如下:

 关闭服务句柄:

BOOL WINAPI CloseServiceHandle(
  __in  SC_HANDLE hSCObject
);

接着需要创建服务:

SC_HANDLE WINAPI CreateService(
  __in       SC_HANDLE hSCManager,
  __in       LPCTSTR lpServiceName,   
  __in_opt   LPCTSTR lpDisplayName,   
  __in       DWORD dwDesiredAccess,    
  __in       DWORD dwServiceType,
  __in       DWORD dwStartType,
  __in       DWORD dwErrorControl,
  __in_opt   LPCTSTR lpBinaryPathName,
//以下五个一般填NULL
  __in_opt   LPCTSTR lpLoadOrderGroup,
  __out_opt  LPDWORD lpdwTagId,
  __in_opt   LPCTSTR lpDependencies,
  __in_opt   LPCTSTR lpServiceStartName,
  __in_opt   LPCTSTR lpPassword
);

参数说明见CreateServiceA function (winsvc.h) - Win32 apps | Microsoft Docs

如果驱动已经存在,则CreateService会失败,可以使用如下函数打开服务

SC_HANDLE WINAPI OpenService(
  __in  SC_HANDLE hSCManager,
  __in  LPCTSTR lpServiceName,
  __in  DWORD dwDesiredAccess
);

第三个参数是服务的访问权限,如下:

 注意不要填成SCM数据库的访问权限

服务创建(安装)示例:

void CControlServiceDlg::OnBnClickedButton1()
{
	//安装服务

	SC_HANDLE hscm=OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);	//连接SCM数据库
	if (hscm != NULL)
	{
		AfxMessageBox("OpenSCManager成功!");
	}
	else
	{
		AfxMessageBox("OpenSCManager失败!");
		return;
	}
	SC_HANDLE hc=CreateService(
		hscm,
		SERVICENAME,
		"你好服务",
		SC_MANAGER_ALL_ACCESS,
		SERVICE_WIN32_OWN_PROCESS,
		SERVICE_DEMAND_START,
		SERVICE_ERROR_NORMAL,
		"C:\\Users\\VMWARE\\Desktop\\Service\\Service_test.exe",
		NULL, NULL, NULL, NULL, NULL
	);
	if (hc != NULL)
	{
		AfxMessageBox("CreateService成功!,服务创建完成!");
	}
	else
	{
		AfxMessageBox("CreateService失败!");
	}
	CloseServiceHandle(hc);
	CloseServiceHandle(hscm);
}

这里我把服务名称设为宏SERVICENAME

服务创建完成后可以使用如下api启动服务:

BOOL WINAPI StartService(
  __in      SC_HANDLE hService,
  __in      DWORD dwNumServiceArgs,    //NULL
  __in_opt  LPCTSTR* lpServiceArgVectors    //NULL
);

启动服务示例:

void CControlServiceDlg::OnBnClickedButton5()
{
	//启动服务
	SC_HANDLE hscm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);	//连接SCM数据库
	if (hscm != NULL)
	{
		AfxMessageBox("OpenSCManager成功!");
	}
	else
	{
		AfxMessageBox("OpenSCManager失败!");
		return;
	}
	SC_HANDLE sc = OpenService(hscm, SERVICENAME, SC_MANAGER_ALL_ACCESS);
	if (sc != NULL)
	{
		AfxMessageBox("获取句柄成功!");
	}
	BOOL flag=StartService(sc, NULL, NULL);
	if (flag != 0)
	{
		AfxMessageBox("服务启动成功!");
	}
	CloseServiceHandle(sc);
	CloseServiceHandle(hscm);
}

服务的暂停、继续、停止等可以通过如下api完成

BOOL WINAPI ControlService(
  __in   SC_HANDLE hService,
  __in   DWORD dwControl,   
  __out  LPSERVICE_STATUS lpServiceStatus    //返回服务状态
);

第二个参数如下:

 示例:

void CControlServiceDlg::OnBnClickedButton2()
{
	//服务暂停
	SC_HANDLE hscm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);	//连接SCM数据库
	if (hscm != NULL)
	{
		AfxMessageBox("OpenSCManager成功!");
	}
	else
	{
		AfxMessageBox("OpenSCManager失败!");
		return;
	}
	SC_HANDLE sc = OpenService(hscm, SERVICENAME, SERVICE_ALL_ACCESS);
	if (sc != NULL)
	{
		AfxMessageBox("获取句柄成功!");
	}
	SERVICE_STATUS ss;
	BOOL flag=ControlService(sc, SERVICE_CONTROL_PAUSE, &ss);
	if (flag == 0)
	{
		AfxMessageBox("ControlService失败!");
	}
	else
	{
		AfxMessageBox("ControlService成功!");
	}
	CloseServiceHandle(sc);
	CloseServiceHandle(hscm);
}


void CControlServiceDlg::OnBnClickedButton3()
{
	//服务继续
	SC_HANDLE hscm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);	//连接SCM数据库
	if (hscm != NULL)
	{
		AfxMessageBox("OpenSCManager成功!");
	}
	else
	{
		AfxMessageBox("OpenSCManager失败!");
		return;
	}
	SC_HANDLE sc = OpenService(hscm, SERVICENAME, SERVICE_ALL_ACCESS);
	if (sc != NULL)
	{
		AfxMessageBox("获取句柄成功!");
	}
	SERVICE_STATUS ss;
	BOOL flag = ControlService(sc, SERVICE_CONTROL_CONTINUE, &ss);
	if (flag == 0)
	{
		AfxMessageBox("ControlService失败!");
	}
	else
	{
		AfxMessageBox("ControlService成功!");
	}
	CloseServiceHandle(sc);
	CloseServiceHandle(hscm);
}

删除服务可以使用如下api,该函数将服务从SCM数据库中移除:

BOOL WINAPI DeleteService(
  __in  SC_HANDLE hService
);

删除服务示例:

void CControlServiceDlg::OnBnClickedButton6()
{
	//删除服务
	SC_HANDLE hscm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);	//连接SCM数据库
	if (hscm != NULL)
	{
		AfxMessageBox("OpenSCManager成功!");
	}
	else
	{
		AfxMessageBox("OpenSCManager失败!");
		return;
	}
	SC_HANDLE sc = OpenService(hscm, SERVICENAME, SERVICE_ALL_ACCESS);
	if (sc != NULL)
	{
		AfxMessageBox("获取句柄成功!");
	}
	DeleteService(sc);	//删除服务
	CloseServiceHandle(sc);
	CloseServiceHandle(hscm);
}

用我们的服务控制程序控制服务和用Windows的Services.msc是一样的

至此,一个没有功能的服务已经编写完成

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二、API Hook的原理 这里的API既包括传统的Win32 APIs,也包括任何Module输出的函数调用。熟悉PE文件格 式的朋友都知道,PE文件将对外部Module输出函数的调用信息保存在输入表中,即.idata段。 下面首先介绍本段的结构。 输入表首先以一个IMAGE_IMPORT_DESCRIPTOR(简称IID)数组开始。每个被PE文件隐式链接 进来的DLL都有一个IID.在这个数组中的最后一个单元是NULL,可以由此计算出该数组的项数。 例如,某个PE文件从两个DLL中引入函数,就存在两个IID结构来描述这些DLL文件,并在两个 IID结构的最后由一个内容全为0的IID结构作为结束。几个结构定义如下: IMAGE_IMPORT_DESCRIPTOR struct union{ DWORD Characteristics; ;00h DWORD OriginalFirstThunk; }; TimeDateStamp DWORD ;04h ForwarderChain DWORD ;08h Name DWORD ;0Ch FirstThunk DWORD ;10h IMAGE_IMPROT_DESCRIPTOR ends typedef struct _IMAGE_THUNK_DATA{ union{ PBYTE ForwarderString; PDWORD Functions; DWORD Ordinal; PIMAGE_IMPORT_BY_NAME AddressOfData; }u1; } IMAGE_IMPORT_BY_NAME结构保存一个输入函数的相关信息: IMAGE_IMPORT_BY_NAME struct Hint WORD ? ;本函数在其所驻留DLL的输出表中的序号 Name BYTE ? ;输入函数的函数名,以NULL结尾的ASCII字符串 IMAGE_IMPORT_BY_NAME ends OriginalFirstThunk(Characteristics):这是一个IMAGE_THUNK_DATA数组的RVA(相对于PE文件 起始处)。其中每个指针都指向IMAGE_IMPORT_BY_NAME结构。 TimeDateStamp:一个32位的时间标志,可以忽略。 ForwarderChain:正向链接索引,一般为0。当程序引用一个DLL中的API,而这个API又引用别的 DLL的API时使用。 NameLL名字的指针。是个以00结尾的ASCII字符的RVA地址,如"KERNEL32.DLL"。 FirstThunk:通常也是一个IMAGE_THUNK_DATA数组的RVA。如果不是一个指针,它就是该功能在 DLL中的序号。 OriginalFirstThunk与FirstThunk指向两个本质相同的数组IMAGE_THUNK_DATA,但名称不同, 分别是输入名称表(Import Name Table,INT)和输入地址表(Import Address Table,IAT)。 IMAGE_THUNK_DATA结构是个双字,在不同时刻有不同的含义,当双字最高位为1时,表示函数以 序号输入,低位就是函数序号。当双字最高位为0时,表示函数以字符串类型的函数名 方式输入,这时它是指向IMAGE_IMPORT_BY_NAME结构的RVA。 三个结构关系如下图: IMAGE_IMPORT_DESCRIPTOR INT IMAGE_IMPORT_BY_NAME IAT -------------------- /-->---------------- ---------- ---------------- |01| 函数1 ||02| 函数2 || n| ... |"USER32.dll" | |--------------------| | | FirstThunk |---------------------------------------------------------------/ -------------------- 在PE文件中对DLL输出函数的调用,主要以这种形式出现: call dword ptr[xxxxxxxx] 或 jmp [xxxxxxxx] 其中地址xxxxxxxx就是IAT中一个IMAGE_THUNK_DATA结构的地址,[xxxxxxxx]取值为IMAGE_THUNK_DATA 的值,即IMAGE_IMPORT_BY_NAME的地址。在操作系统加载PE文件的过程中,通过IID中的Name加载相应 的DLL,然后根据INT或IAT所指向的IMAGE_IMPORT_BY_NAME中的输入函数信息,在DLL中确定函数地址, 然后将函数地址写到IAT中,此时IAT将不再指向IMAGE_IMPORT_BY_NAME数组。这样[xxxxxxxx]取到的 就是真正的API地址。 从以上分析可以看出,要拦截API的调用,可以通过改写IAT来实现,将自己函数的地址写到IAT中, 达到拦截目的。 另外一种方法的原理更简单,也更直接。我们不是要拦截吗,先在内存中定位要拦截的API的地址, 然后改写代码的前几个字节为 jmp xxxxxxxx,其中xxxxxxxx为我们的API的地址。这样对欲拦截API的 调用实际上就跳转到了咱们的API调用去了,完成了拦截。不拦截时,再改写回来就是了。 这都是自己从网上辛辛苦苦找来的,真的很好啊
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值