Windows-注入技术学习总结

目录

1.远程线程注入

2.消息钩子注入

3.输入法注入

4.注册表注入

5.EIP注入

6.APC注入

7.SSDT替换

总结


1.远程线程注入

原理:利用CreateRemoteThread()函数,将自己创建的的线程加载到远程空间进程里去执行。

CreateRemoteThread function (processthreadsapi.h) - Win32 apps | Microsoft Docs

注入流程:

  1. OpenProcess 打开被注入进程句柄。
  2. VirtualAllocEx 在注入进程里申请空间,用于存放注入插件路径的字符串,不然在注入进程里找不到参数。
  3. WriteProcessMemory 往申请的空间里写路径字符串
  4. CreateRemoteThread 注入进程,函数第四个参数写LoadLibrary,因为LoadLibrary函数地址在Kernel32.dll中,在每个进程中映射地址都一样,所以不需要再去注入进程查找该函数地址了。第五个参数写路径字符串地址,为LoadLibrary函数的参数。
  5. WaitForSingleObject 等待线程执行完成返回,VirtualFreeEx释放申请空间。

示例代码:

#define TARGETEXE L"notepad.exe"
WCHAR InjectDll[] = L"C:\\Users\\k\\source\\repos\\ConsoleApplication3\\x64\\Release\\TestDll.dll";

DWORD dTargetpid = 0;
 {
     HANDLE hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
     if (hSnapshot != INVALID_HANDLE_VALUE)
     {
         PROCESSENTRY32W pe32;
         pe32.dwSize = sizeof(PROCESSENTRY32W);

         if (Process32FirstW(hSnapshot, &pe32))
         {
             do {
                 if (wcscmp(pe32.szExeFile, TARGETEXE) == 0)
                 {
                     dTargetpid = pe32.th32ProcessID;
                     break;
                 }
             } while (Process32NextW(hSnapshot, &pe32));
         }
     }
     CloseHandle(hSnapshot);
 }

 HANDLE hTargethandle =  OpenProcess( PROCESS_ALL_ACCESS, FALSE, dTargetpid);
 if (hTargethandle != INVALID_HANDLE_VALUE)
 {
     //必须具备 PROCESS_VM_OPERATION  访问权限
     LPVOID vaAddress =  VirtualAllocEx(hTargethandle, NULL, sizeof(InjectDll), MEM_COMMIT, PAGE_READWRITE);
     if (vaAddress != nullptr)
     {
         if (WriteProcessMemory(hTargethandle, vaAddress, InjectDll, sizeof(InjectDll), NULL))
         {
             HMODULE hKernel32module = GetModuleHandle(L"kernel32.dll");
             if (hKernel32module)
             {
                 LPTHREAD_START_ROUTINE loadLibraryFunction = (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32module, "LoadLibraryW");
             
                 //必须具备 PROCESS_CREATE_THREAD、 PROCESS_QUERY_INFORMATION、 PROCESS_VM_OPERATION、 PROCESS_VM_WRITE和 PROCESS_VM_READ
                 DWORD dThreadid = 0;
                 HANDLE hThreadhandle = CreateRemoteThread(hTargethandle, NULL, NULL, loadLibraryFunction, vaAddress, NULL, &dThreadid);
                 if (hThreadhandle)
                 {
                     WaitForSingleObject(hThreadhandle, INFINITE);
                     CloseHandle(hThreadhandle);
                 }
             
             }
         }
         VirtualFreeEx(hTargethandle, vaAddress, 0, MEM_RELEASE);
     }
    
     CloseHandle(hTargethandle);
 }

注意要点:

       1.写入字符串是否宽字符,对应着GetProcAddress是LoadLibraryW还是LoadLibraryA,出错将导致注入线程执行无效果;不可以是LoadLibrary,编码能使用是因为windows使用了宏替换。

        2.WriteProcessMemory写入是以字节为单位

  

注入特点:

        1.比较稳定  2.注入时机在目标进程运行之后  3.需要目标进程令牌权限

2.消息钩子注入

原理:钩子是windows中消息处理一个机制,通过安装各种钩子,来监视系统里的消息传递,以及在这些消息到达目标窗口程序之前处理它们。用SetWindowsHookEx()这个API,可以设置的自己钩子,将DLL注入到该进程。只适用带窗口界面的进程注入

SetWindowsHookExA function (winuser.h) - Win32 apps | Microsoft Docs

注入流程:

  1. 编写DLL,显式导出需要执行的注入函数。
  2. 先自己加载该DLL,LoadLibrary获取模块地址,GetProcAddress获取导出函数地址。
  3. 根据待注入的进程id,GetThreadId来获取其当前线程的id。
  4. SetWindowsHookEx,第一个参数一般是WH_GETMESSAGE,第二个参数是导出函数地址,第三个是模块地址,第四个是线程id。

示例代码:

//TestDll.dll

LRESULT MsgProc(int code, WPARAM wParam, LPARAM lParam)
{
    //加载注入插件
    HMODULE hmodule = LoadLibraryW(L"C:\\Users\\k\\Desktop\\Inject.dll");
        
    unsigned __int64 tlsBase = __readgsqword(0x30u); // 获取TLS基址
    VOID** ptagLOCALHOOKCOLLECTION = reinterpret_cast<VOID**>(tlsBase + 2296);
    if (ptagLOCALHOOKCOLLECTION)
    {   
        HHOOK hnext = (HHOOK)(*((unsigned __int64*)ptagLOCALHOOKCOLLECTION + 3));
        return CallNextHookEx(hnext, code, wParam, lParam);
    }

    return 0;
}

