• DLL进程注入之CreateRemoteThread()


    Dll注入原理

    Dll注入一般指向一个正在运行的程序注入一个线程,注入的线程运行我们的代码,而我们的代码就以DLL(动态链接库)的形式存在。但是程序不会平白无故的就加载我们的dll,这时候就需要使用我们强大的Windows API了,它为我们提供了大量的函数来附加和操纵其他进程。
    API中的所有函数都包含于DLL文件之中。其中,最重要的是“Kernel32.dll”(包含管理内存,进程和线程相关的函数),“User32.dll”(大部分是用户接口函数),和“GDI32.dll”(绘制图形和显示文本相关的函数)。。

    Dll注入代码实现

    DLL的注入方法有很多,这里介绍一种最经典的注入方法,远程进程注入。
    这种方法灵活性高,同时要求掌握的知识也很多,其核心思想就是在目标进程中开启一个线程调用LoadLibrary函数来加载我们想要注入的dll.大致流程

    首先是OpenProcess函数

      HANDLE OpenProcess(
      DWORD dwDesiredAccess, //渴望得到的访问权限(标志)
      BOOL bInheritHandle, // 是否继承句柄
      DWORD dwProcessId// 进程标示符
            );
    

    获得返回句柄之后再用VirtualAllocEx函数,在目标进程中开辟一块内存存放我们的dll的路径。

      LPVOID VirtualAllocEx( 
      HANDLE hProcess, //目标进程句柄
      LPVOID lpAddress, //保留页面的内存地址,一般用NULL自动分配
      SIZE_T dwSize, //欲分配的内存大小,字节单位
      DWORD flAllocationType, //为特定的页面区域分配内存中或磁盘
    

    的页面文件中的物理存储
    DWORD flProtect //受保护状态
    );
    之后使用WriteProcessMemory函数向目标内存写入dll地址

    BOOL WINAPI WriteProcessMemory(
      _In_  HANDLE  hProcess, //由OpenProcess返回的进程句柄。
      _In_  LPVOID  lpBaseAddress, //要写的内存首地址
      _In_  LPCVOID lpBuffer, //指向要写的数据的指针。
      _In_  SIZE_T  nSize, //要写入的字节数。
      _Out_ SIZE_T  *lpNumberOfBytesWritten //返回值。返回实际写入的字节
    );
    

    用GetProcAddress函数获得LoadLibraryW函数的起始地址。LoadLibraryW函数位于Kernel32.dll中,再用CreateRemoteThread函数让目标进程执行LoadLibraryW来加载被注入的dll。函数结束将返回载入dll后的模块句柄。
    注意:这里的LoadLibrary函数在底层实际调用有两种可能,如果目标程序使用的是ANSI编码方式,LoadLibrary实际调用的是LoadLibraryA,其参数字符串应当是ANSI编码;
      如果目标程序使用的是Unicode编码方式,LoadLibrary实际调用的是LoadLibraryW,其参数字符串应当是Unicode编码。
      这使得注入过程变得很麻烦,为了减少复杂性,不妨直接使用LoadLibraryA或LoadLibraryW而不是用LoadLibrary函数来避免这一麻烦。另外,即使使用的是LoadLibraryA,LoadLibraryA也会将传入的ANSI编码的字符串参数转换成Unicode编码后再调用LoadLibraryW。综上,不妨一致使用LoadLibraryW函数,并且字符串用Unicode编码即可。

    HMODULE hModule = GetModuleHandle(L"kernel32.dll");
        if (!hModule)
        {
            printf("GetModuleHandle Error !
    ");
            GetLastError();
            CloseHandle(hProcess);
            return FALSE;
        }
            // #6.获取LoadLibraryA 函数地址
        LPTHREAD_START_ROUTINE dwLoadAddr = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryW");
        if (!dwLoadAddr)
        {
            printf("GetProcAddress Error !
    ");
            GetLastError();
            CloseHandle(hProcess);
            CloseHandle(hModule);
            return FALSE;
        }
    

    完整代码

    bool Inject(DWORD dwId, WCHAR* szPath)//参数1:目标进程PID  参数2:DLL路径
    {
        //一、在目标进程中申请一个空间
        /*
        【1.1 获取目标进程句柄】
        参数1:想要拥有的进程权限(本例为所有能获得的权限)
        参数2:表示所得到的进程句柄是否可以被继承
        参数3:被打开进程的PID
        返回值:指定进程的句柄
        */
        HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwId);
        /*
        【1.2 在目标进程的内存里开辟空间】
        参数1:目标进程句柄
        参数2:保留页面的内存地址,一般用NULL自动分配
        参数3:欲分配的内存大小,字节单位
        参数4:MEM_COMMIT:为特定的页面区域分配内存中或磁盘的页面文件中的物理存储
        参数5:PAGE_READWRITE 区域可被应用程序读写
        返回值:执行成功就返回分配内存的首地址,不成功就是NULL
        */
        LPVOID pRemoteAddress = VirtualAllocEx(
            hProcess,
            NULL,
            wcslen(szPath) * 2,
            MEM_COMMIT,
            PAGE_READWRITE
        );
    
        //二、 把dll的路径写入到目标进程的内存空间中
    
        DWORD dwWriteSize = 0;
        /*
        【写一段数据到刚才给指定进程所开辟的内存空间里】
        参数1:OpenProcess返回的进程句柄
        参数2:准备写入的内存首地址
        参数3:指向要写的数据的指针(准备写入的东西)
        参数4:要写入的字节数(东西的长度+0/)
        参数5: 返回值。返回实际写入的字节
        */
        BOOL bRet = WriteProcessMemory(hProcess, pRemoteAddress, szPath, wcslen(szPath)*2, NULL);
        //三、 创建一个远程线程,让目标进程调用LoadLibrary
    
        /*
        参数1:该远程线程所属进程的进程句柄
        参数2:一个指向 SECURITY_ATTRIBUTES 结构的指针, 该结构指定了线程的安全属性
        参数3:线程栈初始大小,以字节为单位,如果该值设为0,那么使用系统默认大小
        参数4:在远程进程的地址空间中,该线程的线程函数的起始地址(也就是这个线程具体要干的活儿)
        参数5:传给线程函数的参数(刚才在内存里开辟的空间里面写入的东西)
        参数6:控制线程创建的标志。0(NULL)表示该线程在创建后立即运行
        参数7:指向接收线程标识符的变量的指针。如果此参数为NULL,则不返回线程标识符
        返回值:如果函数成功,则返回值是新线程的句柄。如果函数失败,则返回值为NULL
        */
        // #5.获取模块地址
        HMODULE hModule = GetModuleHandle(L"kernel32.dll");
        if (!hModule)
        {
            printf("GetModuleHandle Error !
    ");
            GetLastError();
            CloseHandle(hProcess);
            return FALSE;
        }
            // #6.获取LoadLibraryA 函数地址
        LPTHREAD_START_ROUTINE dwLoadAddr = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryW");
        if (!dwLoadAddr)
        {
            printf("GetProcAddress Error !
    ");
            GetLastError();
            CloseHandle(hProcess);
            CloseHandle(hModule);
            return FALSE;
        }
        // //7.创建远程线程,加载dll
    
        HANDLE hThread = CreateRemoteThread(
            hProcess,
            NULL,
            0,
            (LPTHREAD_START_ROUTINE)dwLoadAddr,
            pRemoteAddress,
            NULL,
            NULL
        );
        //WaitForSingleObject(hThread, -1); //当句柄所指的线程有信号的时候,才会返回
    
        ///*
        //四、 【释放申请的虚拟内存空间】
        //参数1:目标进程的句柄。该句柄必须拥有 PROCESS_VM_OPERATION 权限
        //参数2:指向要释放的虚拟内存空间首地址的指针
        //参数3:虚拟内存空间的字节数
        //参数4:MEM_DECOMMIT仅标示内存空间不可用,内存页还将存在。
        //       MEM_RELEASE这种方式很彻底,完全回收。
        //*/
        //VirtualFreeEx(hProcess, pRemoteAddress, 1, MEM_DECOMMIT);
        return 0;
    }
    DWORD GetPid(WCHAR* szName)
    {
        HANDLE hprocessSnap = NULL;
        PROCESSENTRY32  pe32 = { 0 };
        hprocessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        /*if (hprocessSnap == (HANDLE)-1) { return 0; }*/
        pe32.dwSize = sizeof(PROCESSENTRY32);
        if (Process32First(hprocessSnap, &pe32))
        {
            do {
                if (!wcscmp(szName, pe32.szExeFile))
                    return (int)pe32.th32ProcessID;
            } while (Process32Next(hprocessSnap, &pe32));
        }
        else
            CloseHandle(hprocessSnap);
        return 0;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
        wchar_t wStr[] = L"C:\Release\Dnew.dll";
        DWORD dwId = 0;
        DWORD dwPid = 0;
        WCHAR S[] = L"sublime_text.exe";
        DWORD SS =  GetPid(S);
        printf("目标窗口的进程PID为:%d
    ", SS);
        //参数1:目标进程的PID
        //参数2:想要注入DLL的路径
        Inject(SS, wStr);
        system("pause");
        return 0;
    }
    

    这里用sublime来测试,随便打开个文档,
    之后再运行我们的注入程序

    可以看到文档处多了个弹框

    而且查看进程,也成功加载了我们的DLL(在这里只是弹窗)

    附赠测试dll代码

    #include <windows.h>
    #include <pch.h>
    
    DWORD WINAPI runBot(LPVOID lpParam) {
        // 此处可以写具体的bot代码
        return 1;
    }
    
    
    BOOL APIENTRY DllMain(HMODULE hModule,
        DWORD  ul_reason_for_call,
        LPVOID lpReserved
    )
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
            MessageBoxA(NULL, "DLL Attached!
    ", "Game Hacking", MB_OK | MB_TOPMOST);
            CreateThread(NULL, 0, &runBot, NULL, 0, NULL);
            break;
        }
        return TRUE;
    }
    
  • 相关阅读:
    算法练习(16)-水平翻转一颗二叉树
    算法练习(15)-设计1个二叉树的序列化与反序列化实现?
    算法练习(14)-二叉树中2个节点的最近公共祖先?
    算法练习(13)-打印纸条对折的折痕类型(凹痕?凸痕?)
    算法练习(12)-二叉树的递归套路
    算法练习(11)-二叉树的各种遍历
    算法练习(10)-求2个(可能有环的)单链表的相交节点
    算法练习(9)-复杂带随机指针的单链表
    mac升级后第三方下载程序无法打开cannot be opened because the developer cannot be verified的解决办法
    算法练习(8)-判断单链表是否回文链表
  • 原文地址:https://www.cnblogs.com/Xy--1/p/14506866.html
Copyright © 2020-2023  润新知