天天看點

PC微信逆向:使用HOOK攔截二維碼

本文作者:鬼手56(信安之路病毒分析小組成員 & 信安之路 2019 年度優秀作者)

成員招募:信安之路病毒分析小組尋找志同道合的朋友

目前微信版本

PC微信逆向:使用HOOK攔截二維碼

尋找微信二維碼基址

PNG 檔案格式

微信二維碼在記憶體中存放形式是 png 格式的二進制資料,是以我們需要眼熟一下 png 的檔案格式,如圖

PC微信逆向:使用HOOK攔截二維碼

前 32 個位元組是固定的,分别是 btPngSignature 和 struct PNG_CHUNK chunk 結構,其中儲存有 png 圖檔的辨別。

其中 NG 和 IHDR 是每個 PNG 檔案都會有的辨別,眼熟一下就好。微信的二維碼圖檔就是通過這種格式在記憶體中存放

使用 CE 過濾基址

首先在微信未登入狀态下附加微信,此時二維碼還未加載

PC微信逆向:使用HOOK攔截二維碼

然後選擇未知的數值,點選首次掃描

PC微信逆向:使用HOOK攔截二維碼

出現三百萬個結果

PC微信逆向:使用HOOK攔截二維碼

此時我們再次點選切換賬号,出現二維碼,讓儲存二維碼的位址被指派

PC微信逆向:使用HOOK攔截二維碼

然後選擇變動的數值 再次掃描

PC微信逆向:使用HOOK攔截二維碼

此時還剩下七萬個結果

PC微信逆向:使用HOOK攔截二維碼

然後用手機掃描二維碼 不要點選登入,再次掃描變動的數值,此時還剩三萬多個結果

PC微信逆向:使用HOOK攔截二維碼

接着随意移動微信框,點選未變動的數值,還剩一萬多個結果。傳回二維碼登入重複以上操作,直到位址欄還剩下兩個綠色的基址,這兩個綠色的基址就是我們要的。

PC微信逆向:使用HOOK攔截二維碼

因為随機基址的存在,這個位址在各位的電腦上是不一樣的。但是低四位是一緻的,這兩個位址應該是 xxxx9194 和 xxxx919C。

使用 OD 确定二維碼基址

然後重新開機一次微信,再用 CE 附加,回到這個狀态

PC微信逆向:使用HOOK攔截二維碼
PC微信逆向:使用HOOK攔截二維碼

用 OD 附加微信,在找到的第一個位址 xxxx9194 下記憶體寫入斷點

PC微信逆向:使用HOOK攔截二維碼

點選切換賬号,在二維碼未加載時程式會斷下。注意,這個地方會斷下來兩次,第二次才是我們要的結果。

因為二維碼是存放在微信的核心子產品 WeChatWin 中的,是以我們在堆棧中找到所有的 WeChatWin 中的函數

PC微信逆向:使用HOOK攔截二維碼

像這種 API 的調用就可以直接排除掉,然後在每一個疑似函數上下斷點。找的時候堆棧盡量往下拉,這個函數會比較靠後。

因為我已經找過一遍了,是以直接告訴你們是這一個。特征是有一個 ecx 傳參。

PC微信逆向:使用HOOK攔截二維碼

接着在這個函數上下斷點,删除記憶體通路斷點,F9 運作

PC微信逆向:使用HOOK攔截二維碼

然後掃一下二維碼,點選傳回二維碼登入,程式斷下

PC微信逆向:使用HOOK攔截二維碼

此時觀察 ecx 指針的内容,明顯是一個結構體,結構體的第一個是位址,第二個好像是大小。然後在這個位址上資料視窗跟随

PC微信逆向:使用HOOK攔截二維碼

裡面是 PNG 檔案的二進制資料,這個就是我們要找的微信二維碼的基址

驗證二維碼基址

打開 PCHunter,選擇微信程序,檢視->檢視程序記憶體,輸入位址和大小,然後将記憶體 dump 下來

PC微信逆向:使用HOOK攔截二維碼

打開圖檔

PC微信逆向:使用HOOK攔截二維碼

現在已經确定就是我們需要的二維碼。然後我們将這個 call 的位址減去子產品基址,記錄下偏移。待會需要 HOOK 這個 call

尋找微信二維碼内容的基址

微信二維碼的存儲内容

二維碼其實是一種開放性的資訊存儲器,它将固定的資訊存儲在自己的黑白小方塊之間。大部分的二維碼都有一個特點,就是裡面存放的其實是一段文本。我們可以利用這個文本來尋找突破口

将微信的二維碼截圖儲存,然後用線上的二維碼解碼器解析微信的二維碼

PC微信逆向:使用HOOK攔截二維碼

可以看到解碼之後的結果是一段網址

使用 CE 尋找二維碼内容的基址

PC微信逆向:使用HOOK攔截二維碼

如果直接搜尋這段網址是找不到任何結果的,原因是因為微信在儲存這段位置的時候,實際上是将它分為了兩部分存儲

複制

第一部分的固定不變的,第二部分被當作一個參數傳入,用戶端從伺服器擷取的隻是第二部分的内容。是以我們去搜尋第二部分。

另外,微信的二維碼會定時重新整理,重新整理的時候會改變第二部分的内容。如果你搜不到的話可能是因為之前的文本已經失效了。從解析文本到搜尋文本最好在一分鐘之内完成

此時 我們直接搜尋第二部分的文本

PC微信逆向:使用HOOK攔截二維碼

搜尋完成之後,等待二維碼自動重新整理,然後找到那個變化之後的位址,用截圖上傳的方式確定找到的是正确的位址

PC微信逆向:使用HOOK攔截二維碼

