Windows:DLL远程线程注入

所谓远程线程,并非指跨主机,而是指跨进程。也即一个进程对另一个进程的注入。

被注入进程

最好还是自己手写一个,不然很多软件的进程应该是有防护机制,注进去的线程会被杀掉,dll也会被卸载,不方便观察。

delete.exe源码

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

int main(void){
    while(1){
        Sleep(100);
    }

    return 0;
}

很简单,就是一段死循环

编写DLL

Windows开发:CLion下编写及动/ 静态调用DLL

library.h

#ifndef DLL_GENERATE_LIBRARY_H
#define DLL_GENERATE_LIBRARY_H

// extern "C":以C方式导出
// __declspec(dllexport) 声明一个导出函数
extern "C" __declspec(dllexport) void MyAdd(int a, int b);


#endif //DLL_GENERATE_LIBRARY_H

library.cpp

#include "library.h"
#include <windows.h>
#include <iostream>


// 实现
void MyAdd(int a, int b) {
    std::cout << a + b << std::endl;
}


extern "C" BOOL APIENTRY DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved){
    switch(fdwReason) {
        case DLL_PROCESS_ATTACH:
            MessageBox(NULL, "It works!", "Status", MB_OK);
        case DLL_PROCESS_DETACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        default:
            break;
    }
    return TRUE;
}

主函数DllMain的作用是首次被load时,弹窗提示。

注入流程

首先使用OpenProcess打开指定PID的进程,权限要足够,不然可能出现无法创建线程的情况。

第二,获得dll文件的绝对路径的字符串长度,并使用VirtualAllocEx在被注入进程中申请相应大小的内存。

第三,使用WriteProcessMemory将dll文件的绝对路径字符串写入被注入进程。

第四,以dll文件的句柄、要查找的函数名作为GetProcAddress的参数,查找本进程中加载的Kernel32.dll中LoadLibrary函数的始址,根据是否采用UNICODE有不同的函数W/ A。因为这个dll在所有进程中加载的位置都相同,所以本进程中的函数位置就是被注入进程中函数的位置。

第五,在被注入进程中使用CreateRemoteThread创建线程,参数为线程函数(LoadLibrary)地址,线程函数参数(dll文件的绝对路径)

第六,若成功注入,因为dll是首次被load,所以调用其DllMain时,fdwReason参数的值为DLL_PROCESS_ATTACH,判断一下,会弹窗提示成功。

枚举流程

注入完成后,枚举一下被注入进程加载了哪些DLL
Windows开发:进程、线程及DLL的枚举

注入及枚举源码

#include <iostream>
#include <windows.h>
#include <tchar.h>
#include <tlhelp32.h>


// 参数:dll路径,被注入进程的PID
bool ModuleInject(LPCTSTR szModule, DWORD dwPid){
    // 参数:权限、是否有子进程继承权限、PID
    HANDLE hProcess = OpenProcess(
            PROCESS_QUERY_INFORMATION | PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE,
            FALSE,
            dwPid
            );
    if (hProcess){
        // 成功获取到进程句柄
        // dll路径字符串的大小
        int cByte = (_tcslen(szModule) + 1) * sizeof(TCHAR);
        // 在被注入进程中申请分配内存,返回首地址
        // 参数:进程句柄、要开始分配内存的位置、分配大小、分配内存类型、内存管理方式
        LPVOID pAddr = VirtualAllocEx(hProcess, NULL, cByte, MEM_COMMIT, PAGE_READWRITE);
        if (pAddr){
            // 分配成功则尝试在被注入进程中写入dll路径
            // 参数:进程句柄、分配好的内存首址、数据内容指针、数据长度、传出(写入了多少字节)
            if (WriteProcessMemory(hProcess, pAddr, szModule, cByte, NULL)){
// 如果定义了UNICODE
#ifdef _UNICODE
    // 使用GMH获得Kernel32.dll的句柄,用GPA在其中找到宽字节的LoadLibraryW函数的地址,此地址在所有进程中相同
    // 强转为PSR类型指针,作为CreateRemoteThread的参数
    PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(_T("Kernel32")), "LoadLibraryW");
#else
    PTHREAD_START_ROUTINE pfnStartAddr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(_T("Kernel32")), "LoadLibraryA");
#endif
                if (pfnStartAddr){
                    // 远程线程ID
                    DWORD dwThreadID = 0;
                    // 远程线程句柄
                    // 参数:进程句柄、安全属性指针、堆栈大小(0默认)、线程函数(LoadLibrary)地址、线程函数参数(dll路径)、创建标志、传出远程线程ID
                    HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, pfnStartAddr, pAddr, 0, &dwThreadID);
                    if (hRemoteThread){
                        // 若成功注入,则dll被load到进程中,其DllMain被执行
                        std::cout << "inject success." << std::endl;
                        // 等待远程线程退出再退出
                        WaitForSingleObject(hRemoteThread, INFINITE);

                        CloseHandle(hRemoteThread);
                        CloseHandle(hProcess);
                    }
                    else{
                        std::cout << "create remote thread failed." << std::endl;
                    }
                }
                else{
                    std::cout << "cannot find func LoadLibrary" << std::endl;
                }
            }
            else{
                std::cout << "write mem failed." << std::endl;
            }
        }
        else{
            std::cout << "alloc mem failed." << std::endl;

            return FALSE;
        }

    }
    else{
        std::cout << "failed to open process." << std::endl;

        return FALSE;
    }
}

