前言

这里所说的代码注入和上篇的DLL注入有类似之处。DLL文件的注入与卸载在上篇中都完成了,整个注入与卸载的过程其实就是让远程线程执行一次LoadLibrary函数或者FreeLibrary函数远程线程装载或者卸载一个DLL文件,通过dllMain()调用DLL中具体功能代码,这样注入DLL以后就可以让DLL做很多事情了。

是否可以不依赖DLL文件直接向目标进程写入要执行的代码,完成特定的功能呢?当然是可以的。这节我们就来实现代码注入到指定的进程中。

代码注入思路

要在目标进程中完成一定的特定功能,就需要使用相关的API函数,不同的API函数实现在不同的DLL中。Kernel32.dll文件在每个进程中的地址是相同的,但是并不代表其他的DLL文件在每个进程中的地址都是一样的。这样,在目标进程中调用每一个API函数时,必须使用LoadLibrary(或GetModuleHandle)函数和GetProcAddress函数动态导出用到的每个API函数必需的)。把想要使用的API函数及API函数所在的DLL文件都封装进一个结构体 (作为写的函数代码的参数)直接写入目标进程的空间中。直接把要在远程执行的代码也写入到目标进程的内存空间中,最后调用CreateRemoteThread函数即可将其运行。

界面效果

有图有真相!我们往QQ.exe进程中 注入代码

qt5 exe release mysql 依赖文件_Windows编程


qt5 exe release mysql 依赖文件_Windows编程_02


qt5 exe release mysql 依赖文件_Windows编程_03


qt5 exe release mysql 依赖文件_LoadLibrary_04


qt5 exe release mysql 依赖文件_GetProcAddress_05

核心代码

选择注入的进程,这里主要是用到了进程遍历,可以参考 Qt:Windows编程—Qt实现进程管理 这篇博文。

封装的结构体

这个数据类型就是要传递 线程回调函数的参数,也就是我们注入的函数代码的参数。

#define STRLEN 20
typedef struct _mydata{
    // 4个不变函数的函数地址
    DWORD dwLoadLibrary;
    DWORD dwGetProcAddress;
    DWORD dwGetModuleFileName;
    DWORD dwGetModuleHandle;

    char user32dll[STRLEN]; // user32.dll 含有MessageBoxA函数
    char MessageBoxFun[STRLEN]; // "MessageBoxA" 字符串

    char msvcrtdll[STRLEN]; // MSVCRT.DLL 微软运行库 含有strcat函数
    char strcatFun[STRLEN]; // "strcat" 字符串

    char caption[STRLEN]; // 消息框标题 "Inject Code!"
    char content[MAX_PATH]; // 消息框内容
}MyData;

注入的代码

// 注入的可执行代码,执行弹框功能
DWORD WINAPI CodeFunc(LPVOID param)
{
    MyData* data = (MyData*)param;
    // 定义函数指针类型 方便下面的类型转换
    typedef HMODULE (__stdcall *MyLoadLibrary)(LPCSTR);
    typedef FARPROC (__stdcall *MyGetProcAddress)(HMODULE,LPCSTR);
    typedef DWORD (__stdcall *MyGetModuleFileName)(HMODULE,LPSTR,DWORD);
    typedef HMODULE (*MyGetModuleHandle)(LPCSTR);
    typedef int (__stdcall *MyMessageBox)(HWND,LPCSTR,LPCSTR,UINT);
    typedef char*(*Mystrcat)(char *dest, const char *src);


    // 获取函数地址
    MyLoadLibrary myLoadLibrary = (MyLoadLibrary)data->dwLoadLibrary;
    MyGetProcAddress myGetProcAddress = (MyGetProcAddress)data->dwGetProcAddress;
    MyGetModuleFileName myGetModuleFileName = (MyGetModuleFileName)data->dwGetModuleFileName;
    MyGetModuleHandle myGetModuleHandle = (MyGetModuleHandle)data->dwGetModuleHandle;
    // 所有用的函数,都需像下面一样 导出函数地址然后再使用,包括标准库函数也一样
    // 笔者刚开始就犯了一个错,直接使用sprintf、strcat 等函数,这样是不行!因为我们自己注入的代码,函数地址需要我们自己确定!
    HMODULE user32dll = myLoadLibrary(data->user32dll);
    HMODULE msvcrtdll = myGetModuleHandle(data->msvcrtdll);// msvcrt.dll 微软运行库,可直接获取
    MyMessageBox myMessageBox = (MyMessageBox)myGetProcAddress(user32dll,data->MessageBoxFun);
    Mystrcat mystrcat = (Mystrcat)myGetProcAddress(msvcrtdll,data->strcatFun);


    // 调用获取到的函数
    char curFile[MAX_PATH] = {0};
    myGetModuleFileName(NULL,curFile,MAX_PATH);
    mystrcat(data->content,curFile);
    myMessageBox(NULL, data->content ,data->caption, MB_OK);
    return 0;
}

