远程注入DLL并自动显示DLL的窗口

以前也可以注入并显示窗口,但是一旦卸载则会导致宿主进程也被关闭,今天终于搞定卸载注入的DLL后不影响宿主进程。

源码在http://wooddoor.ys168.com的VC目录下的“远程注入、卸载.rar”【VC2008的工程】

注入器源码部分:

#include <tlhelp32.h>

namespace pathfileFun
{
    //**************************************************************  
    //*  GetCurrentDirectory得到的不一定是应用程序所在的目录!要得到应用程序所在的目录,这里有一个函数:
    //*函数名:     GetAppPath()  
    //*  
    //*  
    //*返回值         CString                     -   返回路径的形式是   C:/temp/      
    //功能               -   得到应用程序所在的路径,保存到strPathBuffer中  
    //*  
    //**************************************************************  
    inline CString WINAPI GetAppPath()
    {
        CString strPathBuffer;
        TCHAR PathBuffer[MAX_PATH];
        GetModuleFileName(AfxGetInstanceHandle(), PathBuffer, MAX_PATH);
        strPathBuffer.Format(_T("%s"),PathBuffer);
        CString strAppPath=strPathBuffer.Mid(0,strPathBuffer.ReverseFind(_T('//'))+1);
        return strAppPath;
    };
    //格式化路径,使得路径统一成以“/”结尾
    inline CString FormatPath(CString strPath)
    {
        if ( strPath.Right(1) != _T("//") )
            strPath += _T("//");
        return strPath;
    };
    //从文件的全路径中获取文件所在的文件夹路径,返回的路径以“/”结尾
    inline CString GetPathFromFile(CString FilePath)
    {
        return FilePath.Mid(0,FilePath.ReverseFind(_T('//'))+1);
    };
    //从文件的全路径中获取文件名(含后缀)
    inline CString GetFileNameFromFilePath(CString FilePath)
    {
        return FilePath.Right(FilePath.GetLength() - FilePath.ReverseFind(_T('//'))+1);
    };
}

 

//===========================================


