1. 程式人生 > >Crack內網通積分規則(基於版本3.4.3035)

Crack內網通積分規則(基於版本3.4.3035)

    公司內部通訊使用內網通(以下簡稱NWT),他有個好玩的功能:登陸積分可以兌換姓名後的皇冠等。在版本3.4.3035更新前,他的兌換機制還算合理,5000積分可以換個銅冠,所以內網通聊天列表裡很多人名字後面頂著皇冠。(強制)升級到3.4.3035後,需要用以前10倍的積分才能兌換東西(恩,物價飛漲!)。更重要的,以前的刷積分外掛失效了。上帝關上一堵門,必然會為你開啟一扇窗,內網通的作者可能也想到要夠到銅冠太難了,於是,他提供了RMB玩家解決方案----你可以購買積分,如下圖:

    另外,作者還開了網店,通過掃二維碼,進入作者網店消費。通過這種方式,可以獲得可憐兮兮的若干積分。 看著軟體作者手頭比較緊,需要搖錢樹功能的軟體...

    本著不斷人財路的初衷,本文只提供逆向思路,並不會公開逆向的方法(這也是大多數逆向論壇的一貫作風),下面開始正文。先查殼,diewin顯示NWT的主程式ShiYeLine.exe(事業線?作者想表達什麼含義?)並沒有加殼,並且由vs2008編譯生成:

沒加殼,又是c++寫的,看來這軟體是個可以捏的軟柿子。

    NWT本身有很多視窗,而且又由vs2008生成,我決定先用spy++收集視窗和控制元件資訊,畢竟能收集到控制元件資訊可以節省很多分析的時間。但是很可惜,spy++只能捕獲到對話方塊資訊,而對話方塊上的控制元件它一概無法捕捉,換句話說,作者並沒有使用標準控制元件,可能是將Button,Edit等控制元件繪製在對話方塊上。

起初,我只是想修改總積分,所以嘗試用Cheat Engine搜尋並修改總積分,居然還真改成功了:

但是通過CE找到的總積分往往都在NWT的訊息迴圈內(響應OnCopy訊息時,總積分地址包含在訊息體中),雖然可以修改,但有一半的概率會修改失敗,所以我放棄了這種方式,轉而根據錯誤提示來定位和分析NWT產生驗證碼的程式碼:

 NWT新增積分的規則為:點選試試手氣,彈出"試試手氣"對話方塊。對話方塊中含有二維碼,手機掃碼後,會進入作者的網店;同時,手機上出現4位驗證碼,將這串驗證碼輸入"試試手氣"的Edit框,驗證正確NWT會隨機地新增少的可憐的若干積分;驗證失敗提示無效的驗證碼。

    起初我懷疑NWT彈出"試試手氣"對話方塊前會生成併發送驗證碼給伺服器,然而,我抓包後並沒有什麼收穫。看來沒有什麼取巧的辦法了,我只能把NWT拖進IDA進行分析。當IDA載入完畢後,在Functions window中看到NWT匯出了一堆函式名:

百度一下這些函式名發現,原來NWT用了ShinUI框架實現UI部分。ShinUI的設計者本著方便切換各種資源的目的,將對話方塊佈局做成xml檔案,xml檔案中規定了建立對話方塊時需要載入哪些字串/圖片,而"試試手氣"對話方塊的xml檔案位於安裝目錄res\ShiYeLine\layout\ScanCodeDialog.xml中:

<SkinDialog DefaultWidth="375" DefaultHeight="520" Caption="IDS_SCAN_CODE" Animation="SizeChange" Icon="128" SysButton="CLOSE">
	<SkinTextView Id="101" LayoutHeight="28" AlignParentLeft="25" AlignParentRight="25" AlignParentTop="50" Text="IDS_SCAN_CODE_TIPS1" FontStyle="ID_FONT_BOLD_BIG"/>
	<SkinRelativeLayout Id="106" LayoutWidth="300" LayoutHeight="300" AlignParentHorizontalCenter="0" BkgColor="ID_COLOR_WHITE" ToBottomOf="101,17">
		<SkinImageView Id="102" LayoutWidth="WrapContent" LayoutHeight="WrapContent" AlignParentHorizontalCenter="0" AlignParentVerticalCenter="0" />
	</SkinRelativeLayout>
	<SkinTextView Id="103" LayoutHeight="28" AlignParentLeft="25" AlignParentRight="25" ToBottomOf="106,17"Text="IDS_SCAN_CODE_TIPS2" FontStyle="ID_FONT_BOLD_BIG"/>
	<SkinEditView Id="104" LayoutHeight="28" AlignParentLeft="75" AlignParentRight="75" ToBottomOf="103,10" Limit="4" Image="Edit.png"/>
	<SkinTextView Id="105" LayoutHeight="28" AlignParentLeft="75" AlignParentRight="75" ToBottomOf="104,3" FontColor="ID_COLOR_RED"/>
</SkinDialog>

xml中<SkinTextView Id="105">節點,對應了驗證碼出錯時顯示的"無效的驗證碼。"這句話。根據我反反覆覆除錯NWT積累的失敗經驗,我確定NWT為了顯示這句話,必須呼叫CSkinTextView::DrawForeground在對話方塊上繪製字串。而CSkinTextView::DrawForeground又會依次呼叫CPaintHelper::DrawTextW和User32!DrawTextW進行繪製。切入點出現了!DrawTextW的第二個引數是字串引數,如果我設定條件斷點,比較字串引數的內容是否為指定字串,如果是就中斷到偵錯程式再參看呼叫堆疊,那麼一定能定位到比對字串的程式碼的附近。

