游戏逆向_DLL注入技术

DLL注入技术: 是将一个Dll文件强行加载到目标进程中,比如把外挂dll模块注入到游戏进程,这样做的目的在于方便我们通过这个DLL读写目标进程指令或内存数据,(例如 HOOK游戏函数过程或篡改游戏内存数据实现外挂功能),或以被注入进程的身份去执行一些操作等。

全系统注入的优点:利用系统机制实现的全系统进程注入,可绕过比如游戏进程自身的防注入保护机制。比如远线程注入游戏可能会被拦截,但输入法注入,游戏很难拦截。

消息钩子注入

Windows应用程序是基于消息驱动的。应用程序对各种消息响应从而实现各种功能。

消息钩子(Message Hook)是Windows消息处理机制的一个监视点,系统会自动将钩子安装到目标进程中达到监视指定类型消息的功能。也就是说通过SetWindowsHookEx 系统会自动将钩子dll注入到目标进程。

安装钩子的函数原型如下:

HHOOK SetWindowsHookEx(

int idHook, //钩子类型

HOOKPROC lpfn,

HINSTANCE hMod,

DWORD dwThreadId

);

其中dwThreadId为0时,则是全局钩子,即会注入dll到系统所有窗口进程,否则是线程钩子,即只能将dll注入到目标线程所属的进程。当全局钩子时,钩子处理过程HOOKPROC lpfn 必须位于dll中。

步骤和源码

源码将以WH_CBT 钩子为例,实现全系统dll注入。 computer-based training (CBT) 基于电脑的训练,比如创建窗口,移动窗口等操作的都会收到通知。

在以下事件之前,系统都会调用WH_CBT Hook子程,这些事件包括:

  1. 激活,建立,销毁,最小化,最大化,移动,改变尺寸等窗口事件;

  2. 完成系统指令;

  3. 来自系统消息队列中的移动鼠标,键盘事件;

  4. 设置输入焦点事件;

  5. 同步系统消息队列事件。

安装WH_CBT 钩子将CbtHook.dll注入到notepad进程中的效果:
在这里插入图片描述

在这里插入图片描述

编写被注入的Dll和钩子处理过程

编写一个DLL,并且显式导出CBTProc ()钩子处理过程,主要代码如下:

LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)

{

char szFileName[MAX_PATH];

if( (HCBT_CREATEWND == nCode) || (HCBT_ACTIVATE == nCode) )

{

memset(szFileName, 0, sizeof(szFileName));

::GetModuleFileNameA(NULL, szFileName, sizeof(szFileName));

DebugPrintA(0, “Code(%d) (%s)\n”, nCode, szFileName);

}

return CallNextHookEx(g_hCBT, nCode, wParam, lParam);

}

安装HOOK

编写一个exe,使用SetWindowsHookEx()向系统安装钩子,首先需要将HOOK的DLL 加载到exe本身的进程中,以此得到DLL的模块句柄,再使用GetProcAddress()得到DLL中显示导出的函数MyMessageProc()的函数地址,最后遍历出待注入进程的线程ID,这样SetWindowsHookEx()就可以利用这些参数进行HOOK了。主要代码如下图所示:

if (NULL == g_hCbtHook)

g_hCbtHook = ::LoadLibrary(szPathName);

if (NULL == g_hCbtHook)

{

lResult = GetLastError();

::DebugPrintA(0, “%s : Load GlobalHook module ‘%s’ fail(%d)\n”, C_ModuleNameA, szPathName, lResult);

break;

}

CBTProc = (PCBTPROC)::GetProcAddress(g_hCbtHook, “CBTProc”);

if (NULL == g_hCbtHook)

{

lResult = GetLastError();

::DebugPrintA(0, “%s : Get GlobalHook function fail(%d)\n”, C_ModuleNameA, lResult);

break;

}

g_hCBT = SetWindowsHookEx(WH_CBT, CBTProc, g_hCbtHook, 0);

if (NULL == g_hCBT)

{

lResult = GetLastError();

::DebugPrintA(0, “%s : Set GlobalHook fail(%d)\n”, C_ModuleNameA, lResult);

break;

}

卸载钩子

利用LoadLibrary()得到的模块句柄把本身进程的DLL释放掉,代码如下所示:

FreeLibrary(g_hCbtHook);
消息钩子注入只熟悉SetWindowsHookEx()和DLL导出函数就可以很容编写,所以容易实现。