//调整进程权限
bool EnablePrivilege(TCHAR* PrivilegeName,BOOL IsEnable)
{
    HANDLE hToken;
    TOKEN_PRIVILEGES tp;
    LUID luid;

    if(!OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_READ,&hToken))
    {
        return false;
    }
    if(!LookupPrivilegeValue(NULL, PrivilegeName, &luid))
    {
        return false;
    }
    tp.PrivilegeCount           = 1;
    tp.Privileges[0].Luid       = luid;
    tp.Privileges[0].Attributes = (IsEnable) ? SE_PRIVILEGE_ENABLED : 0;
    BOOL bSucc = AdjustTokenPrivileges(hToken,FALSE,&tp,NULL,NULL,NULL);
    CloseHandle(hToken);
    return (GetLastError() == ERROR_SUCCESS);
}
//将指定dll注入指定进程
bool HookProcess(DWORD dwProcessId,CStringA szDllPath)
{

    HANDLE hRemoteProcess    = NULL;
    HANDLE hRemoteThread     = NULL;
    HANDLE hRemoteFunc        = NULL;
    PVOID  pRemoteParam              = NULL;
    DWORD  dwWriten                   = 0;
    BOOL   bRet                      = FALSE;

    char   szDllPathCopy[256] = {0};
    lstrcpyA(szDllPathCopy,szDllPath);

    EnablePrivilege(SE_DEBUG_NAME,true);
    hRemoteProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessId);

    if(hRemoteProcess == NULL)
    {
        EnablePrivilege(SE_DEBUG_NAME,false);
        return false;
    }
    int iSize = (int)strlen(szDllPath);
    pRemoteParam = VirtualAllocEx(hRemoteProcess,NULL,iSize,MEM_COMMIT,PAGE_READWRITE);
    if(pRemoteParam == NULL)
    {
        EnablePrivilege(SE_DEBUG_NAME,false);
        return false;
    }
    bRet = WriteProcessMemory(hRemoteProcess,pRemoteParam,(LPVOID)szDllPathCopy,iSize,&dwWriten);
    if(!bRet)
    {
        if (pRemoteParam)
            VirtualFreeEx(hRemoteProcess,pRemoteParam,0,MEM_RELEASE);
        EnablePrivilege(SE_DEBUG_NAME,false);
        return false;
    }
    hRemoteFunc = GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "LoadLibraryA");
    // #ifdef UNICODE
    //     hRemoteFunc = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryW");
    // #else
    //     hRemoteFunc = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
    // #endif
    hRemoteThread = CreateRemoteThread(hRemoteProcess,0,0,(LPTHREAD_START_ROUTINE)hRemoteFunc,pRemoteParam,0,&dwWriten);

    EnablePrivilege(SE_DEBUG_NAME,false);
    // 等待线程结束
    if (hRemoteThread)
    {
        WaitForSingleObject(hRemoteThread,INFINITE);
        HMODULE g_hRemoteHandle;
        GetExitCodeThread(hRemoteThread,(DWORD*)&g_hRemoteHandle);
    }

    // 清理工作
    if(pRemoteParam)
        VirtualFreeEx(hRemoteProcess, pRemoteParam,0,MEM_RELEASE);
    CloseHandle(hRemoteProcess);
    return true;
}
//查找dll实例句柄
HMODULE GetProcessModuleByName(DWORD dwProcessId,CString lpStrName)
{
    HANDLE hModuleSnap=::CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,dwProcessId);

    //查找相关的DLL
    MODULEENTRY32 me32 = {sizeof(MODULEENTRY32)};
    for(BOOL fok=::Module32First(hModuleSnap,&me32); fok ;fok=::Module32Next(hModuleSnap,&me32))
    {
        if(lstrcmpi(me32.szExePath , lpStrName) == 0 || lstrcmpi(me32.szModule , lpStrName) == 0)
        {
            return me32.hModule;
        }
    }
    return 0;
}
//卸载注入的DLL
bool UnhookProcess(DWORD dwProcessId,CString szDllPath)
{
    HANDLE hRemoteProcess = NULL;
    HANDLE hRemoteThread  = NULL;
    HANDLE hRemoteFunc    = NULL;
    PVOID  pRemoteParam      = NULL;
    DWORD  dwWriten       = 0;
    BOOL   bRet           = FALSE;
    //char   szDllPathCopy[256] = {0};
    //lstrcpyA(szDllPathCopy,szDllPath);
    EnablePrivilege(SE_DEBUG_NAME,true);
    hRemoteProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessId);
    if(hRemoteProcess == NULL)
    {
        EnablePrivilege(SE_DEBUG_NAME,false);
        return false;
    }
    //此dll句柄地址就已经是dwProcessId中的地址,所有不需要写入
    HMODULE hDllModule = GetProcessModuleByName(dwProcessId,szDllPath);
    if (!hDllModule)
    {
        EnablePrivilege(SE_DEBUG_NAME,false);
        return false;
    }
    pRemoteParam = hDllModule;
    hRemoteFunc = GetProcAddress(GetModuleHandle(_T("kernel32.dll")), "FreeLibrary");
    hRemoteThread = CreateRemoteThread(hRemoteProcess,0,0,(LPTHREAD_START_ROUTINE)hRemoteFunc,pRemoteParam,0,&dwWriten);
    EnablePrivilege(SE_DEBUG_NAME,false);
    // 等待线程结束
    if (hRemoteThread)
    {
        WaitForSingleObject(hRemoteThread,INFINITE);
    }
    CloseHandle(hRemoteProcess);
    return true;
}
//获取进程列表
void GetProcessList(CArray<PROCESSENTRY32>& arrProcess)
{
    arrProcess.RemoveAll();
    PROCESSENTRY32    stProcess;
    HANDLE     hSnapShot;
    BOOL b;

    RtlZeroMemory(&stProcess, sizeof(stProcess));
    stProcess.dwSize = sizeof(stProcess);
    hSnapShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    b = Process32First(hSnapShot, &stProcess);
    while ( b )
    {
        arrProcess.Add(stProcess);

        b = Process32Next(hSnapShot, &stProcess);
    }        
    CloseHandle(hSnapShot);
}

 

