3D验证码

本技术来源:

3DCaptcha http://www-personal.umich.edu/~mressl/3dcaptcha/

下载地址 http://code.google.com/p/3dcaptcha/

官方LOGO

image

 

本例完全由此样例程序翻译而来,未作任何附加处理.

以下是C#对此算法的实现

使用方法

效果截图

[转]从IDA中提取代码做汇编注册机之VB篇

作者:laomms      文章来源:http://www.cublog.cn/u1/35278/showart_271165.html

看了deroko 的《Ripping VB code and making keygen out of it》,觉得写得非常好,给写算法注册机带来了不少方便之处。但是作者在最后部分特别是写注册机方面过于烦琐,不直观,尤其是其中引用大部分的宏在TASM中编译的时带来不少麻烦,所以我重新编辑了下,连翻带改写了这篇文章,里面的许多内容及一些处理方法会跟作者的的有所出入,并且添加了许多内容。附上在RADASM中编译通过的MASM32代码。

一、找到关键算法处
这是个VB写的CRACKME,先用VB的反编译利器P32Dasm(http://t4c.ic.cz/forum/showthread.php?t=67)反编译这个软件,得到以下信息:

文件: C:\Documents and Settings\ crackme\crackme.exe
P32Dasm v2.3
VB6 编译类型: NCode
frm1
004028F4   1.1 cmdExit.Click()
004029C6   1.2 cmdRegister.Click()        注册按钮处理过程
00402BD3   1.3 Command1.Click()
00402D3D   1.4 Command2.Click()
0040315C   1.5 Form.Load()
00403332   1.6 txtReg.KeyPress(KeyAscii As Integer)
文件反编译结束。

从这里,我们得到信息,该CRACKME的按钮处理过程在004029C6处。
用OD载入,在004029C6处下断,运行程序后随便输入注册码,按注册后断下。单步跟踪,到下面代码:

00402AB2    .   68 24404000   PUSH crackme.00404024                    ;   00404024   001540A4   UNICODE “Serializer”
00402AB7    .   50            PUSH EAX                                 ;   0012F4EC   0015A04C   UNICODE “6D6074621C09010205737401740207”
00402AB8    .   E8 00090000   CALL crackme.004033BD                    ;   关键算法
00402ABD    .   8BD0          MOV EDX,EAX                              ;   得到注册码
00402ABF    .   8D4D DC       LEA ECX,DWORD PTR SS:[EBP-24]
00402AC2    .   E8 99E7FFFF   CALL <JMP.&msvbvm60.__vbaStrMove>
00402AC7    .   50            PUSH EAX
00402AC8    .   E8 99E7FFFF   CALL <JMP.&msvbvm60.__vbaStrCmp>         ;   对比注册码
00402ACD    .   8BF0          MOV ESI,EAX

将OD中的内存数据窗口切换到“长型”-“地址”,我们在00402AB2和00402AB7处右击-在数据窗口中跟随,可以看到这两处分别是UNICODE字符串“Serializer”和机器码,00402AB8处是关键算法,00402ABD处得到真正的注册码,所以00402AB8处的CALL非常关键,但是我们现在不想了解其中的算法过程,只想拷贝CALL 004033BD里面的代码做注册机。

二、截取代码
启动IDA,反汇编该CRACKME,“跳转到指定地址”,填入004033BD,将004033BD到00403613处代码涂黑(即CALL 004033BD的整个内容),选择菜单“文件”-创建文件-创建ASM文件,我们保存为crackme.asm。
我们将这段代码随便找个汇编注册机模版放进去,这里将它放到我修改的ARTEAM的一个模版里。在RADASM中编译,让它列出错误。

三、分析代码
先讲一些基本的错误:
1、seg000:004033C3                 push     (offset loc_401175+1)
从IDA中可以看出这是个压入异常句柄,置0即可,我们把它改为:
push   offset   dummy_ seh

dummy_seh:      xor     eaxeax
ret
2、mov  eax, large fs:0
此处的large是修饰符,直接去掉,“fs:0” MASM也认不出来,我们在.DATA区加入
assume  fs:nothing
3、seg000:004033DF mov dword ptr [ebp-8], offset dword_401160
我们在OD中可以看到,dword_401160= 8000Eh
所以在.DATA处加入:
dword_401160            dd 8000Eh
4、seg000:00403572 push offset dword_4027BC
这里其实IDA反汇编错误(因为VB的字符串是以UNICODE形式存在的),dword_4027BC 是字符串 “0” 和 2 ,即:
dd 2 <– — 字符串长度
string_0 dd 30h <—– 字符串0
所以在.DATA处加入:
dd 2
dword_4027BC          dd 30h
5、jo   loc_403616
push  offset loc_403602
jmp   short loc_4035E9
这几个可以在IDA中直接跟到代码处拷贝相应的内容:
loc_403616:        ; CODE XREF: seg000:00403488 j
; seg000:004034F2 j …
call     __vbaErrorOverflow
loc_403602:        ; DATA XREF: seg000:004035B9 o
mov  ecx, [ebp-14h]
mov  eax, [ebp-20h]
pop  edi
pop  esi
mov   large fs:0, ecx
pop  ebx
leave
retn   8

loc_4035E9:        ; CODE XREF: seg000:004035BE j
lea  ecx, [ebp-24h]
call    __vbaFreeStr
lea  ecx, [ebp-28h]
call    __vbaFreeStr
lea  ecx, [ebp-2Ch]
call    __vbaFreeStr
retn

现在来讲一个重点错误:
就是所有的VB内部函数,这个在MASM中当然不可能被识别。但是…
感谢Vortex为我们解决了这个问题,它的Dll2inc(http://www.vortex.masmcode.com/)轻松解决了这个烦恼。Vortex原本是想用来生成可以供MASM调用的C运行库的,可是我拿VB的运行库msvbvm60.dll测试了一下,居然成功了。
拿msvbvm60.dll经过Dll2inc转换后得到msvbvm60.def、msvbvm60.inc、msvbvm60.lib,这样就可以在MASM32中调用了。他专门定义了个调用这些函数的宏:cinvoke。我们只要在include和includelib中加入这些INC文件和LIB文件,然后将所有的调用VB内部函数的CALL改为cinvoke就可以“光明正大”的用这些VB内部函数了。

RADASM中编译,可是还是有错误。
我们用OD来跟一下到底问题出在哪里。RADASM中设置好调试器关联后,选择“构建”-“在调试器中运行”,OD自动反汇编了这个有问题的KEYGEN。我们逐步跟踪,到下面的代码时,出现异常,问题就出在__vbaStrCat中。

004012BC    .   E8 2B020000   CALL <JMP.&msvbvm60.__vbaStrCat>

我们跟入__vbaStrCat:

660E5F3A >   55              PUSH EBP
660E5F3B     8BEC            MOV EBP,ESP
660E5F3D     8D45 08         LEA EAX,DWORD PTR SS:[EBP+8]
660E5F40     50              PUSH EAX
660E5F41     FF75 08         PUSH DWORD PTR SS:[EBP+8]
660E5F44     FF75 0C         PUSH DWORD PTR SS:[EBP+C]
660E5F47     FF15 18EE1066   CALL DWORD PTR DS:[6610EE18]             ; DS:[6610EE18]=00000000,问题出在这里

跟到660E5F47时出现异常,我们看到PTR DS:[6610EE18]为0,我们拿原来的CRACKME或者直接拿msvbvm60.dll反汇编,跟入__vbaStrCat:

660E5F47     FF15 18EE1066   CALL DWORD PTR DS:[6610EE18]             ; OLEAUT32.VarBstrCat

可以看出问题所在:PTR DS:[6610EE18]在CRACKEME中有被初始化成VarBstrCat而在KEYGEN中没有。
我们用IDA反汇编msvbvm60.dl后,跟踪__vbaStrCat函数:

.text:660E5F40                 push    eax
.text:660E5F41                 push     [ebp+arg_0]
.text:660E5F44                 push     [ebp+arg_4]
.text:660E5F47                 call     dword_6610EE18
跟进CALL:
.data:6610EE18 dword_6610EE18  dd 0                    ; DATA XREF: sub_66004F58+46C w
.data:6610EE18                                         ; __vbaStrCat+D r
继续跟踪:
.text:660053BA                 push    offset aVarbstrcat ; “VarBstrCat”
.text:660053BF                 push    edi
.text:660053C0                 call    esi ; GetProcAddress
.text:660053C2                 test    eaxeax
.text:660053C4                 mov      dword_6610EE18, eax
我们来看看哪个输出函数能够导出该功能,继续跟踪下去:
.text:66004D81 sub_66004D81    proc near               ; CODE XREF: sub_66004D59+20 p
.text:66004D81                                         ; CreateIExprSrvObj+3E p
CreateIExprSrvObj觉得可疑,跟进看看:
.text:660EA734                  public CreateIExprSrvObj
.text:660EA734 CreateIExprSrvObj proc near
.text:660EA734
.text:660EA734 var_8            = dword ptr -8
.text:660EA734 var_4            = dword ptr -4
.text:660EA734 arg_0            = dword ptr   8
.text:660EA734 arg_4            = word ptr   0Ch
.text:660EA734 arg_8            = word ptr   10h
.text:660EA734
.text:660EA734                 push    ebp
.text:660EA735                 mov     ebpesp
.text:660EA737                 push    ecx
.text:660EA738                 push    ecx
.text:660EA739                 push    ebx
.text:660EA73A                 xor     ebxebx
.text:660EA73C                 cmp      [ebp+arg_4], 4
.text:660EA741                 push    esi
.text:660EA742                 push    edi
.text:660EA743                 mov      [ebp+var_8], ebx
.text:660EA746                 mov      [ebp+var_4], ebx
.text:660EA749                 jnz      loc_660EA7F6
.text:660EA74F                 cmp      [ebp+arg_8], bx
.text:660EA753                 ja       loc_660EA7F6
.text:660EA759                 call     sub_660CCD82
.text:660EA75E                 test    eaxeax
.text:660EA760                 jnz      short loc_660EA782
.text:660EA762                 push     2
.text:660EA764                 push    ebx
.text:660EA765                 push    ebx
.text:660EA766                 push     9
.text:660EA768                 push    ebx
.text:660EA769                 push    ebx
.text:660EA76A                 push     2636h
.text:660EA76F                 push    ebx
.text:660EA770                 push     6
.text:660EA772                 call     sub_66004D81   <—- 就是这个CALL
我们需要这个CALL,deroko对CALL进行了修改并使之运行正常:
push     0
push     4
push     0
call     CreateIExprSrvObj(记得改call为cinvoke)
我们复制到代码区,RADASM中编译已经通过了。

四、解决UNICODE字符串
我们注意到在调用这个算法CALL之前有压入机器码和字符串“Serializer”,这些字符串在VB中都是以UNICODE形式存在的,所以在定义字符串“Serializer”时我们必须定义为UNICODE字符格式。
利用MASM32的宏ucMacros.asm可以轻易做到只要在定义的字符串变量前面加WSTR即可:
WSTR static_string,     “Serializer”
我们还需要在代码中加入压入机器码和字符串“Serializer”的代码,然后才能调用CALL。

五,完善注册机
如果进一步分析,会发现软件机器码的由来。
0040322C    .   50            PUSH EAX
0040322D    .   E8 EEF3FFFF   CALL crackme.00402620                    ;   此CALL跳到GetComputerNameW函数,返回计算机名

00403286    .   57            PUSH EDI                                 ;   00404024   0013F63C   UNICODE “Serializer”
00403287    .   50            PUSH EAX                                 ;   计算机名   UNICODE “OEM-MICRO”
00403288    .   E8 30010000   CALL crackme.004033BD                    ;   算法CALL
0040328D    .   8BD0          MOV EDX,EAX                              ;   得到机器码
也就是说计算机名和字符串“Serializer”经过这个算法CALL后得到机器码。
在注册机中我们就可以直接用改代码获取机器码。

好了,主要代码已经贴在下面了,其实关键CALL内的算法只是字符串间简单的异或操作。这里讨论的不是怎么去分析这个算法过程。我主要是想大家了解的是这种注册机写法的思路。若有差错,希望补充。
Generate proc
push     0
push     4
push     0
cinvoke     CreateIExprSrvObj
; ———————————————————–    计算机器码                                       
mov   computer_name_size,sizeof computer_name
invoke    GetComputerNameW,addr computer_name,addr computer_name_size
xor     eaxeax
mov     edioffset computer_name
stc
sbb     ecxecx
cld
repnz   scasw
not     ecx
dec     ecx
shl     ecx, 1
mov      computer_name_size, ecx
mov     eaxoffset static_string
mov      ptr1, eax
mov     eaxoffset computer_name
mov      ptr2, eax
push    offset ptr1                    ;压入UNICODE字符串”Serializer”
push    offset ptr2                    ;压入计算机名
call     loc_4033BD                     ;算法CALL                      
mov     esieax                       ;得到机器码
mov     edioffset hardware_key
xor     ecxecx

Ollydbg——宇佳仓库管理系统 V1.6.1(VB6 native)

Ollydbg——宇佳仓库管理系统 V1.6.1(VB6 native)

下载页面: http://www.skycn.com/soft/12136.html
软件大小: 9081 KB
软件语言: 简体中文
软件类别: 国产软件 / 共享版 / 商业贸易
应用平台: Win9x/NT/2000/XP
加入时间: 2003-06-18 17:02:29
下载次数: 2356
推荐等级: ***
【软件简介】:通用仓库及货物管理软件;支持多套帐簿(无限制)、多仓库管理(无限制)、多种计量单位(无限制)及多达8种的数量进制、可自定义多种入库及出库单据类型(无限制);允许用户自定义小数点位数(0-8位);支持商品动态分类级次达五级。界面直观、操作简单,支持全键盘操作;支持网络,及多用户;适合于各行各业的仓储及货物的计算机管理。 共享软件 免费注册。
【软件限制】:NAG、功能限制
【作者声明】:初学Crack,只是感兴趣,没有其它目的。失误之处敬请诸位大侠赐教!
【破解工具】:TRW2000娃娃修改版、Ollydbg1.09、PEiD、AspackDie、UPXWin、Guw32、W32Dasm 9.0白金版
—————————————————————————————————
【过 程】:

Waregoods.exe 无壳。 VB6 native 。晕倒,这个东东在我的WIN 98SE上居然无法正常运行,需要更新某某文件……呵呵,幸好我还有第2操作系统XP,只好去XP下分析了。这是我第一次在XP下调试。^O^ ^O^
序列号:IELKLKHIDP
单 位:雨佳商业公司
试炼码:1357 – 2468 (注意:-前后各有1个空格!)
—————————————————————————————————
可下MSVBVM60.rtcMidCharVar断点,生成序列号后会来到下面:

:006629E9 FF90D4000000 call dword ptr [eax+000000D4]
====>生成程序显示的序列号!
:006629EF 3BC3 cmp eax, ebx
:006629F1 7D11 jge 00662A04
:006629F3 68D4000000 push 000000D4
* Possible StringData Ref from Code Obj ->”o晧m<)獿焹5Q8A?”
|
:006629F8 6830544200 push 00425430
:006629FD 57 push edi
:006629FE 50 push eax
* Reference To: MSVBVM60.__vbaHresultCheckObj, Ord:0000h
|
:006629FF E83859DAFF Call 0040833C
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:006629F1(C)
|
:00662A04 8B55C8 mov edx, dword ptr [ebp-38]
====>EDX=IELKLKHIDP 序列号
:00662A07 895DC8 mov dword ptr [ebp-38], ebx
:00662A0A 8D4DD4 lea ecx, dword ptr [ebp-2C]
* Reference To: MSVBVM60.__vbaStrMove, Ord:0000h
|
:00662A0D E83059DAFF Call 00408342
:00662A12 6A01 push 00000001
:00662A14 FF36 push dword ptr [esi]
====>[esi]=1357 – 2468 试炼码
* Possible StringData Ref from Code Obj ->” – ”
|
:00662A16 6820634300 push 00436320
:00662A1B 53 push ebx
* Reference To: MSVBVM60.__vbaInStr, Ord:0000h
|
:00662A1C E8775ADAFF Call 00408498
====>检测输入的试炼码的格式是否是K1 – K2
====>晕倒 ~Q~ 为了这-前后的2个空格我在这儿看了15分钟
:00662A21 8BC8 mov ecx, eax
* Reference To: MSVBVM60.__vbaI2I4, Ord:0000h
|
:00662A23 E81E58DAFF Call 00408246
:00662A28 8945DC mov dword ptr [ebp-24], eax
:00662A2B 663BC3 cmp ax, bx
:00662A2E 0F8E2B010000 jle 00662B5F
====>跳则OVER!
:00662A34 8975A8 mov dword ptr [ebp-58], esi
:00662A37 BB08400000 mov ebx, 00004008
:00662A3C 895DA0 mov dword ptr [ebp-60], ebx
:00662A3F 662D0100 sub ax, 0001
:00662A43 0F809C010000 jo 00662BE5
:00662A49 0FBFC0 movsx eax, ax
:00662A4C 50 push eax
:00662A4D 8D45A0 lea eax, dword ptr [ebp-60]
:00662A50 50 push eax
:00662A51 8D45B0 lea eax, dword ptr [ebp-50]
:00662A54 50 push eax
* Reference To: MSVBVM60.rtcLeftCharVar, Ord:0269h
|
:00662A55 E88459DAFF Call 004083DE
====>取试炼码的前段
:00662A5A 8D45B0 lea eax, dword ptr [ebp-50]
:00662A5D 50 push eax
* Reference To: MSVBVM60.__vbaStrVarMove, Ord:0000h
|
:00662A5E E81559DAFF Call 00408378
:00662A63 8BD0 mov edx, eax
====>EDX=1357 试炼码的前段
:00662A65 8D4DD8 lea ecx, dword ptr [ebp-28]
* Reference To: MSVBVM60.__vbaStrMove, Ord:0000h
|
:00662A68 E8D558DAFF Call 00408342
:00662A6D 8D4DB0 lea ecx, dword ptr [ebp-50]
* Reference To: MSVBVM60.__vbaFreeVar, Ord:0000h
|
:00662A70 E83D58DAFF Call 004082B2
:00662A75 8975A8 mov dword ptr [ebp-58], esi
:00662A78 895DA0 mov dword ptr [ebp-60], ebx
:00662A7B FF36 push dword ptr [esi]
* Reference To: MSVBVM60.__vbaLenBstr, Ord:0000h
|
:00662A7D E8E257DAFF Call 00408264
:00662A82 0FBF4DDC movsx ecx, word ptr [ebp-24]
:00662A86 2BC1 sub eax, ecx
:00662A88 0F8057010000 jo 00662BE5
:00662A8E 83E802 sub eax, 00000002
:00662A91 0F804E010000 jo 00662BE5
:00662A97 50 push eax
:00662A98 8D45A0 lea eax, dword ptr [ebp-60]
:00662A9B 50 push eax
:00662A9C 8D45B0 lea eax, dword ptr [ebp-50]
:00662A9F 50 push eax
* Reference To: MSVBVM60.rtcRightCharVar, Ord:026Bh
|
:00662AA0 E8B759DAFF Call 0040845C
====>取试炼码的后段
:00662AA5 8D45B0 lea eax, dword ptr [ebp-50]
:00662AA8 50 push eax
* Reference To: MSVBVM60.__vbaStrVarMove, Ord:0000h
|
:00662AA9 E8CA58DAFF Call 00408378
:00662AAE 8BD0 mov edx, eax
====>EDX=2468 试炼码的后段
:00662AB0 8D4DCC lea ecx, dword ptr [ebp-34]
* Reference To: MSVBVM60.__vbaStrMove, Ord:0000h
|
:00662AB3 E88A58DAFF Call 00408342
:00662AB8 8D4DB0 lea ecx, dword ptr [ebp-50]
* Reference To: MSVBVM60.__vbaFreeVar, Ord:0000h
|
:00662ABB E8F257DAFF Call 004082B2
:00662AC0 8B07 mov eax, dword ptr [edi]
:00662AC2 8D4DC8 lea ecx, dword ptr [ebp-38]
====>[ebp-38]=IELKLKHIDP 序列号
:00662AC5 51 push ecx
:00662AC6 FF75D4 push [ebp-2C]
:00662AC9 57 push edi
:00662ACA FF90EC000000 call dword ptr [eax+000000EC]
====>算法CALL①!进入!生成前段注册码
:00662AD0 85C0 test eax, eax
:00662AD2 7D11 jge 00662AE5
:00662AD4 68EC000000 push 000000EC
* Possible StringData Ref from Code Obj ->”o晧m<)獿焹5Q8A?”
|
:00662AD9 6830544200 push 00425430
:00662ADE 57 push edi
:00662ADF 50 push eax
* Reference To: MSVBVM60.__vbaHresultCheckObj, Ord:0000h
|
:00662AE0 E85758DAFF Call 0040833C
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00662AD2(C)
|
:00662AE5 33D2 xor edx, edx
:00662AE7 8D4DC4 lea ecx, dword ptr [ebp-3C]
* Reference To: MSVBVM60.__vbaStrCopy, Ord:0000h
|
:00662AEA E8B157DAFF Call 004082A0
:00662AEF 8B07 mov eax, dword ptr [edi]
:00662AF1 8D4DC0 lea ecx, dword ptr [ebp-40]
:00662AF4 51 push ecx
:00662AF5 8D4DC4 lea ecx, dword ptr [ebp-3C]
:00662AF8 51 push ecx
:00662AF9 57 push edi
:00662AFA FF90D8000000 call dword ptr [eax+000000D8]
====>算法CALL②!进入!生成后段注册码
:00662B00 85C0 test eax, eax
:00662B02 7D11 jge 00662B15
:00662B04 68D8000000 push 000000D8
* Possible StringData Ref from Code Obj ->”o晧m<)獿焹5Q8A?”
|
:00662B09 6830544200 push 00425430
:00662B0E 57 push edi
:00662B0F 50 push eax
* Reference To: MSVBVM60.__vbaHresultCheckObj, Ord:0000h
|
:00662B10 E82758DAFF Call 0040833C
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00662B02(C)
|
:00662B15 FF75CC push [ebp-34]
====>[ebp-34]=2468
:00662B18 FF75C0 push [ebp-40]
====>[ebp-40]=CNSZNOG 后段注册码
* Reference To: MSVBVM60.__vbaStrCmp, Ord:0000h
|
:00662B1B E81658DAFF Call 00408336
====>比较后段注册码!
:00662B20 8BF0 mov esi, eax
:00662B22 F7DE neg esi
:00662B24 1BF6 sbb esi, esi
:00662B26 46 inc esi
:00662B27 F7DE neg esi
:00662B29 FF75D8 push [ebp-28]
====>[ebp-28]=1357
:00662B2C FF75C8 push [ebp-38]
====>[ebp-38]=WAYAAYYQTU 前段注册码
* Reference To: MSVBVM60.__vbaStrCmp, Ord:0000h
|
:00662B2F E80258DAFF Call 00408336
====>比较前段注册码!
:00662B34 F7D8 neg eax
:00662B36 1BC0 sbb eax, eax
:00662B38 40 inc eax
:00662B39 F7D8 neg eax
:00662B3B 23F0 and esi, eax
:00662B3D 8D45C0 lea eax, dword ptr [ebp-40]
:00662B40 50 push eax
:00662B41 8D45C4 lea eax, dword ptr [ebp-3C]
:00662B44 50 push eax
:00662B45 8D45C8 lea eax, dword ptr [ebp-38]
:00662B48 50 push eax
:00662B49 6A03 push 00000003
* Reference To: MSVBVM60.__vbaFreeStrList, Ord:0000h
|
:00662B4B E8A457DAFF Call 004082F4
:00662B50 83C410 add esp, 00000010
:00662B53 6685F6 test si, si
:00662B56 7407 je 00662B5F
====>跳则OVER!
:00662B58 C745D001000000 mov [ebp-30], 00000001
* Referenced by a (U)nconditional or (C)onditional Jump at Addresses:
|:006629B0(U), :006629C6(C), :00662A2E(C), :00662B56(C)
|
:00662B5F 668B45D0 mov ax, word ptr [ebp-30]
:00662B63 66898784000000 mov word ptr [edi+00000084], ax
:00662B6A EB0E jmp 00662B7A
…… ……省 略…… ……
:0068E139 E892A1D7FF Call 004082D0
====>BAD BOY!