然後用 OD 附加微信,在找到的位址上下記憶體寫入斷點

PC微信逆向:使用HOOK攔截二維碼

等待二維碼自動重新整理,二維碼重新整理時會往原來存放二維碼的位址寫入新的二維碼資料,程式就會斷下

PC微信逆向:使用HOOK攔截二維碼

此時 eax 指向二維碼的文本内容,我們找到堆棧中的第一個位址,在資料視窗顯示,此時就能找到存放微信二維碼資料的基址了

PC微信逆向:使用HOOK攔截二維碼

然後我們在 CE 中添加 WeChatWin.dll 子產品,找到子產品基址,算出偏移(用 0x104CF618-0xF250000=127F618)。然後将這個位址換成子產品基址+偏移的方式添加到 CE 位址欄。

驗證基址

重新打開微信,用 CE 載入

PC微信逆向:使用HOOK攔截二維碼

保留目前清單,然後将二維碼内容指針的值添加到清單

PC微信逆向:使用HOOK攔截二維碼

點選确定。此時二維碼的内容和解析出來的内容一緻,說明基址有效

PC微信逆向:使用HOOK攔截二維碼

定制微信登入二維碼的可能性

那麼我們拿到這個二維碼的内容有什麼作用呢?我們可以将這個擷取到的二維碼内容調用二維碼生成器的 API 接口進行再次編碼,然後生成一個更加漂亮好看專屬二維碼,效果如圖:

PC微信逆向:使用HOOK攔截二維碼

使用 hook 截取二維碼

接着我們編寫一個 dll,将這個 dll 注入到微信程序中,利用 IAT Hook 截取微信的二維碼。部分關鍵代碼如下:

開啟 HOOK

void StartHook(DWORD dwHookOffset,LPVOID pFunAddr, HWND hWnd)
{
  hDlg = hWnd;
  //拿到子產品基址
  DWORD dwWeChatWinAddr = GetWeChatWinAddr();
  //需要HOOK的位址
  DWORD dwHookAddr = dwWeChatWinAddr + dwHookOffset;

  //填充資料
  jmpCode[0] = 0xE9;
  //計算偏移
  *(DWORD*)(&jmpCode[1]) = (DWORD)pFunAddr - dwHookAddr-5;

  // 儲存以前的屬性用于還原
  DWORD OldProtext = 0;

  // 因為要往代碼段寫入資料,又因為代碼段是不可寫的,是以需要修改屬性
  VirtualProtect((LPVOID)dwHookAddr, 5, PAGE_EXECUTE_READWRITE, &OldProtext);

  //儲存原有的指令
  memcpy(backCode, (void*)dwHookAddr, 5);

  //寫入自己的代碼
  memcpy((void*)dwHookAddr, jmpCode, 5);
  
  // 執行完了操作之後需要進行還原
  VirtualProtect((LPVOID)dwHookAddr, 5, OldProtext, &OldProtext);
}           

複制

解除安裝 HOOK

void UnHook(DWORD dwHookOffset)
{
  DWORD dwWeChatWinAddr = GetWeChatWinAddr();
  DWORD dwHookAddr = dwWeChatWinAddr + dwHookOffset;

  // 儲存以前的屬性用于還原
  DWORD OldProtext = 0;

  // 因為要往代碼段寫入資料,又因為代碼段是不可寫的,是以需要修改屬性
  VirtualProtect((LPVOID*)dwHookAddr, 5, PAGE_EXECUTE_READWRITE, &OldProtext);

  // Hook 就是向其中寫入自己的代碼
  memcpy((LPVOID*)dwHookAddr, backCode, 5);

  // 執行完了操作之後需要進行還原
  VirtualProtect((LPVOID*)dwHookAddr, 5, OldProtext, &OldProtext);
}           

複制

儲存圖檔

void SaveImg(DWORD qrcode)
{
  //擷取圖檔長度
  DWORD dwPicLen = qrcode + 0x4;
  size_t cpyLen = (size_t)*((LPVOID*)dwPicLen);
  //拷貝圖檔的資料
  char PicData[0xFFF] = { 0 };
  memcpy(PicData, *((LPVOID*)qrcode), cpyLen);

  //将檔案寫到本地
  HANDLE hFile = CreateFileA("E:\\qrcode.png",GENERIC_ALL,0,NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,NULL);
  if (hFile==NULL)
  {
    MessageBox(NULL, "建立圖檔檔案失敗", "錯誤", 0);
    return;
  }

  DWORD dwRead = 0;
  if (WriteFile(hFile, PicData, cpyLen, &dwRead, NULL) == 0)
  {
    MessageBox(NULL, "寫入圖檔檔案失敗", "錯誤", 0);
    return;
  }

  CloseHandle(hFile);
  //顯示圖檔
  CImage img;
  CRect rect;
  //拿到控件的句柄
  HWND hPic = GetDlgItem(hDlg, IDC_QRPIC);
  GetClientRect(hPic, &rect);
  //載入圖檔
  img.Load("E:\\qrcode.png");
  img.Draw(GetDC(hPic), rect);

  //顯示二維碼内容
  ShowQrCodeContent(hDlg);

  //完成之後解除安裝HOOK
  UnHook(QrCodeOffset);
}

           

複制

最終效果

最終效果如圖:

PC微信逆向:使用HOOK攔截二維碼

推薦閱讀

原創 PC 端微信技術研究之儲存聊天語言

原創 微信PC端技術研究(3)-如何找到消息發送接口

原創 PC微信逆向:發送與接收消息的分析與代碼實作

原創 PC微信逆向:分析發送xml名片call

原創 PC微信逆向:兩種姿勢教你解密資料庫檔案