//======================================================

 

//点击“注入”按钮
void CwooddoorDlg::OnBnClickedButtonHook()
{
    // TODO: 在此添加控件通知处理程序代码
    if (m_ProcessList.GetCurSel() == -1)
    {
        AfxMessageBox(_T("请先选择目标进程!"));
    }
    else
    {
        CString strDllFile = pathfileFun::GetAppPath() + _T("HookRXJH.dll");
        if ( HookProcess( m_ProcessList.GetItemData(m_ProcessList.GetCurSel()), CStringA(strDllFile.LockBuffer()) ) == false )
        {
            AfxMessageBox(_T("注入失败!"));
        }
        strDllFile.UnlockBuffer();
    }
}
//点击“卸载”按钮
void CwooddoorDlg::OnBnClickedButtonUnhook()
{
    // TODO: 在此添加控件通知处理程序代码
    if (m_ProcessList.GetCurSel() == -1)
    {
        AfxMessageBox(_T("请先选择目标进程!"));
    }
    else
    {
        CString strDllFile = pathfileFun::GetAppPath() + _T("HookRXJH.dll");
        if ( UnhookProcess( m_ProcessList.GetItemData(m_ProcessList.GetCurSel()), strDllFile ) == false )
        {
            AfxMessageBox(_T("卸载失败!"));
        }
    }
}
//在组合框内显示进程列表
void CwooddoorDlg::OnCbnDropdownComboProcesslist()
{
    // TODO: 在此添加控件通知处理程序代码
    m_ProcessList.ResetContent();//清空列表
    CArray<PROCESSENTRY32> arrProcess;
    GetProcessList(arrProcess);
    CString strProcessInfo;
    for (int i=arrProcess.GetCount()-1; i>=0; i--)
    //for (int i=0; i<arrProcess.GetCount(); i++)
    {
        strProcessInfo=_T("");
        strProcessInfo.Format( _T("[%d]%s"), arrProcess[i].th32ProcessID, pathfileFun::GetFileNameFromFilePath(arrProcess[i].szExeFile) );
        //m_ProcessList.AddString(pathfileFun::GetFileNameFromFilePath(arrProcess[i].szExeFile));
        m_ProcessList.SetItemData( m_ProcessList.AddString(strProcessInfo), arrProcess[i].th32ProcessID );
    }
}

 

DLL部分:

创建MFC DLL,名为HookRXJH。

在DLL中插入一个对话框,并给该对话框创建一个类如:

class CMainDialog : public CDialog

在DLL的CHookRXJHApp内创建一个成员变量:

public:
    CMainDialog *m_pMainDialog;
    CFrameWnd *m_pFrame;

给CHookRXJHApp添加成员函数:virtual int ExitInstance();

然后在InitInstance内添加代码:

BOOL CHookRXJHApp::InitInstance()
{
    CWinApp::InitInstance();

    //在dll注入之后显示主对话框:
    m_pMainDialog = new CMainDialog;
    m_pFrame = new CFrameWnd();
    m_pMainWnd = m_pFrame;//用于自动处理消息,省去手动写消息循环,从而不至于对话框刚出现就关闭
    m_pMainWnd = m_pMainDialog;
    ::CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ShowDialog,(LPVOID)m_pMainDialog,NULL,NULL);

    return TRUE;
}

在ExitInstance内添加代码:

int CHookRXJHApp::ExitInstance()
{
    SendMessage(m_pMainDialog->m_hWnd, WM_CLOSE, NULL, NULL);

    return CWinApp::ExitInstance();
}

另外定义一个函数用于显示窗口:

