文章内所提及的任何代码片段仅供学习交流作用,请勿用于实际游戏环境当中并以此谋取不当收益。因此而产生的任何责任与文章作者及网站主无关,如不同意请按下键盘[Alt+F4]停止访问。

自己玩过这个东西的话,其实大家都能清楚VAC没有对游戏进程做防注入的保护,最简单的远程线程都可以直接把DLL注入到游戏进程当中;当然哈,CSGO这游戏市面上还是有很多在卖钱的外挂,无论是不是只能支持官匹,反正卖钱就vans了!这篇文章会分享对于CSGO的DLL注入和隐藏,可能又是一片影响大佬销量的文章了!

  1. 注入cheats.dll到csgo.exe
  2. 注入hidden.dll到csgo.exe
  3. 卸载hidden.dll

首先注入cheats.dll到游戏进程中,然后注入hidden.dll到游戏来断开PEB的三根链表实现隐藏cheats.dll,最后远程卸载hidden.dll来做到毫无痕迹的完成整个注入流程。

  • 注入cheats.dll到csgo.exe
    1.1 获取进程ID;通过ToolHelp32的方法来进行查找进程,其实方法有很多,ToolHelp32毕竟简单暴力。
BOOL GetProcessIdByProcessImageName(HANDLE* ProcessID,const TCHAR* ProcessImageName)
    {
        BOOL IsOk = FALSE;
        HANDLE SnapshotHandle = INVALID_HANDLE_VALUE;
        PROCESSENTRY32 ProcessEntry32;
        ProcessEntry32.dwSize = sizeof(PROCESSENTRY32);    int LastError = 0;
        
        SnapshotHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);    //TH32CS_SNAPPROCESS:Includes all processes in the system in the snapshot.To enumerate the processes,
        if (SnapshotHandle == INVALID_HANDLE_VALUE)
        {
            LastError = GetLastError();        return FALSE;
        }    if (!Process32First(SnapshotHandle, &ProcessEntry32))
        {
    
            LastError = GetLastError();        goto Exit;
        }    do
        {        if (_tcsicmp(ProcessEntry32.szExeFile, ProcessImageName) == 0)
            {
                *ProcessID = (HANDLE)ProcessEntry32.th32ProcessID;
                IsOk = TRUE;            goto Exit;
            }
        } while (Process32NextW(SnapshotHandle, &ProcessEntry32));
    
    Exit:    if (SnapshotHandle != INVALID_HANDLE_VALUE)
        {
            CloseHandle(SnapshotHandle);
        }
        SnapshotHandle = INVALID_HANDLE_VALUE;
        SetLastError(LastError);    return IsOk;
    
    }

1.2 打开进程获取句柄;通过进程ID提权,通过包装的OpenProcess函数来提权,然后打开进程返回进程句柄。

HANDLE OpenProcess(DWORD DesiredAccess, BOOL IsInheritHandle, HANDLE ProcessID)
    {    if (m_EnableDebugPrivilege)
        {
            EnableSeDebugPrivilege(_T("SeDebugPrivilege"), TRUE);
        }
        HANDLE ProcessHandle = ::OpenProcess(DesiredAccess, IsInheritHandle, (DWORD)ProcessID);
    
        DWORD LastError = GetLastError();    if (m_EnableDebugPrivilege)
        {
            EnableSeDebugPrivilege(_T("SeDebugPrivilege"), FALSE);
        }
        SetLastError(LastError);    return ProcessHandle;
        
    }
    
    BOOL CInjectToCsgo::EnableSeDebugPrivilege(const TCHAR * PriviledgeName, BOOL IsEnable)
    {
        BOOL IsOk = FALSE;    int  LastError = 0;    //获取当前进程句柄(伪句柄)
        HANDLE  ProcessHandle = GetCurrentProcess();
        HANDLE  TokenHandle = INVALID_HANDLE_VALUE;
        TOKEN_PRIVILEGES TokenPrivileges = { 0 };    //通过当前进程句柄获得当前进程中令牌句柄
        if (!OpenProcessToken(ProcessHandle, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle))        /*BOOL OpenProcessToken(     ** 得到进程的令牌句柄
             __in HANDLE ProcessHandle, //要修改访问权限的进程句柄
             __in DWORD DesiredAccess, //指定你要进行的操作类型
             __out PHANDLE TokenHandle //返回的访问令牌指针
    )*/
        {
            LastError = GetLastError();        goto Exit;
        }
        LUID     Luid;   //Locally Unique Identifier
        if (!LookupPrivilegeValue(NULL, PriviledgeName, &Luid))        // 通过权限名称查找uID
        //函数查看系统权限的特权值,返回信息到一个LUID结构体里
        /*BOOL LookupPrivilegeValue(
            LPCTSTR lpSystemName,    表示所要查看的系统,本地系统直接用NULL
            LPCTSTR lpName,          指向一个以零结尾的字符串,指定特权的名称
            PLUID lpLuid);           接收所返回的制定特权名称的信息*/
        {
            LastError = GetLastError();        goto Exit;
        }
        TokenPrivileges.PrivilegeCount = 1;        // 要提升的权限个数
        TokenPrivileges.Privileges[0].Attributes = IsEnable == TRUE ? SE_PRIVILEGE_ENABLED : 0;
        TokenPrivileges.Privileges[0].Luid = Luid;    if (!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL))  // 启用或禁用特权一个有TOKEN_ADJUST_PRIVILEGES访问的访问令牌.
            /*BOOL AdjustTokenPrivileges(
                HANDLE TokenHandle, //包含特权的句柄  必须有 TOKEN_ADJUST_PRIVILEGES访问令牌
                BOOL DisableAllPrivileges,//禁用所有权限标志
                PTOKEN_PRIVILEGES NewState,//新特权信息的指针(结构体)
                DWORD BufferLength, //缓冲数据大小,以字节为单位的PreviousState的缓存区(sizeof)
                PTOKEN_PRIVILEGES PreviousState,//接收被改变特权当前状态的Buffer
                PDWORD ReturnLength //接收PreviousState缓存区要求的大小
            );*/
        {
            LastError = GetLastError();        goto Exit;
        }
    
    
        IsOk = TRUE;
    
    Exit:    if (TokenHandle != INVALID_HANDLE_VALUE)
        {
            CloseHandle(TokenHandle);
            TokenHandle = INVALID_HANDLE_VALUE;
        }
    
        SetLastError(LastError);    return IsOk;
    }

