Contents

获取并解密QQ-ClientKey

ClientKey是类似SSO的token,拿到后可以访问各种QQ服务,如空间、邮箱、微博等,2014年分析着玩的,在此记录一下。

分析记录:

QQ:6.3、6.8 7.4 PlatformCore

1、找到KernelUtil!Util::Misc::GetSignature,挂QQ调试,跟进这些函数里看找函数地址,看偏移是否对得上

/posts/2014/dump-qq-clientkey/res/1.png

2、CTXStringW* KernelUtil!Util::Misc::GetSignature 获取ClientKey

/posts/2014/dump-qq-clientkey/res/2.png

struct ITXCore
{
 char* c1; // 00
  c1->common_func1(ITXCore*, 675585B0, &Prelogin); // 24
 char* c2; // 18
  common_func2(c2, ITXCore*,675585B0,&Prelogin); // 20
  char* c3; //2c
   c3->common_func3(ITXCore*,675585B0,&Prelogin) // 4
    c4 = poi(c3+0c)+poi(c3+4)
    c4->common_func4(ITXCore*,675585B0,&Prelogin)// 8
     c5 = poi(c4+1c)
     common_func5(c5,ITXCore*,675585B0,&Prelogin)// 34
      c6 = c5+14
      c6->search1(&&Prelogin,675585B0,c4)
       index = poi(poi(c6+4)+4)+c;
       memcmp(index,675585B0)
};
find_clientkey_script:
1、找到PreLogin对象。

as platform_core 004b69b8
as c2 poi(platform_core+0x18)
as c3 poi(c2+0x2c)
as c4 poi(c3+0x0c)+poi(c3+0x4)
as c5 poi(c4+0x1c)
as c6 c5 + 0x14

step1:
r $t1=004b69b8;r $t2=poi($t1+0x18);r $t3=poi($t2+0x2c);r $t4=poi($t3+0x0c)+poi($t3+0x4);r $t5=poi($t4+0x1c);r $t6=$t5+0x14;
step2:
r $t3=poi($t6+4); r $t0=poi(poi($t6+4)+4);.while(by($t0+0x25)==0){r $t2=0x0;.while($t2<0x10){.if(by($t0+0xc+$t2)!=by(0x675585b0+$t2)){.break};r $t2=$t2+1;};.if($t2<0x10){r $t0=poi($t0)}.else{r $t3=$t0;r $t0=poi($t0+8)}}


004b69b8  = platform_core
c2 = poi(platform_core+0x18)
c3 = poi(c2+0x2c)
c4 = poi(c3+0x0c)+poi(c3+0x4)
c5 = poi(c4+0x1c)
c6 = c5 + 0x14

poi($c6+4)

$platform_core

3、对照脚本和ClientKey.xmind,windbg和IDA分析。

 1 d 75803c39     0001 (0001)  0:**** SHELL32!ShellExecuteW
 4 d 674d7c29     0001 (0001)  0:**** Common!Util::UrlBase::OpenUrlWithTT
 5 d 674d7c18     0001 (0001)  0:**** Common!Util::UrlBase::OpenUrlWithDefBrowser
 6 d 674d7ab9     0001 (0001)  0:**** Common!Util::UrlBase::OpenUrlWithDefBrowser
 7 d 66049a90     0001 (0001)  0:**** Mail!DllUnregisterServer+0x8304
 8 d 660499d1     0001 (0001)  0:**** Mail!DllUnregisterServer+0x8245
 9 d 672e00b5     0001 (0001)  0:**** KernelUtil!Util::Misc::GetSignature
10 e 672e014a     0001 (0001)  0:**** KernelUtil!Util::Misc::GetSignature+0x95

KernelUtil!Util::Misc::GetSignature+0x95

 0 d 674d014a     0001 (0001)  0:**** KernelUtil!Util::Misc::GetSignature+0x95
 1 d 674d00b5     0001 (0001)  0:**** KernelUtil!Util::Misc::GetSignature
 2 d 676b7f28     0001 (0001)  0:**** Common!Util::Encode::Encode16+0x25
 3 d 674d1cd1     0001 (0001)  0:**** KernelUtil!Util::Misc::Get32ByteValueAddedSign+0x10
 4 d 674d0139     0001 (0001)  0:**** KernelUtil!Util::Misc::GetSignature+0x84
 5 d 6539c8c7     0001 (0001)  0:**** PreloginLogic!UtilCSProcessor::CreateDRCSProcessor+0x14b68
 6 e 674d011a     0001 (0001)  0:**** KernelUtil!Util::Misc::GetSignature+0x65
 7 e 674d011d     0001 (0001)  0:**** KernelUtil!Util::Misc::GetSignature+0x68
 8 e 674d00e2     0001 (0001)  0:**** KernelUtil!Util::Misc::GetSignature+0x2d
 9 e 676d570c     0001 (0001)  0:**** Common!Util::Core::GetPlatformCore