void ShowDialog(CMainDialog *pMainDialog)
{
    pMainDialog->DoModal(); //显示对话框
}

  • 0
    点赞
  • 6
    收藏
  • 打赏
    打赏
  • 4
    评论
Ring3下注入DLL另类方法,能过杀软和游戏NP(源码) 注入DLL是做全局钩子或者拦截类软件都有可能用到技术,如果做外挂话我们也有 可能需要注入一个DLL到游戏进程中去干点什么“坏事”。 但我们知道现在要注入DLL是越 来越难了。场景1:制作火星文输入法外挂,原理是利用API HOOK拦截修改输入法相关函 数,需要注入一个DLL到所有进程中,但是后来发现,在开启了瑞星帐号保险箱后,用户 将不能在QQ中输入火星文。原因是瑞星保护了QQ进程,禁止对其注入DLL,解决方法是提示 用户关闭帐号保险箱 -_-| 确实是很降低用户体验一个不是办法办法。场景2:制作某 游戏外挂,需要注入一个DLL到游戏进程中去直接调用游戏函数完成某一功能。结果发现该 游戏有NP保护,OpenProcess打不开,创建远程线程也不行,试用其它方法也一一失败。遇 到上面情况,高手们自然是转到Ring0下面去,使用驱动之类办法来对付啦,不过吾等 菜鸟可就是酒井没法子了 -_-| 不过也别太灰心,凡事总会有办法。我想我们需要一种持久、稳定、不容易被安 全软件屏蔽DLL注入方法,后来发现,输入法程序就是能完成这一任务理想人选。输入 法程序程序到底是什么?它没有自己进程,且在系统还没有登录时就已被加载(在欢迎 界面你也可以调出输入法),它可以在游戏中打开,也可以在控制台程序中打开,还可以在 瑞星保护下QQ中打开,在杀软中也可以打开,这不就是我们要找特性吗。那么,输入法 到底是什么呢?根据Windows规定,输入法其实就是一个DLL,不过它是一个特殊DLL, 它必须具有标准输入法程序所规定那些接口,输入法是由输入法管理器(imm32.dll)控 制,输入法管理器又是由user32.dll控制。输入法在系统目录是以IME为扩展名文件 ,当在应用程序中激活某个输入法时,输入法管理器就会在那个应用程序进程中加载对应 IME文件,注意,加载IME文件跟加载普通DLL没有本质区别,所以,可以认为,输入 法其实就是注入到应用程序中一个DLL文件,且,这种“注入”是不会被杀软和游戏NP 拦截(至少目前是)。现在,我们已经有了一个注入DLL另类方法,那就是利用输入法 。具体流程是这样,首先制作一个标准输入法文件,但是这个输入法不完成文字输入工作 ,它唯一任务就是用来注入DLL,所以称为“服务输入法”,然后,制作一个控制程序, 来控制服务输入法,当然最后还需要一个用于注入目标DLL,这样一共就有3个文件。开始 工作后,控制程序首先将服务输入法安装到系统中,然后传递几个参数给服务输入法,参数 中包括了需要注入DLL文件名称和路径,然后,控制程序将服务输入法设置为系统默 认输入法,这样新程序一打开,服务输入法就会注入那个程序。当然,在服务输入法安装 之前打开程序不会被注入,这时需要向系统中所有窗口POST一条 WM_INPUTLANGCHANGEREQUEST消息,该消息可以在指定窗口中后台激活服务输入法,这样, 系统中所有拥有窗口进程就都被我们服务输入法注入了。服务输入法注入程序之后,就 会根据控制程序传递过来参数加载目标DLL,这样目标DLL也就随着服务输入法一同注入到 目标程序中了。注意服务输入法是控制程序用WM_INPUTLANGCHANGEREQUEST消息在所有窗口自动激活,如果某个窗口自动激活失败,你就需要在那个窗口中手工切换到服务输入法 ,这样才能注入进去了。至于注入以后,你就可以在窗口中切换到别输入法,这不会影 响已经注入进去DLL。我将这一套功能制作成一个完整示例,你可以在以下地址下载: http://www.pen88.com/download/imehook.rar 压缩包中第6个和第8个文件夹演示了此 功能包含所有源代码。其中文件imedllhost09.dll就是服务输入法,运行时会被安装到系 统中,控制程序退出时会自动卸载该输入法,这样用户就不太容易察觉,你还可以重新编译 该输入法,将名称改为“中文(中国)”,这样隐蔽性更好。文件hxwdllwx.dll是演示用 目标DLL,你可以替换成自己DLL,然后那个exe文件就是控制程序了。输入法 imedllhost09.dll在运行时会被复制到系统目录更名为imedllhost09.ime,它导出了2个 函数用于控制。在VB中声明为: Public Declare Function IMESetPubString Lib "imedllhost09.ime" (ByVal RunDLLStr As String, ByVal UnloadDll As Long, ByVal loadNextIme As Long, ByVal DllData1 As Long, ByVal DllData2 As Long, ByVal DllData3 As Long) As Long Public Declare Function IMEClearPubString Lib "imedllhost09.ime" () As Long 其中IMESetPubString用于向输入法传递要注入DLL等参数。RunDLLStr,要注入DLL命令 和完整路径。UnloadDll,当输入法退出时,是否同时卸载目标DLL 0-是,1-否。 loadNextIme,当切换至该服务输入法时,是否直接切换到下一个输入法(这样服务输入法 就好像被跳过了,可最小限度影响用户输入法顺序) 0-否,1-是。DllData1,DllData2 ,DllData3是传递给目标DLL回调函数(函数名称必须为RunDllHostCallBack)参数, 你可以在目标DLL中导出一个函数,名称为RunDllHostCallBack,这样当输入法注入时会调 用目标DLL该回调函数向其传递这3个参数。函数原型为(VC): DWORD RunDllHostCallBack(DWORD calldata1, DWORD calldata2,DWORD calldata3); IMEClearPubString函数用于清除输入法配置,清除后,输入法将停止在新程序中注入 目标DLL,但已注入DLL不会卸载。 好了,利用输入法来注入DLL基本上就是这样了,详细用法大家可以看压缩包中第8个文 件夹,其中服务输入法是VC写,控制程序是VB,代码都是有注释。测试发现该方法能 过目前所有杀软,也能注入冰刃。当然缺点还是有,就是目标程序如果不接受输入法那就 没办法了,但是现在一般游戏都不会禁止玩家在里面打字吧,而且杀软也不能禁止用户输 入汉字吧,哈哈,所以通用性应该还是蛮好。 最后,我再介绍另一个注入DLL方法,估计也很少被用到。是利用一个未公开函数 RegisterUserApiHook,可以在网上搜索关键词“RegisterUserApiHook”,查到有人在 Windows 2003下测试成功,但是我在Windows XP测试却失败。后来终于找到了失效原因。 RegisterUserApiHook函数可以在系统中注册一个全局钩子,你需要在钩子中指定一个DLL和 一个回调函数,然后,所有加载了user32.dll程序就都会在启动时加载你指定这个DLL 。用这个函数来注入DLL也是很不错。但是测试发现它注入能力似乎赶不上上面提到 利用输入法来注入办法,可以注入一般程序和某些安全程序,但是对冰刃无效。而且它 有一个限制,就是系统中只能同时存在一个这样钩子。实际上这个钩子平时是被系统中 Themes服务占用了,Themes服务正是利用这个钩子HOOK了绘制窗口相关API,所以才让所 有程序窗口变成XP主题样式。所以我们要用这个钩子话,必须先关闭Themes服务,这样 在XP下也可以用了,但是这样系统就变成Windows 2000样式了 -_-| RegisterUserApiHook函数VB声明如下: Public Declare Function RegisterUserApiHookXP Lib "user32" Alias "RegisterUserApiHook" (ByVal hInstance As Long, ByVal fnUserApis As Long) As Long Public Declare Function RegisterUserApiHook2003 Lib "user32" Alias "RegisterUserApiHook" (pRegInfo As HookAPIRegInfo2003) As Long 可以看到,在XP和2003下这个函数参数是不一样。关于此函数示例代码,请参见压缩 包中第5个文件夹。 最后最后,再介绍一个未公开函数InitializeLpkHooks,这个函数在网上能找到资料更 少,只有一个声明而已。但是它名称中最后那个“Hooks”误导了我,我以为又是一个可以 用来注入DLL不错函数,用OD反出来一看,原来只是个局部HOOK而已。虽然没太大用,还 是一写上吧,也许谁用得着呢。InitializeLpkHooks顾名思义就是HOOK LPK,Windows 有个lpk.dll,就是支持多语言包那么个功能。测试发现好多程序在TextOut之前似乎是要 调用lpk.dll里面相关函数,可能是支持多语言程序就需要用这个来判断到底要显示 那种语言吧。而InitializeLpkHooks,就是用来HOOK lpk.dll里面4个函数,这4个函数 是LpkTabbedTextOut,LpkPSMTextOut,LpkDrawTextEx,LpkEditControl。我们先打开VB, 在窗体中加入以下代码吧: Private Sub Form_Load() DLLhwnd = LoadLibrary("lpk.dll") '加载DLL DLLFunDre = GetProcAddress(DLLhwnd, "LpkDrawTextEx") '获取回调函数地址 LpkHooksInfo.lpHookProc_LpkTabbedTextOut = 0 LpkHooksInfo.lpHookProc_LpkPSMTextOut = 0 LpkHooksInfo.lpHookProc_LpkDrawTextEx = GetLocalProcAdress(AddressOf HookProc1) '设置要HOOKLPK函数 LpkHooksInfo.lpHookProc_LpkEditControl = 0 InitializeLpkHooks LpkHooksInfo End Sub Private Sub Form_Unload(Cancel As Integer) LpkHooksInfo.lpHookProc_LpkTabbedTextOut = 0 LpkHooksInfo.lpHookProc_LpkPSMTextOut = 0 LpkHooksInfo.lpHookProc_LpkDrawTextEx = DLLFunDre LpkHooksInfo.lpHookProc_LpkEditControl = 0 InitializeLpkHooks LpkHooksInfo FreeLibrary DLLhwnd End Sub 然后新建一个模块,在模块中加入以下代码: Public Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long Public Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long Public Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long ' ----------------未公开函数-------------------------------------- Public Declare Sub InitializeLpkHooks Lib "user32" (lpProcType As Any) Type LpkHooksSetting lpHookProc_LpkTabbedTextOut As Long lpHookProc_LpkPSMTextOut As Long lpHookProc_LpkDrawTextEx As Long lpHookProc_LpkEditControl As Long End Type ' ------------------------------- Public DLLhwnd As Long, DLLFunDre As Long Public LpkHooksInfo As LpkHooksSetting Public Function GetLocalProcAdress(ByVal lpProc As Long) As Long GetLocalProcAdress = lpProc End Function Function HookProc1(ByVal a1 As Long, ByVal a2 As Long, ByVal a3 As Long, ByVal a4 As Long, ByVal a5 As Long, ByVal a6 As Long, ByVal a7 As Long, ByVal a8 As Long, ByVal a9 As Long, ByVal a10 As Long) As Long HookProc1 = 0 End Function 运行一下看看,是不是窗体中标题栏和按钮上文字都没有了,因为我们把函数 LpkDrawTextEx替换成自己函数HookProc1了。这个函数有10个参数,其中几个好像是字符 串指针,似乎可以用来截获窗体要显示文字,然后改成另一种语言文字,我猜想,也许 就是这个用途吧。哈哈,纯属猜测。以上就是函数InitializeLpkHooks用法了。 以上就是全部。 本文所有示例代码下载地址是: http://www.pen88.com/download/imehook.rar 我QQ511795070,欢迎交流。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页
评论 4

打赏作者

gnuljf

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值