—————————————————————————————————
进入算法CALL①:00662ACA call dword ptr [eax+000000EC]

…… ……省 略…… ……
* Reference To: MSVBVM60.__vbaLenBstr, Ord:0000h
|
:00663176 E8E950DAFF Call 00408264
:0066317B 6A01 push 00000001
:0066317D 5B pop ebx
:0066317E 2BC3 sub eax, ebx
:00663180 0F806F020000 jo 006633F5
:00663186 50 push eax
:00663187 8D459C lea eax, dword ptr [ebp-64]
:0066318A 50 push eax
:0066318B 8D45BC lea eax, dword ptr [ebp-44]
:0066318E 50 push eax
* Reference To: MSVBVM60.rtcLeftCharVar, Ord:0269h
|
:0066318F E84A52DAFF Call 004083DE
====>取序列号左边的几位。其实就是E盘序列号的变形
:00663194 8D45BC lea eax, dword ptr [ebp-44]
:00663197 50 push eax
* Reference To: MSVBVM60.__vbaStrVarMove, Ord:0000h
|
:00663198 E8DB51DAFF Call 00408378
:0066319D 8BD0 mov edx, eax
====>[ebp-38]=IELKLKHID
:0066319F 8D4DDC lea ecx, dword ptr [ebp-24]
* Reference To: MSVBVM60.__vbaStrMove, Ord:0000h
|
:006631A2 E89B51DAFF Call 00408342
:006631A7 8D4DBC lea ecx, dword ptr [ebp-44]
* Reference To: MSVBVM60.__vbaFreeVar, Ord:0000h
|
:006631AA E80351DAFF Call 004082B2
:006631AF 8D45E4 lea eax, dword ptr [ebp-1C]
:006631B2 53 push ebx
:006631B3 8945A4 mov dword ptr [ebp-5C], eax
:006631B6 8D459C lea eax, dword ptr [ebp-64]
:006631B9 50 push eax
:006631BA 8D45BC lea eax, dword ptr [ebp-44]
:006631BD 50 push eax
:006631BE 89759C mov dword ptr [ebp-64], esi
* Reference To: MSVBVM60.rtcRightCharVar, Ord:026Bh
|
:006631C1 E89652DAFF Call 0040845C
:006631C6 8D45BC lea eax, dword ptr [ebp-44]
:006631C9 50 push eax
* Reference To: MSVBVM60.__vbaStrVarMove, Ord:0000h
|
:006631CA E8A951DAFF Call 00408378
:006631CF 8BD0 mov edx, eax
:006631D1 8D4DD0 lea ecx, dword ptr [ebp-30]
* Reference To: MSVBVM60.__vbaStrMove, Ord:0000h
|
:006631D4 E86951DAFF Call 00408342
:006631D9 8D4DBC lea ecx, dword ptr [ebp-44]
* Reference To: MSVBVM60.__vbaFreeVar, Ord:0000h
|
:006631DC E8D150DAFF Call 004082B2
:006631E1 8BD7 mov edx, edi
:006631E3 8D4DE0 lea ecx, dword ptr [ebp-20]
* Reference To: MSVBVM60.__vbaStrCopy, Ord:0000h
|
:006631E6 E8B550DAFF Call 004082A0
:006631EB FF75DC push [ebp-24]
* Reference To: MSVBVM60.__vbaLenBstr, Ord:0000h
|
:006631EE E87150DAFF Call 00408264
====>取IELKLKHID的长度
:006631F3 8BC8 mov ecx, eax
====>ECX=9
* Reference To: MSVBVM60.__vbaI2I4, Ord:0000h
|
:006631F5 E84C50DAFF Call 00408246
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:006632DC(U)
|
:006631FA 6A01 push 00000001
:006631FC 8BF8 mov edi, eax
:006631FE 58 pop eax
:006631FF 663BF8 cmp di, ax
:00663202 0F8CD9000000 jl 006632E1
:00663208 8D45DC lea eax, dword ptr [ebp-24]
:0066320B 895DC4 mov dword ptr [ebp-3C], ebx
:0066320E 8945A4 mov dword ptr [ebp-5C], eax
:00663211 8D45BC lea eax, dword ptr [ebp-44]
:00663214 50 push eax
:00663215 C745BC02000000 mov [ebp-44], 00000002
:0066321C 0FBFC7 movsx eax, di
:0066321F 50 push eax
:00663220 8D459C lea eax, dword ptr [ebp-64]
:00663223 50 push eax
:00663224 8D45AC lea eax, dword ptr [ebp-54]
:00663227 50 push eax
:00663228 89759C mov dword ptr [ebp-64], esi
* Reference To: MSVBVM60.rtcMidCharVar, Ord:0278h
|
:0066322B E88451DAFF Call 004083B4
====>倒序取IELKLKHID字符
:00663230 8D45AC lea eax, dword ptr [ebp-54]
:00663233 50 push eax
* Reference To: MSVBVM60.__vbaStrVarMove, Ord:0000h
|
:00663234 E83F51DAFF Call 00408378
:00663239 8BD0 mov edx, eax
:0066323B 8D4DD4 lea ecx, dword ptr [ebp-2C]
* Reference To: MSVBVM60.__vbaStrMove, Ord:0000h
|
:0066323E E8FF50DAFF Call 00408342
:00663243 8D45AC lea eax, dword ptr [ebp-54]
:00663246 50 push eax
:00663247 8D45BC lea eax, dword ptr [ebp-44]
:0066324A 50 push eax
:0066324B 6A02 push 00000002
* Reference To: MSVBVM60.__vbaFreeVarList, Ord:0000h
|
:0066324D E8B450DAFF Call 00408306
:00663252 8B45E0 mov eax, dword ptr [ebp-20]
:00663255 83C40C add esp, 0000000C
:00663258 8945A4 mov dword ptr [ebp-5C], eax
:0066325B C7459C08000000 mov [ebp-64], 00000008
:00663262 FF75D4 push [ebp-2C]
* Reference To: MSVBVM60.rtcAnsiValueBstr, Ord:0204h
|
:00663265 E85051DAFF Call 004083BA
====>依次取字符所对应的HEX值
:0066326A 6603C7 add ax, di
====>依次加上9、8、7、……、1 递减位数
:0066326D 66B91A00 mov cx, 001A
====>CX=1A
:00663271 0F807E010000 jo 006633F5
:00663277 66051700 add ax, 0017
====>AX再加上17
:0066327B 0F8074010000 jo 006633F5
:00663281 6699 cwd
:00663283 66F7F9 idiv cx
====>DX=AX % 1A
:00663286 6683C241 add dx, 0041
====>余数再加41
:0066328A 0F8065010000 jo 006633F5
:00663290 0FBFC2 movsx eax, dx
:00663293 50 push eax
:00663294 8D45BC lea eax, dword ptr [ebp-44]
:00663297 50 push eax
* Reference To: MSVBVM60.rtcVarBstrFromAnsi, Ord:0260h
|
:00663298 E82F51DAFF Call 004083CC
====>依次把上面所得的HEX值转变为所对应的字符
:0066329D 8D459C lea eax, dword ptr [ebp-64]
:006632A0 50 push eax
:006632A1 8D45BC lea eax, dword ptr [ebp-44]
:006632A4 50 push eax
:006632A5 8D45AC lea eax, dword ptr [ebp-54]
:006632A8 50 push eax
* Reference To: MSVBVM60.__vbaVarCat, Ord:0000h
|
:006632A9 E81650DAFF Call 004082C4
====>依次连接所得的字符
:006632AE 50 push eax
* Reference To: MSVBVM60.__vbaStrVarMove, Ord:0000h
|
:006632AF E8C450DAFF Call 00408378
:006632B4 8BD0 mov edx, eax
====>最后得出:WAYAAYYQT
:006632B6 8D4DE0 lea ecx, dword ptr [ebp-20]
* Reference To: MSVBVM60.__vbaStrMove, Ord:0000h
|
:006632B9 E88450DAFF Call 00408342
:006632BE 8D45AC lea eax, dword ptr [ebp-54]
:006632C1 50 push eax
:006632C2 8D45BC lea eax, dword ptr [ebp-44]
:006632C5 50 push eax
:006632C6 6A02 push 00000002
* Reference To: MSVBVM60.__vbaFreeVarList, Ord:0000h
|
:006632C8 E83950DAFF Call 00408306
:006632CD 83C8FF or eax, FFFFFFFF
:006632D0 83C40C add esp, 0000000C
:006632D3 6603C7 add ax, di
:006632D6 0F8019010000 jo 006633F5
:006632DC E919FFFFFF jmp 006631FA
====>循环!
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00663202(C)
|
:006632E1 8B7508 mov esi, dword ptr [ebp+08]
:006632E4 8D4DCC lea ecx, dword ptr [ebp-34]
:006632E7 51 push ecx
:006632E8 FF75E0 push [ebp-20]
====>[ebp-20]=WAYAAYYQT
:006632EB 8B06 mov eax, dword ptr [esi]
:006632ED 56 push esi
:006632EE FF90DC000000 call dword ptr [eax+000000DC]
====>生成前段注册码K1的最后1位:U
:006632F4 85C0 test eax, eax
:006632F6 7D11 jge 00663309
:006632F8 68DC000000 push 000000DC
* Possible StringData Ref from Code Obj ->”o晧m<)獿焹5Q8A?”
|
:006632FD 6830544200 push 00425430
:00663302 56 push esi
:00663303 50 push eax
* Reference To: MSVBVM60.__vbaHresultCheckObj, Ord:0000h
|
:00663304 E83350DAFF Call 0040833C
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:006632F6(C)
|
:00663309 FF75E0 push [ebp-20]
====>[ebp-20]=WAYAAYYQT
:0066330C FF75CC push [ebp-34]
====>[ebp-34]=U 前段注册码K1的最后1位
* Reference To: MSVBVM60.__vbaStrCat, Ord:0000h
|
:0066330F E8EC4FDAFF Call 00408300
====>连接上面2组字符
:00663314 8BD0 mov edx, eax
====>EDX=WAYAAYYQTU 这就是前段注册码K1

—————————————————————————————————
生成前几位注册码K1的最后1位:006632EE call dword ptr [eax+000000DC]

…… ……省 略…… ……
* Reference To: MSVBVM60.rtcMidCharVar, Ord:0278h
|
:00662828 E8875BDAFF Call 004083B4
====>依次取WAYAAYYQT的字符
:0066282D 8D45B4 lea eax, dword ptr [ebp-4C]
:00662830 50 push eax
* Reference To: MSVBVM60.__vbaStrVarMove, Ord:0000h
|
:00662831 E8425BDAFF Call 00408378
:00662836 8BD0 mov edx, eax
:00662838 8D4DD8 lea ecx, dword ptr [ebp-28]
* Reference To: MSVBVM60.__vbaStrMove, Ord:0000h
|
:0066283B E8025BDAFF Call 00408342
:00662840 8D45B4 lea eax, dword ptr [ebp-4C]
:00662843 50 push eax
:00662844 8D45C4 lea eax, dword ptr [ebp-3C]
:00662847 50 push eax
:00662848 53 push ebx
* Reference To: MSVBVM60.__vbaFreeVarList, Ord:0000h
|
:00662849 E8B85ADAFF Call 00408306
:0066284E 83C40C add esp, 0000000C
:00662851 FF75D8 push [ebp-28]
* Reference To: MSVBVM60.rtcAnsiValueBstr, Ord:0204h
|
:00662854 E8615BDAFF Call 004083BA
====>依次取对应的HEX值
:00662859 660345E4 add ax, word ptr [ebp-1C]
①、 ====>AX=57 + 00=57
②、 ====>AX=41 + 55=96
③、 ====>AX=59 + 94=ED
…… ……省 略…… ……
⑨、 ====>AX=54 + 266=2BA

:0066285D 6A01 push 00000001
:0066285F 0F80B7000000 jo 0066291C
:00662865 662BC3 sub ax, bx
①、 ====>AX=57 – 02=55
②、 ====>AX=96 – 02=94
③、 ====>AX=ED – 02=EB
…… ……省 略…… ……
⑨、 ====>AX=2BA – 02=2B8

:00662868 0F80AE000000 jo 0066291C
:0066286E 8945E4 mov dword ptr [ebp-1C], eax
====>[ebp-1C]=EAX
:00662871 58 pop eax
:00662872 6603C7 add ax, di
:00662875 0F80A1000000 jo 0066291C
:0066287B 8BF8 mov edi, eax
:0066287D EB80 jmp 006627FF
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00662803(C)
|
:0066287F 668B45E4 mov ax, word ptr [ebp-1C]
====>AX=2B8 上面的累加运算之和
:00662883 66B91A00 mov cx, 001A
:00662887 6699 cwd
:00662889 66F7F9 idiv cx
====>DX=2B8 % 1A=14
:0066288C 6683C241 add dx, 0041
====>DX=14 + 41=55
:00662890 0F8086000000 jo 0066291C
:00662896 0FBFC2 movsx eax, dx
:00662899 50 push eax
:0066289A 8D45C4 lea eax, dword ptr [ebp-3C]
:0066289D 50 push eax
* Reference To: MSVBVM60.rtcVarBstrFromAnsi, Ord:0260h
|
:0066289E E8295BDAFF Call 004083CC
====>把上面所得的45转变成字符
:006628A3 8D45C4 lea eax, dword ptr [ebp-3C]
:006628A6 50 push eax
* Reference To: MSVBVM60.__vbaStrVarMove, Ord:0000h
|
:006628A7 E8CC5ADAFF Call 00408378
:006628AC 8BD0 mov edx, eax
====>EDX=U 这就是K1的最后1位

————————————————
注:生成程序显示的序列号的最后1位:
对IELKLKHID运算得出:27F % 1A=F F + 41=50 即:P

—————————————————————————————————
进入算法CALL②:00662AFA call dword ptr [eax+000000D8]

我改变E盘序列号测试了几次,发现后段注册码是相同的,或许这是根据单位名来运算的。但是在我机子上安装后的程序单位名是无法改变的,有朋友做的话麻烦验证一下!
…… ……省 略…… ……
:00662669 50 push eax
:0066266A 895DA8 mov dword ptr [ebp-58], ebx
* Reference To: MSVBVM60.rtcMidCharVar, Ord:0278h
|
:0066266D E8425DDAFF Call 004083B4
====>依次取E8 96 73 4F 46 55 1A 4E 6C 51 F8 53 20 00
:00662672 8D45B8 lea eax, dword ptr [ebp-48]
:00662675 50 push eax
* Reference To: MSVBVM60.__vbaStrVarMove, Ord:0000h
|
:00662676 E8FD5CDAFF Call 00408378
:0066267B 8BD0 mov edx, eax
:0066267D 8D4DDC lea ecx, dword ptr [ebp-24]
* Reference To: MSVBVM60.__vbaStrMove, Ord:0000h
|
:00662680 E8BD5CDAFF Call 00408342
:00662685 8D45B8 lea eax, dword ptr [ebp-48]
:00662688 50 push eax
:00662689 8D45C8 lea eax, dword ptr [ebp-38]
:0066268C 50 push eax
:0066268D 56 push esi
* Reference To: MSVBVM60.__vbaFreeVarList, Ord:0000h
|
:0066268E E8735CDAFF Call 00408306
:00662693 8B45E0 mov eax, dword ptr [ebp-20]
:00662696 83C40C add esp, 0000000C
:00662699 8945B0 mov dword ptr [ebp-50], eax
:0066269C C745A808000000 mov [ebp-58], 00000008
:006626A3 FF75DC push [ebp-24]
* Reference To: MSVBVM60.rtcAnsiValueBstr, Ord:0204h
|
:006626A6 E80F5DDAFF Call 004083BA
:006626AB 8BC8 mov ecx, eax
* Reference To: MSVBVM60.__vbaI2Abs, Ord:0000h
|
:006626AD E87E5FDAFF Call 00408630
:006626B2 6699 cwd
:006626B4 66B91A00 mov cx, 001A
:006626B8 66F7F9 idiv cx
:006626BB 6683C241 add dx, 0041
:006626BF 0F80BF000000 jo 00662784
====>上面是求模、相加运算
:006626C5 0FBFC2 movsx eax, dx
:006626C8 50 push eax
:006626C9 8D45C8 lea eax, dword ptr [ebp-38]
:006626CC 50 push eax
* Reference To: MSVBVM60.rtcVarBstrFromAnsi, Ord:0260h
|
:006626CD E8FA5CDAFF Call 004083CC
====>依次把上面所得的HEX值转变为所对应的字符
:006626D2 8D45A8 lea eax, dword ptr [ebp-58]
:006626D5 50 push eax
:006626D6 8D45C8 lea eax, dword ptr [ebp-38]
:006626D9 50 push eax
:006626DA 8D45B8 lea eax, dword ptr [ebp-48]
:006626DD 50 push eax
* Reference To: MSVBVM60.__vbaVarCat, Ord:0000h
|
:006626DE E8E15BDAFF Call 004082C4
====>依次连接所得的字符
:006626E3 50 push eax
* Reference To: MSVBVM60.__vbaStrVarMove, Ord:0000h
|
:006626E4 E88F5CDAFF Call 00408378
:006626E9 8BD0 mov edx, eax
====>最后得出:EDX=CNSZNOG 这就是后段注册码K2
:006626EB 8D4DE0 lea ecx, dword ptr [ebp-20]
* Reference To: MSVBVM60.__vbaStrMove, Ord:0000h
|
:006626EE E84F5CDAFF Call 00408342
:006626F3 8D45B8 lea eax, dword ptr [ebp-48]
:006626F6 50 push eax
:006626F7 8D45C8 lea eax, dword ptr [ebp-38]
:006626FA 50 push eax
:006626FB 56 push esi
* Reference To: MSVBVM60.__vbaFreeVarList, Ord:0000h
|
:006626FC E8055CDAFF Call 00408306
:00662701 83C40C add esp, 0000000C
:00662704 6A01 push 00000001
:00662706 58 pop eax
:00662707 6603C7 add ax, di
:0066270A 7078 jo 00662784
:0066270C 8BF8 mov edi, eax
:0066270E E92DFFFFFF jmp 00662640
====>循环!

★★★★★★★★☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆★★★★★★★★
取E盘的序列号生成程序显示的序列号:006629E9 call dword ptr [eax+000000D4]

* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:00662484(U)
|
:006623A4 8B45E8 mov eax, dword ptr [ebp-18]
:006623A7 663B4588 cmp ax, word ptr [ebp-78]
:006623AB 0F8FD8000000 jg 00662489
:006623B1 8D4DDC lea ecx, dword ptr [ebp-24]
:006623B4 895DCC mov dword ptr [ebp-34], ebx
:006623B7 894DAC mov dword ptr [ebp-54], ecx
:006623BA 8D4DC4 lea ecx, dword ptr [ebp-3C]
:006623BD 0FBFC0 movsx eax, ax
:006623C0 51 push ecx
:006623C1 50 push eax
:006623C2 8D45A4 lea eax, dword ptr [ebp-5C]
:006623C5 C745C402000000 mov [ebp-3C], 00000002
:006623CC 50 push eax
:006623CD 8D45B4 lea eax, dword ptr [ebp-4C]
:006623D0 50 push eax
:006623D1 C745A408400000 mov [ebp-5C], 00004008
* Reference To: MSVBVM60.rtcMidCharVar, Ord:0278h
|
:006623D8 E8D75FDAFF Call 004083B4
====>依次取518787450 E盘序列号的10进制值
:006623DD 8D45B4 lea eax, dword ptr [ebp-4C]
:006623E0 50 push eax
* Reference To: MSVBVM60.__vbaStrVarMove, Ord:0000h
|
:006623E1 E8925FDAFF Call 00408378
:006623E6 8BD0 mov edx, eax
:006623E8 8D4DE0 lea ecx, dword ptr [ebp-20]
* Reference To: MSVBVM60.__vbaStrMove, Ord:0000h
|
:006623EB E8525FDAFF Call 00408342
:006623F0 8D45B4 lea eax, dword ptr [ebp-4C]
:006623F3 50 push eax
:006623F4 8D45C4 lea eax, dword ptr [ebp-3C]
:006623F7 50 push eax
:006623F8 6A02 push 00000002
* Reference To: MSVBVM60.__vbaFreeVarList, Ord:0000h
|
:006623FA E8075FDAFF Call 00408306
:006623FF 8B45E4 mov eax, dword ptr [ebp-1C]
:00662402 83C40C add esp, 0000000C
:00662405 8945AC mov dword ptr [ebp-54], eax
:00662408 C745A408000000 mov [ebp-5C], 00000008
:0066240F FF75E0 push [ebp-20]
* Reference To: MSVBVM60.rtcAnsiValueBstr, Ord:0204h
|
:00662412 E8A35FDAFF Call 004083BA
====>取其字符对应的HEX值
:00662417 66053F00 add ax, 003F
====>依次+3F
:0066241B 66B91B00 mov cx, 001B
:0066241F 0F8014010000 jo 00662539
:00662425 6699 cwd
:00662427 66F7F9 idiv cx
====>依次模1B
:0066242A 6683C241 add dx, 0041
====>余数+41
:0066242E 0F8005010000 jo 00662539
:00662434 0FBFC2 movsx eax, dx
:00662437 50 push eax
:00662438 8D45C4 lea eax, dword ptr [ebp-3C]
:0066243B 50 push eax
* Reference To: MSVBVM60.rtcVarBstrFromAnsi, Ord:0260h
|
:0066243C E88B5FDAFF Call 004083CC
====>取上面的HEX值对应的字符
:00662441 8D45A4 lea eax, dword ptr [ebp-5C]
:00662444 50 push eax
:00662445 8D45C4 lea eax, dword ptr [ebp-3C]
:00662448 50 push eax
:00662449 8D45B4 lea eax, dword ptr [ebp-4C]
:0066244C 50 push eax
* Reference To: MSVBVM60.__vbaVarCat, Ord:0000h
|
:0066244D E8725EDAFF Call 004082C4
====>依次连接所得字符
:00662452 50 push eax
* Reference To: MSVBVM60.__vbaStrVarMove, Ord:0000h
|
:00662453 E8205FDAFF Call 00408378
:00662458 8BD0 mov edx, eax
====>最后得出:EDX=IELKLKHID
:0066245A 8D4DE4 lea ecx, dword ptr [ebp-1C]
* Reference To: MSVBVM60.__vbaStrMove, Ord:0000h
|
:0066245D E8E05EDAFF Call 00408342
:00662462 8D45B4 lea eax, dword ptr [ebp-4C]
:00662465 50 push eax
:00662466 8D45C4 lea eax, dword ptr [ebp-3C]
:00662469 50 push eax
:0066246A 6A02 push 00000002
* Reference To: MSVBVM60.__vbaFreeVarList, Ord:0000h
|
:0066246C E8955EDAFF Call 00408306
:00662471 83C40C add esp, 0000000C
:00662474 6A01 push 00000001
:00662476 58 pop eax
:00662477 660345E8 add ax, word ptr [ebp-18]
:0066247B 0F80B8000000 jo 00662539
:00662481 8945E8 mov dword ptr [ebp-18], eax
:00662484 E91BFFFFFF jmp 006623A4
====>循环!
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:006623AB(C)
|
:00662489 8B07 mov eax, dword ptr [edi]
:0066248B 8D4DD4 lea ecx, dword ptr [ebp-2C]
:0066248E 51 push ecx
:0066248F FF75E4 push [ebp-1C]
:00662492 57 push edi
:00662493 FF90DC000000 call dword ptr [eax+000000DC]
====>对IELKLKHID运算 生成序列号最后1位
====>详见:生成前几位注册码K1的最后1位
:00662499 3BC6 cmp eax, esi
:0066249B 7D11 jge 006624AE
:0066249D 68DC000000 push 000000DC
* Possible StringData Ref from Code Obj ->”o晧m<)獿焹5Q8A?”
|
:006624A2 6830544200 push 00425430
:006624A7 57 push edi
:006624A8 50 push eax
* Reference To: MSVBVM60.__vbaHresultCheckObj, Ord:0000h
|
:006624A9 E88E5EDAFF Call 0040833C
* Referenced by a (U)nconditional or (C)onditional Jump at Address:
|:0066249B(C)
|
:006624AE FF75E4 push [ebp-1C]
====>[ebp-1C]=IELKLKHID
:006624B1 FF75D4 push [ebp-2C]
====>[ebp-2C]=P 序列号最后1位
* Reference To: MSVBVM60.__vbaStrCat, Ord:0000h
|
:006624B4 E8475EDAFF Call 00408300
====>连接以上2组字符
:006624B9 8BD0 mov edx, eax
====>EDX=IELKLKHIDP 这就是序列号