注册表注入

注册表(Reg)注入原理是利用在Windows 系统中,当REG以下键值中存在有DLL文件路径时,会跟随EXE文件的启动加载这个 DLL文件路径中的DLL文件。

AppInit_Dlls注册表:

注册表项 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows中有两个值:

LoadAppInit_Dlls:键值中指定要注入的DLL

AppInit_Dlls:若其键值为1,则注入LoadAppInit_Dlls中指定的DLL,若为0则不注入。注:(1)LoadAppInit_Dlls中的值当如 果遇到有多个DLL文件时,需要用逗号或者空格隔开多个DLL文件的路径,所以DLL的路径中最好不要有空格。

使用范围:

任何加载User32.DLL的程序,user32.dll的DllMain会先尝试加载注册表项AppInit_Dlls中的DLL。因为所有的GUI应用程序在启动时都会加载User32.dll,因此这种方法会影响所有的GUI程序。
在这里插入图片描述
使用Process Explorer查看进程模块来确认目标dll是否被注入。
在这里插入图片描述

步骤和源码

需要解决的就是关于注册表操作的Windows API了,如下所示:

RegOpenKeyEx

打开注册表键值

RegQueryValueEx

查询键值

RegSetValueEx

设置键值

RegCloseKey

关闭键值

主要代码如下:

BOOL AddRegItem(CHAR* szInjectFilePath)

