DLL是通过问卷映射实现,只占一个内存空间,每个DLL收到一份映像,DLL被加载之后系统页面文件明显变大,内存使用明显变大。
DLL引用分显示隐式
如果有lib+dll+头文件
则:建工程的时候选择导出符号
#pragma comment(lib, “libname.lib”)
#include “libname.h”
func();
只有DLL文件 LoadLibrary GetProcAddress
因为在C++中有函数重整,(函数名会被自动改变),我们需要在DLL中,在函数前加上 extern "C"
DLL劫持
就是Loadlibrary写相对路径,Windows会按着程序所在目录,加载dll的当前目录,系统system32,Windows目录,Path环境变量中的目录去加载dll,所以破坏者就在比正真dll前的路径去加载dll。
微软才去的安全措施是将加载优先顺序改变,吧加载dll时所在的当前目录放后到windos目录后。策略在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\safedllsearchmode里。
还有就是在HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs中的必须在SYSTEM32目录加载DLL。(但是ExcludeFromKnownDlls中可以不从SYSTEM32里加载)
防护措施
LoadLibrary()使用绝对路径
白名单,
SetDllDirectrory()把当前目录从搜索范围去掉。
解决措施
释放dll时候可以通过文件过滤监控*.dll,拿到dll去查MD5值。
全局钩子
指调用SetWindowsHookEx,为一些消息注册回调函数。比如鼠标键盘。这个钩子就能被利用到。
SetWindowsHookEx 全局钩子
HHOOK WINAPI SetWindowsHookEx(
__in int idHook, \\钩子类型
__in HOOKPROC lpfn, \\回调函数地址
__in HINSTANCE hMod, \\包含lpfn的实例句柄,就是这个函数所在的进程
__in DWORD dwThreadId); \\线程ID,如果为0,则监控所有线程的全局钩子
钩子的类型
WH_CALLWNDPROC and WH_CALLWNDPROCRET(sendMessge()要注入
WH_CBT
WH_DEBUG//要比其他钩子级别高,会先调用,可以用来屏蔽其他钩子
WH_FOREGROUNDIDLE
WH_GETMESSAGE
WH_JOURNALPLAYBACK
WH_JOURNALRECORD
WH_KEYBOARD_LL//键盘钩子,全局有效,无需注入,调试钩子屏蔽不了,LL低级钩子LowLevel,系统处理前处理,用LowLevelHooksTimeOut控制超时
WH_KEYBOARD//在系统处理后处理,注入式键盘钩子,要先注入dll
WH_MOUSE_LL
WH_MOUSE
WH_MSGFILTER and WH_SYSMSGFILTER
WH_SHELL
得到控制权的钩子函数在完成对消息的处理后,如果想要该消息继续传递,那么它必须调用另外一个SDK中的API函数CallNextHookEx来传递它。钩子函数也可以通过直接返回TRUE来丢弃该消息,并阻止该消息的传递。
全局钩子:
实现文件如下。其中g_hWnd为所有进程共享,并且绕过了系统对可写数据的写时复制机制,维护的是一份拷贝
// Hook.cpp
#include <windows.h>
HHOOK g_hMouse = NULL;
HHOOK g_hKeyboard = NULL;
// 为Hook.DLL创建一个新的节,将全局变量g_hWnd放入其中
#pragma data_seg("MySec")
HWND g_hWnd = NULL;
#pragma data_seg()
// 设置刚创建的节为共享的节
#pragma comment(linker, "/section:MySec,RWS")
// 鼠标钩子过程
LRESULT CALLBACK MouseProc(
int nCode, // hook code
WPARAM wParam, // message identifier
LPARAM lParam // mouse coordinates
)
{
return 1; // 屏蔽所有鼠标消息
}
// 键盘钩子过程
LRESULT CALLBACK KeyboardProc(
int code, // hook code
WPARAM wParam, // virtual-key code
LPARAM lParam // keystroke-message information
)
{
// 后门按键用于结束该进程
if (VK_F2 == wParam)
{
::SendMessage(g_hWnd, WM_CLOSE, 0, 0);
UnhookWindowsHookEx(g_hKeyboard);
UnhookWindowsHookEx(g_hMouse);
}
else
{
return 1; // 屏蔽所有键盘消息
}
}
// 安装鼠标钩子过程的函数
void SetHook(HWND hwnd) // 参数是为了让dll获得调用进程的主窗口的句柄
{
g_hWnd = hwnd;
// hook所有进程的鼠标、键盘消息
g_hMouse = SetWindowsHookEx(WH_MOUSE, MouseProc, GetModuleHandle("Hook.dll"), 0);
g_hKeyboard = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, GetModuleHandle("Hook.dll"), 0);
}
WH_KEYBOARD_LL 低级键盘钩子 不需要DLL 直接设置即可
// keyboardhook.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include<iostream>
#include<windows.h>
using namespace std;
HHOOK g_Hook;
LRESULT CALLBACK LowLevelKeyboardProc(INT nCode, WPARAM wParam, LPARAM lParam)
{//wParam是类型,比如按下或弹起,lPARAM是详细信息,比如那个键
KBDLLHOOKSTRUCT *pkbhs = (KBDLLHOOKSTRUCT *)lParam;
BOOL bControlKeyDown = 0;
switch (nCode)
{
case HC_ACTION:
{
// Check to see if the CTRL key is pressed
bControlKeyDown = GetAsyncKeyState (VK_CONTROL) >> ((sizeof(SHORT) * 8) - 1);
//Disable CTRL+ESC
if (pkbhs->vkCode == VK_ESCAPE && bControlKeyDown)
return 1;
if(wParam == WM_KEYUP)
printf("%c", pkbhs->vkCode);
break;
}
}
//return CallNextHookEx(g_Hook, nCode, wParam, lParam); //回调
return 1;
}
int _tmain(int argc, _TCHAR* argv[])
{
MSG msg;
g_Hook=(HHOOK)SetWindowsHookEx(WH_KEYBOARD_LL,
(HOOKPROC)LowLevelKeyboardProc, GetModuleHandleW(0),0);
while(GetMessageW(&msg,0,0,0))DispatchMessageW(&msg);
return 0;
}
WH_KEYBOARD的调用dll实现
/
// CHookTestDlg message handlers
// 导入函数
__declspec(dllimport) void SetHook(HWND hwnd);
BOOL CHookTestDlg::OnInitDialog()
{
// TODO: Add extra initialization here
// 顶层窗口及最大化窗口的实现
int cxScreen, cyScreen;
cxScreen = GetSystemMetrics(SM_CXSCREEN);
cyScreen = GetSystemMetrics(SM_CYSCREEN);
SetWindowPos(&wndTopMost, 0, 0, cxScreen, cyScreen, SWP_SHOWWINDOW);
// 调用DLL中的函数
SetHook(m_hWnd);
return TRUE; // return TRUE unless you set the focus to a control
}
QQ安全密码框的全局钩子
qq和其他人的低级键盘钩子组织在一个链表里面,注册越晚的越早拿到消息,
1,WH_DEBUG , WH_KEYBOARD_LL, WH_MOUSE_LL
2, WH_DEBUG> WH_KEYBOARD_LL> WH_KEYBOARD
3,后加载的先获得消息并执行
4,QQ定时UNHOOK和HOOK上面3个钩子,保证自己最后被安装,自然就是先取得正确的按键信息,然后阻止这个 消息继续传递下来给别人,就算传下去交给别人,也要修改按键信息,传一个错误的下去
挂钩ShadowSSDT NtUserSetWindowsHookEx保护键盘钩子。
-----------------------------------------------------------------------------------------------------------------------------------------
DLL注入
在下钩子的时候可能我们需要用到通信 可以使用映射,管道,
其实还有一种方便的,就是PE共享节
放到共享节里面的变量时是同实例可以访问的 一般用到多开通信 多开检测,全局dll钩子
PE共享节
#pragma data_seg (".shared")
ULONG g_ulNum;
#pragma data_seg ()
#pragma comment(linker,"/SECTION:.shared,RWS") //定义成为共享节的话
RWS:读/写/共享,共享段里的变量可供进程的多个实例访问,普通的全局变量,只对一个实例有效,不同进程的变量之类的一般都是私有隔离的
直接注入DLL文件
1。打开目标进程
2。获取待注入的DLL路径,分配一块目标进程内的内存,将路径拷贝到该内存中
3。获取kernel32中的LoadLibraryA地址
4。调用CreateRemoteThread,在目标进程中执行loadlibrary + DLL的动作
5。DLL中的DLLMAIN执行
6。释放分配的目标进程中的内存
7。获取kernel32中的FreeLibrary地址
8。调用CreateRemoteThread,在目标进程中执行FreeLibrary + DLL的动作
要打开目标进程,要Debug权限打开
BOOL AddDebugPrivilege(void)
{
TOKEN_PRIVILEGES tp;
LUID luid;
HANDLE hToken;
if(!LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid))
{
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid=luid;
tp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
if(!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES,&hToken))
{
return FALSE;
}
if(!AdjustTokenPrivileges(hToken,FALSE,&tp,sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES)NULL,(PDWORD)NULL))
{
return FALSE;
}
return TRUE;
}
HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD |
PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE |
PROCESS_VM_READ,FALSE, PID);
有些受保护的进程无法打开,需要驱动去关闭进程EPROCESS里的PSIsProtectedProcess标志位和关闭DLL签名策略
其实并不需要这么多函数 但一下函数只能使用在发布版中
typedef long (*RTLADJUSTPRIVILEGE)(ULONG,ULONG,ULONG,PVOID);
RTLADJUSTPRIVILEGE RtlAdjustPrivilege;
RtlAdjustPrivilege=(RTLADJUSTPRIVILEGE)GetProcAddress(LoadLibraryW(L"ntdll.dll"),"RtlAdjustPrivilege");
RtlAdjustPrivilege(20,1,0,&dwRetVal);//debug
RtlAdjustPrivilege(19,1,0,&dwRetVal);
注入代码
int InjectDll( HANDLE hProcess, TCHAR* szLibPath)
{
HANDLE hThread;
void* pLibRemote = 0;//注入到哪
DWORD hLibModule = 0;
HMODULE hKernel32 = ::GetModuleHandle(_T("Kernel32"));
LPTHREAD_START_ROUTINE pLoadFunc = NULL;
pLoadFunc = (LPTHREAD_START_ROUTINE) ::GetProcAddress(hKernel32,"LoadLibraryW");
if (szLibPath == NULL ||hProcess == NULL ||pLoadFunc == NULL)
{
return FALSE;
}
pLibRemote = ::VirtualAllocEx( hProcess, NULL, (_tcslen(szLibPath) + 1)*sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE );
if( pLibRemote == NULL )
return false;
::WriteProcessMemory(hProcess, pLibRemote, (void*)szLibPath,(_tcslen(szLibPath) + 1)*sizeof(TCHAR),NULL);
hThread = ::CreateRemoteThread( hProcess, NULL, 0,(LPTHREAD_START_ROUTINE)pLoadFunc,
pLibRemote, 0, NULL );
if( hThread == NULL )
goto JUMP;
DWORD dwError = GetLastError();
::WaitForSingleObject( hThread, INFINITE );
dwError = GetLastError();
::GetExitCodeThread( hThread, &hLibModule );
::CloseHandle( hThread );
JUMP:
::VirtualFreeEx( hProcess, pLibRemote, sizeof(szLibPath), MEM_RELEASE );
if( hLibModule == NULL )
return false;
return hLibModule;
}
内存注入
写一个loadlibrary1就是先把DLL加载到内存中,进行重建,然后执行DllMain
loadlibrary1负责解析PE,构建内存节,重定位修复,导入表,IAT初始化,对该DLL使用的其它DLL使用LdrLoadDLL来加载,最后执行dllmain。
步骤:
将用于memload代码(loadlibrary1)保存为普通文件(shellcode)
将dll文件读入内存
将保存负责解析加载dll的shellcode(loadlibrary1)的文件读入内存
通过WriteProcessMemory把上面代码(loadlibrary1)和数据(dll,对应api)写入进程目标。
CreateRemoteThread让上面代码和数据执行。
loadlibrary1(dll)
参考资料
更多钩子https://msdn.microsoft.com/en-us/library/ms644959(VS.85).aspx