★★★★★★★★☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆★★★★★★★★
程序启动时的比较:

:0052692A FF91E0000000 call dword ptr [ecx+000000E0]
====>进入运算比较CALL!
:00526930 85C0 test eax, eax
:00526932 DBE2 fclex
:00526934 7D12 jge 00526948
====>跳则OVER!

—————————————————————————————————
【完 美 爆 破】:

想做完美爆破还是有点麻烦的。呵 呵 ~@~ ~@~

—————————————————————————————————
【注册信息保存】:

Windows Registry Editor Version 5.00
[HKEY_USERS\S-1-5-21-1614895754-436374069-1957994488-1003\Software\VB and VBA Program Settings\Account\RegisterCode]
“RegisterCode”=”WAYAAYYQTU – CNSZNOG”
—————————————————————————————————
【整 理】:

序列号:IELKLKHIDP
单 位:雨佳商业公司
注册码:WAYAAYYQTU – CNSZNOG
—————————————————————————————————

, _/
/| _.-~/ \_ , 青春都一饷
( /~ / \~-._ |\
\\ _/ \ ~\ ) 忍把浮名
_-~~~-.) )__/;;,. \_ //'
/'_,\ --~ \ ~~~- ,;;\___( (.-~~~-. 换了破解轻狂
~ _( ,_..–\ ( ,;” / ~– /._\
/~~//' /'
~\ ) /–.._, )_ ~
"
~” ” " /~'\ `\\~~\
” ” “~’ “”

Cracked By 巢水工作坊——fly [OCN][FCG]
2003-06-23 2:00

飞雪日历调试笔记

[简介] 飞雪日历V1.62.0437 绿色软件 有万年历、时钟、世界时间、定时、限时、提醒、备忘录、系统热键、光驱控制、定期清理、语音报时等功能。

[来源] http://www.zheng99.com/

[限制] 未注册版没有时间和功能限制,但是定时弹出提醒注册的窗口

[工具] PEid, OllyDbg, Numega SmartCheck

[声明] 调试分析纯属兴趣,请不要用于其他目的

[过程]

先注册,随便填写注册名abc、注册码1234567890,程序仅提示已保存机器码和用户输入的注册名、注册码,没有其他反应。

PEiD查壳UPX 0.89.6 – 1.02 / 1.05 – 1.24 -> Markus & Laszlo,手工脱壳后再查是VB写的,但运行脱壳后的程序有”不精确的浮点数运算结果异常”而退出,看雪老大在脱壳基础第六课没有说这种粘壳的粽子怎么剥,

   没法子只好带壳调试。

用SmartCheck打开运行原程序,是多线程,Thread0下的ThunRTMain有很多DispatchMessageA向下翻到一个TimerTmp(1)_Timer,展开看到有机器码等三项注册用的字串,估计是这里验证是否已注册,向前翻看可知是读自子目录FXSYS下的文件FXSYS.INI,还看到前面调用Randomize、用处理器信息字符串各ASCII值*2累加成机器码,有这些信息在OD就好定位了。

用OD打开、停下,翻屏找POPAD和长JMP,在真正入口点00405CE0下断,F9运行再停下时程序已脱壳,入口点上面几行就是输入表的跳转命令,找到MSVBVM60.rtcRandomize是JMP [DWORD DS:4010DC],查找命令CALL [DWORD DS:4010DC]只有一处。向下翻看见到读注册文件的地方就是我们要找的了。

00465880    55              PUSH EBP

…注册过一次就能到这里,这些忽略过…

00465B42    8B55 E0         MOV EDX,[DWORD SS:EBP-20]                ; 注册名地址

00465B45    52              PUSH EDX

00465B46    FF15 2C104000   CALL [DWORD DS:40102C]                   ; MSVBVM60.__vbaLenBstr

00465B4C    3BC7            CMP EAX,EDI                              ; 注册名长度,不能少于2字节

00465B4E    0F8C 4F060000   JL FXRL.004661A3                         ; 跳就完

00465B54    C745 88 0100000>MOV [DWORD SS:EBP-78],1

00465B5B    897D 80         MOV [DWORD SS:EBP-80],EDI

00465B5E    8D45 80         LEA EAX,[DWORD SS:EBP-80]

00465B61    50              PUSH EAX

00465B62    6A 01           PUSH 1

00465B64    8B4D E0         MOV ECX,[DWORD SS:EBP-20]

00465B67    51              PUSH ECX

00465B68    8B3D 20114000   MOV EDI,[DWORD DS:401120]                ; MSVBVM60.rtcMidCharBstr

00465B6E    FFD7            CALL EDI

00465B70    8BD0            MOV EDX,EAX                              ; 注册名第1个字符’a'(地址)

00465B72    8D4D A0         LEA ECX,[DWORD SS:EBP-60]

00465B75    FFD6            CALL ESI

00465B77    50              PUSH EAX

00465B78    FF15 50104000   CALL [DWORD DS:401050]                   ; MSVBVM60.rtcAnsiValueBstr

00465B7E    8BD8            MOV EBX,EAX                              ; 返回’a’的ASCII值61

00465B80    6A 01           PUSH 1

00465B82    8B55 CC         MOV EDX,[DWORD SS:EBP-34]                ; 注册码地址

00465B85    52              PUSH EDX

00465B86    FF15 CC124000   CALL [DWORD DS:4012CC]                   ; MSVBVM60.rtcLeftCharBstr

00465B8C    8BD0            MOV EDX,EAX                              ; 注册码第1个字符(地址)

00465B8E    8D4D 98         LEA ECX,[DWORD SS:EBP-68]

00465B91    FFD6            CALL ESI

00465B93    50              PUSH EAX

00465B94    6A 01           PUSH 1

00465B96    53              PUSH EBX

00465B97    FF15 08104000   CALL [DWORD DS:401008]                   ; MSVBVM60.__vbaStrI2

00465B9D    8BD0            MOV EDX,EAX                              ; 注册名第1字符ASCII值(十进制)转换为字符串”97″

00465B9F    8D4D 9C         LEA ECX,[DWORD SS:EBP-64]

00465BA2    FFD6            CALL ESI

00465BA4    50              PUSH EAX

00465BA5    FF15 EC124000   CALL [DWORD DS:4012EC]                   ; MSVBVM60.rtcRightCharBstr

00465BAB    8BD0            MOV EDX,EAX

00465BAD    8D4D 94         LEA ECX,[DWORD SS:EBP-6C]

00465BB0    FFD6            CALL ESI

00465BB2    50              PUSH EAX                                 ; “97”的最后1个字符’7′(地址)

00465BB3    FF15 60114000   CALL [DWORD DS:401160]                   ; MSVBVM60.__vbaStrCmp

00465BB9    8BD8            MOV EBX,EAX                              ; 比较’7’和注册码第1字符,如果相等就返回0

00465BBB    F7DB            NEG EBX

00465BBD    1BDB            SBB EBX,EBX

00465BBF    F7DB            NEG EBX

00465BC1    F7DB            NEG EBX

00465BC3    8D45 94         LEA EAX,[DWORD SS:EBP-6C]

00465BC6    50              PUSH EAX

00465BC7    8D4D 98         LEA ECX,[DWORD SS:EBP-68]

00465BCA    51              PUSH ECX

00465BCB    8D55 9C         LEA EDX,[DWORD SS:EBP-64]

00465BCE    52              PUSH EDX

00465BCF    8D45 A0         LEA EAX,[DWORD SS:EBP-60]

00465BD2    50              PUSH EAX

00465BD3    6A 04           PUSH 4

00465BD5    FF15 70124000   CALL [DWORD DS:401270]                   ; MSVBVM60.__vbaFreeStrList

00465BDB    83C4 14         ADD ESP,14

00465BDE    8D4D 80         LEA ECX,[DWORD SS:EBP-80]

00465BE1    FF15 20104000   CALL [DWORD DS:401020]                   ; MSVBVM60.__vbaFreeVar

00465BE7    66:85DB         TEST BX,BX

00465BEA    0F85 B3050000   JNZ FXRL.004661A3                        ; 上面字符比较相等,这里就不会跳走,否则跳就完。此时知道注册码第1字符对应注册名a应该是7才对。

…然后比较注册名第2字符’b’与注册码第2字符,知道第2个应该是’8’。过程同上略过…

00465CA5    0F85 F8040000   JNZ FXRL.004661A3                        ; 不能跳

00465CAB    C745 88 0400028>MOV [DWORD SS:EBP-78],80020004

00465CB2    C745 80 0A00000>MOV [DWORD SS:EBP-80],0A

00465CB9    8D55 80         LEA EDX,[DWORD SS:EBP-80]

00465CBC    52              PUSH EDX

00465CBD    6A 03           PUSH 3

00465CBF    8B45 CC         MOV EAX,[DWORD SS:EBP-34]

00465CC2    50              PUSH EAX

00465CC3    FFD7            CALL EDI

00465CC5    8BD0            MOV EDX,EAX                              ; 取得注册码第3字符开始的右边子串(地址)

00465CC7    8D4D CC         LEA ECX,[DWORD SS:EBP-34]

00465CCA    FFD6            CALL ESI

00465CCC    8D4D 80         LEA ECX,[DWORD SS:EBP-80]

00465CCF    FF15 20104000   CALL [DWORD DS:401020]                   ; MSVBVM60.__vbaFreeVar

00465CD5    8B4D CC         MOV ECX,[DWORD SS:EBP-34]                ; 截取注册码第3字符开始的右边子串

00465CD8    51              PUSH ECX                                 ; 入栈,下面把它转换成浮点数

00465CD9    FF15 50134000   CALL [DWORD DS:401350]                   ; MSVBVM60.rtcR8ValFromBstr

00465CDF    FF15 14114000   CALL [DWORD DS:401114]                   ; MSVBVM60.__vbaFpR8

00465CE5    DC1D 30234000   FCOMP [QWORD DS:402330]                  ; 和999999.0比较,要比这个常数大才行

00465CEB    DFE0            FSTSW AX

00465CED    F6C4 41         TEST AH,41

00465CF0    C785 CCFEFFFF 0>MOV [DWORD SS:EBP-134],1

00465CFA    74 0A           JE SHORT FXRL.00465D06                   ; 必须跳(以使之赋值为1后面马上用到)

00465CFC    C785 CCFEFFFF 0>MOV [DWORD SS:EBP-134],0                 ; 然后同法比较机器码,暂时不管

00465D06    8D55 A4         LEA EDX,[DWORD SS:EBP-5C]

00465D09    52              PUSH EDX

00465D0A    8D45 A0         LEA EAX,[DWORD SS:EBP-60]

00465D0D    50              PUSH EAX

00465D0E    FF15 0C124000   CALL [DWORD DS:40120C]                   ; MSVBVM60.__vbaStrVarVal

00465D14    50              PUSH EAX

00465D15    FF15 50134000   CALL [DWORD DS:401350]                   ; MSVBVM60.rtcR8ValFromBstr

00465D1B    FF15 14114000   CALL [DWORD DS:401114]                   ; MSVBVM60.__vbaFpR8

00465D21    DC1D 28234000   FCOMP [QWORD DS:402328]                  ; 和101.0比较

00465D27    DFE0            FSTSW AX

00465D29    F6C4 41         TEST AH,41

00465D2C    75 07           JNZ SHORT FXRL.00465D35                  ; 这里正常地是不跳的

00465D2E    BB 01000000     MOV EBX,1

00465D33    EB 02           JMP SHORT FXRL.00465D37

00465D35    33DB            XOR EBX,EBX

00465D37    8B4D E0         MOV ECX,[DWORD SS:EBP-20]                ; 注册名,检查它的长度

00465D3A    51              PUSH ECX

00465D3B    FF15 2C104000   CALL [DWORD DS:40102C]                   ; MSVBVM60.__vbaLenBstr

00465D41    33D2            XOR EDX,EDX

00465D43    83F8 01         CMP EAX,1

00465D46    0F9FC2          SETG DL                                  ; 长度大于1则条件为真,寄存器DL设置为1

00465D49    F7DA            NEG EDX

00465D4B    8B85 CCFEFFFF   MOV EAX,[DWORD SS:EBP-134]

00465D51    F7D8            NEG EAX

00465D53    23D0            AND EDX,EAX

00465D55    F7DB            NEG EBX

00465D57    23DA            AND EBX,EDX

00465D59    8D4D A0         LEA ECX,[DWORD SS:EBP-60]

00465D5C    FF15 4C134000   CALL [DWORD DS:40134C]                   ; MSVBVM60.__vbaFreeStr

00465D62    66:85DB         TEST BX,BX

00465D65    0F84 38040000   JE FXRL.004661A3                         ; 上面几个两个跳转正确到这里就不会跳走,继续

00465D6B    BB 01000000     MOV EBX,1

00465D70    899D 48FFFFFF   MOV [DWORD SS:EBP-B8],EBX

00465D76    C785 40FFFFFF 0>MOV [DWORD SS:EBP-C0],2

00465D80    8D95 40FFFFFF   LEA EDX,[DWORD SS:EBP-C0]

00465D86    8D4D BC         LEA ECX,[DWORD SS:EBP-44]

00465D89    FF15 18104000   CALL [DWORD DS:401018]                   ; MSVBVM60.__vbaVarMove

00465D8F    C785 F0FEFFFF 0>MOV [DWORD SS:EBP-110],3

00465D99    66:3B9D F0FEFFF>CMP BX,[WORD SS:EBP-110]                 ; 循环机器码前3个字符

00465DA0    0F8F 91000000   JG FXRL.00465E37

00465DA6    C745 88 0100000>MOV [DWORD SS:EBP-78],1

00465DAD    C745 80 0200000>MOV [DWORD SS:EBP-80],2

00465DB4    8D45 80         LEA EAX,[DWORD SS:EBP-80]

00465DB7    50              PUSH EAX

00465DB8    0FBFCB          MOVSX ECX,BX

00465DBB    51              PUSH ECX

00465DBC    8D55 A4         LEA EDX,[DWORD SS:EBP-5C]

00465DBF    52              PUSH EDX

00465DC0    8D45 A0         LEA EAX,[DWORD SS:EBP-60]

00465DC3    50              PUSH EAX

00465DC4    FF15 0C124000   CALL [DWORD DS:40120C]                   ; MSVBVM60.__vbaStrVarVal

00465DCA    50              PUSH EAX

00465DCB    FFD7            CALL EDI

00465DCD    8BD0            MOV EDX,EAX                              ; 机器码一字符(地址)

00465DCF    8D4D 9C         LEA ECX,[DWORD SS:EBP-64]

00465DD2    FFD6            CALL ESI

00465DD4    50              PUSH EAX

00465DD5    FF15 50104000   CALL [DWORD DS:401050]                   ; MSVBVM60.rtcAnsiValueBstr

00465DDB    66:8985 38FFFFF>MOV [WORD SS:EBP-C8],AX                  ; 返回其ASCII值

00465DE2    C785 30FFFFFF 0>MOV [DWORD SS:EBP-D0],2

00465DEC    8D4D BC         LEA ECX,[DWORD SS:EBP-44]

00465DEF    51              PUSH ECX

00465DF0    8D95 30FFFFFF   LEA EDX,[DWORD SS:EBP-D0]

00465DF6    52              PUSH EDX

00465DF7    8D85 70FFFFFF   LEA EAX,[DWORD SS:EBP-90]

00465DFD    50              PUSH EAX

00465DFE    FF15 AC124000   CALL [DWORD DS:4012AC]                   ; MSVBVM60.__vbaVarAdd,跟进看到第1字符ASCII值加到1上,其他的再累加上去

00465E04    8BD0            MOV EDX,EAX

00465E06    8D4D BC         LEA ECX,[DWORD SS:EBP-44]

00465E09    FF15 18104000   CALL [DWORD DS:401018]                   ; MSVBVM60.__vbaVarMove

00465E0F    8D4D 9C         LEA ECX,[DWORD SS:EBP-64]

00465E12    51              PUSH ECX

00465E13    8D55 A0         LEA EDX,[DWORD SS:EBP-60]

00465E16    52              PUSH EDX

00465E17    6A 02           PUSH 2

00465E19    FF15 70124000   CALL [DWORD DS:401270]                   ; MSVBVM60.__vbaFreeStrList

00465E1F    83C4 0C         ADD ESP,0C

00465E22    8D4D 80         LEA ECX,[DWORD SS:EBP-80]

00465E25    FF15 20104000   CALL [DWORD DS:401020]                   ; MSVBVM60.__vbaFreeVar

00465E2B    B8 01000000     MOV EAX,1

00465E30    03D8            ADD EBX,EAX

00465E32  ^ E9 62FFFFFF     JMP FXRL.00465D99

00465E37    8D45 A4         LEA EAX,[DWORD SS:EBP-5C]                ; 上面循环体跳出到此

00465E3A    50              PUSH EAX

00465E3B    8D4D 80         LEA ECX,[DWORD SS:EBP-80]

00465E3E    51              PUSH ECX

00465E3F    FF15 B4104000   CALL [DWORD DS:4010B4]                   ; MSVBVM60.__vbaLenVar

00465E45    50              PUSH EAX

00465E46    FF15 24124000   CALL [DWORD DS:401224]                   ; MSVBVM60.__vbaI2Var

00465E4C    8985 E8FEFFFF   MOV [DWORD SS:EBP-118],EAX

00465E52    BB 04000000     MOV EBX,4

00465E57    66:3B9D E8FEFFF>CMP BX,[WORD SS:EBP-118]                 ; 循环机器码的第4字符开始后面的各字符

00465E5E    0F8F 91000000   JG FXRL.00465EF5

00465E64    C745 88 0100000>MOV [DWORD SS:EBP-78],1

00465E6B    C745 80 0200000>MOV [DWORD SS:EBP-80],2

00465E72    8D55 80         LEA EDX,[DWORD SS:EBP-80]

00465E75    52              PUSH EDX

00465E76    0FBFC3          MOVSX EAX,BX

00465E79    50              PUSH EAX

00465E7A    8D4D A4         LEA ECX,[DWORD SS:EBP-5C]

00465E7D    51              PUSH ECX

00465E7E    8D55 A0         LEA EDX,[DWORD SS:EBP-60]

00465E81    52              PUSH EDX

00465E82    FF15 0C124000   CALL [DWORD DS:40120C]                   ; MSVBVM60.__vbaStrVarVal

00465E88    50              PUSH EAX                                 ; 返回机器码地址

00465E89    FFD7            CALL EDI

00465E8B    8BD0            MOV EDX,EAX

00465E8D    8D4D 9C         LEA ECX,[DWORD SS:EBP-64]

00465E90    FFD6            CALL ESI

00465E92    50              PUSH EAX

00465E93    FF15 50104000   CALL [DWORD DS:401050]                   ; MSVBVM60.rtcAnsiValueBstr

00465E99    66:8985 38FFFFF>MOV [WORD SS:EBP-C8],AX

00465EA0    C785 30FFFFFF 0>MOV [DWORD SS:EBP-D0],2

00465EAA    8D45 BC         LEA EAX,[DWORD SS:EBP-44]

00465EAD    50              PUSH EAX

00465EAE    8D8D 30FFFFFF   LEA ECX,[DWORD SS:EBP-D0]

00465EB4    51              PUSH ECX

00465EB5    8D95 70FFFFFF   LEA EDX,[DWORD SS:EBP-90]

00465EBB    52              PUSH EDX

00465EBC    FF15 C0114000   CALL [DWORD DS:4011C0]                   ; MSVBVM60.__vbaVarMul,跟进看到第一轮是上面那个循环的累加值为被乘数,注册码第4字符ASCII值为乘数,积作为下一轮的被乘数;乘数依次变为注册码第4字符以后各字符的ASCII值

00465EC2    8BD0            MOV EDX,EAX

00465EC4    8D4D BC         LEA ECX,[DWORD SS:EBP-44]

00465EC7    FF15 18104000   CALL [DWORD DS:401018]                   ; MSVBVM60.__vbaVarMove

00465ECD    8D45 9C         LEA EAX,[DWORD SS:EBP-64]

00465ED0    50              PUSH EAX

00465ED1    8D4D A0         LEA ECX,[DWORD SS:EBP-60]

00465ED4    51              PUSH ECX

00465ED5    6A 02           PUSH 2

00465ED7    FF15 70124000   CALL [DWORD DS:401270]                   ; MSVBVM60.__vbaFreeStrList

00465EDD    83C4 0C         ADD ESP,0C

00465EE0    8D4D 80         LEA ECX,[DWORD SS:EBP-80]

00465EE3    FF15 20104000   CALL [DWORD DS:401020]                   ; MSVBVM60.__vbaFreeVar

00465EE9    B8 01000000     MOV EAX,1

00465EEE    03D8            ADD EBX,EAX

00465EF0  ^ E9 62FFFFFF     JMP FXRL.00465E57

00465EF5    8D55 BC         LEA EDX,[DWORD SS:EBP-44]                ; 上面循环体跳出到此

//设上面两个循环体对机器码计算结果为RES(64位双精度浮点数)

00465EF8    52              PUSH EDX

00465EF9    FF15 80124000   CALL [DWORD DS:401280]                   ; MSVBVM60.__vbaR8Var

00465EFF    E8 5AF9F9FF     CALL FXRL.0040585E                       ; JMP 到 MSVBVM60._CIsqrt

00465F04    DD9D 04FFFFFF   FSTP [QWORD SS:EBP-FC]                   ; 上面求出RES的平方根

00465F0A    6A 05           PUSH 5

00465F0C    8B85 08FFFFFF   MOV EAX,[DWORD SS:EBP-F8]

00465F12    50              PUSH EAX

00465F13    8B8D 04FFFFFF   MOV ECX,[DWORD SS:EBP-FC]

00465F19    51              PUSH ECX

00465F1A    8B1D A0114000   MOV EBX,[DWORD DS:4011A0]                ; MSVBVM60.__vbaStrR8

00465F20    FFD3            CALL EBX                                 ; 把sqrt(RES)从浮点数转换成字串(长度16,小数部分11位,四舍五入)

00465F22    8BD0            MOV EDX,EAX

00465F24    8D4D A0         LEA ECX,[DWORD SS:EBP-60]

00465F27    FFD6            CALL ESI

00465F29    50              PUSH EAX

00465F2A    FF15 EC124000   CALL [DWORD DS:4012EC]                   ; MSVBVM60.rtcRightCharBstr

00465F30    8BD0            MOV EDX,EAX                              ; 返回EAX=该串最后5个字符子串(地址)

00465F32    8D4D 9C         LEA ECX,[DWORD SS:EBP-64]

00465F35    FFD6            CALL ESI

00465F37    50              PUSH EAX

00465F38    FF15 50134000   CALL [DWORD DS:401350]                   ; MSVBVM60.rtcR8ValFromBstr,子串再转换成浮点数TAIL

00465F3E    DD9D FCFEFFFF   FSTP [QWORD SS:EBP-104]

00465F44    8D55 BC         LEA EDX,[DWORD SS:EBP-44]

00465F47    52              PUSH EDX

00465F48    FF15 80124000   CALL [DWORD DS:401280]                   ; MSVBVM60.__vbaR8Var

00465F4E    E8 0BF9F9FF     CALL FXRL.0040585E                       ; JMP 到 MSVBVM60._CIsqrt

