一、回调函数概念理解
调用程序发出对回调函数的调用后,不等函数执行完毕,立即返回并继续执行。这样,调用程序执和被调用函数同时在执行。当被调函数执行完毕后,被调函数会反过来调用某个事先指定函数,以通知调用程序:函数调用结束。这个过程称为回调(Callback),这正是回调函数名称的由来
打个比方,有一家旅馆提供叫醒服务,但是要求旅客自己决定叫醒的方法。可以是
打客房电话,也可以是派服务员去敲门,睡得死怕耽误事的,还可以要求往自己头
上浇盆水。这里,“叫醒”这个行为是旅馆提供的,相当于库函数,但是叫醒的方式
是由旅客决定并告诉旅馆的,也就是回调函数。而旅客告诉旅馆怎么叫醒自己的动
作,也就是把回调函数传入库函数的动作,称为登记回调函数(to register
a callback function)
回调函数举例
#include<stdio.h>
typedef int (*p)(int ,int);
int call(int a,int b,p p)//系统的dll定义参数
{
return (*p)(a,b);
}
int add(int a,int b)//用户自己写的实现函数add
{
return a+b;
}
int sub(int a,int b)//用户自己写的实现函数add
{
return a-b;
}
int main()
{
printf("%d",call(1,1,add));
printf("\n");
printf("%d",call(1,1,sub));
return 0;
}
二、进程 线程 句柄概念
- 进程
~进程是执行中的程序:程序是一段描述指令的文本,是一个静态的概念,把这段指令运行起来,每次运行就得到了一个进程,进程是动态的概念;操作系统会为进程分配资源。
~ 进程是资源分配的基本单位:也就是说操作系统会为不同的进程分配不同的资源,如Window句柄、文件系统句柄、内核对象、虚拟内存等,我们可以说操作系统把Window句柄分配给某个进程,而不能说操作系统把Window句柄分配给某个线程。
~ 进程之间可以并发执行:如你可以运行多个记事本程序,运行多个QQ。
2.线程
~线程有一个优先级、实际上正在处理的程序的位置记数器、一个存储其局部变量的栈。每个线程都有自己的栈,但程序代码和堆由一个进程的所有线程共享。由于该进程的所有线程都寻址相同的虚拟地址,这使得一个进程所有的线程之间通信非常快。
~线程之间也可以并发执行:如你运行Word程序时,一个线程等待用户输入,一个线程进行拼写检查,一个线程将数据存入临时文件中…
~ 比如媒体播放器播放一个电影中同时放音乐也同时播放画面,如果没有多线程,那么操作系统必须等到音乐播完或者画面放完才能进行下一个操作。
简而言之:进程是一个容器,线程在进程这个容器中执行,是实际执行的代码。一
个进程至少拥有一个线程,而这个线程通常被称之为主线程(执行Main()方法的
线程通常被称为主线程),主线程的入口点也是应用程序的起始点。线程除了必不
可少的资源(程序计数器 一组寄存器和栈)之外,不拥有系统资源,所有进程内
的线程共享分配给这个进程拥有的所有资源。
3.句柄
句柄实际上是一个数据,是一个Long (整长型)的数据。
句柄是一种指向指针的指针
本质:WINDOWS程序中并不是用物理地址来标识一个内存块,文件,任务或动态装入模块的,相反的,WINDOWS API给这些项目分配确定的句柄,并将句柄返回给应用程序,然后通过句柄来进行操作。
三、DLL生成及调用
- 生成
// yz.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
#ifdef __cplusplus // if used by C++ code
extern "C" { // we need to export the C interface
#endif
__declspec(dllexport) int addfun(int a, int b)//定义导出函数
{
return a+b;
}
#ifdef __cplusplus
}
#endif
// user.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
using namespace std;
#define DEF_DLL_NAME "yz.dll"
typedef int(*FUNA)(int,int);
void main()
{
HMODULE hMod = LoadLibraryA("yz.dll");//dll路径
if (hMod)
{
FUNA addfun = (FUNA)GetProcAddress(hMod, ("addfun"));//直接使用原工程函数名
if (addfun != NULL)
{
cout<<addfun(5, 4)<<endl;
}
else
{
cout<<"ERROR on GetProcAddress"<<endl;
}
FreeLibrary(hMod);
}
else
cout<<"ERROR on LoadLibrary"<<endl;
while(1);
}
四、HOOK钩取
钩子
是Windows消息处理机制的一个平台,应用程序可以在上面设置子程以监视指定窗口的某种消息,而且所监视的窗口可以是其他进程所创建的。当消息到达后,在目标窗口处理函数之前处理它。钩子机制允许应用程序截获处理window消息或特定事件。
每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权。这时钩子函数即可以加工处理(改变)该消息,也可以不作处理而继续传递该消息,还可以强制结束消息的传递。
实现方法:SetWindowsHookEx方式注入
目的:将DLL注入到其他进程中并执行恶意代码
具体步骤:
假设A.exe要将 val.dll注入到B.exe,主要的流程如下:
①编写val.dll,其中val.dll必须至少导出一个函数,因为后面要传给SetWindowsHookEx作为回调函数。
②A加载val.dll,获取回调函数地址f.
③调用SetWindowsHookEx设置全局钩子,钩子的类型由自己决定。学习中使用的是WH_KEYBOARD。传入回调函数地址,dll的句柄,线程ID,设置为0表示对全局有效。如果函数返回非NULL,则说明设置成功。
④B运行起来后,如果触发了和钩子对应的消息,则会去运行f,但f在val.dll中,并不在进程空间里,因此B会去加载val.dll。这样,dll就被注入到目标进程中了。
SetWindowsHookEx只会负责把你的DLL加载到其他的执行程序(不会执行),所以我们的替换动作必须在DLLMAIN函数中(或调用)完成
当SetWindowsHookEx最后一个参数为0时,代表全局钩子。监视全部进程。
工程代码:
//HookMain.cpp
#include "stdio.h"
#include "conio.h"
#include "windows.h"
#define DEF_DLL_NAME "KeyHook.dll"
#define DEF_HOOKSTART "HookStart"
#define DEF_HOOKSTOP "HookStop"
typedef void (*PFN_HOOKSTART)();
typedef void (*PFN_HOOKSTOP)();
void main()
{
HMODULE hDll = NULL;
PFN_HOOKSTART HookStart = NULL;
PFN_HOOKSTOP HookStop = NULL;
char ch = 0;
// 加载DLL句柄
hDll = LoadLibraryA(DEF_DLL_NAME);
if( hDll == NULL )
{
printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError());
return;
}
// 导出DLL中的函数 (显示调用)
HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);
// 执行函数
HookStart();
// 循环
printf("press 'q' to quit!\n");
while( _getch() != 'q' ) ;
// 结束
HookStop();
//释放
FreeLibrary(hDll);
}
//KeyHook.cpp
#include "stdio.h"
#include "windows.h"
#define DEF_PROCESS_NAME "notepad.exe"
HINSTANCE g_hInstance = NULL;
HHOOK g_hHook = NULL;
HWND g_hWnd = NULL;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpvReserved)
{
switch( dwReason )
{
case DLL_PROCESS_ATTACH:
g_hInstance = hinstDLL;//获取当前进程
break;
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
char szPath[MAX_PATH] = {0,};
char *p = NULL;
if( nCode >= 0 )
{
// bit 31 : 0 => press, 1 => release
if( !(lParam & 0x80000000) )
{
GetModuleFileNameA(NULL, szPath, MAX_PATH);//获取进程路径
p = strrchr(szPath, '\\');
if( !_stricmp(p + 1, DEF_PROCESS_NAME) )
return 1;
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);//调用下一个钩子
}
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void HookStart()
{
g_hHook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, g_hInstance, 0);//钩子函数,全局钩子
}
__declspec(dllexport) void HookStop()//释放函数
{
if( g_hHook )
{
UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
}
}
#ifdef __cplusplus
}
#endif