VOID EnumProcessModule(int myPid) {
    MODULEENTRY32 me32 = {0};
    me32.dwSize = sizeof(MODULEENTRY32);

    // 创建DLL型快照,参数为进程ID
    HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, myPid);
    if (hSnap == INVALID_HANDLE_VALUE){
        std::cout << "failed to create module snapshot." << std::endl;
    }
    else{
        BOOL enumStatus = Module32First(hSnap, &me32);
        int i = 0;
        while (enumStatus){
            std::cout << me32.szModule << ", " << me32.szExePath << std::endl;
            i++;
            enumStatus = Module32Next(hSnap, &me32);
        }
    }

    CloseHandle(hSnap);
}


int main() {
    
    int myPid = 0;
    std::cin >> myPid;
    ModuleInject(_T("D:\\CLion_workspace\\win32_programing\\dll_programing\\dll_inject\\lib\\libdll_generate.dll"), myPid);
    Sleep(5000);
    EnumProcessModule(myPid);

    return 0;
}

演示

首先打开delete.exe,使用IDE运行或直接运行编译出的exe都可以,查看其PID

在这里插入图片描述

确认dll位置正确,运行注入程序,输入PID,杀软放行

在这里插入图片描述
弹窗提示
在这里插入图片描述

查看被注入进程加载的dll,注入成功。
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
//官方网站:www.feiyuol.com //郁金香灬老师 //QQ 150330575 //个人网站:www.yjxsoft.com 跨进程调用CALL 跨进程调用带多个的参数CALL // myInject_dll.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include #include"RWA.h" //PVOID 跨进程分配内存(WORD nSize ); //1、获取进程句柄 //2、读写 分配内存 创建线程 //3、跨进程分配内存 void mycall() { PVOID p1=跨进程分配内存(1000); printf("分配的内存地址=%p\n",p1); printf("按回车键 释放内存\n"); getchar(); 跨进程释放内存(p1,1000); printf("已经 释放内存\n"); } LPTHREAD_START_ROUTINE a; BOOL 跨进程调用CALL(PVOID pcall地址,PVOID plst参数 ); //LoadLibraryA(dll名字指针) //MessageBeep(1) void Test远程调用MessageBeep() { 跨进程调用CALL(MessageBeep,(PVOID)0x12768); } //注入my022MFC.dll到目标进程 void Test3() { char szDllName[]="my022MFC.dll"; //全路径 // char szDllName[]="C:\\Users\\yjxsoft\\Documents\\visual studio 2010\\Projects\\my022\\Debug\\my022MFC.dll"; PVOID p1=跨进程分配内存(1000); printf("分配的内存地址=%p\n",p1); WN((DWORD)p1,szDllName,sizeof(szDllName));//WriteProcessMemory /*跨进程调用CALL(LoadLibraryA,(PVOID)szDllName);*/ 跨进程调用CALL(LoadLibraryA,(PVOID)p1); } void Test4() { // char szDllName[]="my022MFC.dll"; //全路径 char szDllName[]="E:\\1905\\代码\\my022-24\\Debug\\my022MFC.dll"; PVOID p1=跨进程分配内存(1000); printf("分配的内存地址=%p\n",p1); WN((DWORD)p1,szDllName,sizeof(szDllName));//WriteProcessMemory /*跨进程调用CALL(LoadLibraryA,(PVOID)szDllName);*/ 跨进程调用CALL(LoadLibraryA,(PVOID)p1); } int _tmain(int argc, _TCHAR* argv[]) { //mycall(); Test3(); Test4(); return 0; } //作业 //1、练习跨进程注入DLL //2、跨进程分配的内存内存 使用完后 用VirtualFreeEx释放掉 //3、进程句柄使用完后用CloseHandle释放句柄资源
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值