1.3 在csgo.exe进程中申请内存空间,并将cheats.dll路径写入目标进程地址空间。

VirtualAddress = VirtualAllocEx(csgoHandle, NULL, BufferLength, MEM_COMMIT, PAGE_READWRITE);    if (VirtualAddress == NULL)
        {        goto Exit;
        }    if (ProcessMemoryWriteSafe(csgoHandle, VirtualAddress, FilePath.GetString(), BufferLength, &ReturnLength) == FALSE)
        {        
            goto Exit;
        }
    
    
    BOOL CInjectToCsgo::ProcessMemoryWriteSafe(HANDLE ProcessHandle, LPVOID VirtualAddress, LPCVOID BufferData, SIZE_T BufferLength, SIZE_T * ReturnLength)
    {
        SIZE_T  v1 = 0;
        SIZE_T* v2 = 0;    int    LastError = 0;
        DWORD  OldProtect = 0;
        BOOL   IsOk = FALSE;    if ((ProcessHandle == 0) || (VirtualAddress == 0) || (BufferData == 0) || (BufferLength == 0))
        {
    
            LastError = ERROR_INVALID_PARAMETER;        goto Exit;
        }    if (!ReturnLength)
        {
            v2 = &v1;
        }    else
        {
            v2 = ReturnLength;
        }    if (!WriteProcessMemory(ProcessHandle, VirtualAddress, BufferData, BufferLength, v2))        /*BOOL WriteProcessMemory(  ****函数能写入某一进程的内存区域
                HANDLE hProcess,       由OpenProcess返回的进程句柄。如参数传数据为 INVALID_HANDLE_VALUE 【即-1】目标进程为自身进程
                LPVOID lpBaseAddress,  要写的内存首地址 再写入之前,此函数将先检查目标地址是否可用,并能容纳待写入的数据
                LPVOID lpBuffer,       指向要写的数据的指针。
                DWORD nSize,           要写入的字节数。
                LPDWORD lpNumberOfBytesWritten  实际数据的长度
            );*/
        {        if (VirtualProtectEx(ProcessHandle, VirtualAddress, BufferLength, PAGE_EXECUTE_READWRITE, &OldProtect))
            {            if (WriteProcessMemory(ProcessHandle, VirtualAddress, BufferData, BufferLength, v2))
                {
                    IsOk = TRUE;
                }            else
                {
                    LastError = GetLastError();
                }
                VirtualProtectEx(ProcessHandle, VirtualAddress, BufferLength, OldProtect, &OldProtect);
            }        else
            {
                LastError = GetLastError();
            }
        }    else
        {
            IsOk = TRUE;
        }
    Exit:
    
        SetLastError(LastError);    return IsOk;
    }

1.4 远程线程注入;利用CreateRemoteThread,将线程其实地址设为LoadLibrary的地址。

HANDLE ThreadHandle = CreateRemoteThread
        (
            csgoHandle,  //线程所属进程的进程句柄
            NULL,           // 结构指定新线程的安全描述符,并确定子进程是否可以继承返回的句柄 NULL,则线程获取默认安全描述符,并且不能继承句柄
            0,              //线程栈初始大小,以字节为单位,如果该值设为0,那么使用系统默认大小.
            (LPTHREAD_START_ROUTINE)FunctionAddress, //在远程进程的地址空间中, 该线程的线程函数的起始地址.
            VirtualAddress, // 
            0,              //控制线程创建的标志
            NULL);          //线程的创建标志  NULL则不返回线程标识符.
    
        if (ThreadHandle == NULL)
        {
            
            VirtualFreeEx(csgoHandle, VirtualAddress, BufferLength, MEM_RELEASE);        goto Exit;
        }    //等待远程线程结束
        WaitForSingleObject(ThreadHandle, INFINITE);

将DLL文件注入到目标进程后,使用火绒剑查看进程中“未知文件”的模块会发现看不到被注入的目标DLL。

最后修改:2021 年 01 月 15 日 12 : 47 AM
如果觉得我的文章对你有用,请随意赞赏