extern "C" __declspec(dllexport) HHOOK Messagehook(WCHAR* InjectDll, HMODULE hDllmodule)
{
    return SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)MsgProc, hDllmodule, 0);;
}

注意要点:

        如果设置全局钩子,想要继续调用CallNextHookEx,需要SetWindowsHookEx 返回的HHook句柄,但是进程隔离会导致无法访问。查阅资料,一种思路是设置共享数据段,将HHook句柄置入共享数据段,在其他进程调用MsgProc时候,读取共享,获取到HHook句柄

        个人觉得十分繁琐,于是使用ida  user32.dll 查看 CallNextHookEx实现

只有CLocalHookManager::CallNextHookInChain 使用到

发现该入参并没有实际使用到,只是和CLocalHookManager::GetLocalCollection() 中取出的值对比是否一致,不一致直接返回。

继续看GetLocalCollection()

从TLS上 偏移 2296 上,返回struct tagLOCALHOOKCOLLECTION 指针

于是,避免使用共享数据段,直接在MsgProc里获取该struct tagLOCALHOOKCOLLECTION指针,取出HHook,传入CallNextHookEx,测试不会崩溃。

测试过程中发现,消息注入调用MsgProc时候,目标进程会首先加载钩子注册的Dll模块,会触发DllMain,然后再回调MsgProc。

Anti消息钩子注入:

SetWindowsHookEx在内核中大概流程是:

NtUserCallNextHookEx->UserCallNextHookEx->co_HOOK_CallHookNext->co_IntCallHookProc->KeUserModeCallback

KeUserModeCallback从R0返回R3,执行用户的回调函数。可以hook该函数,过滤掉回调函数;可以hook NtUserSetWindowsHookEx函数防止键盘钩子。

注入特点:

        1  基于windows消息机制,适用于带窗口界面的ui程序  2 注入时机在目标程序窗口线程运行,触发挂钩消息之后  

3.输入法注入

原理:切换输入法时,windows会把当前输入法所需要的ime文件装载到当前进程中,这个Ime文件类似Dll,微软提供了开发框架。在这个Ime中可以加载注入插件,从而注入目的进程。缺点很多:目的进程需要加载输入法场景,大概率是需要UI界面;注入时机不确定;很容易被检测出来

注入流程:

  1. 在IMESetPubString里设置Loadlibrary。
  2. 输入法安装到系统里,把ime文件写入目录C:\WINDOWS\system32后调用ImmInstallIME,参数一写ime文件路径,参数二写输入法名称。

4.注册表注入

原理:Windows 系统中以下注册表键值,会在EXE文件的启动加载,注入时间等同User32.dll。如果有多个DLL文件,需要用逗号或者空格隔开多个DLL文件的路径。

HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs

注入流程:

1.写注册表

注入特点:

        1.注入稳定   2.易检查   3.全局注入   4.注入时机在系统启动之后

5.EIP注入

原理:挂起目的进程,开启空间把二进制机器指令和数据写进去,修改EIP强行跳转到注入指令处,然后跳转回来。缺点是,兼容性不好。

注入流程:

  1. OpenProcess获取目的进程句柄,OpenThread获取线程id,SuspendThread挂起线程
  2. GetThreadContext获取线程上下文,主要是为了获取eip
  3. VirtualAllocEx分配空间,WriteProcessMemory写注入后需要执行的机器码,路径数据
  4. 修改线程上下文eip,SetThreadContext 设置线程上下文
  5. ResumeThread恢复线程

6.APC注入

原理:利用线程被唤醒时,APC中的注册函数会被执行的机制,以此去执行我们的DLL加载代码,进而完成DLL注入。APC全称是,异步过程调用;APC队列是线程状态改变时待执行的函数队列。缺点是:待执行线程必须进入警惕状态,类似调用SleepEx()或者WaitForSingleObjectEx()。操作系统有两个APC,user和系统的,分别可对应应用层,内核注入。

QueueUserAPC function (processthreadsapi.h) - Win32 apps | Microsoft Docs

注入流程:

  1. OpenProcess打开注入进程,VirtualAllocEx申请存放插件路径字符串的空间,WriteProcessMemory写进去。
  2. OpenThread或者其他方式获取线程句柄。
  3. QueueUserAPC插入APC函数,参数1填LoadLibrary函数指针,参数2填线程句柄,参数3填注册函数参数,也就是字符串路径。

7.SSDT替换

原理:内核中有两个系统服务描述符表,用于处理应用层下发的各个API操作请求,比如当执行ReadFile时候,通过Nt*系列函数,最终根据需要调用的服务号,在SSDT表中查找服务函数地址,从而跳转调用。修改SSDT表,让函数执行之前先执行注入代码,再调用原函数,那么即可实现注入。

SSDT数组内维护了大量服务函数,可以用多个角度切入注入。例如hook NtCreateThread函数这类,在创建进程时机所需要调用的服务函数。

因为自己对这方面不熟悉,故不能写出注入流程。

总结

以上注入方法是目前主流通用手段,既然能行得通,那么也会有对应的防注入的手段,与其知道方法,不如知道本质。

注入无非是想尽各种办法让进程执行被注入代码,或通过加载dll,或通过直接修改PE文件,或直接写入机器码等等,最根本的还是需要熟悉windows创建进程,进程初始化等流程机制,思考如何可以为我所用,方能自己摸索出别出心裁的注入方法。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值