首页 > 网络新闻 > 站长博客 > 正文

QQ2011多账号登录逆向调试分析

时间:2011-11-01 08:47 作者:QQ地带 我要评论

作 者: shuax
从QQ2011 Beta3开始QQ提供了多账户登录功能,但是很遗憾,一次最多登录3个QQ而已。
于是想办法能不能突破呢?
 
依然记得QQ2008那个时候的命令行登录,所以先看一下命令行吧
 
额,我的两个QQ密码可不是一样的,我不相信后面这一串是密码哈希。
 
找啊找,在AFUtil.dll找到了如下函数
看看这个函数
DWORD __cdecl Util::MultiLogin::WriteMultiLoginDataToFileMapping(char a1, int a2)
{
  DWORD v3; // edi@4
  const WCHAR *v4; // eax@4
  HANDLE v5; // eax@4
  const WCHAR *v6; // eax@5
  LPVOID v7; // eax@6
  rsize_t MaxCount; // [sp+10h] [bp-1Ch]@1
  void *hMem; // [sp+14h] [bp-18h]@7
  int v10; // [sp+18h] [bp-14h]@1
  int v11; // [sp+1Ch] [bp-10h]@1
  int v12; // [sp+28h] [bp-4h]@1
 
  v12 = 0;
  CTXStringW::operator_(&unk_65BF535C, &a1);
  v10 = 0;
  v11 = 0;
  if ( !Util::Encrypt::CryptDataWinProtect(a2, 840, L"LoginInfo", &MaxCount, &v10, &v11) )
  {
    sub_65B24E80(L"file", 24, L"func", 2, L"MultiLogin", L"Encrpyt LoginInfo Failed, nErrorCode=%d, dwLastErr=%u", v10);
LABEL_3:
    v12 = -1;
    CTXStringW::_CTXStringW(&a1);
    return 0;
  }
  v3 = MaxCount + 3;
  v4 = (const WCHAR *)CTXStringW::operator wchar_t_const__(&unk_65BF535C);
  v5 = OpenFileMappingW(2u, 0, v4);
  if ( !v5 )
  {
    v6 = (const WCHAR *)CTXStringW::operator wchar_t_const__(&unk_65BF535C);
    v5 = CreateFileMappingW((HANDLE)0xFFFFFFFF, 0, 4u, 0, v3, v6);
    if ( !v5 )
      goto LABEL_3;
  }
  v7 = MapViewOfFile(v5, 6u, 0, 0, 0);
  if ( v7 )
  {
    *(_BYTE *)v7 = 0;
    *((_BYTE *)v7 + 1) = 0;
    *((_BYTE *)v7 + 2) = 0;
    memcpy_s((char *)v7 + 3, MaxCount, hMem, MaxCount);
  }
  LocalFree(hMem);
  v12 = -1;
  ((void (*)(void))CTXStringW::_CTXStringW)();
  return v3;
}
大概就是把传进来的东西a2进行了加密,生成一个名字为a1的内存映射。
现在来看看Util::Encrypt::CryptDataWinProtect这个函数
signed int __cdecl Util::Encrypt::CryptDataWinProtect(int a1, int a2, int a3, int a4, int a5, int a6)
{
  HMODULE v7; // eax@8
  HMODULE v8; // ebx@8
  FARPROC v9; // eax@10
  int v10; // [sp+8h] [bp-12Ch]@12
  int v11; // [sp+Ch] [bp-128h]@12
  int v12; // [sp+10h] [bp-124h]@1
  int v13; // [sp+14h] [bp-120h]@1
  int v14; // [sp+18h] [bp-11Ch]@1
  struct _OSVERSIONINFOW VersionInformation; // [sp+1Ch] [bp-118h]@3
  unsigned int v16; // [sp+130h] [bp-4h]@1
  int v17; // [sp+134h] [bp+0h]@1
 
  v16 = (unsigned int)&v17 ^ dword_65BF49C0;
  v12 = a1;
  v14 = a3;
  v13 = a4;
  if ( !a5 || !a6 )
    return 0;
  *(_DWORD *)a5 = 0;
  *(_DWORD *)a6 = 0;
  VersionInformation.dwOSVersionInfoSize = 276;
  if ( !GetVersionExW(&VersionInformation) )
  {
    *(_DWORD *)a5 = -1;
    return 0;
  }
  if ( VersionInformation.dwMajorVersion < 5 )
  {
    *(_DWORD *)a5 = -2;
    return 0;
  }
  v7 = LoadLibraryW(L"Crypt32.dll");
  v8 = v7;
  if ( !v7 )
  {
    *(_DWORD *)a5 = -3;
    *(_DWORD *)a6 = GetLastError();
    return 0;
  }
  v9 = GetProcAddress(v7, "CryptProtectData");
  if ( v9 )
  {
    v11 = v12;
    v10 = a2;
    if ( ((int (__stdcall *)(int *, int, _DWORD, _DWORD, _DWORD, signed int, int))v9)(&v10, v14, 0, 0, 0, 4, v13) )
    {
      FreeLibrary(v8);
      return 1;
    }
    *(_DWORD *)a5 = -5;
  }
  else
  {
    *(_DWORD *)a5 = -4;
  }
  *(_DWORD *)a6 = GetLastError();
  FreeLibrary(v8);
  return 0;
}
也不过是调用CryptProtectData来加密罢了。
参数分别a1、a2、a3是传入数据,a4是传出数据,a5、a6是ErrorCode和dwLastErr。
这个函数很简单,我们可以自己把它给写了
 