00465F53    DC0D 20234000   FMUL [QWORD DS:402320]                   ; 上面再一次计算RES的平方根,这里再乘以10000.0,合起来就是sqrt(RES)*10000.0

00465F59    FF15 30134000   CALL [DWORD DS:401330]                   ; MSVBVM60.__vbaFPInt,取整

00465F5F    DC85 FCFEFFFF   FADD [QWORD SS:EBP-104]                  ; 取整后再加上TAIL,得W=Int(sqrt(RES)*10000.0)+TAIL

00465F65    DD9D 48FFFFFF   FSTP [QWORD SS:EBP-B8]

00465F6B    C785 40FFFFFF 0>MOV [DWORD SS:EBP-C0],5

00465F75    8D95 40FFFFFF   LEA EDX,[DWORD SS:EBP-C0]

00465F7B    8D4D BC         LEA ECX,[DWORD SS:EBP-44]

00465F7E    FF15 18104000   CALL [DWORD DS:401018]                   ; MSVBVM60.__vbaVarMove

00465F84    8D45 9C         LEA EAX,[DWORD SS:EBP-64]

00465F87    50              PUSH EAX

00465F88    8D4D A0         LEA ECX,[DWORD SS:EBP-60]

00465F8B    51              PUSH ECX

00465F8C    6A 02           PUSH 2

00465F8E    FF15 70124000   CALL [DWORD DS:401270]                   ; MSVBVM60.__vbaFreeStrList

00465F94    83C4 0C         ADD ESP,0C

00465F97    C745 88 0100000>MOV [DWORD SS:EBP-78],1

00465F9E    C745 80 0200000>MOV [DWORD SS:EBP-80],2

00465FA5    8D55 80         LEA EDX,[DWORD SS:EBP-80]

00465FA8    52              PUSH EDX

00465FA9    6A 01           PUSH 1

00465FAB    8B45 E0         MOV EAX,[DWORD SS:EBP-20]

00465FAE    50              PUSH EAX

00465FAF    FFD7            CALL EDI

00465FB1    8BD0            MOV EDX,EAX

00465FB3    8D4D A0         LEA ECX,[DWORD SS:EBP-60]

00465FB6    FFD6            CALL ESI

00465FB8    50              PUSH EAX

00465FB9    FF15 50104000   CALL [DWORD DS:401050]                   ; MSVBVM60.rtcAnsiValueBstr

00465FBF    8985 0CFFFFFF   MOV [DWORD SS:EBP-F4],EAX

00465FC5    8D4D BC         LEA ECX,[DWORD SS:EBP-44]

00465FC8    51              PUSH ECX

00465FC9    FF15 80124000   CALL [DWORD DS:401280]                   ; MSVBVM60.__vbaR8Var

00465FCF    E8 8AF8F9FF     CALL FXRL.0040585E                       ; JMP 到 MSVBVM60._CIsqrt

00465FD4    E8 79F8F9FF     CALL FXRL.00405852                       ; JMP 到 MSVBVM60._CIlog

00465FD9    DD9D 04FFFFFF   FSTP [QWORD SS:EBP-FC]                   ; 上面几行命令计算W的平方根,再求出根的自然对数LN

00465FDF    8B55 CC         MOV EDX,[DWORD SS:EBP-34]                ; 注册码第3字符开始的子串,现在还是假的,下面把它转换成浮点数HM

00465FE2    52              PUSH EDX

00465FE3    FF15 40124000   CALL [DWORD DS:401240]                   ; MSVBVM60.__vbaR8Str

00465FE9    DD9D C4FEFFFF   FSTP [QWORD SS:EBP-13C]

00465FEF    DD85 04FFFFFF   FLD [QWORD SS:EBP-FC]

00465FF5    DC0D 18234000   FMUL [QWORD DS:402318]                   ; 对数LN乘100.0

00465FFB    FF15 30134000   CALL [DWORD DS:401330]                   ; MSVBVM60.__vbaFPInt,对积取整得到LN100

00466001    8B85 0CFFFFFF   MOV EAX,[DWORD SS:EBP-F4]                ; 取注册名第1字符ASCII值

00466007    83C0 F9         ADD EAX,-7                               ; 加-7,设为CA

0046600A    0FBFC8          MOVSX ECX,AX

0046600D    898D C0FEFFFF   MOV [DWORD SS:EBP-140],ECX

00466013    DB85 C0FEFFFF   FILD [DWORD SS:EBP-140]                  ; CA入浮点栈

00466019    DD9D B8FEFFFF   FSTP [QWORD SS:EBP-148]

0046601F    DC8D B8FEFFFF   FMUL [QWORD SS:EBP-148]                  ; 将LN100与CA相乘

00466025    DCAD C4FEFFFF   FSUBR [QWORD SS:EBP-13C]                 ; HM-(LN100*CA),下面把差(整数)转换成字串SS

0046602B    83EC 08         SUB ESP,8

0046602E    DD1C24          FSTP [QWORD SS:ESP]

00466031    FFD3            CALL EBX

00466033    8BD0            MOV EDX,EAX

00466035    8D4D CC         LEA ECX,[DWORD SS:EBP-34]

00466038    FFD6            CALL ESI

0046603A    8D4D A0         LEA ECX,[DWORD SS:EBP-60]

0046603D    FF15 4C134000   CALL [DWORD DS:40134C]                   ; MSVBVM60.__vbaFreeStr

00466043    8D4D 80         LEA ECX,[DWORD SS:EBP-80]

00466046    FF15 20104000   CALL [DWORD DS:401020]                   ; MSVBVM60.__vbaFreeVar

//下面是最后一个循环体

0046604C    8B55 E0         MOV EDX,[DWORD SS:EBP-20]

0046604F    52              PUSH EDX

00466050    FF15 2C104000   CALL [DWORD DS:40102C]                   ; MSVBVM60.__vbaLenBstr

00466056    8BD8            MOV EBX,EAX                              ; 注册名长度

00466058    B8 02000000     MOV EAX,2                                ; 循环结束条件是经计算过注册名第2字符以后

0046605D    66:3BD8         CMP BX,AX

00466060    8B45 CC         MOV EAX,[DWORD SS:EBP-34]                ; 第1轮循环这个地址指向字串SS,是该循环体的初始值,以后各轮它指向的字串是前一轮计算结果

00466063    0F8C 02010000   JL FXRL.0046616B

00466069    8985 48FFFFFF   MOV [DWORD SS:EBP-B8],EAX

0046606F    C785 40FFFFFF 0>MOV [DWORD SS:EBP-C0],8

00466079    C785 78FFFFFF 0>MOV [DWORD SS:EBP-88],1

00466083    C785 70FFFFFF 0>MOV [DWORD SS:EBP-90],2

0046608D    8D8D 70FFFFFF   LEA ECX,[DWORD SS:EBP-90]

00466093    51              PUSH ECX

00466094    0FBFD3          MOVSX EDX,BX

00466097    52              PUSH EDX

00466098    8B45 E0         MOV EAX,[DWORD SS:EBP-20]

0046609B    50              PUSH EAX

0046609C    FFD7            CALL EDI                                 ; 倒取注册名各字符

0046609E    8BD0            MOV EDX,EAX

004660A0    8D4D A0         LEA ECX,[DWORD SS:EBP-60]

004660A3    FFD6            CALL ESI

004660A5    50              PUSH EAX

004660A6    FF15 50104000   CALL [DWORD DS:401050]                   ; MSVBVM60.rtcAnsiValueBstr

004660AC    83E8 32         SUB EAX,32                               ; 当前字符的ASCII值减去32,设为Cn

004660AF    66:8985 28FFFFF>MOV [WORD SS:EBP-D8],AX

004660B6    C785 20FFFFFF 0>MOV [DWORD SS:EBP-E0],2

004660C0    8D4D BC         LEA ECX,[DWORD SS:EBP-44]

004660C3    51              PUSH ECX

004660C4    FF15 80124000   CALL [DWORD DS:401280]                   ; MSVBVM60.__vbaR8Var

004660CA    E8 83F7F9FF     CALL FXRL.00405852                       ; JMP 到 MSVBVM60._CIlog

004660CF    DC0D 18234000   FMUL [QWORD DS:402318]                   ; 上句求出W的自然对数,这句乘以100.0,下句把积取整;明显该值在各轮是相同的,设为D

004660D5    FF15 30134000   CALL [DWORD DS:401330]                   ; MSVBVM60.__vbaFPInt

004660DB    DD9D 18FFFFFF   FSTP [QWORD SS:EBP-E8]

004660E1    C785 10FFFFFF 0>MOV [DWORD SS:EBP-F0],5

004660EB    8D95 40FFFFFF   LEA EDX,[DWORD SS:EBP-C0]

004660F1    52              PUSH EDX

004660F2    8D45 BC         LEA EAX,[DWORD SS:EBP-44]

004660F5    50              PUSH EAX

004660F6    8D4D 80         LEA ECX,[DWORD SS:EBP-80]

004660F9    51              PUSH ECX

004660FA    FF15 04104000   CALL [DWORD DS:401004]                   ; MSVBVM60.__vbaVarSub,跟进看到SS串变成浮点数减去W,注意各轮SS是用上一轮的结果

00466100    50              PUSH EAX

00466101    8D95 20FFFFFF   LEA EDX,[DWORD SS:EBP-E0]

00466107    52              PUSH EDX

00466108    8D85 60FFFFFF   LEA EAX,[DWORD SS:EBP-A0]

0046610E    50              PUSH EAX

0046610F    FF15 AC124000   CALL [DWORD DS:4012AC]                   ; MSVBVM60.__vbaVarAdd,差加上Cn

00466115    50              PUSH EAX

00466116    8D8D 10FFFFFF   LEA ECX,[DWORD SS:EBP-F0]

0046611C    51              PUSH ECX

0046611D    8D95 50FFFFFF   LEA EDX,[DWORD SS:EBP-B0]

00466123    52              PUSH EDX

00466124    FF15 AC124000   CALL [DWORD DS:4012AC]                   ; MSVBVM60.__vbaVarAdd,再加上D,得到这一轮计算结果,转换成字串用于下一轮

0046612A    50              PUSH EAX

0046612B    FF15 30104000   CALL [DWORD DS:401030]                   ; MSVBVM60.__vbaStrVarMove

00466131    8BD0            MOV EDX,EAX

00466133    8D4D CC         LEA ECX,[DWORD SS:EBP-34]

00466136    FFD6            CALL ESI

00466138    8D4D A0         LEA ECX,[DWORD SS:EBP-60]

0046613B    FF15 4C134000   CALL [DWORD DS:40134C]                   ; MSVBVM60.__vbaFreeStr

00466141    8D85 50FFFFFF   LEA EAX,[DWORD SS:EBP-B0]

00466147    50              PUSH EAX

00466148    8D8D 60FFFFFF   LEA ECX,[DWORD SS:EBP-A0]

0046614E    51              PUSH ECX

0046614F    8D95 70FFFFFF   LEA EDX,[DWORD SS:EBP-90]

00466155    52              PUSH EDX

00466156    6A 03           PUSH 3

00466158    FF15 3C104000   CALL [DWORD DS:40103C]                   ; MSVBVM60.__vbaFreeVarList

0046615E    83C4 10         ADD ESP,10

00466161    83C8 FF         OR EAX,FFFFFFFF

00466164    03D8            ADD EBX,EAX

00466166  ^ E9 EDFEFFFF     JMP FXRL.00466058

0046616B    50              PUSH EAX                                 ; 循环的最后一轮处理过注册名第2字符’b’后,跳出到这里,准备返回上级

0046616C    FF15 40124000   CALL [DWORD DS:401240]                   ; MSVBVM60.__vbaR8Str

00466172    DC25 20144000   FSUB [QWORD DS:401420]

00466178    DD5D D8         FSTP [QWORD SS:EBP-28]

0046617B    FF15 D8104000   CALL [DWORD DS:4010D8]                   ; MSVBVM60.__vbaExitProc

00466181    9B              WAIT

00466182    68 13624600     PUSH FXRL.00466213

00466187    EB 69           JMP SHORT FXRL.004661F2

004661F2    8D4D E0         LEA ECX,[DWORD SS:EBP-20]

004661F5    8B35 4C134000   MOV ESI,[DWORD DS:40134C]                ; MSVBVM60.__vbaFreeStr

004661FB    FFD6            CALL ESI

004661FD    8D4D CC         LEA ECX,[DWORD SS:EBP-34]

00466200    FFD6            CALL ESI

00466202    8D4D BC         LEA ECX,[DWORD SS:EBP-44]

00466205    8B35 20104000   MOV ESI,[DWORD DS:401020]                ; MSVBVM60.__vbaFreeVar

0046620B    FFD6            CALL ESI

0046620D    8D4D A4         LEA ECX,[DWORD SS:EBP-5C]

00466210    FFD6            CALL ESI

00466212    C3              RETN

上面返回到下面这部分

004D2BE5    FF15 4C104000   CALL [DWORD DS:40104C]                   ; MSVBVM60.__vbaFreeObjList

004D2BEB    83C4 0C         ADD ESP,0C

004D2BEE    C745 FC 1900000>MOV [DWORD SS:EBP-4],19

004D2BF5    E8 862CF9FF     CALL FXRL.00465880                       ; 调用注册验证部分

004D2BFA    DD9D 78FFFFFF   FSTP [QWORD SS:EBP-88]                   ; 验证结果的字串转换成浮点数

004D2C00    DD05 D04B4000   FLD [QWORD DS:404BD0]

004D2C06    DC85 78FFFFFF   FADD [QWORD SS:EBP-88]                   ; 加常数98.0

004D2C0C    DC0D C84B4000   FMUL [QWORD DS:404BC8]                   ; 乘常数3.0

004D2C12    FF15 14114000   CALL [DWORD DS:401114]                   ; MSVBVM60.__vbaFpR8

004D2C18    DC1D C04B4000   FCOMP [QWORD DS:404BC0]                  ; 与常数291.0相比,必须相等

004D2C1E    DFE0            FSTSW AX

004D2C20    F6C4 40         TEST AH,40

004D2C23    74 0C           JE SHORT FXRL.004D2C31                   ; 不跳就是注册版

由(0-1+98)*3=291可知,调用返回的字串必须是”0″

总结:

1. 程序自己计算出机器码;用户注册名最少2字符

注册码是数字组成的字串,开头两个字符分别是注册名前两字符ASCII值的十进制个位数字

2. 由机器码前3字符(加法)和后半部分(乘法)计算出RES

TAIL是sqrt(RES)浮点数转换成字符串(长度16,小数部分11位)后的最后5个数字,变成浮点数(整)

W=Int(sqrt(RES)*10000.0)+TAIL,此W后面多次要用到

注册名第1字符ASCII值减7,变成浮点数,设为CA

注册码第3字符开始的余部设为HM

SS=HM-Int(ln(sqrt(W))*100.0)*CA,此SS是最后循环体计算用的初始值

3. 最后,循环倒取注册名各字符,直到第2字符计算过

Cn=当前字符ASCII值减32(转换成浮点数)

D=Int(ln(W)*100.0)

SS=SS-W+Cn+D

循环最后SS要等于0

4. 根据第2步不涉及注册码余部的一些计算结果,对第3步反向循环不难得出初始的SS,由它可得注册码剩余部分

HM=Int(ln(sqrt(W))*100.0)*CA+W*n-(D*n+CN)

其中n是注册名长度减1、CN是注册名除第1字符外各字符ASCII值减32h后的总和(转换成浮点数),Int表示取整,ln表示求自然对数,sqrt表示求平方根。

第一次写这么多,感谢你读完全文!

月中人

于2006年端午节

海狼三人组CrackMe分析过程

