Crack内网通积分规则(基于版本3.4.3035)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lixiangminghate/article/details/86018184

    公司内部通讯使用内网通(以下简称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产生积分的代码。当然,怎么做我就不公布了,我输不起官司~

猜你喜欢

转载自blog.csdn.net/lixiangminghate/article/details/86018184