#include <wincrypt.h>
 
typedef struct _CRYPTPROTECT_PROMPTSTRUCT {
  DWORD   cbSize;
  DWORD   dwPromptFlags;
  HWND    hwndApp;
  LPCWSTR szPrompt;
} CRYPTPROTECT_PROMPTSTRUCT, *PCRYPTPROTECT_PROMPTSTRUCT;
 
bool CryptDataWinProtect(struct _CRYPTOAPI_BLOB DataUnp,struct _CRYPTOAPI_BLOB *DataOut)
{
    HMODULE cryptdll = LoadLibraryW(L"Crypt32.dll");
    BOOL (WINAPI *pCryptProtectData)(DATA_BLOB* pDataIn, LPCWSTR szDataDescr, DATA_BLOB* pOptionalEntropy, PVOID pvReserved, CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, DATA_BLOB* pDataOut);
    pCryptProtectData = (BOOL (WINAPI *)(DATA_BLOB* pDataIn, LPCWSTR szDataDescr, DATA_BLOB* pOptionalEntropy, PVOID pvReserved, CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, DWORD dwFlags, DATA_BLOB* pDataOut))::GetProcAddress(cryptdll, "CryptProtectData");
    return pCryptProtectData(&DataUnp, L"LoginInfo", 0, 0, 0, 4, DataOut);
}
然后再把前面的Util::MultiLogin::WriteMultiLoginDataToFileMapping写出来,对了,我们还不知道WriteMultiLoginDataToFileMapping的数据从哪里来的呢,上OD,断一下,找到了,来自LoginPanel.dll
int __thiscall sub_651181B0(void *this)
{
  int v1; // edi@1
  int v2; // ebx@1
  int v3; // eax@2
  int v4; // eax@3
  int v5; // edx@4
  int v6; // ecx@6
  int v7; // eax@7
  int v8; // ecx@10
  int v9; // eax@11
  int v10; // ecx@14
  int v11; // eax@15
  int v12; // ecx@18
  int v13; // eax@19
  unsigned int v14; // esi@22
  int v15; // eax@27
  int *v16; // eax@27
  __int16 v17; // di@29
  const wchar_t *v18; // eax@32
  const void *v19; // eax@33
  const wchar_t *v20; // eax@35
  const void *v21; // eax@36
  const wchar_t *v22; // eax@38
  const void *v23; // eax@39
  int result; // eax@46
  int v25; // [sp-8h] [bp-390h]@46
  rsize_t v26; // [sp-4h] [bp-38Ch]@32
  int v27; // [sp+Ch] [bp-37Ch]@1
  int v28; // [sp+10h] [bp-378h]@1
  int v29; // [sp+14h] [bp-374h]@4
  char v30; // [sp+18h] [bp-370h]@27
  int v31; // [sp+1Ch] [bp-36Ch]@27
  char Dst; // [sp+20h] [bp-368h]@3
  int v33; // [sp+120h] [bp-268h]@38
  char v34; // [sp+124h] [bp-264h]@39
  __int16 v35; // [sp+134h] [bp-254h]@40
  char v36; // [sp+136h] [bp-252h]@35
  int v37; // [sp+238h] [bp-150h]@35
  char v38; // [sp+23Ch] [bp-14Ch]@36
  __int16 v39; // [sp+24Ch] [bp-13Ch]@37
  char v40; // [sp+24Eh] [bp-13Ah]@32
  int v41; // [sp+350h] [bp-38h]@32
  char v42; // [sp+354h] [bp-34h]@33
  __int16 v43; // [sp+364h] [bp-24h]@34
  GUID pguid; // [sp+368h] [bp-20h]@2
  unsigned int v45; // [sp+378h] [bp-10h]@1
  int v46; // [sp+384h] [bp-4h]@27
  int v47; // [sp+388h] [bp+0h]@1
 
  v45 = (unsigned int)&v47 ^ dword_651646B0;
  v1 = (int)this;
  v2 = (int)((char *)this + 1488);
  v27 = (int)this;
  v28 = (int)((char *)this + 1488);
  if ( CTXBSTR::IsEmpty((char *)this + 1488, (unsigned int)&v47 ^ dword_651646B0) )
  {
    CoCreateGuid(&pguid);
    v3 = CTXBSTR::operator_(v2);
    Util::Com::GuidToString(&pguid, v3, 0);
  }
  memset(&Dst, 0, 0x348u);
  v4 = *(_DWORD *)(v1 + 368);
  if ( v4 )
  {
    v29 = (*(_DWORD *)(v1 + 372) - v4) >> 2;
    v5 = v29;
  }
  else
  {
    v5 = 0;
    v29 = 0;
  }
  v6 = *(_DWORD *)(v1 + 384);
  if ( v6 )
    v7 = (*(_DWORD *)(v1 + 388) - v6) >> 2;
  else
    v7 = 0;
  if ( v5 != v7
    || ((v8 = *(_DWORD *)(v1 + 400)) != 0 ? (v9 = (*(_DWORD *)(v1 + 404) - v8) >> 2) : (v9 = 0),
        v5 != v9
     || ((v10 = *(_DWORD *)(v1 + 416)) != 0 ? (v11 = (*(_DWORD *)(v1 + 420) - v10) >> 1) : (v11 = 0),
         v5 != v11
      || ((v12 = *(_DWORD *)(v1 + 432)) != 0 ? (v13 = (*(_DWORD *)(v1 + 436) - v12) >> 2) : (v13 = 0), v5 != v13))) )
  {
    result = 0;
  }
  else
  {
    v14 = 0;
    if ( v5 )
    {
      do
      {
        v12 = *(_DWORD *)(v1 + 432);
        if ( !v12 || v14 >= (*(_DWORD *)(v1 + 436) - v12) >> 2 )
        {
          invalid_parameter_noinfo();
          v5 = v29;
        }
        if ( *(_DWORD *)(*(_DWORD *)(v1 + 432) + 4 * v14) )
        {
          v15 = sub_65117080(v14);
          CTXBSTR::CTXBSTR(&v30, v15);
          v46 = 0;
          v16 = (int *)sub_65117080(v14);
          v31 = 0;
          if ( &v31 != v16 )
            sub_65102150(*v16);
          LOBYTE(v46) = 1;
          v17 = *(_WORD *)sub_65117110(v14);
          if ( v14 )
          {
            if ( v14 == 1 )
            {
              v26 = CTXBSTR::Length(&v30);
              v20 = (const wchar_t *)CTXBSTR::operator wchar_t__(&v30);
              wcsncpy_s((wchar_t *)&v36, 0x80u, v20, v26);
              v37 = (unsigned int)sub_651021A0(&v31) <= 0x10;
              if ( (unsigned int)sub_651021A0(&v31) <= 0x10 )
              {
                v26 = sub_651021A0(&v31);
                v21 = (const void *)sub_65113D80(&v31);
                memcpy_s(&v38, 0x10u, v21, v26);
              }
              v39 = v17;
            }
            else
            {
              if ( v14 == 2 )
              {
                v26 = CTXBSTR::Length(&v30);
                v18 = (const wchar_t *)CTXBSTR::operator wchar_t__(&v30);
                wcsncpy_s((wchar_t *)&v40, 0x80u, v18, v26);
                v41 = (unsigned int)sub_651021A0(&v31) <= 0x10;
                if ( (unsigned int)sub_651021A0(&v31) <= 0x10 )
                {
                  v26 = sub_651021A0(&v31);
                  v19 = (const void *)sub_65113D80(&v31);
                  memcpy_s(&v42, 0x10u, v19, v26);
                }
                v43 = v17;
              }
            }
          }
          else
          {
            v26 = CTXBSTR::Length(&v30);
            v22 = (const wchar_t *)CTXBSTR::operator wchar_t__(&v30);
            wcsncpy_s((wchar_t *)&Dst, 0x80u, v22, v26);
            v33 = (unsigned int)sub_651021A0(&v31) <= 0x10;
            if ( (unsigned int)sub_651021A0(&v31) <= 0x10 )
            {
              v26 = sub_651021A0(&v31);
              v23 = (const void *)sub_65113D80(&v31);
              memcpy_s(&v34, 0x10u, v23, v26);
            }
            v35 = v17;
          }
          LOBYTE(v46) = 0;
          if ( v31 )
          {
            (*(void (__stdcall **)(int))(*(_DWORD *)v31 + 8))(v31);
            v31 = 0;
          }
          v46 = -1;
          CTXBSTR::_CTXBSTR(&v30);
          v1 = v27;
          v5 = v29;
        }
        ++v14;
      }
      while ( v14 < v5 );
      v2 = v28;
    }
    v26 = (rsize_t)&Dst;
    v25 = v12;
    v28 = (int)&v25;
    CTXStringW::CTXStringW(&v25, v2);
    result = Util::MultiLogin::WriteMultiLoginDataToFileMapping(v25, v26);
  }
  return result;
}
我表示,我不怎么看得懂,直接看了一下OD。参数1是CoCreateGuid生成的一个GUID,参数2是840字节,内容为QQ号(一个的时候,多个我不太看得懂)
于是写出我们自己的代码
int CreateMap(const wchar_t *name)
{
    BYTE pbDataInput[840];
    memset(pbDataInput,0,840);
    wsprintf((WCHAR*)pbDataInput,L"%d",你的QQ号码);
    
    DATA_BLOB DataUnp;
    DATA_BLOB DataOut;
    DataUnp.cbData = 840;
    DataUnp.pbData = pbDataInput;
    
    CryptDataWinProtect(DataUnp,&DataOut);
    
    HANDLE hMapFile;
    hMapFile = OpenFileMapping(FILE_MAP_WRITE, FALSE, name);
    if(!hMapFile)
    {
        hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, DataOut.cbData + 3,name);
        if(!hMapFile)
        {
            return 0;
        }
    }
    
    BYTE* pbFile = (BYTE*)MapViewOfFile( hMapFile, FILE_MAP_WRITE, 0, 0, 0);
    pbFile[0] = 0;
    pbFile[1] = 0;