【文章标题】: 海狼三人组CrackMe分析过程 【文章作者】: 书呆彭 【下载地址】: http://bbs.pediy.com/showthread.php?t=78925 【编写语言】: VB 【使用工具】: OD 【操作平台】: Windows XP x64 SP2 【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
———————-8<———————————————————————–
【详细过程】 通常,对于VB编译器生成的东西,我称之为“又臭又长”,不喜欢动它。
幸好,有个VB Decompiler,能省去不少麻烦事。
不过这个工具对于有些VB的程序不认识,比如这个CrackMe。所以,不得已,只能硬着头皮去看这“又臭又长”的代码了。
这句是写完后补上的:写完后才发现实在是太长了,不知道有没有人愿意把它看完。
书归正文,直入主题。
整个分析过程有这么几个步骤
————————8<————————————————————————
1.由于程序通过创建子进程来脱离调试的管辖,要先过了这一关。
VB是UNICODE的,所以下W版本的API断点:
bp CreateProcessW
断下来,看堆栈:
0013F584 660CCEBF /CALL 到 CreateProcessW 来自 MSVBVM60.660CCEB9 0013F588 00000000 |ModuleFileName = NULL 0013F58C 0023BE74 |CommandLine = “F:\Debugee\Crack Me\021.exe” 0013F590 00000000 |pProcessSecurity = NULL 0013F594 00000000 |pThreadSecurity = NULL 0013F598 00000000 |InheritHandles = FALSE 0013F59C 00000000 |CreationFlags = 0 0013F5A0 00000000 |pEnvironment = NULL 0013F5A4 00000000 |CurrentDir = NULL 0013F5A8 0013F5BC |pStartupInfo = 0013F5BC 0013F5AC 0013F600 \pProcessInfo = 0013F600 0013F5B0 660FEA4C MSVBVM60.__vbaFreeVar 0013F5B4 00000008 0013F5B8 660DC1FB MSVBVM60.rtcRandomize 0013F5BC 00000044 0013F5C0 00000000 0013F5C4 00000000 0013F5C8 00000000 0013F5CC 00000000 0013F5D0 00000000 0013F5D4 00000000 0013F5D8 00000000 0013F5DC 00000000 0013F5E0 00000000 0013F5E4 00000000 0013F5E8 00000001 0013F5EC 00000001 0013F5F0 00000000 0013F5F4 00000000 0013F5F8 00000000 0013F5FC 00000000 0013F600 00000007 0013F604 00FACFA9 0013F608 00000000 0013F60C 0013F6FC 0013F610 /0013F6FC 0013F614 |0040FC1F 返回到 021.0040FC1F 来自 MSVBVM60.rtcShell -> 鼠标点一下这里,按回车
我们来到创建子进程的代码,附近的代码是这样的:
0040FBF6 > \>FLD QWORD PTR SS:[EBP-44] 0040FBF9 . >FCOMP QWORD PTR DS:[401328] 0040FBFF . >FSTSW AX 0040FC01 . >TEST AH, 41 0040FC04 . />JNZ SHORT 021.0040FC29 0040FC06 . |>LEA ECX, DWORD PTR SS:[EBP-48] 0040FC09 . |>MOV DWORD PTR SS:[EBP-78], ECX 0040FC0C . |>MOV DWORD PTR SS:[EBP-80], 4008 0040FC13 . |>PUSH 1 0040FC15 . |>LEA EDX, DWORD PTR SS:[EBP-80] 0040FC18 . |>PUSH EDX 0040FC19 . |>CALL DWORD PTR DS:[<&MSVBVM60.#600>] ; MSVBVM60.rtcShell 0040FC1F . |>FSTP ST ; 这里就是堆栈中看到的返回地址 0040FC21 . |>JMP SHORT 021.0040FC23 0040FC23 > |>CALL DWORD PTR DS:[<&MSVBVM60.__vbaEnd>] ; MSVBVM60.__vbaEnd 0040FC29 > \>CALL DWORD PTR DS:[<&MSVBVM60.__vbaExitProc>] ; MSVBVM60.__vbaExitProc 0040FC2F . >WAIT 0040FC30 . >PUSH 021.0040FC8E 0040FC35 . >JMP SHORT 021.0040FC6C …
我们看到0040FC04有一个跳转,把下面的对rtcShell的调用给跳过了。所以这里一定要跳。
注:这里的判断逻辑我没有搞清楚,只是直接修改跳转指令了,不知道上面是如何判断的。不过从上面对Randomize函数的调用来猜测,并且我发现有时用OD运行程序时它会直接运行,所以我猜这里只是随机的吧?
————————8<————————————————————————
2.程序限制鼠标的活动范围,使你无法切换到调试器界面。
刚开始,我直接bp ClipCursor,断下来后返回程序,把call指令给Nop掉,结果还会调用ClipCursor,并且是通过DllFunctionCall来调用的,不是直接调用,而这里无法简单Nop,否则程序调用其它API会出错。
并且由于断点的作用,看到程序是用了一个定时器来每隔一定时间就ClipCursor一下的。
于是Ctrl+F2,重来。先过了第1步,然后Ctrl+G,输入ClipCursor,看一下这个函数:
7D96A753 ClipCursor >MOV EAX, 122C 7D96A758 >LEA EDX, DWORD PTR SS:[ESP+4] 7D96A75C >MOV ECX, 4 7D96A761 >CALL DWORD PTR FS:[C0] 7D96A768 >RETN 4
注:我发现在我的64位系统上,很多API都与32位的实现不同了。我听说在64位系统中,win32子系统是通过wow64子系统来模拟的。 所以这个函数与大家看到的很不一样。不过这不重要,请接着往下看。
我们看一下,函数的返回指令是retn 4,那就是清一个dword栈参数。好,我们修改函数第一条指令为retn 4。成功绕过对鼠标的限制。
————————8<————————————————————————
3.关键代码的定位。
先F9让程序跑起来,随便输入个注册码,点注册,并没有一个MsgBox,而是窗口的标题内容发生变化,变成了“注册码有问题,小心翼翼”。
由于之前我说过,我试了VB Decompiler Pro(我的版本是5.0),结果它不认识这个程序的格式,所以直接获得按钮函数的方法是不行了。
但是,我有绝招。绝对是绝招。今天借这篇文章,就把它公布出来。我不知道这个方法其它高手是不是已经在用了,反正我是自己琢磨出来的。
什么方法呢,API断点。
先别笑。如果我说了我下断点用的API,你可能就不会笑了。这个断点几乎是VB程序的万能断点。
那就是__vbaExitProc
由于VB的框架性质,用户代码只是由被框架代码所调用的函数组成的。所以,必须有一种机制,用来通知框架,我已经干完活了。
这就是__vbaExitProc
我们Alt+E,双击一下MSVBVM60.DLL,然后再按Ctrl+N,打开名字列表。在键盘上依次输入v、b、a、e、x、i、t、p,OD会自动匹配,来到:
66106554 ENGINE 输出 __vbaExitEachAry 66106576 ENGINE 输出 __vbaExitEachColl 66106534 ENGINE 输出 __vbaExitEachVar 660E3F0D .text 输出 __vbaExitProc
我们在__vbaExitProc这一行按F2下断点。
回到程序,点击程序的“注册”按钮,OD马上中断!
注:此方法威力巨大,对nativ的VB程序几乎是通杀!有时候直接使用这种方法虽然行不通,只要稍加改进,用一点小手段,还是可以用的。
来看一下堆栈:
0013F19C 00409D55 返回到 021.00409D55 来自 MSVBVM60.__vbaExitProc
我们来到返回地址00409D55,并一直(此刻大家能感受到VB编译器的“又臭又长”了吧)往上翻,直到来到有很多初始化栈空间变量的代码处,才算是到“头”了。
现在找到函数地址004091B0,它就是按钮的响应函数了。我们取消前面的断点,在这里按F2下断点。
先F9运行程序(别忘了,我们现在断在了__vbaExitProc函数,而这里说明按钮事件已经处理完了),在注册码处输入一个假码。
注:我的习惯是,用户名就输入“name”,注册码就输入“code”,序列号就输入“serial”。 这样的好处是,在跟踪的时候,你根据程序正在处理的字符串的内容,就知道它是对什么东西进行运算。
这里,就算是第3步完成了吧。
————————8<————————————————————————
4.算法流程分析
现在,才进入了我们的重头戏,才开始和“又臭又长”的代码打交道了。深吸一口气,Let’s GO!
注:由于程序窗口置顶,挡住了部分视野,还好我的屏幕是1280×800的宽屏,而我又懒得去寻找它置顶的代码,所以看代码时需要不停地翻动窗口。 如果大家发现它挡着太难受,可以自己想办法把它的置顶去掉。
这一步前面的分析没什么特别的,无非是F8往下走,F4往下跳等,我就简单地粘一些代码片段及注释来说明了。
>————————————————————————————–
4.1算法第一部分
004092E2 . 8>LEA EDX, DWORD PTR SS:[EBP-38] 004092E5 . >PUSH EDX 004092E6 . >PUSH EDI 004092E7 . >CALL DWORD PTR DS:[ECX+A0] 004092ED . >FCLEX 004092EF . >CMP EAX, EBX 004092F1 . />JGE SHORT 021.00409305 004092F3 . |>PUSH 0A0 004092F8 . |>PUSH 021.00407DCC 004092FD . |>PUSH EDI 004092FE . |>PUSH EAX 004092FF . |>CALL DWORD PTR DS:[<&MSVBVM60.__vbaHr>; MSVBVM60.__vbaHresultCheckObj 00409305 > \>MOV EAX, DWORD PTR SS:[EBP-38] 00409308 . >PUSH EAX ; eax=UNICODE “code”。哈,正是我们的注册码。我们也知道了上面004092E7的call是取得输入,放入[EBP-0x38] 00409309 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaLe>; MSVBVM60.__vbaLenBstr 0040930F . >XOR ECX, ECX 00409311 . >CMP EAX, 10 00409314 . >SETL CL ; 注册码长度小于0x10(==16)吗? 00409317 . >NEG ECX 00409319 . >MOV EDI, ECX 0040931B . >LEA ECX, DWORD PTR SS:[EBP-38] 0040931E . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFr>; MSVBVM60.__vbaFreeStr 00409324 . >LEA ECX, DWORD PTR SS:[EBP-40] 00409327 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFr>; MSVBVM60.__vbaFreeObj 0040932D . >CMP DI, BX 00409330 . >JE SHORT 021.00409375 ; 小于16,这里不跳,出错。 …
这里出错了,F9运行,显示“注册码有问题,小心翼翼”。
既然要16位以上,我们就输入个code123456789abc,刚好16位,再次跟踪。
00409330 . />JE SHORT 021.00409375 ; 这回跳了 00409332 . |>MOV EDX, DWORD PTR DS:[ESI] … | ; 这里省略几行 0040936A . |>CALL DWORD PTR DS:[<&MSVBVM60.__vbaFr>; MSVBVM60.__vbaFreeObj 00409370 . |>JMP 021.00409D4F 00409375 > \>MOV EAX, DWORD PTR DS:[ESI] ; 跳到这里 00409377 . >PUSH ESI ; 继续往下走,略去
接着往下走,到这里有一个循环:
004093D0 . >MOV ECX, DWORD PTR SS:[EBP-20] 004093D3 . >PUSH ECX ; ECX = UNICODE “104E0295″,是机器码,看到[EBP-20]是机器码 004093D4 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaLe>; MSVBVM60.__vbaLenBstr 004093DA . >MOV ECX, EAX 004093DC . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaI2>; MSVBVM60.__vbaI2I4 004093E2 . >MOV DWORD PTR SS:[EBP-2A0], EAX ; [epb-0x2a0] = 8,机器码的长度 004093E8 . >MOV EDI, 1 ; for i = 0 to len(machine) step 1 –> 本人对BASIC语法不通,不知道是不是这么写。 004093ED > />CMP DI, WORD PTR SS:[EBP-2A0] ; di为循环变量 004093F4 . |>JG 021.004094A9 ; 循环出口 004093FA . |>MOV DWORD PTR SS:[EBP-48], 1 ; VB“又臭又长”的原因在于,它不是编译期强类型,所以必须在运行时做各种类型相关的处理 00409401 . |>MOV DWORD PTR SS:[EBP-50], 2 ; [ebp-0x50]处的Variable对象的类型是整数型,值即为1 00409408 . |>LEA EDX, DWORD PTR SS:[EBP-20] 0040940B . |>MOV DWORD PTR SS:[EBP-1F8], EDX ; 这是[ebp-200]处的Variable对象的值域,是[EBP-20],即机器码 00409411 . |>MOV DWORD PTR SS:[EBP-200], 4008 ; 4008是Variable中的类型识别域(表示值域部分是指针) 0040941B . |>LEA EAX, DWORD PTR SS:[EBP-50] ; 以后关于Variable对象构造的代码不再解释了 0040941E . |>PUSH EAX ; Arg4 = ebp-50,即立即数1 0040941F . |>MOVSX ECX, DI 00409422 . |>PUSH ECX ; Arg3 = i,循环变量 00409423 . |>LEA EDX, DWORD PTR SS:[EBP-200] 00409429 . |>PUSH EDX ; Arg2 = ebp-200,机器码 0040942A . |>LEA EAX, DWORD PTR SS:[EBP-60] 0040942D . |>PUSH EAX ; Arg1 = ebp-60,注意这个参数是out参数 0040942E . |>CALL DWORD PTR DS:[<&MSVBVM60.#632>] ; MSVBVM60.rtcMidCharVar 00409434 . |>MOV ECX, DWORD PTR SS:[EBP-34] ; 以上“又臭又长”的代码完成的只是个machinecode[i]的功能,C++编译器生成的代码用三五条指令就完成 00409437 . |>PUSH ECX ; 这个变量需要注意一下,记住它的偏移是0x34 00409438 . |>LEA EDX, DWORD PTR SS:[EBP-60] ; machinecode[i] 0040943B . |>PUSH EDX 0040943C . |>LEA EAX, DWORD PTR SS:[EBP-38] ; 注意ebp-38实际是个临时变量,这里用来接收结果,已经不再指向输入的注册码,释放对象的代码省略了。 0040943F . |>PUSH EAX 00409440 . |>CALL DWORD PTR DS:[<&MSVBVM60.__vbaSt>; MSVBVM60.__vbaStrVarVal 00409446 . |>PUSH EAX 00409447 . |>CALL DWORD PTR DS:[<&MSVBVM60.#516>] ; MSVBVM60.rtcAnsiValueBstr 0040944D . |>PUSH EAX 0040944E . |>CALL DWORD PTR DS:[<&MSVBVM60.__vbaSt>; MSVBVM60.__vbaStrI2 00409454 . |>MOV EDX, EAX ; 这里eax指向UNICODE“49”,这是??? 其实就是code[0]的ASCII值的10进制。 00409456 . |>LEA ECX, DWORD PTR SS:[EBP-3C] 00409459 . |>CALL DWORD PTR DS:[<&MSVBVM60.__vbaSt>; MSVBVM60.__vbaStrMove 0040945F . |>PUSH EAX ; 和谁strcat??? 注意上面我说的ebp-0x34。 00409460 . |>CALL DWORD PTR DS:[<&MSVBVM60.__vbaSt>; MSVBVM60.__vbaStrCat 00409466 . |>MOV EDX, EAX 00409468 . |>LEA ECX, DWORD PTR SS:[EBP-34] ; cat后再存回ebp-0x34 0040946B . |>CALL DWORD PTR DS:[<&MSVBVM60.__vbaSt>; MSVBVM60.__vbaStrMove 00409471 . |>LEA ECX, DWORD PTR SS:[EBP-3C] ; 下面几条指令不用看了。这也是VB“又臭又长”的表现。 00409474 . |>PUSH ECX ; 我们已经弄清了这个循环的过程是把机器码的每一个字符转换成它的ASCII码十进制形式 00409475 . |>LEA EDX, DWORD PTR SS:[EBP-38] ; 结果存放在ebp-0x34这个变量。 00409478 . |>PUSH EDX 00409479 . |>PUSH 2 0040947B . |>CALL DWORD PTR DS:[<&MSVBVM60.__vbaFr>; MSVBVM60.__vbaFreeStrList 00409481 . |>LEA EAX, DWORD PTR SS:[EBP-60] 00409484 . |>PUSH EAX 00409485 . |>LEA ECX, DWORD PTR SS:[EBP-50] 00409488 . |>PUSH ECX 00409489 . |>PUSH 2 0040948B . |>CALL DWORD PTR DS:[<&MSVBVM60.__vbaFr>; MSVBVM60.__vbaFreeVarList 00409491 . |>ADD ESP, 18 00409494 . |>MOV EAX, 1 00409499 . |>ADD AX, DI 0040949C . |>JO 021.00409E73 004094A2 . |>MOV EDI, EAX 004094A4 .^\>JMP 021.004093ED ; 循环 004094A9 > >LEA EDX, DWORD PTR SS:[EBP-34] ; 循环结束后来到这里。

我们在数据窗口看一下ebp-0x34,输入d ebp-0x34,看到:
0013F44C 002501BC UNICODE “4948526948505753” 0013F450 00000018 0013F454 00000018 0013F458 00000000 0013F45C 00010000 UNICODE “=::=::\” 0013F460 00292684 UNICODE “104E0295” 0013F464 0013F9BC 指向下一个 SEH 记录的指针 0013F468 00401496 SE处理程序 0013F46C 0013F1A0 0013F470 00401210 021.00401210
注:在调VB程序时,由于总是间接指针,所以我习惯把数据窗口显示方式改为 长型->地址,所以会是上面的格式。
可以看到[ebp-0x34]是值正是机器码的ASCII码十进制值转化来的,说明对上面的这个循环分析没有错误。
>—————————————————————————————
4.2算法第二部分
接着往下走。
004094A9 > \>LEA EDX, DWORD PTR SS:[EBP-34] ; 循环结束 004094AC . >MOV DWORD PTR SS:[EBP-1F8], EDX 004094B2 . >MOV DWORD PTR SS:[EBP-200], 4008 004094BC . >PUSH 8 ; arg3 = 8 004094BE . >LEA EAX, DWORD PTR SS:[EBP-200] ; +8偏移处的指针指向 UNICODE “4948526948505753” 004094C4 . >PUSH EAX ; arg2 = ASCII_machinecode 004094C5 . >LEA ECX, DWORD PTR SS:[EBP-50] 004094C8 . >PUSH ECX ; arg1 = ebp-50,out参数 004094C9 . >CALL DWORD PTR DS:[<&MSVBVM60.#617>] ; MSVBVM60.rtcLeftCharVar 004094CF . >LEA EDX, DWORD PTR SS:[EBP-50] ; [ebp-50] = ASCII_machine的前8位 004094D2 . >PUSH EDX 004094D3 . >LEA EAX, DWORD PTR SS:[EBP-38] 004094D6 . >PUSH EAX 004094D7 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaStrVarVal>] ; MSVBVM60.__vbaStrVarVal 004094DD . >PUSH EAX 004094DE . >CALL DWORD PTR DS:[<&MSVBVM60.#581>] ; MSVBVM60.rtcR8ValFromBstr 004094E4 . >FSTP QWORD PTR SS:[EBP-30] ; 看一下浮点寄存器窗口,返回值是49485269.00000 004094E7 . >LEA ECX, DWORD PTR SS:[EBP-38] ; 返回的浮点数存放于ebp-30处,注意是qword 004094EA . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr …
这一段代码得到一个64位双精度浮点数,存放于[ebp-30](以及[ebp-2c])处。继续往下,没有循环,这次的代码有点长,不过很好理解:
004094A9 > \>LEA EDX, DWORD PTR SS:[EBP-34] ; 循环结束 004094AC . >MOV DWORD PTR SS:[EBP-1F8], EDX 004094B2 . >MOV DWORD PTR SS:[EBP-200], 4008 004094BC . >PUSH 8 ; arg3 = 8 004094BE . >LEA EAX, DWORD PTR SS:[EBP-200] ; +8偏移处的指针指向 UNICODE “4948526948505753” 004094C4 . >PUSH EAX ; arg2 = ASCII_machinecode 004094C5 . >LEA ECX, DWORD PTR SS:[EBP-50] 004094C8 . >PUSH ECX ; arg1 = ebp-50,out参数 004094C9 . >CALL DWORD PTR DS:[<&MSVBVM60.#617>] ; MSVBVM60.rtcLeftCharVar 004094CF . >LEA EDX, DWORD PTR SS:[EBP-50] ; [ebp-50] = ASCII_machine的前8位 004094D2 . >PUSH EDX 004094D3 . >LEA EAX, DWORD PTR SS:[EBP-38] 004094D6 . >PUSH EAX 004094D7 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaStrVarVal>] ; MSVBVM60.__vbaStrVarVal 004094DD . >PUSH EAX 004094DE . >CALL DWORD PTR DS:[<&MSVBVM60.#581>] ; MSVBVM60.rtcR8ValFromBstr 004094E4 . >FSTP QWORD PTR SS:[EBP-30] ; 看一下浮点寄存器窗口,返回值是49485269.00000 004094E7 . >LEA ECX, DWORD PTR SS:[EBP-38] ; 返回的浮点数存放于ebp-30处,注意是qword 004094EA . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr 004094F0 . >LEA ECX, DWORD PTR SS:[EBP-50] 004094F3 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeVar>] ; MSVBVM60.__vbaFreeVar 004094F9 . >MOV ECX, DWORD PTR DS:[ESI] 004094FB . >PUSH ESI 004094FC . >CALL DWORD PTR DS:[ECX+304] 00409502 . >PUSH EAX 00409503 . >LEA EDX, DWORD PTR SS:[EBP-40] 00409506 . >PUSH EDX 00409507 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaObjSet>] ; MSVBVM60.__vbaObjSet 0040950D . >MOV EDI, EAX 0040950F . >MOV EAX, DWORD PTR DS:[EDI] 00409511 . >LEA ECX, DWORD PTR SS:[EBP-38] ; 前面说了,ebp-38充当临时变量 00409514 . >PUSH ECX 00409515 . >PUSH EDI 00409516 . >CALL DWORD PTR DS:[EAX+A0] ; 这次用来接收输入的注册码的值 0040951C . >FCLEX 0040951E . >CMP EAX, EBX 00409520 . />JGE SHORT 021.00409534 00409522 . |>PUSH 0A0 00409527 . |>PUSH 021.00407DCC 0040952C . |>PUSH EDI 0040952D . |>PUSH EAX 0040952E . |>CALL DWORD PTR DS:[<&MSVBVM60.__vbaHresultCheck>; MSVBVM60.__vbaHresultCheckObj 00409534 > \>MOV EAX, DWORD PTR SS:[EBP-38] 00409537 . >MOV DWORD PTR SS:[EBP-38], EBX 0040953A . >MOV DWORD PTR SS:[EBP-48], EAX 0040953D . >MOV DWORD PTR SS:[EBP-50], 8 00409544 . >PUSH 8 00409546 . >LEA EDX, DWORD PTR SS:[EBP-50] 00409549 . >PUSH EDX 0040954A . >LEA EAX, DWORD PTR SS:[EBP-60] 0040954D . >PUSH EAX ; 也取输入注册码的前8位 0040954E . >CALL DWORD PTR DS:[<&MSVBVM60.#617>] ; MSVBVM60.rtcLeftCharVar 00409554 . >LEA ECX, DWORD PTR SS:[EBP-60] 00409557 . >PUSH ECX 00409558 . >LEA EDX, DWORD PTR SS:[EBP-3C] 0040955B . >PUSH EDX 0040955C . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaStrVarVal>] ; MSVBVM60.__vbaStrVarVal 00409562 . >PUSH EAX ; 前8位由字符串直接转化为浮点数 00409563 . >CALL DWORD PTR DS:[<&MSVBVM60.#581>] ; MSVBVM60.rtcR8ValFromBstr 00409569 . >MOV EDI, DWORD PTR DS:[<&MSVBVM60.__vbaFpI4>] ; 返回值在浮点寄存器中 0040956F . >CALL EDI ; <&MSVBVM60.__vbaFpI4> 00409571 . >MOV EDX, EAX ; __vbaFpI4是把浮点数转化成4字节(即32位)整数,返回值在EAX 00409573 . >FLD QWORD PTR SS:[EBP-30] ; 由机器码前8位转化而来的浮点数 00409576 . >MOV DWORD PTR SS:[EBP-2C8], EDX 0040957C . >CALL EDI ; 也转化成32位整数 0040957E . >MOV ECX, DWORD PTR SS:[EBP-2C8] 00409584 . >XOR ECX, EAX ; 这两个整数(由机器码前8位和由注册码前8位分别转化过来的)进行异或 00409586 . >MOV DWORD PTR SS:[EBP-2CC], ECX ; 结果暂存于ebp-0x2cc 0040958C . >FILD DWORD PTR SS:[EBP-2CC] 00409592 . >FSTP QWORD PTR SS:[EBP-30] ; 这两条指令是再将整型转换为双精度浮点型,还存于ebp-0x30 00409595 . >LEA ECX, DWORD PTR SS:[EBP-3C] 00409598 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr 0040959E . >LEA ECX, DWORD PTR SS:[EBP-40] 004095A1 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeObj>] ; MSVBVM60.__vbaFreeObj 004095A7 . >LEA EDX, DWORD PTR SS:[EBP-60] 004095AA . >PUSH EDX 004095AB . >LEA EAX, DWORD PTR SS:[EBP-50] 004095AE . >PUSH EAX 004095AF . >PUSH 2 004095B1 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeVarList>>; MSVBVM60.__vbaFreeVarList 004095B7 . >ADD ESP, 0C 004095BA . >MOV EDI, DWORD PTR SS:[EBP-2C] 004095BD . >PUSH EDI 004095BE . >MOV ECX, DWORD PTR SS:[EBP-30] 004095C1 . >PUSH ECX ; 这两条push实际是压入一个双精度浮点数。 004095C2 . >CALL DWORD PTR DS:[<&MSVBVM60.#587>] ; MSVBVM60.rtcLog 004095C8 . >FSTP QWORD PTR SS:[EBP-28C] ; 取对数,结果存放于[ebp-0x28c],注意也是qword 004095CE . >PUSH EDI 004095CF . >MOV EDX, DWORD PTR SS:[EBP-30] 004095D2 . >PUSH EDX 004095D3 . >CALL DWORD PTR DS:[<&MSVBVM60.#587>] ; MSVBVM60.rtcLog 004095D9 . >FSTP QWORD PTR SS:[EBP-2D4] ; 还是同一个值,存入[ebp-0x2d4] 004095DF . >FLD QWORD PTR SS:[EBP-28C] ; 这是第一次存入的对数值 004095E5 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFPFix>] ; 舍入到整数 004095EB . >FSUBR QWORD PTR SS:[EBP-2D4] ; 再减第二次存入的对数值,注意是fsubr,所以是减数,而不是被减数 004095F1 . >FSTP QWORD PTR SS:[EBP-30] ; 剩下的差是小数部分,存入ebp-30 004095F4 . >FSTSW AX 004095F6 . >TEST AL, 0D 004095F8 . >JNZ 021.00409E6E ; 异常检查。注意VB没有使用80×87的硬件异常,而是是软件判断状态位来实现的“软异常” 004095FE . >FLD QWORD PTR SS:[EBP-30] ; 取刚才计算出的小数部分 00409601 . >FMUL QWORD PTR DS:[401238] ; 乘以程序内置的常数,值为1000000000.0000,即10的9次方 00409607 . >FSTP QWORD PTR SS:[EBP-48] ; 结果存放[ebp-0x48] 0040960A . >FSTSW AX 0040960C . >TEST AL, 0D 0040960E . >JNZ 021.00409E6E ; 同上,“软异常”
这面这段代码是一段关键的计算过程,如果把VB的无用代码除去,其实也不算长。到这里,我们得到一个浮点数,存放于[ebp-0x48]处的一个qword变量里。
>————————————————————————————–
4.3算法第三部分
请注意前面这个地址,因为下面马上就用到它的值了,但是在OD中并没有出现[ebp-0x48]这样的字眼,这也是调试VB的另一个特点,就是你必须时刻注意变量的偏移量其它附近的变量,因为很可能它们是同一个变量。
请接着看:
0040960E . />JNZ 021.00409E6E ; 同上,软异常 00409614 . |>MOV DWORD PTR SS:[EBP-50], 5 ; [ebp-0x50]这个变量的类型域,5表示双精度浮点。 0040961B . |>PUSH 8 ; 取左边8位 0040961D . |>LEA EAX, DWORD PTR SS:[EBP-50] ; 请注意,[ebp-0x50]的值域恰是ebp-0x48 00409620 . |>PUSH EAX ; 现在它的值是 7.1718556708082199100e+16 00409621 . |>LEA ECX, DWORD PTR SS:[EBP-60] ; [ebp-0x60]用来接收结果 00409624 . |>PUSH ECX 00409625 . |>CALL DWORD PTR DS:[<&MSVBVM60.#617>] ; MSVBVM60.rtcLeftCharVar 0040962B . |>LEA EDX, DWORD PTR SS:[EBP-60] 0040962E . |>PUSH EDX 0040962F . |>CALL DWORD PTR DS:[<&MSVBVM60.__vbaStrVarMove>] ; MSVBVM60.__vbaStrVarMove 00409635 . |>MOV EDX, EAX 00409637 . |>LEA ECX, DWORD PTR SS:[EBP-20] ; 最后把高8位转化成的字符串”71718556″存放于[ebp-20]处的变量 0040963A . |>CALL DWORD PTR DS:[<&MSVBVM60.__vbaStrMove>] ; MSVBVM60.__vbaStrMove 00409640 . |>LEA EAX, DWORD PTR SS:[EBP-60] 00409643 . |>PUSH EAX 00409644 . |>LEA ECX, DWORD PTR SS:[EBP-50] 00409647 . |>PUSH ECX 00409648 . |>PUSH 2 0040964A . |>CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeVarList>>; MSVBVM60.__vbaFreeVarList

这里记着[ebp-0x20]这个变量,再接着往下走:
00409669 . >MOV ECX, DWORD PTR DS:[EDI] 0040966B . >LEA EDX, DWORD PTR SS:[EBP-38] ; 临时变量ebp-0x38再次上场,这次还是用来接收输入的注册码 0040966E . >PUSH EDX 0040966F . >PUSH EDI 00409670 . >CALL DWORD PTR DS:[ECX+A0] ; 取输入的注册码 00409676 . >FCLEX 00409678 . >CMP EAX, EBX 0040967A . >JGE SHORT 021.0040968E 0040967C . >PUSH 0A0 00409681 . >PUSH 021.00407DCC 00409686 . >PUSH EDI 00409687 . >PUSH EAX 00409688 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaHresultCheck>; MSVBVM60.__vbaHresultCheckObj 0040968E > >MOV EAX, DWORD PTR SS:[EBP-38] 00409691 . >MOV DWORD PTR SS:[EBP-38], EBX 00409694 . >MOV DWORD PTR SS:[EBP-48], EAX ; 如上所说,这里反汇编显示的是[ebp-0x48],而实际变量使用时是ebp-0x50 00409697 . >MOV DWORD PTR SS:[EBP-50], 8 0040969E . >PUSH 8 004096A0 . >LEA EAX, DWORD PTR SS:[EBP-50] 004096A3 . >PUSH EAX 004096A4 . >LEA ECX, DWORD PTR SS:[EBP-60] ; 这次[ebp-0x60]这个变量用来存放输入注册码的最右8位 004096A7 . >PUSH ECX 004096A8 . >CALL DWORD PTR DS:[<&MSVBVM60.#619>] ; MSVBVM60.rtcRightCharVar 004096AE . >MOV EDX, DWORD PTR SS:[EBP-20] ; [ebp-0x20]是什么变量,还记得吧 004096B1 . >MOV DWORD PTR SS:[EBP-1F8], EDX ; 把它放入[ebp-0x200]这个变量的值域 004096B7 . >MOV DWORD PTR SS:[EBP-200], 8008 ; 类型域是8008,不用管它具体是什么意思,只要记住到偏移08处找它真正的值就行了 004096C1 . >LEA EAX, DWORD PTR SS:[EBP-60] 004096C4 . >PUSH EAX 004096C5 . >LEA ECX, DWORD PTR SS:[EBP-200] 004096CB . >PUSH ECX 004096CC . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaVarTstEq>] ; 比较[ebp-0x200]和[ebp-0x60]是不是相等 004096D2 . >MOV EDI, EAX ; 不相等,返回值为false,即0。结果暂存于edi 004096D4 . >LEA ECX, DWORD PTR SS:[EBP-40] 004096D7 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeObj>] ; MSVBVM60.__vbaFreeObj 004096DD . >LEA EDX, DWORD PTR SS:[EBP-60] 004096E0 . >PUSH EDX 004096E1 . >LEA EAX, DWORD PTR SS:[EBP-50] 004096E4 . >PUSH EAX 004096E5 . >PUSH 2 004096E7 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeVarList>>; MSVBVM60.__vbaFreeVarList 004096ED . >ADD ESP, 0C 004096F0 . >CMP DI, BX ; 记得di中是什么值吧 004096F3 . >JE SHORT 021.00409724 ; di为0,跳走。 004096F5 . >MOV ECX, DWORD PTR DS:[ESI]

看来,我们找到关键的比较点了。这里由于我们输入的码没有通过验证,这里跳走了。F9,显示“注册码错误,加油,英雄榜等着你”
好,我们整理一下,上面的计算过程是
将机器码的前8位和注册码的前8位分别(按照不同的方法)转换成整数值,然后二者进行异或,再对结果取对数。
然后取对数的结果的小数部分乘以一个10的9次幂(将小数点向右移动9位),再将这个数的高8位与注册码的最右边8位进行比较。
我们把试炼的16位注册码改成code123471718558,再点注册。。。。
“注册失败,最好脑筋急转弯”
Nani???
这是怎么回事?
>—————————————————————————————-
4.4脑筋急转弯
回想刚才的分析流程,很顺利,也想不出哪里有问题。
于是我把代码又重新看了一遍,把所有的比较远的跳转(包括条件和非条件)都做个标记,并下断点。然后在OD中把所有可能的执行路径都走了一遍,确实没有遗漏了啊!
于是分析陷入了僵局。思前想后,不知道怎么落入了作者的陷阱。
先休息一下,听首歌。陈思思,《梦入桃花源》,MV的画面很美。。。
一边听着歌,一边再翻看着代码。想着刚才的过程,很顺利,没什么异常情况啊。。。
突然,一拍脑袋,异常!正因为没有异常情况所以没有注册正确!
正听到歌词是“忽风桃花岸,白云裹红团,桃花源头随梦源,忽在天际,忽眼前。。。”
马上翻到函数开头。按照我的想法,安装SEH不就是几个push操作嘛。结果再次被“又臭又长”的VB所打倒,函数开头是这个样子的:
004091B0 $ >PUSH EBP 004091B1 . >MOV EBP, ESP 004091B3 . >SUB ESP, 14 004091B6 . >PUSH <JMP.&MSVBVM60.__vbaExceptHandler> ; SE 处理程序安装 004091BB . >MOV EAX, DWORD PTR FS:[0] 004091C1 . >PUSH EAX 004091C2 . >MOV DWORD PTR FS:[0], ESP 004091C9 . >SUB ESP, 2B8 004091CF . >PUSH EBX 004091D0 . >PUSH ESI 004091D1 . >PUSH EDI 004091D2 . >MOV DWORD PTR SS:[EBP-14], ESP 004091D5 . >MOV DWORD PTR SS:[EBP-10], 021.00401210 004091DC . >MOV ESI, DWORD PTR SS:[EBP+8] 004091DF . >MOV EAX, ESI 004091E1 . >AND EAX, 1 004091E4 . >MOV DWORD PTR SS:[EBP-C], EAX 004091E7 . >AND ESI, FFFFFFFE 004091EA . >MOV DWORD PTR SS:[EBP+8], ESI 004091ED . >XOR EBX, EBX 004091EF . >MOV DWORD PTR SS:[EBP-8], EBX 004091F2 . >MOV ECX, DWORD PTR DS:[ESI] 004091F4 . >PUSH ESI 004091F5 . >CALL DWORD PTR DS:[ECX+4] 004091F8 . >MOV DWORD PTR SS:[EBP-20], EBX ; 这里开始是一长串的变量初始化。 004091FB . >MOV DWORD PTR SS:[EBP-28], EBX 004091FE . >MOV DWORD PTR SS:[EBP-34], EBX 00409201 . >MOV DWORD PTR SS:[EBP-38], EBX …

它安装的seh居然是VM中的例程。我想不想掉到VM中去。虽然我不清楚VB的异常处理框架是什么样的,但我猜测最张的异常处理代码还应该在注册异常处理的附近。
毕竟它也要实现类似try … catch的语义,哦,想起来了,在VB中大概是叫OnError。
再看看,看到它那个MOV DWORD PTR [EBP-14], 00401210很可疑。
于是d 401210,发现00401210处是个某种结构:
00401210 00100017 00401214 00409D55 021.00409D55 00401218 00409E39 021.00409E39 0040121C 00409D60 021.00409D60 00401220 00401228 021.00401228 00401224 00000000 00401228 00000001 0040122C 00000001 00401230 00409753 021.00409753 00401234 00000000 00401238 00000000 00401238 00000000 0040123C 41CDCD65 00401240 00080001 00401244 00409ECC 021.00409ECC 00401248 00080085 0040124C 0040A9DA 021.0040A9DA 00401250 00000000 00401254 0040A9E4 021.0040A9E4

我想这很可能是跟异常处理入口登记有关的结构,于是在看了一下那几个地址,前3个地址是代码入口,看了一下都指向OnRegistClick()函数结尾的部分。
第4个指针恰好指向下一个结构,而这个结构中只有一个指针,当来到这个代码入口时,直觉告诉我就是这里。
为什么呢?这就是地址00409753附近的代码:
00409748 .^\>JGE 021.00409367 0040974E .^ >JMP 021.00409358 ; 这两个跳转都是往上走的 00409753 . >CALL DWORD PTR DS:[<&MSVBVM60.#685>] ; MSVBVM60.rtcErrObj 00409759 . >PUSH EAX 0040975A . >LEA EAX, DWORD PTR SS:[EBP-40]

记得我上面说把正常执行的所有执行路径都走了一遍,而这段代码根本没有被正常执行路径所覆盖,并且按Ctrl+R所列出的参考中,并没有向这里的跳转也证实了这一点。
那么一段不在正常执行路径当中的代码是干什么的呢?当然是异常处理了!而且我们看,它一开始就调用一个rtcErrObj的API,从字面来理解,应该是获得一个描述所发生异常的对象了。
我们在这里F2下断点。
现在的问题是,怎么发生异常呢?
我首先想到了上面我们提到的“软异常”,虽然不知道FPU中的状态字的具体定义,但我们可以强制修改跳转指令。
好,F9运行程序,点“注册”,断在函数入口入,然后F4到这里:
004095E5 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFPFix>] ; 舍入到整数 004095EB . >FSUBR QWORD PTR SS:[EBP-2D4] ; 再减第二次存入的对数值,注意是fsubr,所以是减数,而不是被减数 004095F1 . >FSTP QWORD PTR SS:[EBP-30] ; 剩下的差是小数部分,存入ebp-30 004095F4 . >FSTSW AX 004095F6 . >TEST AL, 0D 004095F8 . >JNZ 021.00409E6E ; 异常检查。注意VB没有使用80×87的硬件异常,而是是软件判断状态位来实现的“软异常” 004095FE . >FLD QWORD PTR SS:[EBP-30] ; 取刚才计算出的小数部分 00409601 . >FMUL QWORD PTR DS:[401238] ; 乘以程序内置的常数,值为1000000000.0000,即10的9次方 00409607 . >FSTP QWORD PTR SS:[EBP-48] ; 结果存放[ebp-0x48] 0040960A . >FSTSW AX 0040960C . >TEST AL, 0D 0040960E . >JNZ 021.00409E6E ; 同上,软异常
当执行JNZ指令时,本来是不跳的,我直接修改EIP到它的目的地址,来到:
00409E6E >^\>JMP <JMP.&MSVBVM60.__vbaFPException>
然后F9,果然中断在了
00409753 . >CALL DWORD PTR DS:[<&MSVBVM60.#685>] ; MSVBVM60.rtcErrObj 00409759 . >PUSH EAX
这回先不看它的算法,只看一下它的执行流程,找到这两个条件跳转指令是跨越比较大的
004097A8 . />JE 021.00409A9E 00409A26 . />JE 021.00409D4F
多次试验发现,第一个如果跳转成立,是真正的“异常”,会显示“意外错误,请检测输入是否有误”
如果第一个不跳,第二个跳,则是注册码错误
如果强行让第二个也不跳,则会出现0指针的问题,程序无法执行下去。

好了,我们来整理一下,第一个条件跳转是判断异常类型,即,并不是所有的异常它都处理,这相当于seh中的except_filter;
如果确实是被filter过滤的异常(实际是有意引发的异常)发生了,那么就进入下面的比较过程,而第二个条件跳转就是比较点。
但是经过刚才的实验,知道如果异常类型不正确而强行进行判断,并爆破的话,会有0指针出现。
那么我们现在需要知道上面的运算中会产生多少种异常。
我们已经知道这样的浮点异常不是想要的:
0040960A . >FSTSW AX 0040960C . >TEST AL, 0D
再找一下,在上面的循环中有这样的代码:
00409494 . >MOV EAX, 1 00409499 . >ADD AX, DI 0040949C . />JO 021.00409E73 004094A2 . |>MOV EDI, EAX … | ; 这里省略很多代码 00409E73 > \>CALL DWORD PTR DS:[<&MSVBVM60.__vbaErrorOverflo>; MSVBVM60.__vbaErrorOverflow

再试试这个整数溢出异常,结果这次可以爆破成功,没有0指针错误。
但是细想一下,刚才那个浮点异常还可能由输入特定的注册码来触发,这个整数溢出如果不修改程序是不可能发生的,因为它出现在对机器码处理的那个循环当中,而机器码的长度是固定的,不随注册码改变的。
看起来又陷入了僵局。
>——————————————————————————————
4.5脑筋再转弯
刚才找到的浮点异常不对,而整数溢出又不是可以由输入特定注册码来触发的,由输入的注册码还能触发什么异常呢?
想想对输入注册码的处理过程
把前8位字符串转换成浮点数 -> 这一步不会异常,如果输入不是数字就返回0值
把浮点数转换成整数 -> 这一步更不会异常
把异或的结果取对数 -> 哎,,,,这一步可能会异常!
虽然我不知道IEEE浮点数标准是如何规定的,但猜想对0取对数不和用0作除数一样,照理应该会产生异常的。
好,既然这样,就先验证一下吧,修改试练注册码的前8位为49485269,然后运行程序,果然中断在于异常处理的代码。
那么剩下的部分就和前面一样了,分析算法吧。慢着,既然前面的部分是通过__vbaVarTstEqu来比较的,后面的部分也差不多。
先往下翻代码,找到:
004099B8 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaVarTstEq>] ; MSVBVM60.__vbaVarTstEq 004099BE . >MOV ESI, EAX ; 比较结果放esi 004099C0 . >LEA ECX, DWORD PTR SS:[EBP-40] 004099C3 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeObj>] ; MSVBVM60.__vbaFreeObj 004099C9 . >LEA EDX, DWORD PTR SS:[EBP-110] 004099CF . >PUSH EDX … ; 这里省略若干行代码 00409A14 . >LEA EDX, DWORD PTR SS:[EBP-50] 00409A17 . >PUSH EDX 00409A18 . >PUSH 0D 00409A1A . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeVarList>>; MSVBVM60.__vbaFreeVarList 00409A20 . >ADD ESP, 38 00409A23 . >CMP SI, BX ; 二者是否相等。 00409A26 . >JE 021.00409D4F

而这个条件跳转正是最后一步的所谓“关键”跳。我们F4来到004099B8这一行,然后看一下比较是什么内容。
d [esp]
0013F370 00000008 0013F374 00000000 0013F378 05A4A21C UNICODE “DC32D905-8AD3E679-3623AD4F” 0013F37C 00000002
d [esp+4]
0013F410 00220008 0013F414 0251133C 0013F418 002C0CC4 UNICODE “56789abc” 0013F41C 6603BF9C 返回到 MSVBVM60.6603BF9C 来自 MSVBVM60.66066716
看出数据窗口设置成这种显示格式的方便了吧,呵呵。
我们看到TstEq的两个对象,其中一个正是我们输入的注册码后面部分啊,那么另一串字符串应该它正确的值了。
我们把它抄下来,前面放上我们得到的“前8位”,合起来就是“49485269DC32D905-8AD3E679-3623AD4F”
把它填入程序中,再点“注册”——
终于恭喜我了。。。。
————————8<————————————————————————
5.追求完美
至此,我们已经知道了正确的注册码了,发到论坛上一看,居然是第一个完成的,哈哈。
不过我是个追求完美的人,必须把后半部分的算法找出来的。
看了一下后半部分的处理过程,整个过程只有一次调用自定义函数,其它都是调用VB的库,把数据Move来Move去的(这也是我不喜欢VB的另一点原因,总把数据到处移动):
004097F9 . >LEA EDX, DWORD PTR SS:[EBP-3C] 004097FC . >PUSH EDX 004097FD . >CALL 021.00411CD0
F4到这里后,d [esp] 看到参数是机器码“104E0295”。注意这次参数是真正的值,不是偏移+8处的值。为啥?因为在参数入栈之前调用了__vbaStrMov,这个函数会取出真正的值。
F8过去后,EAX=04BD004C, (UNICODE “41F23069DC32D9058AD3E6793623AD4F562FA8D3”)
仔细看一下,最后形成后半部分注册码的字符串(”DC32D905-8AD3E679-3623AD4F”)就是这个长串的某几段合成的。不详细说了。
这个函数明显是一个HASH函数。是什么呢?我从它结果的长度,猜测它是SHA160。
用kanal检测,居然什么都没有。进入这个函数看一下,再次被VB打败,好丑的代码,往下翻呀翻,终于找到了有意思的东西:
00412162 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFpI4>] ; MSVBVM60.__vbaFpI4 00412168 . >MOV EBX, DWORD PTR DS:[<&MSVBVM60.__vbaStrCopy>; MSVBVM60.__vbaStrCopy 0041216E . >MOV EDX, 021.00408A68 ; UNICODE “5A827999” … 0041219F . >LEA ECX, DWORD PTR SS:[EBP-A0] 004121A5 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr 004121AB . >MOV EDX, 021.00408A80 ; UNICODE “6ED9EBA1” … 004121DA . >CALL EDI 004121DC . >LEA ECX, DWORD PTR SS:[EBP-A0] 004121E2 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr 004121E8 . >MOV EDX, 021.00408A98 ; UNICODE “8F1BBCDC” … 00412217 . >CALL EDI 00412219 . >LEA ECX, DWORD PTR SS:[EBP-A0] 0041221F . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr 00412225 . >MOV EDX, 021.00408AB0 ; UNICODE “CA62C1D6” … 00412256 . >LEA ECX, DWORD PTR SS:[EBP-A0] 0041225C . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr 00412262 . >MOV EDX, 021.00408AC8 ; UNICODE “67452301” 00412267 . >LEA ECX, DWORD PTR SS:[EBP-A0] … 00412290 . >LEA ECX, DWORD PTR SS:[EBP-A0] 00412296 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr 0041229C . >MOV EDX, 021.00408AE0 ; UNICODE “EFCDAB89” 004122A1 . >LEA ECX, DWORD PTR SS:[EBP-A0] … 004122CB . >CALL EDI 004122CD . >LEA ECX, DWORD PTR SS:[EBP-A0] 004122D3 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr 004122D9 . >MOV EDX, 021.00408AF8 ; UNICODE “98BADCFE” 004122DE . >LEA ECX, DWORD PTR SS:[EBP-A0] … 0041230A . >LEA ECX, DWORD PTR SS:[EBP-A0] 00412310 . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr 00412316 . >MOV EDX, 021.00408B10 ; UNICODE “10325476” 0041231B . >LEA ECX, DWORD PTR SS:[EBP-A0] … 0041234A . >CALL DWORD PTR DS:[<&MSVBVM60.__vbaFreeStr>] ; MSVBVM60.__vbaFreeStr 00412350 . >MOV EDX, 021.00408B28 ; UNICODE “C3D2E1F0”

本人密码学知识不懂,前几个不认识,但最后5名相貌奇特的数字碰巧认识.
从这几个常数基本断定它就是SHA160,最后我自己用计算器算了一下,确定这个HASH就是SHA160
怪不得KANAL不认识它们,这VB把它们都当字怎么符串来存储的。可怜KANAL一世英名,也顺便BS一下比尔大门的VB。
6.完
——————-8<————————————————————-
【经验总结】 文章写完了,呵呵,太长了,写了三个小时,不知道你把它看完用了多长时间,总之谢谢你把它看完。
PS1.实际我第一次分析这个CrackMe时,大约用了20分钟完成前一部分,但脑筋一直没有转过弯来,转了将近一个小时才 转过来,后面的分析虽然文章写得很简单,实际花的时间比前面还长,也用了快一个小时,主要是当时没看到对那个hash 函数的call,误跟进去了,结果在里面转了好几圈,实在晕得受不了,就按个了个Ctrl+F9,没想到一眼看到EAX,才恍然大悟。
PS2.另外,由于“期末考试突击时期”到了,本人可能在论坛出现的机会很少了,所以很耐心地写完了这篇文章,如果 你看了后 能从中得到些许帮助,那我的目的就达到了。
PS3.还是买不起。
PS4.sessiondiy兄如愿地“抢”到了第8名的好位置。也顺便对大家说一声:恭喜发财!
——————-8<————————————————————- 【

版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2008年12月29日 16:36:55

朱令—孙维事件 铊中毒 触目惊心

说不出的感觉。

1973年11月,朱令(随母姓)出生,家里有一姐姐吴今(随父姓)。

1987年9月,朱令的姐姐吴今进入北大生物系学习。

1989年4月,吴今与同学周末去野山坡春游失踪,三天后在一个悬崖下面找到了尸体。

1994年12月5日,朱令首次因不明原因发病,腹、腰四肢关节痛。在北京同仁医院治疗近一个月。病因无法确诊,头发全部掉光后病情好转出院。

急性铊中毒的患者开始有恶心、呕吐、腹泻和胃肠道出血等症状,随后有胸痛、呼吸困难、震颤、多发性神经炎和精神障碍等。慢性铊中毒患者的症状主要是食欲减退、头晕、头痛、全身乏力、消瘦、下肢麻木和疼痛、视力减退等。但最典型的症状是脱发,可出现斑秃,也可在短期内全秃。铊经口进入人体后,潜伏期长短与剂量大小有关,一般约在12~24小时,甚至长达48小时。—数据来源:网上回帖和google。

1994年12月5日是星期一。

1994年12月11日,清华民乐队在北京文艺厅专场演出纪念一二九。朱令参加大多数节目,并且古琴独奏“广陵散”。

孙维是朱令同班同学,也是乐队成员,一二九演出孙维是朱令的替补。—数据来源:贝至城的公安朋友和民乐队朋友告知贝至城。

熟悉清华民乐队的成员对此说表示怀疑,认为一来二人练习的乐器不同(孙维练习中阮,朱令练习古琴),二来孙维的音乐水平无法充当朱令的替补。—数据来源:天涯回帖。

1995年2月,开学一周后,朱令再次因不明原因发病,双脚疼痛难忍、双手麻木,再次脱发。

急性铊中毒的患者开始有恶心、呕吐、腹泻和胃肠道出血等症状,随后有胸痛、呼吸困难、震颤、多发性神经炎和精神障碍等。慢性铊中毒患者的症状主要是食欲减退、头晕、头痛、全身乏力、消瘦、下肢麻木和疼痛、视力减退等。但最典型的症状是脱发,可出现斑秃,也可在短期内全秃。铊经口进入人体后,潜伏期长短与剂量大小有关,一般约在12~24小时,甚至长达48小时。—数据来源:网上回帖和google。

1995年3月9日,朱令前往北京市协和医院神经内科专家门诊就医,神内主任李舜伟教授接诊后,经初诊“高度怀疑铊中毒”,并请中国预防医学科学院劳动与卫生职业病研究所的张寿林大夫会诊,张提出可能是“铊中毒”或“砷中毒”,但因该所当时不能做检测,张建议到朝阳医院做检测,但没去。朱令就在协和医院急诊室一边观察一边等待住院床位。

1995年3月15日,朱令住院。协和医院曾怀疑她犯中毒性疾病的可能。朱令否认自己有过重金属接触史。而且临床病程表现不像,所以一再排除。协和医院有过多种诊断考虑,主要考虑为‘急性波散性脑脊髓神经根神经炎’。

1995年3月20日,朱令病历显示丙肝抗体正常。

1995年3月22日,朱令吃东西开始呛,医院对她做为了抢救和维持生命必须的气管切开术,手术中她产生昏迷。

1995年3月24日,协和医院对朱令开始的血浆置换疗法尽管对在确诊不得的情况下维持她的生命起了相当的作用,前后8次,每次均在1000CC以上的换血量却使她感染了丙肝。

1995年3月25日,朱令出现吸氧不稳定的情况。

1995年3月26日,朱令被收入ICU,靠呼吸机生存。

1995年3月28日,朱令进入长达两个多月的深度昏迷。

1995年4月5日,朱令事件引起社会关注。

1995年4月8日,朱令中学同学贝至城和5名中学同学去医院看望朱令。事后希望能找到方法帮助朱令。

贝至城和朱令是中学同学,初三同班,当时关系不错。但之后朱令姐姐意外身故后,朱令性格比较孤僻了,打交道就少了。大学后完全没见过面,所以朱令第一次中毒也没有去看望过。第二次朱令大概05年3月中中毒,也是到4月有同学告知说朱令可能不行了,去见最后一面吧,才去探访的。—数据来源:天涯ID“花沐兰”,也是贝至城借用发贴的ID。

1995年4月10日,贝至城在互联网上发贴,描述朱令病情,希望得到专家意见以确定病因。

1995年4月下旬,贝至城和同学希望朱令班级一起帮忙翻译收到的电子邮件。双方产生误会。朱令方团支书组织班级里同学将邮件翻译好后没有交回给贝方,而是经由系里老师转给协和。

1995年4月18日,贝至城到协和ICU病区门口等大夫传送翻译好的电子邮件,希望能够被采纳,从早上8点等到下午5点,除了少数愿意看但是不起作用的年轻大夫外,其他人谁也不看。

1995年4月20日,收到的回应主要指向铊中毒。

1995年4月28日,协和开始用普鲁士蓝化学剂排毒,一个月后朱令体内的铊含量基本排除,中毒的症状消失,然而严重的后遗症却将和她相伴终生。      1995年4月底,协和认为朱令是二次中毒。公安部门介入调查。

1995年下半年,朱令的同学,熟人,和朋友被广泛的调查,其中包括孙维和贝至城。

1995年下半年,警方从孙维床下孙维的箱子里搜出了一个彻底洗干净的朱令的咖啡杯。孙维说在朱令住院期间,怕杯子落灰洗干净了收藏好。—数据来源:贝至城听朱令的母亲说,朱令的母亲听市公安局的一位离休干部说。

警方公开搜查朱令的宿舍,带走朱令东西的时候没有出现发现咖啡杯的事情。—数据来源:孙维和朱令地同宿舍同学,当时在场,并且和警方合作指出朱令的东西。
1995年下半年,清华有谣言说朱令之所以中毒是因为朱令的父亲走私铊,不小心沾染的。谣言的起源最终查明是孙维。—数据来源:贝至城听大学同学的清华女朋友说。    从来没有听到过这个谣言。—数据来源:孙维和朱令的大学同学。

1995年底,孙维被列为唯一嫌疑人。    警方调查时,清华的校方先是矢口否认有任何学生可以接触到铊的来源。后来承认孙维是唯一能接触铊的学生。—数据来源:贝至城的公安朋友告知贝至城。孙维1997年从警方直接得知。

1995年底以前,贝至城得知孙维是唯一嫌疑人。—数据来源:贝至城的公安朋友告知贝至城。

“怀疑孙维并不是我的臆断,1995年之前,我根本不知道孙维是谁。朱令铊中毒距现在已经11年了,警方透露给朱令家属的唯一嫌疑人,就是孙维。并不是我以及朱令家属怀疑孙维,警方才开始调查孙维,而是警方长时间地调查孙维,我们才知道了孙维是这个案件的嫌疑人。”—数据来源:贝至城
1995年12月9日,孙维的爷爷孙越崎去世,享年103岁。孙越崎曾任民革副主席。

孙越崎去世前,江总书记探访,孙拉着江总书记的手求情要求把孙维放出来。—数据来源:贝至城的公安朋友告知贝至城。

北京市公安局长大发雷霆,说“放他妈什么放,打死了装在麻袋里放出去”—数据来源:贝至城听朱令的父母说,朱令的父母听一位已经过世的老公安说

孙维从来没有被警察关押过,根本谈不上求情放出来这一说法,可以随时去公安查证。—数据来源:孙维。

1996年,警方没有传讯任何人。

1996年12月,朱令的父亲朱明新决定起诉协和医疗事故。

1997年3月,朱令家人致信北京市公安局长,指出朱令的同学即将毕业离校,其中很多人将出国留学,此案急需抓紧侦破。不久又上书国家领导人。

1997年4月2日,孙维被公安局14处作为朱令投毒案唯一嫌疑人带走讯问,持续8小时,然后放回。此后再也没找过孙维。孙维在这次讯问中得知清华出具材料声称孙维是唯一能接触到铊的学生,而且实验室的管理非常严格。

这是孙维被公安局讯问的唯一一次,可以查证。—数据来源:孙维

孙维胡说,在95年的时候孙维就被调查过,1995年底之前贝至城就知道怀疑是孙维。—数据来源:贝至城。

1997年4月,孙维方写信至清华派出所了解情况。之后给系办、系领导、校办、校保卫处、校派出所、公安14处等相关部门多次去信去电面谈。

1997年4月,孙维哥哥多次独自一人在白天工作时间进出清华化学系实验楼,取出有毒试剂,带出实验楼并全程录像。每次都无人过问。

1997年4月11日,朱令舅舅给孙维父亲打电话要求私下谈谈,声称手里有不利于孙维的证据。孙父拒绝,并将详细通话记录以文字形式递交了公安机关。      1997年4月26日,孙维收到朱令舅舅寄到宿舍的恐吓信件,明指孙维为凶手,并且提到了黑社会。相似的信件也发给了班里其他一些同学,要求同学们配合朱家提供有关孙维是凶手的证据。

1997年4月29日,孙维方因收到恐吓信去清华派出所报案,同时提出对孙维测谎的要求,没有收到答复。

1997年5月5日,孙维找到清华党委办公室,把孙维哥哥拍摄的录像放映,要求学校向公安反映真实情况,说明孙维不是唯一能够接触到铊的学生。

1997年5月6日,清华的实验室大整改,要求师生停下工作,把所有药品严格分类管理,有毒试剂上锁,并由保卫处进行了拍摄。

1997年5月9日,孙维收到朱令舅舅寄到宿舍的第二封恐吓信。

1997年6月30日,清华大学92级毕业典礼。之前,化学系领导通知孙维,由于孙维被公安调查,学校通过官方渠道接到公安通知缓发孙维毕业证书和学位证书。孙维方当即去公安局14处了解情况,14处否认发出此通知,表示警方只管破案,学籍管理是学校自己的事儿,和公安没关系,公安局从来没有,也不可能向学校发这样通知的。      1997年7月18日,孙维方把孙维哥哥拍摄的录像带和查到的其他师生使用铊的文献交给公安。

1997年7月28日,国家教委办公厅发布了“关于加强学校实验室化学危险品管理工作的通知”(教备厅[1997]13号),指出:“1995年5月,1997年5月,清华大学、北京大学先后发生了两起学生铊盐中毒案件。除涉嫌人为作案外,铊盐未按剧毒品管理是其重要原因。”

1997年7,8月,孙维方给清华党委领导写信,要求学校将缓发毕业证书的决定尽快以书面形式通知孙维方并加盖公章。经多次交涉,学校坚持不给书面通知。      1997年8月下旬,在孙维方要求下,清华校党委、校办及系领导等再次在校招待所(丙所)接待孙维及其家人,谈话不欢而散。之后,孙维方又给党委领导打了两次电话,坚持要求学校如不发证书就应该给一份不发证书的书面通知。

1997年9月29日,清华大学化学系领导打电话给孙维方通知孙维第二天去学校领证书。

1997年10月,北京市医疗事故鉴定中心作出鉴定,认为协和医院在朱令案中没有过失,不属于医疗事故。

1998年8月26日,公安14处宣布解除对孙维的嫌疑,承认没有任何证据证明孙维和朱令中毒有关。

1999年4月2日,朱明新起诉协和败诉。

1999年12月,朱明新委托浩天律师事务所向北京市第二中级人民法院提出再次对朱令作出鉴定的申请。

2000年6月,鉴定基本否认协和医院曾对朱令铊中毒四处寻求检测机构的努力。从而认为:“(协和医院)该不作为的行为导致被鉴定人朱令病情被诊断的延误,因此,北京协和医院在本次医疗行为上存在一定的不当之处。”

2000年11月26日,北京市第二中级人民法院终审判决协和医院补偿朱令医疗等损失10万元。

2002年,贝至城发表文章“朱令案件的一些情况”,明确表示怀疑孙维为凶手。

2002年至2005年,朱令事件每年都在网上流传,至少mitbbs几乎每年一次,其中孙维祖父拉最高领导人求情说和公安局长的麻袋说广为传播,每次的传言都指明了孙维是凶手。

2005年11月30日,skyoneline在天涯贴出“天妒红颜:十年前的清华女生被毒事件”。不知道是原创还是转载。此前我在别的地方看过该文。

 

看法

关于朱令中毒事件,我是今年11月份看到的,最初不是从天涯网站,而是我同事告知于我的。我同事也是清华的毕业生,而且非常爱自己的母校,所以总是很关心关于自己母校的任何新闻。当然,这个不算是新闻,但是绝对是震惊世人的。为什么会有这么多人关注这个问题,首先清华是中国最知名的大学,在中国人心中有非常高的地位,有关于清华学生的事件,毫无疑问会引起更多的关注;其次因为是铊中毒,而铊是一种重金属,很多人根本不会了解,除开学化学的,我想很少人会了解这个元素,因此似乎铊中毒本身就比其他常见毒药要更让人和有高等文化的人挂上勾;当然最让人疑惑和揣测的是这个事件本身没有一个结果。为什么会没有结果?是不是因为案情太复杂,公安部门无法破案?还是因为嫌疑犯强大的背景阻止了侦查的继续?还是根本就是当成一次事故处理了?种种疑问都环绕于这件事情。

应该说,中毒事件必定是人为的。为什么不可能是因为朱令自己因为失误接触了铊,或者食用了或者饮用了受过铊污染的食物或水?朱令不可能有接触铊的机会,这个应该是很容易证实的,因为铊这个东西不像铝铁那样到处都是,而且若不是实验,根本不会有人去用。那么会不会是朱令吃了什么受过铊污染的东西?有些食品厂缺乏道德,可能会用作保鲜剂?这个我想假如没有大范围人群的中毒,是可以排除的。而且朱令两次中毒,更加排除了这个可能。

言规正传。我看了孙维的声明,毫无遗漏。那么是不是代表着孙维是无辜的?是不是代表其实很多人其实一直冤枉了孙维?是不是一直认为是罪魁祸首的人其实是完完全全清白的?当然这个只有孙维心里才明白。我们所有的想法都只能是猜测。

我不是警察,很多东西只能是通过一种自我认为合理的推理,这种推理往往是局限的,没有任何根据的,仅仅是依靠自己的判断。我自己首先得知的信息是对孙维的不利的,这种不利首先占据了我的头脑,一直以来我都认为孙维是投毒者。但是任何网上的言论仅仅是传言,没有任何法律效力,也无从考证的。要证实一切,必须经过多方面的调查取证,这个是警察的事情。

我对孙维的有罪推论,仅仅代表我个人的看法,我不负法律责任,也声明仅仅是个人的主观推论,不要对事情本身带来什么误导。

 

一, 投毒者必定是朱令身边的朋友

为什么我说必定是朱令的朋友?原因是多方面的。第一,只有熟悉朱令的人才能下手。和朱令不接近的人无法投毒,根本没有机会。我们先以朱令是喝了带有铊的水来推论。有人可能要举例说大学宿舍的同学其实是可以串门的。这个自然,但是绝对不可能说串门过去把毒投到朱令的杯子,然后朱令又恰好能够喝下去。事实不难想象,是有人把投好毒的东西给朱令,朱令又毫无顾忌的喝了下去。要想给一个人下毒是非常需要计划性的事情。不是你下毒就一定能够成功,你必须非常了解对方的饮食习惯,非常能够接近对方,而且不能出现这种情况:下毒没有毒倒对方,反而毒倒了其他的人。对朱令的两次中毒,那么可见投毒者的目标很明确,就是朱令。那么朱令身边的人就都有嫌疑了。孙维当然也有。

 

二, 关于孙维说自己在97年唯一的一次被讯问

如果说公安机关早就以投毒对朱令案定性。那么在97年才开始调查很明显不符合逻辑。孙维是朱令的好朋友。为什么以前公安机关没有着手调查?难道这个嫌疑犯是在97年才被警察注意?决不可能。事情本身来说并不会很复杂,只要警察想查,就一定能够查个水落石出。大学生相对犯罪分子来说是稚嫩的,这个是符合常理的。因为大学生其实还是很单纯的一类人,可能思考问题会更加慎密但是绝对达不到一个犯罪老手的程度,因为他们接触太多的书本而不是社会。为什么当时警察没有调查孙维?是不是当时还有更可疑的疑犯?还是根本就没有当场投毒案?如果公安机关以投毒来立案,而孙维又很明显可疑,那么没有调查必定是有原因的。这个应该牵扯到了孙维的家族背景。当然这个不能断定,但是这一点是绝对值得思考的地方。

 

三, 孙维证明自己不是学校唯一能接触铊的学生

这个自然,我也不认为孙维是学校唯一能接触铊的学生。读过大学的人都知道,大学的实验室管理是不会特别严格的,校内学生只要有心进实验室就一定可以做到。

但是孙维以这个来作为自己不是投毒者的依据就显得有点太下功夫了。据她说,她哥哥曾经独自一人到清华实验室拿出了铊并且录了相。这个恰好是孙维值得怀疑的地方。为什么?因为第一孙维忽略了接触铊和了解铊的性质,接触铊和使用铊是完全没有任何联系的。孙维花了很大心思证明自己不是唯一接触铊的学生,这一点是一个无辜者不会使用的手段。我想无辜的人绝对不会在这个地方做文章。为什么?因为铊完全有可能是从其他途径得到的。网上不是说耗子药里面也有铊么?其他途径完全也可能得到铊,只要我想得到铊,那么我也一定有办法。因此孙维从这一点出发证明自己的清白是没有说服力的。何况知道铊的性质,了解铊中毒的隐秘性不是普通人明白的。孙维在声明中还说后来清华大学实验室在孙维证明学校的实验室管理松散以后马上采取了整顿措施,容易讲我们引入一个误区:清华为了推卸责任,让孙维做了挡箭牌。这一部分孙维做了浓重的描写,还将国家教委颁发的通知详细说明。我不信清华会为此推脱责任,我相信清华的领导是负责任的。不会如此处理。投毒事件本身属于犯罪,清华本身无需负太多责任。实验室的管理只要清华制定的有制度,那么清华就没有错误。为什么这么说,因为犯罪是无法事先预知的。清华作为一个强势的团体,更没有必要担心为此事件损害到学校或者学校领导。清华本身不需要洗脱什么,说得透彻一点,清华死了一个人,清华本身并不会收到很大影响。

 

四, 学校扣发孙维学位证

这个是孙维事件最重要的一点。为什么清华会扣留孙维的证书?是不是真的像孙维说的:当时接待我们的一位校党委领导还说“在朱令中毒的案件中,清华经过多次反思,认为校方没有任何责任。” 校党委领导竟然说:“现在有两条路让你选择:要么要学校承认错误,要么解决你的问题。”又说,“你想让清华认错,是绝对不可能的!”谈话不欢而散。

我想清华作出扣留证书的决定一定有内在的道理,如果孙维的嫌疑解除了,那么学校就没有任何理由扣留孙维的证书。作为一个学校,而且是中国最杰出的学校,它必定具有一种魅力叫做责任。学校如果说出孙维说的那种话,那么我只能说这个领导实在是没有水平。孙维只是学校的一个学生,学校党委领导不可能对孙维说“在朱令中毒的案件中,清华经过多次反思,认为校方没有任何责任”,更不会说“你想让清华认错,是绝对不可能的!”这两句话看似平常其实包含了两个内涵:第一,学校领导不是在和他们的一个学生谈话,而是在和一个平等的人和一个势均力敌的对手面对面的交手,这个可能性很小,即使孙维家族背景很强,作为她本身也只是清华的一个学生。她自己本身对清华来说并不是一个平等对话的个体。如果真如孙维所说,那么这个领导毫无疑问是无能的。第二,学校既然以被公安机关调查为名扣压孙维的学位证书,那么就一定不会和公安说根本没听说过孙维学籍的事矛盾,甚至公安表示:“警方只管破案,学籍管理是学校自己的事儿,和公安没关系,公安局从来没有,也不可能向学校发这样通知的,如果真打过电话,一定会有记录的,但是我们没查到任何记录。”清华扣留学生的证书必定会有理由,而且这个理由是可以堂而皇之符合一切规定的,不可能是如此肤浅经不起证明的。清华不会如此的没有水平。

 

结论:

1,清华扣留孙维的证书必定有深层次的原因。

2,孙维说的对话对清华不利,其实这种不利是不能够推敲的。

3,清华后来又给了孙维证书说明清华受到了某些方面的压力,是不是反证了孙维家族势力?

来源地址:http://blog.xiaonei.com/GetEntry.do?id=393479464&owner=257500625

来源: 陈应骏的日志

——————————————————————————————

(comments)

为了获取孙维邻居信任,05年底06年初他在天涯率先注册了揭露真相博客,以此作为礼物通过我的女朋友送给孙维邻居,表达他希望借助网络的力量对朱令事件大肆炒作的建议。可惜我们这位正义的朋友并不是一个笨蛋,支援朱令的目的也不是恶意炒作出名和牟利,在了解到王雪峰赠送天涯揭露真相博客另有目的之后,孙维邻居没有和他继续联络,同时为了避免王雪峰的别有用心借朱令事件谋取私利,我们的朋友甚至在很久以来不再使用揭露真相的天涯博客。
回想05年底06年初,王雪峰赠送揭露真相博客的事情,我真的很佩服孙维邻居的眼力。王雪峰果然就是一个心怀不轨的卑鄙小人,当时以正义为名的声援,无非是蓄意利用朱令案件炒作获利,现在想想如果当初我的女朋友没有听好朋友的话而把孙维邻居真实的身份透露给了王雪峰,那么这个人渣可能做出更加无耻的勾当。
王雪峰很清楚帮助我们的孙维邻居是他的心腹大患,只要他帮助我们一天,只要他不停止调查,那么王雪峰这个骗子将迟早有一天走投无路。于是,在最近一个多月里面他一直在威胁恐吓我和我的女朋友,通过我们转发给孙维邻居恐吓邮件,同时,以自己已经偷取了当初赠送的揭露真相博客密码相威胁,逼迫我们删除网络上揭露他恶行的文章。王雪峰一直警告我们,他会让孙维邻居的真实身份暴光,他会逼迫孙维邻居现身。
当然,面对他的无耻威胁,我们选择了勇敢面对。这就有了前一段,王雪峰冒名揭露真相名义去天涯博客发贴的事件。面对王雪峰冒名揭露真相,我们很快作出了应对,在第一时间揭穿了他妄图恐吓敲诈勒索的阴谋。很快,那些误以为他是真孙维邻居的朱令支持者不再和他联系,不再相信他的谎言。看着自己酝酿已久的阴谋没有得逞,而网络上又出现了揭露王雪峰向朱令伸出黑手的阴谋报道,王雪峰在惶恐之余,又想出了另外的计策报复我们。
王雪峰确实有些聪明的过头了,他虽然不知道孙维邻居是谁,但是他知道自己的侄女是谁,他也知道我蒋建伟是谁。好一个狸猫换太子的计策!网络暴光我女朋友的真实身份,同时欲盖弥彰的说她就是孙维邻居,一方面想借孙维支援者的手,妄图迫害我女朋友,骚扰她的正常工作和生活,另一方面,他希望我女朋友承受不住压力出来说明不是真正孙维邻居的真相,暴光自己朋友的真实身份,或者他更希望孙维邻居不忍看到自己的好朋友为他承受压力而主动的站出来。王雪峰一箭双雕、借刀杀人的计策真的是够歹毒、够卑鄙!
面对网络上王雪峰漏洞百出、条理不清、前后矛盾的暴光文章,我们感觉他真的卑鄙的可恶、愚蠢的可笑!他真的以为网络上狸猫换太子就能让我们怕了他,就能逼着我们出卖一直帮助我们的朋友。孙维的邻居是谁?孙维的邻居在哪里?需要正义的地方就有他的声音,需要帮助的人就会听到他的名字!但是,你王雪峰这样的卑鄙小人不配叫他站出来!你没有这个资格,你也不会有这个机会!
王雪峰,你可以继续的玩狸猫换太子的游戏,你可以继续告诉大家我女朋友更多真实的信息,我们承认我们就是孙维邻居的好朋友,我们承认我们支持朱令、鄙视那个真凶,也鄙视你!我的女朋友绝对不会否认她自己不是孙维的邻居,因为她一样来自于北京的部长楼!我们同样不会否认我们为正义做过的每一件事情!同时,我们还要告诉你,我们会继续讨伐你,继续揭露你,继续让更多的人了解你的为人,继续让你王雪峰的恶名在网络和现实里面流传,我们不会放弃对你的追踪,直到你还钱!直到你给被你欺骗的亲朋好友一个交待!
谣言止于智者,王雪峰!请继续在博客论坛造谣,请继续用你的败笔写可笑的烂故事,但是无论你怎么诽谤,你欠债不还是事实!你利用他人隐私谋取私利是事实!你在朱令事件中扮演了一个妄图浑水摸鱼的丑陋角色也是事实!无论你说了什么,无论你的谣言对我们造成了什么伤害,我们都不会对你低头,我们都不会因为你的阴谋而泄漏朋友的任何真实信息让你有报复的机会!也许你还不知道,孙维邻居,他不是你想象中的某个人!他是代表正义的符号,他是一种揭露真相的精神!
我们会一直保护我们的朋友,我们会一直揭露你卑鄙的骗子行为,宁波将不再是你骗人钱财的城市!世界的每个角落都不再给你施展阴谋的舞台!既然你的造谣诽谤已经登峰造极,那么我们相信你离原形毕露也不远了。
上帝始其灭亡,必先始其疯狂!王雪峰,你利用朱令事件敲诈勒索的阴谋不会得逞,你利用朱令事件想转移视线抵赖债务的阴谋也不会得逞!无数的眼睛在看着你,看着你怎么造谣诽谤,看着你怎么做一个跳梁小丑,看着你这个人渣还能折腾多久!
出来混,迟早要还的!这句话送给害朱令的真凶,也送给你这个卑鄙人渣!
王雪峰背景:
男,1966年2月12日出生,宁波慈城人,现无业,暂寄居宁波。
早年在慈城以修表谋生,后去广东打工。曾经就职于台湾天心软件公司,后因职业道德败坏等原因被迫离职,离职之后投靠珠海万佳达公司,又因品性败坏、招摇撞骗被拒之门外。
在珠海声名狼藉,后回宁波发展,以假名王泽注册宁波讯达利咨询公司,借珠海公司名义出卖盗版ERP软件,在宁波期间招摇撞骗毫无结果,从而骗取亲朋钱财。王雪峰还以江湖郎中名义无照行医,通过给他人看病获取病人隐私同时加以利用威胁。
06年王雪峰曾在杭州靠假简历骗取杭州简略科技公司总经理职位,但终因债主追踪和滥竽充数被识破而离开杭州。
回到宁波之后,王雪峰借宁波中华纸业副总董凤希女士名义四处以包揽中华纸业集团大量采购订单为名行骗,同时,王雪峰虚构进口煤炭等生意意图谋取私利。此人目前经常以宁波老外滩怡和茶馆等茶楼为行骗场所。
伪装大款道具:菲罗伦斯西服、国外进口烟斗和烟草、企业采购订单目录等
行骗方式:谎称政府背景雄厚、企业人脉广阔,中医水平绝世,有祖传秘方。
通常假借以下人士的名义行骗:
宁波中华纸业常务副总董凤希女士、宁波江北区城管局局长张伟、宁波工行某副行长郑伟民等。 —————————–
那些同朱令案件有关吗?

请把掌声给前8所后面的学校

每年高考成绩揭晓时,大家的眼光都会情不自禁的集中在前几所学校的上线情况,而这些学校也会比较配合的在学校的网页上打出今年一本上线比例多少多少。大家在感叹于这些学校一本率的同时也忽视了后面一直在默默努力、年年创新高的学校。

以前 我也和大家一样,一直在关注前八所甚至前三所的学校每年的高考情况。也希望有朝一日自己的孩子能考进前三所,最差最差绝对不能跌出前八所。因为后面的学校 真的是奇差无比,这些学校的老师要水平没水平,要敬业精神没敬业精神。多少年来,这样的思想一直紧紧的驻扎在我们的内心深处。但是近年来,随着对后面学校 的逐步了解,发觉自己甚至整个社会都对后面的学校存有太多的偏见了。虽然每年这些学校一本的上线率不高,也没有学生考上清华北大,也没有学生考进浙江省前 10名、前100名。让我们丝毫感觉不到一种带着刺激的快感。我想说的是,你是否看到了他们的增量。三年前,全市最优秀的学生去了二中、学军、杭高、十四 中……,不太好的学生去了杭9中、十一中、七中这些学校。前者这些学校比后这些学校的学生中考成绩都要高好几十分、高百分之十几。但三年后,后面的学校有相当一部分学生上了一本,按理中考成绩来说,前面这些学校的学生都应该全部考上一本,但事实呢,连我们最最优秀的二中也做不到100%,我们不是说二中不够优秀,二中的确非常优秀,但三年里小孩子的变数太多了,这种变数跟很多因素有关系。其中的一个因素,就是老师的关注程度吧。

以J中为例,考进去的学生应该来说是中考成绩都比较差的学生了。但是通过三年的努力,同样会有非常多的同学考上一本。之前对J中这个学校的认识就是,这是一个比较差的学校,中考填志 愿可以最后一个填J中,保底。同时也带着一副有色眼镜看待J中的老师,J中老师老师教学水平差、解题能力弱。但随着跟朋友(J中老师)的交流,慢慢的使我 对后面的这学校有了新的认识。这些老师其实是非常的低调(估计这也是没办法的低调),同时也是非常的认真、对学生的关注程度很高。现在你去打开杭J中的网 站你会看到这样一则喜报,说今年杭J中有一个班级有学生考上了浙大、武汉大学……这些名校。我第一眼看到这个喜报的反应就是,“太假了!实在是太假了!J中怎么可能有学生考上浙大和武汉大学呢”。你要知道考上浙大是非常不容易的一件事情。

我在读高一高二的时候,浙大一直是我的dream大学,但到了高三, 放弃了,浙大实在太遥远了。而且这几年浙大本科生减招,更加难考。所以即使二中学军每年也只有不多的学生能进浙大,十四中、四中、浙大附中这些学校还不一 定每年都有吧。多方打听,今年杭J中考上浙大学生并不是一开始所想的打的是擦边球,什么体育生考上浙大、艺术生考上浙大,来作为宣传的一个噱头。而是实打 实的参加高考考上的。真的很不容易。

我想,二中培养一个浙大的学生跟杭J中培养一个浙大的学生,这当中体现的价值是完全不一样的。就好比一个出生企业家族 的人,年薪100万,而另一人出生农村,通过自己的努力考上大学,再通过自己的努力创业,最后年薪也有100万。对于这两个人我想我们应该把掌声更多的给予后者。

以前曾经听说过这样的的一个说法,中考能考进2中的学生,即使你三年不给他上任何一节课,三年后,同样也有非常多的学生考上名校。说的就是这些学 生的自学能力超强。所以,中考成绩实际上是对学生自学能力的一种体现。我相信,在对一个自学能力比较差的学生,通过三年不断的培养,使他考上本科或者一本 或者985、211这些名校,这样的老师更值得我们尊敬。因为他们给我们创造了一个奇迹,把一个学生的潜力最大化的挖掘了出来。

再看看这几年杭州中考志愿的填报。因为很多家长对学校的不了解,最后就是看哪个学校在广告宣传上做的好,哪个学校能通过各种关系人脉深入到初中去挖生源。最后导致的一个结果就是真正在做事、但不太重视去广告宣传的学校,填报的人越来越少,分数线也越来越低。那些到处在打广告(甚至带有吹嘘成分)的学校,填报 的人越来越多,每年的分数线也是节节高。我认为这已经到了一种病态的程度了。或者说我们已经无奈了。学校的招生也有了很浓的商业味。看看这几年杭七中的分 数线,就是个很好的例子。杭七中是一所很不错的学校,但却不太重视宣传,所以这几年的分数线越来越低,以至于今年在第一批中考招生中,杭七中分数最低。我 想七中校长当时看到这个分数的时候,想死的心都有。这方面,大学招生似乎要更加好点。但那种什么职业技术学院也在千方百计的抢生源。前段时间说有个高中的好几个学生的志愿都被改成同一个技术学院了。

最后,我想说的是,在我们关注前几所学校的同时,也要关注后面的学校,更多的应该关注这些学校三年来给学生创造的增量,这种增量正是我们所需要的。甚至,我在做这样一个大胆的设想。以二中和J中为例,把二中的一个班放到J中,让J中的老师教,把J中的一个班放到二中,让二中的老师教。我们看看一年后或者三年 后,会出现什么样的情况,其实这是我非常想知道的一个结果,但估计我永远也不会看到这样的情况。

张小龙谈移动互联网产品

2012年7月24日下午14:20—23:30,我应邀参加了《腾讯大讲堂》的特别讲座,由微信之父张小龙主讲《微信背后的产品观》。张小龙 的讲演长达8小时20分,是我此生参加过的最漫长讲座。腾讯为此开设17个分会场,同步直播讲座,参加者超过1700人。晚上19:10分听众晚 餐,20:30继续进行。恢复演讲时在线直播用户依然有600余人,加上分会场听众,总计有超过800人听完全程讲演。很多人并不知道,这可能是张小龙第 一次也是最后一次做公开讲演,在公众面前讨论产品。在这次演讲中,他把自己15年来关于产品的所有经验和心得一次性全部公开,今后将不再做类似公开课。

这一讲座受到腾讯保密协定约束,因此其中令人惊叹的新产品特性演示不可以对外公开。同时,腾讯大讲堂举办的这一公开课的目标群体是腾讯两万余员工中 的产品经理,其中涉及大量腾讯公司业务也不宜对外公开。因此,作为唯一的受邀外界自媒体,我只能根据授权部分内容进行阐述。因此,腾讯相关人士请先阅读下 一段落的内容:

作为腾讯前员工,我清楚地知道这一课程的全部视频和PPT资料都储存于腾讯KM平台,请务必在阅读本文前完整浏览一次8小时20分的视频资料。张小 龙关于移动互联网产品的全部思考和分析,都在这8小时20分的视频之内。其中,所有内容都来自张小龙业余时间用手机记录下来的产品心法300余条。经过精 炼和分类之后,形成了长达180余页的PPT文档。这是最原始,也是最真实的素材,完整记录了一个产品经理的个人思考和感悟。因此,可以很容易地发现前后 文逻辑背反抵触之处,很容易地看到一个真实的人内心深处的挣扎和纠结。它很清晰地给予以下启示:

1、 一个亿级用户的产品经理,无需做到透彻思考人性和产品的所有方面,得出定性结论,但依然可以做出成功的产品。

2、 一个成功的产品经理,需要在极端现实主义和极端理想主义之间取得平衡,把它们作为整体一并接受下来,彻底去除其中的相对性,丝毫不会觉得其中的矛盾和冲突之处。

因此,我的体系化阐释和原文相比,完全落到了下乘。阐释只是为了便于受众接受和理解,但是永远无法和原文素材相提并论。因为实际操作的产品经理在实 践中,并未受到任何体系化阐释的指导和影响。相反的,这种阐释很可能成为个人理解的障碍。以下内容和张小龙的PPT内容并非一一对应,完全基于我个人对张 小龙的认知和理解衍生而成。请在完整浏览视频资料之后,再行继续阅读本文:

一、微信的演化

2010年11月19日23时58分,张小龙在腾讯微博上写下了这么一句话:

我对iPhone5的唯一期待是,像iPad (3G)一样,不支持电话功能。这样,我少了电话费,但你可以用kik跟我短信,用google voice跟我通话,用facetime跟我视频。

(http://t.qq.com/p/t/2509031165724)

第二天,2010年11月20日,腾讯微信正式立项。一年零8个月之后的2012年7月,随着微信4.2版本的发布,可以看到那一晚张小龙关于 iPhone5的所有狂想都在微信上得以实现。而就在此时此刻,张小龙都未必意识到一个简单的念头会在一年多之后如此深切地影响到一亿多用户;意识到一个 普通用户此时此刻,仅凭一台iTouch和网络就可以完全改变以往有电话相随的生活。

产品经理之美就体现在这里。

微信1.0版的诞生是完全出自纯粹功利主义和现实主义的考虑,立项后2个月,微信1.0版的释出很大程度上是受到了KiK的启示—在新的移动互 联网平台上,必将出现相应的移动通讯工具。KiK是一种免费短信,它很可能成为QQ在移动互联网时代的强大竞争对手。因此,从战略角度分析,腾讯必须开发 一款移动通讯工具,这就是微信诞生的缘起。

尽管开发速度惊人,从立项到产品上线仅仅经过了2个月。但是微信1.0的免费模式在中国并不受欢迎。和欧美不同,中国运营商提供了丰富的套餐服务,正常用户每个月的包月短信根本消费不完。以省短信费为卖点的类KiK产品,在中国完全没有出路。

微信1.2版迅速转向了图片分享。从理性的角度分析,移动互联网时代必然是一个图片为王的时代。人们在有限的载体上没有耐心进行深度阅读,对图片的 消费量会达到一个空前的程度。于是,微信1.2的主体功能变成了图片分享。然而市场对此反应冷淡,数据冷酷地证明了用户对手机图片分享没有兴趣,根本无法 构成一种基本需求。

微信2.0的推出是微信团队在极短时间内的第三次试错。免费短信在利益上毫无吸引力,图片分享市场并不认可,产生内容的门槛远比预计要高。从用户在 手机上输入内容的便利性出发,微信2.0将产品重心完全投入了语音通讯工具。作为一种重要指标,新浪微博每分钟出现一条关于微信的搜索结果,确立了微信 2.0快速流行和传播的基调。

微信3.0是教科书式的经典产品开发节奏教程。当时,微信的竞争对手米聊拥有先发优势。米聊的产品经理甚至预判微信3.0的新功能是抄袭米聊的涂鸦 功能。但是,微信此时已经初步明确了产品方向,没有做涂鸦功能,而是依托用户基础,提供了“查看附近的人”和“视频”功能。“查看附近的人”成为微信的爆 发点,从此微信开始使用QQ邮箱和腾讯自身资源,进行强推广,用户突破2000万人大关,产品日新增用户以数十万量级增长,确立对竞争对手的绝对优势。

当用户超过一亿之后,微信4.0推出“朋友圈”,建立手机上的熟人社交圈,开放API接口打造移动社交平台。微信4.2推出视频通话功能,彻底封闭 手机通讯工具上的任何其它可能。从此,微信确立了移动互联网时代生活方式的产品地位。未来一系列新功能的演进,都将围绕这一核心价值进行。任何试图以节省 电话费、短信费作为竞争卖点的产品,此时已经完全失去了竞争的可能。因为微信提供的已经不再是单纯的通讯服务,而是移动互联网时代的生活方式。除非竞品能 够提供一种更为便利和流行的模式,否则无法构成竞争关系。

二、移动互联网产品的方法论

手机作为一种联系工具,天然决定了移动互联网的主要服务对象是人群而非个人。移动互联网产品经理的主要任务是服务人群,主要工作是研究人类群落的行为模式。用产品为人群提供服务,并且要预判人群卷入之后行为模式的变化。

从方法论的角度思考,人类群落也处于演进过程中。早期人类是村落人群模式,村落中的每个人认识每个人,人际关系以非常实际的亲戚关系和职业关系为纽 带。现代人类是社会化人群模式,人群数目极大增长,人际关系依托于抽象的社会生产关系:契约、合同、雇佣关系。。。。。。产品经理曾经是村落里的铁匠,他 认识每个村民,每个村民也认识他。而在今天,产品经理完全退居幕后,他不可能认识每一个用户,用户甚至不知道这个人的存在。

因此,产品经理在方法论上存在着各种选择:他可以提供某种基于熟人群落的旧式服务,以增进熟人群落的情感为产品目的;他也可以提供某种基于大数人群 的新式基础服务,以便于人们彼此认识,协调工作,从自利为基础的无序活动中自组织处有序活动;或者,他也可以提供某种中间类型的产品,帮助人们平滑度过两 种人群模式,减少由于变化而产生的阵痛。

但是,产品经理的主流应该是服务于未来。先于人众接触新技术新知识,并把这种认知转化为产品,利用产品提供现代人类社会中的各种人群。尽一切可能,降低学习成本,利用人类的本能设计产品,使得人群得以顺利“滑入”新的产品使用场景。

在所有这一切之上,产品经理的目标应该是实现社会美和善和总量。

三、移动互联网产品经理的素养

1、 敏锐感知潮流变化。移动互联网产品会从相对匮乏时代进入相对富足时代,用户可以选择的产品会随时日流逝而日渐增加,产品终将成为一种时尚业。产品经理若是沉溺于各种新鲜玩意儿之中,追逐新奇,很可能错过真实的时代潮流,无法把握人群的真实需求。

2、 放弃理性思维。移动互联网的最大特点是变化极快,传统的分析用户,调研市场,制定产品三年规划,在新的时代里已经落伍。人类群落本身也在迁移演变,产品经理更应该依靠直觉和感性,而非图表和分析,把握用户需求。产品经理永远都应该是文艺青年,而非理性青年。

3、 海量的实践。尽管移动互联网方兴未艾,没有任何人可以自称是领域内的专家。但是,这不意味着存在天降天才的可能。《异类》中提出的一万小时定律,同样适用于产品经理。他们需要超过千次的产品实践,才能称得上是了解产品设计,拥有解决问题的能力。

4、 博而不专的积累。美术、音乐、阅读、摄影、旅游等等文艺行为貌似不能直接转化为生产力,但是合格的产品经理需要广博的知识储备,以此才能了解和认识大数量的人群,理解时代的审美,让自己的所思所感符合普通用户的思维范式。以此为基础,设计的产品才不会脱离人群。

5、 负责的态度。拥有合适的方法论和合适的素养,成功的产品经理还应该有对自己和产品负责的态度,唯其如此,产品经理才能足够偏执,清楚地知道自己究竟要做什么,抵挡住来自上级和绩效考核的压力,按照自己的意志不变形、不妥协地执行产品策划。

四、移动互联网产品设计的原则

1、 绝不考虑Web形态,一切考虑都基于APP。

2、 产品优先级:

(1)有趣高于功能,产品必须有趣,必须Cool,才可能形成传播和口碑。

(2)功能高于交互,明确的功能满足明确的需求,用户不会在意炫酷交互效果。

(3)交互高于UI。便捷、快速的交互设计为先,围绕具体功能实现UI,而非有优质UI方案为此专门设立一个功能。

3、 聚焦:一个APP只做一件事情,一个大而全的APP意味着全面的平庸。

4、 永远一维化:让用户在一个维度里解决具体的问题,Twitter的Timeline就是一个好的范例。而类似Facebook、Path那样的滑出式菜单则是一个灾难,因为这使得产品拥有两个维度,加大了用户理解的困难。

5、 保持主干清晰,枝干适度。产品的主要功能架构是产品的骨骼,它应该尽量保持简单、明了,不可以轻易变更,让用户无所适从。次要功能丰富主干,不可以喧宾夺主,尽量隐藏起来,而不要放在一级页面。

6、 不要让用户选择。同一个页面之内,有多个入口;同一个功能,有多个实现方式;同一个界面,有多个展示方式。这对于用户来说是一种痛苦而非享受,因为他们只会因此而感觉到困惑和恐惧。用户宁可采取重复操作漫长而固定的操作路径,也不愿意使用多变的快捷方式。

7、 隐藏技术,永远展现简单的、人性化的、符合人类直觉的界面。开发不可以为了炫技而展示功能,产品不可以为了炫耀而功能堆砌。

8、 拒绝个性化。除了依靠设计特色而立身的APP,换肤一类的个性化设计,除了让产品经理幻觉自己做了许多工作而自我满足之外,没有任何价值。它只能证明产品 经理对自己的产品不自信,因为自信的产品经理凭借默认皮肤就可以满足用户。延伸开去,一个好的产品,其功能应该满足全球用户需求,无需为地区做特别定制 化。

9、 产品一定程度上是为了满足人性中的贪嗔痴,这是用户的痛点。能把握住之后,产品经理应该超越其上,用产品帮助人们得以解脱。

10、想清楚自己究竟要做什么,不去迎合上司,不去讨好用户,不去取悦自己。

11、 分类!分类!分类!这是产品经理在确定产品主要功能构架之后,唯一应该为用户做的事情。分类无助于降低产品使用的难度,但是可以帮助用户认知产品和周边的世界。

12、永远围绕功能而做设计,永远不要倒过来做这件事情。

13、 一个产品的基本功能不受用户认可,做加法也无济于事。

14、 想不清楚一个功能点之前,宁可不做。

15、千万不要让用户在产品里“管理”什么。

五、“自然流”的设计思路

好的产品应该隐藏产品经理的个人意图,用户仅仅凭借直觉和经验就可以顺利使用,以达到“自然而然”的境界。坏的产品提供产品说明书,其恶劣程度和Tip和文字说明数量正相关。为此,好的产品经理可以和用户之间平等对话,无需刻意谄媚、恶意卖萌,产品本身就会说话。

自然流的产品,它本身就可以可用户交流。针对用户的任意一个动作,给出唯一的、清晰的反馈,并且能让用户没有任何偏差地接受。它没有人造物的冰冷生 硬,而有一种温暖的人性存在。例如在IOS中微信朋友圈里的评论按钮,按下之后弹出“赞”和“评论”弹窗。这一弹窗快速向左滑动,然后像碰到什么东西一样 快速向右反弹一个很小的距离,然后才最终停下来。用户可能根本未能意识到这一微小的停顿,但是在潜意识里,他们会感受到这个弹窗并非全然的人造物。因为根 据生活经验,世界上没有任何东西可以从运动直接变到绝对静止。

绝对不要让用户在使用产品的过程中感受到产品经理的伟大和聪慧,产品经理应该完全隐没在产品之中。用户应该可以不假思索地上手,按照设计意图行事, 从中获得既定的正向反馈。这一切应该自然而然地发生,用户感觉似乎产品天生就应该这样使用,从产品还未出现之前很久就应该如此。好的产品不会强调自己存在 于世界之上,它只是努力地、毫无痕迹地成为这个世界的一部分。

做自然流的产品,必然会在美学上倾向于简单,反逻辑。产品经理必然的选择是做减法,在诸多功能中选取最能解决实际问题的一个,在诸多特性中选取最符 合直觉的一项,于是产品也就拥有了优雅和简洁,让人难以忘怀。极简和极自然,使得模仿无法存在,因为没有人可以造出更好的体验来。

六、思考题

1、 为什么微信的对话界面中,是针对信息长按弹出删除,而不是在页面左上角放“编辑”键,点击进入编辑删除?

2、 为什么微信朋友圈不做分组功能?

3、 为什么微信视频对话,不可以直接发起,也没有通知声?

七、结语

我所说的,都是错的。

—张小龙

拖着老娘,携着幼子,乞讨7年,是游手好闲,还是走投无路?

请帮忙想想他还能怎么办?
五月初,在文一西路乞讨的一对父女引起了张力的关注,他发了一条围脖——“这是我今天在杭州文一西路拍到的一对乞讨的父女。两人来自安徽,父亲45岁,女儿6岁,孩子的母亲不幸去世,两人相依为命。父亲腰有残疾,耳朵有点聋,因不堪重负,两人一路乞讨来到杭州。当时我就被孩子天真的笑容所感染,停下来跟他们交谈了许久,还给他们留下了我的电话,希望会有后续报道。”

后续的密切关注中我们也陆续解了到了他们的真实境况。每天,老人都带着孩子流窜在杭州街头,因为白天路人都赶着上班行色匆匆不太会分神施舍路人,所以他们白天都不会开工,他们会在大超市里逛逛,乘乘凉,而孩子则热衷于玩超市里成列的智能手机和ipad。到了傍晚下班高峰期到晚上9点他们就会坐在物美天桥、文一路附近乞讨,晚上他们则睡在西城广场一家便利店的门口,随身携带的就是一床破被子、一条写了字的床单、各式各样的药、旧照片和一些乱七八糟的杂物。每天的吃食就是靠乞讨得来的钱买现成的,实在脏的受不了则会在公厕的自来水龙头下简单洗一洗。现在天气渐热,直到晚上八九点钟杭州的公路路面上都是滚烫的,而他们就坐在路边,扇着蒲扇指望着路过的行人施舍点零钱。

在杭州每天都会看到形形色色的乞讨者,但这个孩子纯真的笑容的确让人记忆深刻,在这条微博发布后很多热心的博友都非常感动,张罗着捐钱捐物,但在其中也有报社的记者一针见血的指出这两个是有名的职业乞丐,而那个可爱的小女孩是由小男孩假扮的,他们这种靠路人怜悯过生活的方式实在是让人鄙夷,根本不值得同情。

杭州乞丐里的职业红人
在得知这位这位老父亲的真名叫做王四美之后,在网上输入关键词“王四美乞讨”之后,百度新闻得到了99篇含有相关词条的新闻消息,而大部分的新闻标题都是“男子携五岁幼子外出乞讨,老家已分田建房办低保”、“城里磕头家乡造楼,‘职业乞讨’富了谁伤了啥?”、“八旬母亲风中乞讨续:老太太被送回家乡”……

从这些报道里我们可以总结出这样的信息,这个叫王四美的安徽籍男人是个不愿意劳动的劳动力,从2005年起就推着板车带着老母亲来杭州乞讨,妻子是个精神病患者,在儿子生下来后就不幸溺亡了,现在他就带着老人和孩子辗转在杭州的各个角落乞讨为生,老母亲前几年去世了,而他独自一个人靠乞讨拉扯着唯一的儿子小兵兵,甚至为了儿子不被人抱走让他假扮小女孩。父子俩每年都会被好心人送到救助站,由救助站送回老家,但每次回去后不久还会回到杭州重操旧业。在他的老家他们也是有田有房有低保的,只是在杭乞讨每月有三四千收入的他根本不把那几百块的低保补助放在眼里,有这样的职业乞讨者存在还真是让人不敢再同情弱者。

真是没办法才来乞讨的
而从老王的口中我们听到了一个版本不同的故事:老王是67年出生的,在他的上面还有两个哥哥,而从小体弱多病、干不了重活的他直到三十好几都没娶上媳妇。后来他就和老家一个有精神问题的女人结了婚,好不容易得了一个宝贝儿子,却不想媳妇落水死了。原本还能外出打点临工的他只能专心带起了孩子,而自己的两个哥哥因为超生养了不少孩子,就把老母亲推给了他。没有谋生之路的老王就拉着板车带着孩子出来乞讨了。

据老王说,在他刚刚来杭州讨生活的那几年,比现在的收入好得多,小兵兵没有妈妈,所有奶粉钱和尿布钱都是靠爸爸和奶奶乞讨得来的。那几年,他们就已经被很多人关注,经常有好心人打电话给救助站,而救助站则会将他们送回老家,但事实上,老王一家的生活并没有因为多次被“救助”而改善。报纸上写的补助金他从来没有拿到过,因为年纪还不够大,不能领老保,而每年几百块的低保又是整个村的人争着抢着的,他家的两亩旱地种庄稼还不够自己家吃的,所谓的房子只有两间,一间灶房另一间也没法住人,在老家他们实在是无法过日子……

他们还能怎样生活?
一边是白纸黑字的新闻报道,一边是老人动情的述说,我们愿意以最大的善意来看待这是世界,却不能忘记社会基本的道德准则。

老王到底有没有劳动能力?从外形上看,他的确非常老迈,有医院诊断的颈椎、腰椎和高血压,干农活非常勉强。至于外出打工,会招收低学历、低技术的企业大多需要的是能够付出大量体力劳动的年轻人,显然这不是老王能够胜任的。如果有企业能够给老王提供一个看大门的工作,这对于他来说会是一件天大的好事。
如果待在老家,他们的生活会是怎样?老王只有两亩旱地,而现在在农村一亩地种粮食只能得几百块钱,如果遇上坏年头,还有可能亏本,在他们老家已经很少有人家只靠种地过日子了。而只身拉扯着儿子的老王在农村的生活也是可想而知的。

他们到底有没有拿到补助?新闻报道中说老王的母亲王邓氏是有的,但是老母亲已经过世多年。老王则说现在他们是没有低保的,他们唯一能够拿到的钱是每年60块的独生子女鼓励。(关于这一点,还有待和当地政府的核实)

在杭州乞讨的他们的收入究竟怎样?在杭乞讨者的收入也是我们非常好奇的一问题,据老王自己说这几年的要比过去难过得多,现在他们每天最好的时候能够讨到八九十元,而在我采访他们的那天,直到晚上九点,他们的破盆里也只有35元零钱。

直到现在,还是无法判断老王究竟是游手好闲,还是走投无路,但这个问题其实不是终点,不管是真的生活贫困,失去保障,还是懒得劳动,甘于乞讨,躲藏在表象下的核心问题依然是整个社会的问题,是整个大环境逼得有人选择这样的生活方式。

在和老王父子接触之后最让我们心疼还是那个孩子,六岁的孩子他还不懂事、不识字、没有朋友,只会简单的数数,他的童年记忆就是跟着父亲在城市街头“游荡”。他也想和别的孩子一样念书,但也喜欢整天玩闹的生活。还有一年就是这个孩子该入学的年龄了,但他的父亲还没有做好送他上学的物质准备,而野惯了的孩子能够乖乖做到学堂里上课么。现在,老王越来越无力抚养这个孩子,也不知道等到自己撒手人寰之后这个孩子该怎么生存,只想着有没有好心人愿意领养。

卖外挂的骗子,339503844 1332206455

骗子信息:

————————————————————————————————————————–

网址:www.chinawalker.net

信箱:lhongfu@gmail.com

姓名:李洪福

QQ:1872385460

QQ:339503844 梦魔,(真名:姜池)

QQ:1332206455 华为-YIY (真名:李洪福 )手机是13384655285

 

开户行:建设银行

卡 号:

6222801141061067838(收款人:李洪福)

6228480170506027813(农    行:姜    池)

 

tel: 055187654321

fax: 055187675432

地址:合肥 包河区屯溪路193号(合肥工业大学)

————————————————————————————————————————–

 

十足的骗子,大家注意了,这个是骗子,昨天晚上骗了我500块,

说他有斗牛外挂,我钱打给他他的人就失踪了,可能被撞死了,

发给我的外挂早已失效,而且要登陆密码,就是个软件外壳,

然后我查了该软件网站,软件标价才190,

然后我就又打190到该网站购买,买了以后又叫我交150保证金,然后我又交了保证金,

结果电话QQ都打不通,他妈的气人啊!~连环骗啊~!

总共骗了我840块啊~!

希望大家看到不要上当啊~!

从地址上看,李洪福跟合肥工业大学有某种关系,那里毕业的?或者在那里附近生活?

电话0551也是合肥的区号