注入代码操作

// 注入Code操作
void WorkerThread::injectCode()
{
    HANDLE targetProc = OpenProcess(PROCESS_ALL_ACCESS,FALSE,m_pId);
    if( targetProc == NULL )
    {
        qDebug() << "OpenProcess error";
        return;
    }

    MyData data = {0};
    strcpy(data.user32dll,"user32.dll");
    strcpy(data.msvcrtdll,"msvcrt.dll");
    strcpy(data.MessageBoxFun,"MessageBoxA");
    strcpy(data.strcatFun,"strcat");
    strcpy(data.caption,"Inject Code!");
    strcpy(data.content,"current process:");// 这里使用中文会乱码

    HMODULE module = GetModuleHandleA("kernel32.dll");
    data.dwLoadLibrary = (DWORD)GetProcAddress(module,"LoadLibraryA");
    data.dwGetProcAddress = (DWORD)GetProcAddress(module,"GetProcAddress");
    data.dwGetModuleFileName = (DWORD)GetProcAddress(module,"GetModuleFileNameA");
    data.dwGetModuleHandle = (DWORD)GetProcAddress(module,"GetModuleHandleA");
    int dataLen = sizeof(MyData);

    // 1.目标进程申请空间 存放data数据,也就是注入的 可执行代码函数 的参数
    LPVOID pData = VirtualAllocEx(targetProc,NULL,dataLen,MEM_COMMIT | MEM_RESERVE,PAGE_READWRITE );
    if( pData == NULL )
    {
        qDebug() << "VirtualAllocEx error 1";
        return;
    }
    int ret = WriteProcessMemory(targetProc,pData,&data,dataLen,NULL);
    if( ret == 0 )
    {
        qDebug() << "WriteProcessMemory error 1";
        return;
    }

    // 2.目标进程申请空间 存放CodeFunc可执行代码函数
    int codeLen = 0x4000; // 4 * 16^3 = 16KB足够了
    // 第5个参数,数据具有可执行权限
    LPVOID pCode = VirtualAllocEx(targetProc,NULL,codeLen,MEM_COMMIT,PAGE_EXECUTE_READWRITE );
    if( pCode == NULL )
    {
        qDebug() << "VirtualAllocEx error 2";
        return;
    }
    ret = WriteProcessMemory(targetProc,pCode,(LPCVOID)&CodeFunc,codeLen,NULL);
    if( ret == 0 )
    {
        qDebug() << "WriteProcessMemory error 2";
        return;
    }

    // 3.创建远程线程 执行可执行代码
    HANDLE tHandle = CreateRemoteThread(targetProc,NULL,NULL,
                       (LPTHREAD_START_ROUTINE)pCode,pData,NULL,NULL);

    if(tHandle == NULL)
    {
        qDebug() << "CreateRemoteThread error";
        return ;
    }
    qDebug() << "注入,wait ..." ;
    WaitForSingleObject(tHandle,INFINITY);
    CloseHandle(tHandle);
    CloseHandle(targetProc);
    qDebug() << "注入,finish ...";
    emit doInjectFinish();
}

完整代码

完整代码工程请在这里下载