pbFile[2] = 0;
//强迫搞3个0字节,不知道干嘛滴
    
    memcpy(pbFile+3,DataOut.pbData,DataOut.cbData);
    
    //printf("%d",DataOut.cbData);
    LocalFree(&DataOut);
    return DataOut.cbData + 3;
}
#define CHARS_IN_GUID   39
int main()
{
    GUID guidPipe;
    WCHAR wszGUID[CHARS_IN_GUID + 1];
    WCHAR wszNAME[CHARS_IN_GUID + 1 + 5 + 2];
    CoCreateGuid(&guidPipe);
    StringFromGUID2(guidPipe, wszGUID, CHARS_IN_GUID);
    wcscpy(wszNAME,wszGUID+1);
    wszNAME[wcslen(wszNAME)-1] = 0;
    wcscpy(wszGUID,wszNAME);
    int res = CreateMap(wszNAME);
    if(res)
    {
        for(int i=0;i<1;i++)
        {
            wsprintf(wszNAME,L"%s_%d_%d",wszGUID,res,i);
            printf("%S\n",wszNAME);
            
            wchar_t exeString[MAX_PATH + 1];
            wsprintf(exeString,L"%s /MLogin:%s",L"C:\\Program Files (x86)\\Tencent\\QQ\\Bin\\QQ.exe",wszNAME);
            
            STARTUPINFO si = {0};
            PROCESS_INFORMATION pi = {0};
            si.cb = sizeof(STARTUPINFO);
            if(CreateProcessW(NULL, exeString, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
            {
                //getchar();
                Sleep(500);
            }
        }
    }
    //getchar();
}
//注意,程序退出需要减缓一下,要不然QQ还没有读取到内存数据的时候,你都退出了,数据就没了。
 

顶一下
(0)
0%
踩一下
(0)
0%

Google提供的广告