TLS and keyboard hooking
TLS回调
TLS(Thread Local Storage) 线程本地存储,主要用于提供本地线程访问局部数据的句柄。
下面来一段官方解释:
使用线程本地存储 (TLS),可以为进程可以使用全局索引访问的每个线程提供唯一数据。一个线程分配索引,其他线程可以使用它来检索与索引关联的唯一数据
原理图:
==TLS主要用于程序的反调试检测==
它在运行EP(entrypoint 程序入口代码)代码前执行
TLS结构体:
可以看到TLS结构体中保存着回调函数的地址
可以打开CFF explore软件直接查看存放TLS回调函数数组的地址
打开od查看它的地址为401000
汇编直接定位过去就出来了
TLS回调函数是在进程或线程创建或终止时调用,如果某个进程存在一个线程,那从程序的创建到销毁需要4次调用回调函数
TLS回调函数定义:
注册TLS程序示例代码:
#include <windows.h>
#pragma comment(linker, "/INCLUDE:__tls_used")
void print_console(char* szMsg)
{
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
WriteConsoleA(hStdout, szMsg, strlen(szMsg), NULL, NULL);
}
void NTAPI TLS_CALLBACK1(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
char szMsg[80] = {0,};
wsprintfA(szMsg, "TLS_CALLBACK1() : DllHandle = %X, Reason = %d\n", DllHandle, Reason);
print_console(szMsg);
}
void NTAPI TLS_CALLBACK2(PVOID DllHandle, DWORD Reason, PVOID Reserved)
{
char szMsg[80] = {0,};
wsprintfA(szMsg, "TLS_CALLBACK2() : DllHandle = %X, Reason = %d\n", DllHandle, Reason);
print_console(szMsg);
}
#pragma data_seg(".CRT$XLX")
PIMAGE_TLS_CALLBACK pTLS_CALLBACKs[] = { TLS_CALLBACK1, TLS_CALLBACK2, 0 };
#pragma data_seg()
DWORD WINAPI ThreadProc(LPVOID lParam)
{
print_console("ThreadProc() start\n");
print_console("ThreadProc() end\n");
return 0;
}
int main(void)
{
HANDLE hThread = NULL;
print_console("main() start\n");
hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
WaitForSingleObject(hThread, 60*1000);
CloseHandle(hThread);
print_console("main() end\n");
return 0;
}
调试TLS回调函数
直接在运行前设置调试选项
添加TLS这里不做阐述
keyboard hooking
消息钩取工作原理
使用SetWindowsHookEx()可以实现消息钩子,钩子函数需要存在于dll文件中,由操作系统调用回调函数
API定义:
在某进程拥有消息,操作系统就会将dll注入进程序中,实现消息钩取
只需要一个程序加载安装钩子所在的dll程序,然后SetWindowsHookEx()注册钩子,操作系统就会检测每一个程序的行为,一旦发生键盘输入,就将dll文件注入。
加载dll注册hook实例:
#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;
// KeyHook.dll 肺爹
hDll = LoadLibraryA(DEF_DLL_NAME);
if( hDll == NULL )
{
printf("LoadLibrary(%s) failed!!! [%d]", DEF_DLL_NAME, GetLastError());
return;
}
// export 窃荐 林家 掘扁
HookStart = (PFN_HOOKSTART)GetProcAddress(hDll, DEF_HOOKSTART);
HookStop = (PFN_HOOKSTOP)GetProcAddress(hDll, DEF_HOOKSTOP);
// 饶欧 矫累
HookStart();
// 荤侩磊啊 'q' 甫 涝仿且 锭鳖瘤 措扁
printf("press 'q' to quit!\n");
while( _getch() != 'q' ) ;
// 饶欧 辆丰
HookStop();
// KeyHook.dll 攫肺爹
FreeLibrary(hDll);
}
dll代码:
#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, '\\');
// 泅犁 橇肺技胶 捞抚阑 厚背秦辑 父距 notepad.exe 扼搁 0 酒囱 蔼阑 府畔窃
// => 0 酒囱 蔼阑 府畔窍搁 皋矫瘤绰 促澜栏肺 傈崔登瘤 臼澜
if( !_stricmp(p + 1, DEF_PROCESS_NAME) )
return 1;
}
}
// 老馆利牢 版快俊绰 CallNextHookEx() 甫 龋免窍咯
// 览侩橇肺弊伐 (趣篮 促澜 扰) 栏肺 皋矫瘤甫 傈崔窃
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
调试hook
用字符串搜索直接定位到main函数
上面得到hookStart地址,然后call,F7直接步入就能进入hookstart函数
这里就是dll中注册键盘hook
直接根据注册时得到的地址进行跳转就能得到hook函数
也可以打开notepad,进行附加attach,先摁F9,让hook注册成功
然后在新加一个模块处停止,option设置
附加后在键盘输入字母,od会立马断住。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 jaytp@qq.com