{

//打开键值

LSTATUS nReg = ERROR_SUCCESS;

HKEY hKey;

CHAR szRegPath[MAX_PATH] = “SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows”;

nReg = RegOpenKeyEx(

HKEY_LOCAL_MACHINE,

szRegPath,

0,

KEY_ALL_ACCESS,

&hKey);

if (nReg != ERROR_SUCCESS)

{

return FALSE;

}

//查询键值

DWORD dwReadType;

DWORD dwReadCount;

TCHAR szReadBuff[1024] = { 0 };

nReg = RegQueryValueEx(hKey,

_T(“AppInit_DLLs”),

NULL,

&dwReadType,

(BYTE*)&szReadBuff,

&dwReadCount);

if (nReg != ERROR_SUCCESS)

{

return FALSE;

}

//若dll名称已经在内容中,则不用重复添加

if (StrStrI(szReadBuff,szInjectFilePath))

{

printf(“dll already in reg=%s\n”, szReadBuff);

return FALSE;

}

//原来已有内容就加入空格后再附加新dll串

if (0 != _tcscmp(szReadBuff, _T(“”)))

{

_tcscat_s(szReadBuff, _T(" "));

}

_tcscat_s(szReadBuff, szInjectFilePath);

//1.把dll路径设置到注册表中

nReg = RegSetValueEx(hKey,

_T(“AppInit_DLLs”),

0,

REG_SZ,

(CONST BYTE*)szReadBuff,

(_tcslen(szReadBuff) + 1)*sizeof(TCHAR));

//2.启动 注册表加载dll

BYTE byEnable[4] = { 0x1 };

nReg = RegSetValueEx(hKey,

_T(“LoadAppInit_DLLs”),

0,

REG_DWORD,

(CONST BYTE*)byEnable,

4);

printf(“RegSetValueEx AppInit_DLLs = %s Result=%d\n”, szReadBuff, nReg);

}

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
远程注入DLL方法有很多种,也是很多木马病毒所使用的隐藏进程的方法,因为通过程序加载的DLL在进程管理器是没有显示的.这里介绍一种用 CreateRemoteThread 远程建立线程的方式注入DLL. 首先,我们要提升自己的权限,因为远程注入必不可免的要访问到目标进程的内存空间,如果没有足够的系统权限,将无法作任何事.下面是这个函数是用来提升我们想要的权限用的. function EnableDebugPriv: Boolean; var hToken: THandle; tp: TTokenPrivileges; rl: Cardinal; begin Result := false; //打开进程令牌环 OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, hToken); //获得进程本地唯一ID if LookupPrivilegeValue(nil, 'SeDebugPrivilege', tp.Privileges[0].Luid) then begin tp.PrivilegeCount := 1; tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; //调整权限 Result := AdjustTokenPrivileges(hToken, false, tp, SizeOf(tp), nil, rl); end; end; 关于 OpenProcessToken() 和 AdjustTokenPrivileges() 两个 API 的简单介绍: OpenProcessToken():获得进程访问令牌的句柄. function OpenProcessToken( ProcessHandle: THandle; //要修改访问权限的进程句柄 DesiredAccess: DWORD; //指定你要进行的操作类型 var TokenHandle: THandle//返回的访问令牌指针 ): BOOL; AdjustTokenPrivileges() :调整进程的权限. function AdjustTokenPrivileges( TokenHandle: THandle; // 访问令牌的句柄 DisableAllPrivileges: BOOL; // 决定是进行权限修改还是除能(Disable)所有权限 const NewState: TTokenPrivileges; { 指明要修改的权限,是一个指向TOKEN_PRIVILEGES结构的指针,该结构包含一个数组, 数据组的每个项指明了权限的类型和要进行的操作; } BufferLength: DWORD; //结构PreviousState的长度,如果PreviousState为空,该参数应为 0 var PreviousState: TTokenPrivileges; // 指向TOKEN_PRIVILEGES结构的指针,存放修改前的访问权限的信息 var ReturnLength: DWORD //实际PreviousState结构返回的大小 ) : BOOL; 远程注入DLL其实是通过 CreateRemoteThread 建立一个远程线程调用 LoadLibrary 函数来加载我们指定的DLL,可是如何能让远程线程知道我要加载DLL呢,要知道在Win32系统下,每个进程都拥有自己的4G虚拟地址空间,各个进程之间都是相互独立的。所我们需要在远程进程的内存空间里申请一块内存空间,写入我们的需要注入DLL 的路径. 需要用到的 API 函数有: OpenProcess():打开目标进程,得到目标进程的操作权限,详细参看MSDN function OpenProcess( dwDesiredAccess: DWORD; // 希望获得的访问权限 bInheritHandle: BOOL; // 指明是否希望所获得的句柄可以继承 dwProcessId: DWORD // 要访问的进程ID ): THandle; VirtualAllocEx():用于在目标进程内存空间中申请内存空间以写入DLL的文件名 function VirtualAllocEx( hProcess: THandle; // 申请内存所在的进程句柄 lpAddress: Pointer; // 保留页面的内存地址;一般用nil自动分配 dwSize, // 欲分配的内存大小,字节单位;注意实际分 配的内存大小是页内存大小的整数倍 flAllocationType: DWORD; flProtect: DWORD ): Pointer; WriteProcessMemory():往申请到的空间中写入DLL的文件名 function WriteProcessMemory( hProcess: THandle; //要写入内存数据的目标进程句柄 const lpBaseAddress: Pointer; //要写入的目标进程的内存指针, 需以 VirtualAllocEx() 来申请 lpBuffer: Pointer; //要写入的数据 nSize: DWORD; //写入数据的大小 var lpNumberOfBytesWritten: DWORD //实际写入的大小 ): BOOL; 然后就可以调用 CreateRemoteThread 建立远程线程调用 LoadLibrary 函数来加载我们指定的DLL. CreateRemoteThread() //在一个远程进程中建立线程 function CreateRemoteThread( hProcess: THandle; //远程进程的句柄 lpThreadAttributes: Pointer; //线程安全描述字,指向SECURITY_ATTRIBUTES结构的指针 dwStackSize: DWORD; //线程栈大小,以字节表示 lpStartAddress: TFNThreadStartRoutine; // 一个TFNThreadStartRoutine类型的指针,指向在远程进程中执行的函数地址 lpParameter: Pointer; //传入参数的指针 dwCreationFlags: DWORD; //创建线程的其它标志 var lpThreadId: DWORD //线程身份标志,如果为0, 则不返回 ): THandle; 整个远程注入DLL的具体实现代码如下: function InjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: Pointer; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * SizeOf(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx(hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin lpThreadId := 0; // 计算LoadLibraryW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'LoadLibraryW'); // 启动远程线程LoadLbraryW,通过远程线程调用创建新的线程 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 如果执行成功返回 True; if (hRemoteThread 0) then Result := true; // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end; 接下来要说的是如何卸载注入目标进程中的DLL,其实原理和注入DLL是完全相同的,只是远程调用调用的函数不同而已,这里要调用的是FreeLibrary,代码如下: function UnInjectDll(const DllFullPath: string; const dwRemoteProcessId: Cardinal): Boolean; // 进程注入和取消注入其实都差不多,只是运行的函数不同而已 var hRemoteProcess, hRemoteThread: THandle; pszLibFileRemote: PChar; pszLibAFilename: PwideChar; pfnStartAddr: TFNThreadStartRoutine; memSize, WriteSize, lpThreadId, dwHandle: Cardinal; begin Result := false; // 调整权限,使程序可以访问其他进程的内存空间 if EnableDebugPriv then begin //打开远程线程 PROCESS_ALL_ACCESS 参数表示打开所有的权限 hRemoteProcess := OpenProcess(PROCESS_ALL_ACCESS, false, dwRemoteProcessId); try // 为注入dll文件路径分配内存大小,由于为WideChar,故要乘2 GetMem(pszLibAFilename, Length(DllFullPath) * 2 + 1); // 之所以要转换成 WideChar, 是因为当DLL位于有中文字符的路径下时不会出错 StringToWideChar(DllFullPath, pszLibAFilename, Length(DllFullPath) * 2 + 1); // 计算 pszLibAFilename 的长度,注意,是以字节为单元的长度 memSize := (1 + lstrlenW(pszLibAFilename)) * SizeOf(WCHAR); //使用VirtualAllocEx函数在远程进程的内存地址空间分配DLL文件名空间 pszLibFileRemote := VirtualAllocEx(hRemoteProcess, nil, memSize, MEM_COMMIT, PAGE_READWRITE); if Assigned(pszLibFileRemote) then begin //使用WriteProcessMemory函数将DLL的路径名写入到远程进程的内存空间 if WriteProcessMemory(hRemoteProcess, pszLibFileRemote, pszLibAFilename, memSize, WriteSize) and (WriteSize = memSize) then begin // 计算GetModuleHandleW的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'GetModuleHandleW'); //使目标进程调用GetModuleHandleW,获得DLL在目标进程中的句柄 hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, pszLibFileRemote, 0, lpThreadId); // 等待GetModuleHandle运行完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 获得GetModuleHandle的返回值,存在dwHandle变量中 GetExitCodeThread(hRemoteThread, dwHandle); // 计算FreeLibrary的入口地址 pfnStartAddr := GetProcAddress(LoadLibrary('Kernel32.dll'), 'FreeLibrary'); // 使目标进程调用FreeLibrary,卸载DLL hRemoteThread := CreateRemoteThread(hRemoteProcess, nil, 0, pfnStartAddr, Pointer(dwHandle), 0, lpThreadId); // 等待FreeLibrary卸载完毕 WaitForSingleObject(hRemoteThread, INFINITE); // 如果执行成功返回 True; if hRemoteProcess 0 then Result := true; // 释放目标进程中申请的空间 VirtualFreeEx(hRemoteProcess, pszLibFileRemote, Length(DllFullPath) + 1, MEM_DECOMMIT); // 释放句柄 CloseHandle(hRemoteThread); end; end; finally // 释放句柄 CloseHandle(hRemoteProcess); end; end; end;
Get_dll_from_dumped_bin.exe是一种安卓Unity 3D DLL文件解密工具。在游戏或应用程序中,开发者通常会将一些敏感或重要的代码以DLL文件的形式保存起来,以防止被未经授权的人员访问或修改。然而,有时候我们可能需要对这些DLL文件进行解密,以便进行分析、修改或逆向工程。 Get_dll_from_dumped_bin.exe工具可以帮助我们完成这个任务。它可以从已被转储(dumped)的二进制文件中提取DLL文件。通常,我们可以使用一些专门的工具或技术将应用程序或游戏中的内存转储为二进制文件,然后使用Get_dll_from_dumped_bin.exe工具来处理这些文件,提取出所需的DLL文件。 这个工具的使用方法相对简单。我们只需要运行Get_dll_from_dumped_bin.exe,然后选择我们想要提取的DLL文件所在的转储文件。接下来,工具会自动解析该文件并提取出所需的DLL文件。解密后的DLL文件可以供我们进一步分析,理解其中的代码逻辑或进行修改。 需要注意的是,使用该工具必须遵守法律和道德规范。我们应该仅在合法范围内使用此工具,例如进行研究、教育或开发等目的。滥用此工具可能会涉及侵犯他人的知识产权或违反相关法律法规,因此请务必谨慎使用。 Get_dll_from_dumped_bin.exe是一个有用的工具,对于那些需要解密安卓Unity 3D DLL文件的开发人员或研究人员来说,它提供了方便和高效的解密过程,帮助我们更好地理解和改进应用程序或游戏中的代码。

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

douluo998

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值