调用流程

/posts/2014/dump-qq-clientkey/res/3.png

编写windbg脚本提取ClientKey

为了简单直接讲QQ.exe存个dump,然后用脚本从dump文件中提取。

find_clientkey.ws是windbg脚本

$$used with QQ.exe.dmp
.printf "\n\n\t$$ **Find QQ ClientKey Windbg Script**\n"
r $t1=dwo(dwo(Common!Util::CoreCenter::GetPlatformCore+4))
r $t2=dwo($t1+0x18);
r $t3=dwo($t2+0x2c);
r $t4=dwo($t3+0x0c)+dwo($t3+0x4);
r $t5=dwo($t4+0x1c);
r $t6=$t5+0x14;
r $t3=dwo($t6+4);
r $t0=dwo(dwo($t6+4)+4);
$$eax=03cbe550
eb @eip 4e 0f 99 3a bc 95 00 4f ae 52 fd d9 fb ff 30 3e
.while(by($t0+0x25)==0)
{
	r $t4=0;
	.if(dwo($t0+0xc)<0x3a990f4e)
	{
		r $t4=1;
	}
	.elsif(dwo($t0+0xc)>0x3a990f4e)
	{
		r $t4=0;
	}
	.else
	{
		r $t2=0x0;
		.while($t2<0x10)
		{
			.if(by($t0+0xc+$t2)<by(eip+$t2))
			{
				r $t4=1;
				.break;
			}
			r $t2=$t2+1;
		}
	}
	.if($t4==0)
	{
		r $t3=$t0;
		r $t0=dwo($t0)
	}
	.else
	{
		r $t0=dwo($t0+8)
	}
}
r $t9=dwo($t3+0x20)
.printf "[+] PreLogin: %x\n",$t9
r $t1=dwo($t9+0x14)
r $t1=dwo($t1+0x2c)
.printf "[+] PreLogin2: %x\n",$t1

$$hash_string
r $t3=0xa5177eef
.printf "[+] HashValue: %x\n",$t3

$$find_hash_data
!for_each_module "
	r @$t7 = @#End - @#Base;
	.if($spat(\"@#ModuleName\", \"Common\") != 0)
	{
		.foreach(found1 {s -[1]b @#Base L@$t7 55 8b ec 53 8b 5d 10 56 57 0f b7 fb 6b ff 0c})
		{
			.echo ${found1}
			r $t5=dwo(${found1}+0x11)
			r $t6=dwo(${found1}+0x1d)
			.break
		}
	}
	
"
r $t2=dwo(@$t5+0x5f334)
.printf "[+] HashValue2: %x\n",$t2

.for(r $t4=0;1==1;r $t2=dwo($t2+4))
{
	.if($t2==0)
	{
		r $t4=0;
		.break	
	}
	.if(dwo($t2)==$t3)	
	{
		r $t4=$t2+8
		.if(dwo($t6+0x5f334)==0){.break}
		as /mu ${/v:signstr} $t4;
		.block
		{
			.if($sicmp(\"${signstr}\",\"buf32ByteValueAddedSignature\") == 0)
			{
				.break
			}
		}
	}
}
.printf "[+] HashValue3: %x\n",$t4

$$find HashTable index
r $t2=wo($t1+0x22)
.printf "[+] HashTableLength: %x\n",$t2
r $t5=dwo($t1+0x28)
.for(r $t3=0;$t3<$t2;r $t3=$t3+1)
{
	.if(dwo($t5)==$t4)
	{
		.break
	}
	r $t5=$t5+0x9
}
.printf "[+] TableIndex: %x\n",$t3
r $t5=dwo(dwo($t1+0x28)+$t3*9+4)
r $t6=dwo($t5-4)
.printf "[+] ClientKeyAddress: %x\n",$t5
.printf "[+] ClientKeyLength: %x\n",$t6
.printf "[+] Number:\n"
.printf "%d\n",dwo($t9+0x78)
.printf "[+] ClienKey:\n"
.for(r $t2=0;$t2<$t6;r $t2=$t2+1)
{
	.printf "%02X",by($t5+$t2)
}
.printf "\n[+] Bye.\n\n"

BAT调用

@echo off
::%1 is QQ Dump filepath
call %~dp0/windbg32/cdb.exe -z %1 -c "$$><%~dp0/find_clientkey.ws"
pause