参考 DLL注入_拦截技术之Hook方式: 可知

  • 如果我想要使用hook的dll注入方式来获取其他程序的数据,则关键的对象有:

    1. 操作 启动程序和给dll传递程序线程id 的一个程序
    2. 对目标程序的消息进行处理的dll
    3. 目标程序.exe

1. 不使用dll的钩子

1.1 新建项目

新建项目->找到 控制台应用(在Windows终端运行代码)->填写其他项目后,看到 将解决方案和项目放在同一目录中。

根据 终于理解了解决方案和项目之间的关系了可知:项目和解决方案的关系是:一个解决方案可包含多个项目(解决方案的下拉框里还有一项是 添加到解决方案)。
还可以看看VS2017 C++解决方案和项目目录文件夹 这个说的更详细写


稍等一会就建好了,熟悉又陌生的c++

1.2 关于钩子

全局钩子 实例(不使用DLL和使用DLL两种),网上搜到的资料里很多都说 钩子要放在dll里XXX,全局钩子,XXX。

大家应该都知道,全局消息钩子要依赖于一个DLL才能够正常工作。于是呢,我也就理所当在地认为全局钩子都要依赖于一个DLL才能正常工作的。但有某些全局钩子可以不依赖于任何DLL而正常工作的。这些钩子包括,WH_JOURNALPLAYBACK,WH_JOURNALRECORD,WH_KEYBOARD_LL,WH_MOUSE_LL。为什么这些钩子可以不依赖于DLL而正常工作呢?
可以从MSDN中得到答案,MSDN中对于这四种钩子都这样的描述“This hook is called in the context of the thread that installed it.”,翻译成中文意思是钩子函数的调用是在安装钩子的线程上下文中进行的,说得更明白些,意思就是这些钩子是在哪个线程当中安装的,其钩子函数就在哪个线程中执行。所以使用这四种钩子是达不到代码注入的效果的,当然也就可以不依赖于任何DLL了。MSDN中只对个别钩子指出了必须还是没有必要使用DLL。

以上结论来源:
MSDN上关于SetWindowsHookEx的说明文档:这里的Remarks部分(关于Hook的类型说明很明确了) Hook的范围取决于Hook的类型。 有些钩子只能在全局范围内设置; 也可以只为特定线程设置其他线程。

Hook的官方定义-MSDN Hook:

A hook is a point in the system message-handling mechanism where an application can install a subroutine to monitor the message traffic in the system and process certain types of messages before they reach the target window procedure.(钩子 就是在操作系统向某个应用程序发送消息前,先于该应用程序得到这个操作系统给的消息(拦截/获取应用程序的消息/数据))

1.3 不使用DLL的钩子 代码demo

参考:全局钩子 实例(不使用DLL和使用DLL两种):主要是kbhook.cpp这个程序,以下给出正确运行的修改过的代码