前面我也說過ShinUI設計為方便開發者替換各種資源,包括字串資源。在除錯過程使用中文字串往往會出錯,所以,我定位到安裝目錄下res\ShiYeLine\value\string.xml(這是ShinUI規定的字串表)中IDS_SCORE_CODE_INVALID的值,將其從"無效的驗證碼。"替換為"dbgstr"。再次啟動NWT,並輸入錯誤的驗證碼,就會提示dbgstr字樣:

現在可以附加windbg並新增條件斷點了:

bp 0142ad20 "as /mu $ustr poi(@esp+4);.block {r @$t0=$scmp(@\"$ustr\",@\"dbgstr\");.if(@$t0==0){};.else{gc;}}"

附註:地址0142ad20是NWT內部函式sub_142AC30呼叫DrawTextW的地址,關於條件斷點的設定,可以參考我部落格中的相關文章。

    再次輸入錯誤的驗證碼,程式中斷後得到下列呼叫堆疊:

00 00d5e834 01438d9c 75012494 04519e38 00d5e8a4 ShiYeLine!CPaintHelper::operator=+0x4080
01 00d5e864 014211e3 75012494 00d5e8c0 04519e38 ShiYeLine!CPaintHelper::DrawTextW+0x9c
02 00d5e904 01419392 75012494 01a0865c 04519bf0 ShiYeLine!CSkinTextView::DrawForeground+0x103

03 00d5e974 01341432 00000001 022093b9 036fb048 ShiYeLine!CSkinView::RedrawView+0x352

04 00d5eaa8 0140fc15 0000040b 00000068 08521048 ShiYeLine!CSkinView::GetClientRect+0x1312
05 00d5eae4 0142720b 0000040b 00000068 08521048 ShiYeLine!CSkinWndHost::WindowProc+0x165
06 00d5eb00 74bebe6b 00140d1e 0000040b 00000068 ShiYeLine!CPaintHelper::operator=+0x56b
07 00d5eb2c 74be833a 014271e0 00140d1e 0000040b USER32!AddClipboardFormatListener+0x49b
08 00d5ec14 74be7bee 014271e0 00000000 0000040b USER32!DispatchMessageW+0x97a
09 00d5ec90 74be79d0 d558274a 00d5ecdc 013ff241 USER32!DispatchMessageW+0x22e
0a 00d5ec9c 013ff241 00d5ecbc 04523920 00d5ed30 USER32!DispatchMessageW+0x10
0b 00d5ecdc 01344c2c 0027065c 022096d1 036fbcc0 ShiYeLine!CSkinDialog::DoModal+0xe1
0c 00d5f580 0140faf9 00000193 044fa1f0 00000111 ShiYeLine!CSkinView::GetClientRect+0x4b0c
0d 00d5f5b4 0142720b 00000111 00000193 044fa1f0 ShiYeLine!CSkinWndHost::WindowProc+0x49
0e 00d5f5d0 74bebe6b 0027065c 00000111 00000193 ShiYeLine!CPaintHelper::operator=+0x56b
0f 00d5f5fc 74be833a 014271e0 0027065c 00000111 USER32!AddClipboardFormatListener+0x49b
10 00d5f6e4 74be7bee 014271e0 00000000 00000111 USER32!DispatchMessageW+0x97a
11 00d5f760 74be79d0 d558274a 00d5f7a8 013ff241 USER32!DispatchMessageW+0x22e

藉助呼叫棧中的返回地址(第3列),依次檢視該地址所在的函式,終於在frame 3#中找到載入字串"IDS_SCORE_CODE_INVALID"的線索

 

這段程式碼中我圈了3個call指令:第1個call,比較輸入的字串長度是否為4;第2個call,比較輸入的字串是否是NWT生成的目標字串;第3個call,前一次比較失敗時,載入字串"IDS_SCORE_CODE_INVALID"。由此可知,第二個call是關鍵所在,進入該函式會看到它前後呼叫了__wcsicmp用於比較輸入和_rand用於產生積分:

.text:014BF6E0 sub_14BF6E0     proc near               ; CODE XREF: sub_1341350+76↑p
.text:014BF6E0
                               ......
.text:014BF6FA ; ---------------------------------------------------------------------------
.text:014BF710
.text:014BF710 loc_14BF710:                            ; CODE XREF: sub_14BF6E0+29↑j
.text:014BF710 ; __unwind { // 14BF73C                 ; wchar_t *
.text:014BF710                 push    eax
.text:014BF711                 push    ecx             ; wchar_t *
.text:014BF712                 call    __wcsicmp
.text:014BF717                 add     esp, 8
.text:014BF71A                 test    eax, eax
.text:014BF71C                 jz      short loc_14BF726
.text:014BF71E                 xor     al, al
                               ......
.text:014BF726 ; ---------------------------------------------------------------------------
.text:014BF726
.text:014BF726 loc_14BF726:                            ; CODE XREF: sub_14BF6E0+3C↑j
.text:014BF726                 push    1
.text:014BF728                 mov     eax, offset dword_19FFBA0
.text:014BF72D                 call    sub_14BEB80
                               .......
.text:014BF780 ; } // starts at 14BF770
.text:014BF786                 cmp     dword_19FFD48, 0
.text:014BF78D                 jbe     short loc_14BF7BF
.text:014BF78F                 call    _rand
.text:014BF794                 cdq
.text:014BF795                 mov     ecx, 32h
.text:014BF79A                 idiv    ecx

根據這段程式碼,我們還能順藤摸瓜,找到NWT產生的驗證碼,不過對於Cracker來說這已經不重要,完全可以patch產生積分的程式碼。當然,怎麼做我就不公佈了,我輸不起官司~