• 远程线程注入DLL


    0x00 原理介绍:

    上一篇文章介绍了在Windows下如何在其他进程创建一个远程线程:https://www.cnblogs.com/DarkBright/p/10820582.html

    调用 CreateRemoteThread 创建远程线程所需要的过程函数的标准形式为:

    DWORD WINAPI ThreadProc(
      _In_ LPVOID lpParameter
    );
    

    Win32编程加载DLL的API为:

    HMODULE WINAPI LoadLibrary(
      _In_ LPCTSTR lpFileName
    );
    

    观察发现 ThreadProc 与 LoadLibrary 函数类型是一致的,所以可以通过 CreateRemoteThread 在其他的进程中创建一个线程调用 LoadLibrary 加载我们准备的DLL

    系统启动后,kernel32.dll 的加载基址在各个进程中是相同的,因此导出函数(LoadLibrary)的地址也相同,即自己程序空间的LoadLibrary函数地址和其他进程空间的LoadLibrary函数地址相同

    1

    0x01 目标程序:

    目标程序与上一篇文章(https://www.cnblogs.com/DarkBright/p/10820582.html)中的一致:

    #include <windows.h>
    #include <stdio.h>

    void Fun(void)
    {
         for(size_t i = 0; i < 10; i++)
             printf("%s addr:0x%p ", __FUNCTION__, Fun);
    }

    int main(int argc, char* argv[])
    {
         Fun();
         //MessageBox(NULL, TEXT("执行完成!"), TEXT("提示"), MB_OK);

        getchar();
         return 0;
    }

    编译生成 Project1.exe

    0x02 要注入的DLL:

    在DLL成功注入到目标进程之后弹出一个信息框,代码如下:

    BOOL APIENTRY DllMain( HMODULE hModule,
                            DWORD  ul_reason_for_call,
                            LPVOID lpReserved
                          )
    {
         switch (ul_reason_for_call)
         {
         case DLL_PROCESS_ATTACH:
             MessageBox(NULL, TEXT("DLL注入成功!"), TEXT("提示"), MB_OK);
         case DLL_THREAD_ATTACH:
         case DLL_THREAD_DETACH:
         case DLL_PROCESS_DETACH:
             break;
         }
         return TRUE;
    }

    将生成的TestDLL.dll拷贝至桌面

    0x03 注入程序:

    注入程序编写流程大致如下:

    1、 获取目标进程的句柄

    2、 向目标进程中申请一片内存,要能够容纳 DLL路径信息字符串 的长度

    3、 将 DLL路径信息字符串 写入到步骤2申请的内存中

    4、 获取注入程序中 LoadLibraryA 的函数地址(前面已经说明该地址在目标程序中同样适用)

    5、 调用 CreateRemoteThread 在目标进程中创建一个线程,该线程调用 LoadLibraryA 加载我们准备的DLL

    具体实现代码如下:

    #include <windows.h>
    #include <string.h>
    #include "Process.h"

    #define        NAME    L"Project1.exe"
    #define        DLL_NAME    "C:\Users\DarkBright\Desktop\TestDLL.dll"
    //#define  DLL_NAME    "TestDLL.dll"

    int main(int argc, char* argv[])
    {
         Process* pProcess = new Process(NAME, FALSE);
         if (pProcess->hProcess == NULL) {
             OutputDebugString(TEXT("未找到目标进程! "));
             DWORD ret = GetLastError();
             exit(-1);
         }
         //OutputDebugString(TEXT("已找到目标进程! "));
         PVOID pDllName = VirtualAllocEx(pProcess->hProcess, NULL, strlen(DLL_NAME)+1,
                                         MEM_COMMIT, PAGE_READWRITE);
         if(pDllName == NULL){
             delete pProcess;
             OutputDebugString(TEXT("分配空间失败! "));
             exit(-1);
         }
         pProcess->WriteByteArray(pDllName, (BYTE *)DLL_NAME, strlen(DLL_NAME) + 1);
         HMODULE hModule = GetModuleHandle(TEXT("Kernel32.dll"));
         if(hModule == NULL){
             VirtualFreeEx(pProcess->hProcess, pDllName, 0, MEM_RELEASE);
             delete pProcess;
             OutputDebugString(TEXT("GetModuleHandle失败! "));
             exit(-1);
         }
         FARPROC pfnLoadLibraryA = GetProcAddress(hModule, "LoadLibraryA");
         HANDLE hThread = CreateRemoteThread(pProcess->hProcess, NULL, 0,
                                             (LPTHREAD_START_ROUTINE)pfnLoadLibraryA,
                                             pDllName, 0, NULL);
         if(hThread == NULL){
             VirtualFreeEx(pProcess->hProcess, pDllName, 0, MEM_RELEASE);
             delete pProcess;
             OutputDebugString(TEXT("CreateRemoteThread失败! "));
             exit(-1);
         }
         CloseHandle(hThread);

        delete pProcess;
         return 0;
    }

    #pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" )

    其中 Process 类的相关内容参考:https://www.cnblogs.com/DarkBright/p/10809092.html

    最后一行:#pragma comment( linker, "/subsystem:windows /entry:mainCRTStartup" ) 表示隐藏控制台

    运行 Project1.exe 并且适用调试器附加(注意是附加,不要直接用调试器打开程序):

    2

    将 TestDLL.dll 拷贝至桌面后运行注入程序:

    3

    弹框提示注入成功,并且在调试器的模块列表中也可以发现新注入的DLL

  • 相关阅读:
    MySQL令人咋舌的隐式转换
    阿里规范中为什么要求表必须有主键id
    理解Python闭包,这应该是最好的例子
    MySQL 高级(进阶) SQL 语句精讲(二)
    什么是可串行化MVCC
    Oracle11g:数据库恢复总结
    以友盟+U-Push为例,深度解读消息推送的筛选架构解决方案应用与实践
    一万字详解 Redis Cluster Gossip 协议
    [JS]给String对象添加方法,使传入的字符串字符之间以空格分开输出
    [JS]计算字符串中出现最多的字符和其出现次数
  • 原文地址:https://www.cnblogs.com/DarkBright/p/10821038.html
Copyright © 2020-2023  润新知