#define _WIN32_WINNT_WIN10   0x0A00
#define _WIN32_WINNT_WINTHRESHOLD 0x0A00
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
DWORD   g_main_tid = 0;
HHOOK   g_kb_hook = 0;BOOL CALLBACK con_handler(DWORD)
{    PostThreadMessage(g_main_tid, WM_QUIT, 0, 0);return TRUE;
};
LRESULT CALLBACK kb_proc(int code, WPARAM w, LPARAM l)
{    PKBDLLHOOKSTRUCT p = (PKBDLLHOOKSTRUCT)l;const char* info = NULL;if (w == WM_KEYDOWN)info = "key dn";else if (w == WM_KEYUP)info = "key up";else if (w == WM_SYSKEYDOWN)info = "sys key dn";else if (w == WM_SYSKEYUP)info = "sys key up";printf("%s - vkCode [%04x], scanCode [%04x]\n",info, p->vkCode, p->scanCode);// always call next hookreturn CallNextHookEx(g_kb_hook, code, w, l);
};
int main(void)
{    g_main_tid = GetCurrentThreadId();SetConsoleCtrlHandler(&con_handler, TRUE);g_kb_hook = SetWindowsHookEx(WH_KEYBOARD_LL,&kb_proc,GetModuleHandle(NULL), // 不能为NULL,否则失败0);if (g_kb_hook == NULL){   fprintf(stderr,  "SetWindowsHookEx failed with error %d\n", ::GetLastError());return 0;};// 消息循环是必须的,想知道原因可以查msdnMSG msg;while (GetMessage(&msg, NULL, 0, 0)){   TranslateMessage(&msg);DispatchMessage(&msg);};UnhookWindowsHookEx(g_kb_hook);return 0;
};

但是运行后一直报错:WH_KEYBOARD_LL标识符未定义 。搜索后,发现问题在于 我的运行环境是 Win7.

参考: 转载——VC6中使用高版本API的方法 转载自-> 原创——VC6中使用高版本API的方法,

使用hook主要用到 SetWindowsHookEx()这个函数,查看MSDN上关于SetWindowsHookEx的说明文档

上述程序运行后,Visual Studio解决方法 外部依赖项目就可以看到所依赖的 XX.h各种头文件(重点看看 Windows.hWinUser.h这两个)

#if (_WIN32_WINNT >= 0x0400)
#define WH_KEYBOARD_LL     13
#define WH_MOUSE_LL        14
#endif // (_WIN32_WINNT >= 0x0400)

WinUser.h中搜索 WH_KEYBOARD_LL 就可以看到宏定义了,如果能在依赖文件中找到这个 宏定义,就说明其实已经引入了文件了。

  • 问题在于_WIN32_WINNT,只有当它大于0x0400的时候才定义WH_KEYBOARD_LL。故搜索后MSDN关于_WIN32_WINNT的文档:

    • 使用Windows SDK时,可以指定代码可以在哪些Windows版本上运行。
    • 预处理程序宏WINVER和_WIN32_WINNT是用来指定代码支持的最低操作系统版本
    • Visual Studio和Microsoft C ++编译器支持以Windows 7 SP1以及其后的更高版本。
    • 较旧的工具集包括对Windows XP SP2,Windows Server 2003 SP1,Vista和Windows Server 2008的支持。
    • 不支持Windows 95,Windows 98,Windows ME,Windows NT和Windows 2000。
    • 升级较旧的项目时,可能需要更新WINVER或_WIN32_WINNT宏。 如果为它们分配了不受支持的Windows版本的值,则可能会看到与这些宏相关的编译错误。
    • 2020年的现在,常见的应该就是以下了。。
    #define _WIN32_WINNT_WIN7                   0x0601 // Windows 7
    #define _WIN32_WINNT_WIN8                   0x0602 // Windows 8
    #define _WIN32_WINNT_WINBLUE                0x0603 // Windows 8.1
    #define _WIN32_WINNT_WINTHRESHOLD           0x0A00 // Windows 10
    #define _WIN32_WINNT_WIN10                  0x0A00 // Windows 10
    
    • 但是遇到了一个很奇怪的现象,我在上面程序中规定的版本是 Win10的_WIN32_WINNT,但是上述代码在win7环境下也可以运行。。(是因为我安装了Visual studio 2019的时候安装了一些其他额外的库吗)。在win10的_WIN32_WINNT宏定义前加上win7的
      #define _WIN32_WINNT_WIN7 0x0601 // Windows 7
      在win10环境下/win7环境下运行依然正常,虽然不知道为什么,但是就先过吧。
    • 关于Windows.h这个头文件的使用,还可以参考MSDN-Using the Windows Headers

2. dll(Dynamic Link Library)文件编写

  1. Forums—微软人员对这个问题MSFlexGrid UIAutomation C++ API的回答
  2. 老外写的——Hooks and DLLs:代码格式很规范,页面排版有点乱,但是代码结构很清晰,适合初学者入门

2.1 新建项目

这里由于要使用DLL,所以在Visual Studio中新建时应该使用 新建 动态链接库(DLL) (这个项目就可以在创建时 选择 添加到解决方案 就会和上面的那个项目建在同一个解决方案里了。)

说明: 根据DLL注入_拦截技术之Hook方式可知,使用dll注入的方式分为以下两种:

  • 远程线程注入

    • 搜了一圈,这个的资料很多:
    • 这个没有用到hook,就是单纯的远程线程注入,代码可以看看,当个范例模板,微信Hook实战记录2:动手实现恶意dll内存插入器
    • 老外写得,也没用到hook,但是非要把这个词加到标题上,内容比较全面,有讲解,比较好Windows API Hooking and DLL Injection
  • 利用hook注入(可以过卡巴斯基)
    • 通过Hook将DLL注入进程:属于文字说明,大致讲清楚了 目标程序和调用程序以及dll三者之间的调用关系,可以看看

2.2 使用DLL的钩子 代码demo

参考:全局钩子 实例(不使用DLL和使用DLL两种):在创建好DLL(Win32 Dynamic-Link Library )工程后,AppWizard会生成相关文件

参考:看雪论坛——[原创]萌新逆向学习笔记——消息钩子键盘记录: 原理和流程讲的很清晰 代码结构也说的很清楚 让我终于不模糊的一个博客 谢谢

参考:Using Hooks->Monitoring System Events 由于MSDN也一直在更新,存在一定版本问题,旧版本查看MSDN中SetWindowsHookEx的例子代码
C++ DLL导出的两种方式和链接的两种方式

2.2.1 问题

2.2.1.1提示#include "stdafx.h"无法打开源文件

这是因为:VS2019新建个MFC单文档应用程序,却发现里面根本没有stdafx.h。那是因为VS2019开始,stdafx.h改名叫pch.h了。[为什么我的程序中没有stdafx.h头文件?](https://blog.csdn.net/aquapisces/article/details/104596602?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param)

2.2.1.2 提示 "const char*"类型的实参与"LPCWSTR"类型的形参不兼容

vs2019中出现“char*”类型的实参与“LPCWSTR”类型的形参不兼容

或者直接在字符串前面加上’L’
使用char变量:“const char *” 类型的实参与 "LPCWSTR"类型的形参不兼容


2.2.1.3 关于TCHAR格式字符的打印输出

鉴于有些地方(函数)要求参数必须为TCHAR格式,所以有时候不能使用char来定义字符串,感谢
如何用c++输出汉字?:第一页最下面的回答。


例如:(VS2019环境)

#include "tchar.h"TCHAR s[30]={0};
GetWindowText(hwnd,s,20);
_tprintf(_T("句柄值为: %p\n"),hwnd);
// 如果使用printf 则控制台显示出的中文就是乱码

2.2.2 函数说明


2.2.2.1 DllMain

MSDN-DllMain entry point

 BOOL WINAPI DllMain(_In_ HINSTANCE hinstDLL,_In_ DWORD     fdwReason,_In_ LPVOID    lpvReserved
);
//或者 在visual studio中新建一个dll后,wizard会自动产一个类似:
BOOL APIENTRY DllMain(
_In_ HMODULE   hModule,_In_ DWORD     fdwReason,_In_ LPVOID    lpvReserved
)

hinstDLL [in]:DLL模块的句柄。 该值是DLL的基地址。DLLHINSTANCEDLLHMODULE相同,因此hinstDLL可用于对需要模块句柄的函数的调用。

fdwReason [in]: 暂时还没有见人用过,似乎不是很有用?

lpvReserved [in]: 同上


2.2.2.2 SetWindowsHookExA

SetWindowsHookExA function 这个函数要写在编译生成dll的.cpp文件中

HHOOK SetWindowsHookExA(int       idHook,HOOKPROC  lpfn,HINSTANCE hmod,DWORD     dwThreadId
);

int hook——指定事件的钩子ID,如键盘事件WH_KEYBOARD,设置后只对键盘输入起反应。(我用的是 WH_GETMESSAGE 序号为3 检测发送给消息队列的消息 )

HOOKPROC lpfn——钩子的处理函数,若设置的是键盘输入钩子,必须是微软定义的一个叫KeyboardProc的函数。(同理,如果设置的是WH_GETMESSAGE 则最好使用GetMsgProc callback function这个函数,下面会介绍)

HINSTANCE hmod——模块句柄,因此一般设置钩子的地方在DLL中。(在DLL初始化,即 DllMain()中可以获取到当前本dll模块的句柄 就是第一个参数 hinstDLLhModule

DWORD dwThreadId——需要设置钩子的线程ID,倘若为0则为全局钩子(所有程序都钩)(也可以设置成一个 threadId 只钩自己想要的那个进程的)。

返回值 HHOOK所以一般都会现在DLLMain程序附近定义一个HHOOK,


2.2.2.3 GetMsgProc

GetMsgProc callback function

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

作用:用于SetWindowsHookEx 的一个 程序或者库 定义的回调函数,当GetMessagePeekMessage收到一个来自程序消息队列的信息后被系统调用,在将检索到的信息返回给调用者(程序)之前,系统会先把信息传给hook程序。HOOKPROC 类型定义了一个指向这个回调函数的指针.(GetMsgProc 是用于程序定义或者库定义函数的一个占位符,所以推荐采用这个钩子类型的时候,使用这个函数来写自己的功能)

示例:可以参考MSDN给出的Using Hooks->Monitoring System Events 中有关于 WH_GETMESSAGE 部分的函数

code [in] int.: 明确这个hook程序是否必须要处理这个消息,如果code值为HC_ACTION(hook chain action),则这个hook程序必须处理这个消息;如果code值小于0,则hook程序必须把这个消息传给CallNextHookEx函数而无需(这个hook函数)进一步处理,同时由CallNextHookEx返回值

wParam [in] type: WPARAM 指示这个消息是否要从队列中移除,有以下可选值: PM_NOREMOVE 0x0000,即 pm no remove, 这个消息还没有从队列中移除,如果有程序调用PeekMessage函数,则需要明确 PM_NOREMOVE 标志);PM_REMOVE 0x0001 这个消息以及被从消息队列中移除了,如果某个应用程序调用了 GetMessagePeekMessage,需要明确PM_REMOVE标志。

lParam [in] LPARAM 指向包含了消息详细内容的MSG结构体的一个指针

返回值: 如果 code值小于0,则这个hook程序必须返回CallNextHookEx返回的值;如果code值大于等于0,还是强烈建议调用CallNextHookEx函数并返回它返回的值,否则,其他安装了WH_GETMESSAGE钩子的程序就无法收到hook的消息,就可能达不到预期的正确反应(影响了程序收到消息的正确反应了)。如果这个hook程序没有调用CallNextHookEx 则返回值应该为0

注意事项:GetMsgProc钩子程序可以检查或者修改消息。当hook程序把控制权交还给系统后, GetMessagePeekMessage函数会返回消息及修改的内容给最初调用它的那个应用程序
通过调用SetWindowsHookEx时明确WH_GETMESSAGE钩子类型和指向这个程序钩子的指针,一个程序就安好了这种钩子程序。


2.2.2.4 UnhookWindowsHookEx

UnhookWindowsHookEx function 这个函数用来移除通过SetWindowsHookEx安装在钩子链上的hook程序

 BOOL UnhookWindowsHookEx(HHOOK hhk);

HHOOK hhk 要被移除的钩子句柄,钩子是调用 SetWindowsHookEx获取的钩子
返回值 返回0 代表函数执行失败 ; 非0 代表函数执行成功


2.2.2.5 CallNextHookEx

CallNextHookEx function:将hook信息传递给当前钩子链中的下一个hook程序。一个hook程序可以在处理hook信息之前或者之后调用这个函数

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

hhk Type: HHOOK This parameter is ignored. 这个参数是被忽略的。。。??? 所以传递不传递其实无所谓。。

nCode Type: int:传递给当前hook程序的hook code,下一个hook程序会使用这个code来决定如何处理发来的hook信息。

wParam Type: WPARAM 传递给当前的hook程序的wParam值,这个参数的含义取决于与当前的hook链相关的hook类型

lParam Type: LPARAM 传递给当前hook程序的lParam的值. 这个参数的含义取决于与当前的hook链相关的hook类型

Return value Type: LRESULT这个值是由hook链中下一个hook程序返回的。当前的hook程序必须也返回这个值。返回值的含义取决于hook类型。更多信息,需要看每个具体的hook程序的描述。


2.2.2.6 StringCchPrintfA

StringCchPrintf is a replacement for the following functions:

  • sprintf, swprintf, _stprintf
  • wsprintf
  • wnsprintf
  • _snprintf, _snwprintf, _sntprintf
STRSAFEAPI StringCchPrintfA(STRSAFE_LPSTR  pszDest,size_t         cchDest,STRSAFE_LPCSTR pszFormat,...
);

2.2.2.7 GetModuleFileNameA

检索包含指定模块的文件的标准路径。 该模块必须已被当前进程加载。
要定位由另一个进程加载的模块的文件,请使用GetModuleFileNameEx函数。

DWORD GetModuleFileNameA(HMODULE hModule,LPSTR   lpFilename,DWORD   nSize
);

hModule 被加载模块的句柄(需要这个模块的路径)。如果这个参数为NULL,则GetModuleFileName 函数会检索当前进程的可执行文件的路径.GetModuleFileName函数不会检索使用LOAD_LIBRARY_AS_DATAFILE标志加载的模块的路径。 有关更多信息,请参见LoadLibraryEx。

lpFilename 指向存储模块路径的buffer的指针,如果路径的长度小于nSize参数指定的,则函数执行成功,路径作为一个null-terminated的字符串返回;如果路径长度超过nSize参数指定的,则函数也执行成功,返回包括终止空字符在内的截断后的nSize长度的字符串。
Windows XP: The string is truncated to nSize characters and is not null-terminated.

The string returned will use the same format that was specified when the module was loaded. Therefore, the path can be a long or short file name, and can use the prefix “?”. For more information, see Naming a File.

nSizelpFilename 申请的buffer大小,数据类型是:TCHARs.(可以查看下面 常见数据定义中关于 TCHAR的说明)

常见使用参考MSDN提供的示例程序-Installing a Service:

TCHAR szPath[MAX_PATH];if( !GetModuleFileName( "", szPath, MAX_PATH ) ){printf("Cannot install service (%d)\n", GetLastError());return;}

其中,MAX_PATH是预定义的常量 ,如下


2.2.2.8 wcsrchr和_wcsicmp

  1. MSDN document——strrchr, wcsrchr, _mbsrchr, _mbsrchr_l
wchar_t *wcsrchr(wchar_t *str,wchar_t c
); // C++ only
const wchar_t *wcsrchr(const wchar_t *str,wchar_t c
); // C++ only

这一系列的函数都是用来在str中搜索c最后一次出现的位置(字符串索引表示)
str 执行搜索的非终止字符串(String) 字符串 双引号

c 要定位的字符(Character ) 单个字符 单引号

返回值 返回cstr中最后一次出现的位置的指针,如果没有找到,就返回NULL

  1. MSDN文档——_stricmp, _wcsicmp, _mbsicmp, _stricmp_l, _wcsicmp_l, _mbsicmp_l
    这系列函数进行字符串间的比较(大小写敏感)
int _wcsicmp(
const wchar_t *string1,
const wchar_t *string2
);

string1, string2 待比较的两个非终止字符串

locale 特定区域设置 en-US zh-CN(主要用来进行语言设置)

返回值
< 0 则 string1 <string2;
0 则string1=string2;
> 0 则string1 >string2

可以通过调用setlocale_wcsicmp工作在Latin 1字符环境下。C locale是默认有效的,因此,ä 不等于 Ä(C locale是区分大小写的)。在调用_wcsicmp前调用setlocale 并填入除了C locale之外的任何locale,下面的程序就说明了_wcsicmp是极易受到locale的影响的。

#include <string.h>
#include <stdio.h>
#include <locale.h>int main() {setlocale(LC_ALL,"C");   // in effect by default 默认有效printf("\n%d",_wcsicmp(L"ä", L"Ä"));   // compare failssetlocale(LC_ALL,"");printf("\n%d",_wcsicmp(L"ä", L"Ä"));   // compare succeeds
}

MSDN文档——setlocale, _wsetlocale
这两个函数一般是连用的(_wcsicmp页面给出的示例程序)

char *setlocale(int category,const char *locale
);setlocale( LC_ALL, "" );

最常遇到的上述这个情况,设置locale为默认,
which is the user-default ANSI code page obtained from the operating system. The locale name is set to the value returned by GetUserDefaultLocaleName. The code page is set to the value returned by GetACP.


2.2.2.9 OutputDebugString

MSDN官方文档-OutputDebugStringW function

void OutputDebugStringW(
LPCWSTR lpOutputString
);

这个函数的作用是 把字符串发到debugger中进行显示。

lpOutputString 非终止字符串 (没有返回值 直接打印在debugger中了)

注意:以前操作系统不会输出通过OutputDebugStringW 函数输出的Unicode字符串,而是只输出ASCII字符串。为了强制OutputDebugStringW 正确输出Unicode字符串,debuggers 需要调用WaitForDebugEventEx 来选择新的行为。一旦调用WaitForDebugEventEx ,操作系统就知道debugger支持Unicode并并且专门选择接收Unicode字符串。


2.2.2.10 EnumChildWindows

  1. EnumChildWindows function
  2. EnumChildProc callback function
BOOL EnumChildWindows(HWND        hWndParent,WNDENUMPROC lpEnumFunc,LPARAM      lParam
);

hWndParent Type: HWND. 要遍历的子窗口的父窗口的句柄,如果这个参数是NULL,则这个函数就等价于EnumWinodws

lpEnumFunc Type: WNDENUMPROC指向一个自己(写的程序)定义的回调函数,可以参考EnumChildProc

lParam Type: LPARAM自己(写的程序)的一个定义的值,会被传递给回调函数

BOOL CALLBACK EnumChildProc(_In_ HWND   hwnd,_In_ LPARAM lParam
);

用户自己写的一个回调函数,被用于EnumChildWindows函数,接收子窗口句柄。EnumChildProc是一个用于用户自定义函数名字的占位符。

hwnd [in]一个指向在EnumChildWindows中指定的父窗口的子窗口的句柄

lParam [in] EnumChildWindows给出的用户自定义的值。

注意:如果想继续遍历,则这个回调函数需要返回TRUE,要停止遍历,就返回FALSE

记录一波,一个获取某个父窗口下一个特定类名的子窗口的句柄(要获取返回的句柄进行进一步操作,找了很久,终于在GitHub-TiebaManager/TiebaManager/LogHelper.cpp
)里找到了参考的示例demo。Github项目地址是dariner/TiebaManager,具体的文件是 LogHelper.cpp

m_explorerHwnd = m_logExplorer.m_hWnd;
EnumChildWindows(m_explorerHwnd, EnumChildProc, (LPARAM)&m_explorerHwnd);
//或者更简单的可以写成HWDN childhWnd;
EnumChildWindows(m_explorerHwnd, EnumChildProc, (LPARAM)&childhWnd);
// 这里记住 句柄值前面要加 &
printf("想要的子窗口的句柄为:%p\n",childhWnd)// 枚举寻找Internet Explorer_Server窗口
BOOL CALLBACK CLog::EnumChildProc(HWND hwnd, LPARAM lParam)
{TCHAR buf[30];GetClassName(hwnd, buf, _countof(buf));if (_tcscmp(buf, _T("Internet Explorer_Server")) == 0){*(HWND*)lParam = hwnd;// 把lParam回传回去 需要做类型转换return FALSE;}elsereturn TRUE;
}

2.2.3 常见数据定义

2.2.3.1 HRESULT

Common HRESULT Values

2.2.3.2 MSG

MSG structure

typedef struct tagMSG {HWND   hwnd;UINT   message;WPARAM wParam;LPARAM lParam;DWORD  time;POINT  pt;DWORD  lPrivate;
} MSG, *PMSG, *NPMSG, *LPMSG;

hwnd Type: HWND 收到这个消息的窗口程序的窗口句柄,当消息是一个线程消息(Thread message)的时候这个值为NULL

message Type: UINT消息标识符。 应用程序只能使用低位字; 高位字由系统保留。

wParam Type: WPARAM关于消息的额外信息,其准确的含义依赖于当前消息成员的值

lParam Type: LPARAM关于消息的额外信息,其准确的含义依赖于当前消息成员的值

time Type: DWORD 消息发送的时间

pt Type: POINT消息发送时光标在屏幕坐标系的位置

lPrivate 没有说明??

2.2.3.X 系统钩子和线程钩子

SetWindowsHookEx()函数的最后一个参数决定了此钩子是系统钩子还是线程钩子。

线程钩子 用于监视指定线程的事件消息。线程勾子一般在当前线程或者当前线程派生的线程内。

系统勾子 监视系统中的所有线程的事件消息。因为系统勾子会影响系统中所有的应用程序,所以勾子函数必须放在独立的动态链接库(DLL) 中。系统自动将包含"钩子回调函数"的DLL映射到受钩子函数影响的所有进程的地址空间中,即将这个DLL注入了那些进程。

如果对于同一事件(如鼠标消息)既安装了线程钩子又安装了系统钩子,那么系统会自动先调用线程钩子,然后调用系统钩子。
SetWindowsHookExA function——Remarks:WH_GETMESSAGE既可以是Thread钩子又可以是global钩子

2.2.3.3 LPTSTR

Windows Data Types


A pointer to a null-terminated string of 16-bit Unicode characters

A pointer to a null-terminated string of 8-bit Windows (ANSI) characters.


2.2.3.4 TCHAR

TCHAR-百度百科

因为C++支持两种字符串,即常规的ANSI编码(使用"“包裹)和Unicode编码(使用L”“包裹),这样对应的就有了两套字符串处理函数,比如:strlen和wcslen,分别用于处理两种字符串
微软将这两套字符集及其操作进行了统一,通过条件编译(通过_UNICODE和UNICODE宏)控制实际使用的字符集,这样就有了_T(”")这样的字符串,对应的就有了_tcslen这样的函数
为了存储这样的通用字符,就有了TCHAR:
当没有定义_UNICODE宏时,TCHAR = char,_tcslen =strlen
当定义了_UNICODE宏时,TCHAR = wchar_t , _tcslen = wcslen
当我们定义了UNICODE宏,就相当于告诉了编译器:我准备采用UNICODE版本。这个时候,TCHAR就会摇身一变,变成了wchar_t。而未定义UNICODE宏时,TCHAR摇身一变,变成了unsignedchar。这样就可以很好的切换宽窄字符集。
tchar可用于双字节字符串,使程序可以用于中日韩等国 语言文字处理、显示。使编程方法简化。

MSDN-TCAHR,最后还是直接看官方文档最管用了
一个Win32字符串可以使用ANSI,DBCS或者Unicode字符串来描述,对于ANSI和DBCS平台,TCHAR定义如下:

typedef char TCHAR;

对于Unicode平台,TCHAR类型就等同于WCHAR类型。MAPI客户端可以使用TCHAR数据类型来表示WCHAR或者char类型的字符串。要确保使用时限制了语言类型是UNICODE编码。MAPI将解释平台信息,并在内部将TCHAR转换为适当的字符串。 MAPI属性类型PT_TSTRING的工作方式与TCHAR数据类型一样。 当平台支持Unicode时,将在编译时将类型PT_TSTRING的属性分配为类型PT_UNICODE。 当平台不支持Unicode时,将为这些属性分配类型PT_STRING8。

PS:之前在发生什么 提示 "const char*"类型的实参与"LPCWSTR"类型的形参不兼容 这个问题的时候 将项目属性中的字符集设置为了 多字节编码,得改回去了。
下次在遇到这种问题,就每个字符串前面加上L 或者TEXT()就好咧


2.2.4 C++常规知识复习

2.2.4.1 单引号和双引号

不同于Python,在C++中,单引号括住的只是字符,双引号括住的是字符串

'\\' //这是转义符的\

2.3 DLL的概念

去看 MSDN威武-什么是DLL?

动态链接库 (DLL):
使用 DLL 有助于促进代码的模块化、代码重用、内存的有效使用和减少所占用的磁盘空间。 因此,操作系统和程序能够更快地加载和运行,并且在计算机中占用较少的磁盘空间。
当程序使用 DLL 时,一个称为依赖性的问题可能导致该程序无法运行。 当程序使用 DLL 时,就会创建一个依赖项。 如果其他程序改写和损坏了该依赖项,原来的那个程序就可能无法成功运行。
在引入 Microsoft .NET Framework 之后,大多数依赖性问题都已经通过使用程序集消除了。

DLL 是一个包含可由多个程序同时使用的代码和数据的库。 例如,在 Windows 操作系统中,Comdlg32 DLL 执行与对话框有关的常见函数。 因此,每个程序都可以使用该 DLL 中包含的功能来实现“打开”对话框。 这有助于促进代码重用和内存的有效使用。

2.3.1如何查看dll里的函数

使用Visual Studio自带的工具,菜单栏->工具->命令行->开发者命令提示,命令行里输命令:dumpbin /exports dll文件完整路径,即可得到DLL的接口。

dumpbin /exports "C:\Windows\System32\user32.dll"
或者
dumpbin /exports "C:\Windows\System32\user32.dll">1.txt
(会在 你当前这个Visual Studio项目下(从这里打开的 这个命令行工具) 看到 1.txt这个文件)

这样就能查看dll中的函数信息,上面这个返回以下,很多东西

查看静态库的信息要用命令行来实现:
  dumpbin /LINKERMEMBER Test.lib > 1.txt

2.3.2 如何查看exe依赖的dll

因为在安装程序的时候经常可以看到 进度条上面飞快滚动安装 C:\Windows\System32\XXX.dll
同样也可以通过 dumpbin这个命令行工具来查看,
dumpbin /dependents [文件路径]
比如要查看c盘根目录下的a.exe就输入
dumpbin /dependents “C:\a.exe"

dumpbin /dependents "D:\负荷箱校准装置\负荷箱校准装置.exe" #这个打不开有可能是因为是中英文的问题。。。 不确定 也可能是程序编写的不标准 所以显示不出来
dumpbin /dependents "D:\PotPlayer64\PotPlayerMini64.exe"

2.3.3 DLL 开发

什么是DLL?中 DLL 开发部分,给出了如何去编写一个DLL文件的标准操作/流程以及一个代码demo

  • DLL 的类型
    当您在应用程序中加载 DLL 时,可以使用两种链接方法来调用导出的 DLL 函数。 这两种链接方法是加载时动态链接和运行时动态链接。
    加载时动态链接
    在加载时动态链接中,应用程序像调用本地函数一样对导出的 DLL 函数进行显式调用。 要使用加载时动态链接,请在编译和链接应用程序时提供头文件 (.h) 和导入库文件 (.lib)。 当您这样做时,链接器将向系统提供加载 DLL 所需的信息,并在加载时解析导出的 DLL 函数的位置。
    运行时动态链接
    在运行时动态链接中,应用程序调用 LoadLibrary 函数或 LoadLibraryEx 函数以在运行时加载 DLL。 成功加载 DLL 后,可以使用 GetProcAddress 函数获得要调用的导出的 DLL 函数的地址。 在使用运行时动态链接时,无需使用导入库文件。

  • https://www.cnblogs.com/hanxinle/p/4042831.html

  • https://www.cnblogs.com/xiangshancuizhu/archive/2011/09/13/2175108.html

  • https://blog.csdn.net/iwilldoitx/article/details/78698213

  • https://blog.csdn.net/qq_34097715/article/details/79540933

  • https://support.microsoft.com/zh-cn/help/815065/what-is-a-dll

  • https://blog.csdn.net/weixin_41320468/article/details/101305139?utm_medium=distribute.pc_relevant_download.none-task-blog-blogcommendfrombaidu-1.nonecase&depth_1-utm_source=distribute.pc_relevant_download.none-task-blog-blogcommendfrombaidu-1.nonecas

  • DLL注入_拦截技术之Hook方式

Visual Studio关于hook项目的简单使用相关推荐

  1. 如何打开sln文件并显示窗口_在.sln文件中设置Visual Studio默认启动项目的简单方法...

    昨天在一台电脑上用git新签出一个项目进行build,却出现一堆编译错误,而在原先的开发机上build无任何错误.对比分析后发现,开发机上VS的启动项目(startup project)与这台电脑上的 ...

  2. 在.sln文件中设置Visual Studio默认启动项目的简单方法

    昨天在一台电脑上用git新签出一个项目进行build,却出现一堆编译错误,而在原先的开发机上build无任何错误.对比分析后发现,开发机上VS的启动项目(startup project)与这台电脑上的 ...

  3. 【Qt】Visual Studio编译Qt项目报Qt Version错误

    00. 目录 文章目录 00. 目录 01. 问题描述 02. 开发环境 03. 问题分析 04. 问题解决 05. 附录 01. 问题描述 使用Visual Studio编译Qt项目时候,报以下错误 ...

  4. Visual Studio Code打开项目

    Visual Studio Code打开项目:

  5. visual studio(vs)中项目解决方案的目录组织安排

    visual studio(vs)中项目解决方案的目录组织安排 参考文章: (1)visual studio(vs)中项目解决方案的目录组织安排 (2)https://www.cnblogs.com/ ...

  6. 2. C++ Visual Studio中同一个项目包含多个有main函数的源文件怎么分别运行?

    C++编程问题 C++编程问题-目录 2. C++ Visual Studio中同一个项目包含多个有main函数的源文件怎么分别运行? C++编程问题-目录 2. C++ Visual Studio中 ...

  7. 使用Visual Studio建立网站项目

    使用Visual Studio建立网站项目 第一步.依次点击文件->新建->项目(不是网站) 第二步.选择要新建的项目类型 在下图中把易错的地方已经圈出来了,注意不要选择其他的,选择空We ...

  8. 通过Visual Studio 2005的项目模板来加速开发过程

    page by Ameet Phadnis Feedback     简介: 记得在我第一次做项目的时候,我就在尝试找出一种方法来提高项目开始的效率(jump start my projects).在 ...

  9. 如何创建从Visual Studio到Wolfram Mathematica的简单调用

    目录 介绍 如何创建 结论 介绍 如今,随着计算机技术的飞速发展,并非总是能够使用一种编程语言来创建复杂的应用程序.不同的语言各有优缺点,通常,它们都不满足已开发应用程序的要求.解决这种情况的方法是使 ...

最新文章

  1. gamma分布_轻松理解gamma分布
  2. 【bzoj1444】[Jsoi2009]有趣的游戏 AC自动机+矩阵乘法
  3. 操作系统习题4—进程死锁
  4. JLabel标签文字换行
  5. Java(发布/订阅模式)
  6. php获取页面的可视内容高度,网页制作技巧:获取页面可视区域的高度_css
  7. C. Vanya and Scales
  8. 90TB显存!英伟达发布新一代SuperPod超算,AI算力新巅峰!
  9. 螃蟹为什么横着走,今天为大家介绍为什么螃蟹横着走
  10. 【Linux复习——温故知新
  11. 运行的程序暂停_黄岩人注意!2天后,这项重要业务系统将暂停运行!
  12. 管理系统中计算机er图怎么画,使用PowerDesigner绘制ER图的详细教程
  13. 2022软件测试常见抓包工具
  14. Mapped Statements collection does not contain value for的解决方法
  15. STM32驱动PCF8563,使用模拟IIC
  16. java 的 MD5 算法介绍 总结 使用
  17. C++ 头插法 链表
  18. 获取项目服务器ip,java获取服务器ip地址
  19. 华为WLAN基础全套学习笔记整理
  20. Java校招期望薪资_2019互联网校招薪资出炉,整体超越 BAT、华为

热门文章

  1. scrum回顾_如何开好scrum回顾会议
  2. 【数据结构与算法分析】0基础带你学数据结构与算法分析09--线索二叉树 (TBT)
  3. HBase Region原理总结归纳
  4. php网页文字,PHP语言之网页文字简繁转换函数
  5. 【论文简述】Point-MVSNet:Point-Based Multi-View Stereo Network(ICCV 2019)
  6. HTTP 2 还没整明白呢,HTTP 3 又来了?
  7. 浅谈Visual C#进行图像处理
  8. 安徽高二计算机vb基础知识,vb计算机考试_安徽文师良心产品
  9. Linux服务器使用WonderShaper进行网络速度限制
  10. 成都有哪些有名的互联网公司?