• 标 题:Photocaster xtra v3.0.3 注册过程的分析 (15千字)
  • 作 者:robot
  • 时 间:2001-11-22 23:07:50
  • 链 接:http://bbs.pediy.com

软件简介:Director的一个插件,支持直接导入用PhotoShop的分层PSD图形文件,并按原样在舞台上自动排列。未注册版会在导入的图形中加上很多蓝色的斜线,从而导致无法正常使用。我在Director中用了一下,感觉确实很不错,稍微遗憾的是,该软件在读取含有汉字的图层文件时,对汉字有点“水土不服”,显示有问题,不过,可以在Director下编辑该层,重新输入一遍汉字就OK了。
下载地址:http://www.medialab.com/downloads/PhotoCaster3Win.zip
引用网页:http://www.medialab.com/products/downloadlist.htm
目标文件:Photocaster.x32  380928 bytes
分析工具:SoftICE 4.05,IDA Pro 4.15
Director运行版本:8.5,序列号:WDW850-02044-87235-26475
前言:这个xtra是网上的一个朋友huihuicn问的,当时问我是否有这个xtra,于是我就用google搜索了一下,还真找到了下载URL,顺便还找到了一篇关于Photocaster v2的破解文章,这篇文章对我进行分析还是有不少帮助的,让我知道了一些信息,少走了一些弯路,感谢该文的作者。另外,这个xtra有点奇怪,没有什么壳,但不能用W32Dasm反汇编,估计该软件可能作了防W32Dasm处理。不过用IDA可以反汇编,这正是我所希望的(否则在SoftICE中分析岂不痛苦?)。

破解过程:首先Photocaster.x32复制到Director的xtra文件夹(我一般是新建个Photocaster文件夹,然后将相关文件复制到该文件夹下,这样便于管理),启动Director 8.5,选择Insert-->Media Lab Media-->PhotoCaster,调入PhotoCaster,单击About按钮,填入Unlock Code:654321,Ctrl+D进入SoftICE,设断:bpx getdlgitemtexta,来到下面:

:10017281        call    ds:GetDlgItemTextA  ;取得注册码字符串
:10017287        test    eax, eax
:10017289        jz      loc_1001733B
:1001728F        lea    eax, [esp+2Ch+String]
:10017293        mov    ecx, esi
:10017295        push    eax
:10017296        call    sub_1001C640        ;计算并比对的call
:1001729B        test    eax, eax            ;eax做旗标
:1001729D        jz      loc_1001733B        ;等于0则错误

:1001C640 arg_0          = 注册码
:1001C640        sub    esp, 5Ch
:1001C643        push    ebx
:1001C644        mov    ebx, [esp+60h+arg_0]

:1001C65F        mov    edi, ebx
:1001C661        or      ecx, 0FFFFFFFFh
:1001C664        xor    eax, eax
:1001C666        repne scasb
:1001C668        not    ecx
:1001C66A        dec    ecx
:1001C66B        cmp    ecx, 6    ;注册码长度是否为6
:1001C66E        jz      short loc_1001C679  ;是则转
:1001C670        pop    edi

:1001C679        push    ebx
:1001C67A        call    sub_100024F0  ;将大写字符转为小写
:1001C67F        mov    al, [ebx+5]  ;取第6个注册码字符
:1001C682        add    esp, 4
:1001C685        cmp    al, 30h
:1001C687        jl      short loc_1001C691
:1001C689        cmp    al, 39h
:1001C68B        jg      short loc_1001C691
:1001C68D        add    al, 0D0h
:1001C68F        jmp    short loc_1001C693
:1001C691        add    al, 0C9h
1c685-1c691为取该字符的“0-9A-Z”的排列值

:1001C693        mov    esi, [esi+10h]  ;常数,我的是23675,这个值很关键,后面说其产生过程
:1001C696        movsx  edi, al        ;第一个注册码的取值放到edi
:1001C699        push    esi
:1001C69A        mov    [esp+6Ch+var_3C], edi
:1001C69E        call    _atol          ;转为长整数
:1001C6A3        add    esp, 4
:1001C6A6        add    eax, edi        ;加上第一个注册码的值
:1001C6A8        push    eax
:1001C6A9        push    offset a05lu    ; "%05lu"
:1001C6AE        push    esi
:1001C6AF        call    _sprintf        ;转为字符串
:1001C6B4        push    5
:1001C6B6        mov    [esp+78h+var_33], 0
:1001C6BB        mov    [esp+78h+var_30], 7
:1001C6C3        mov    [esp+78h+var_2C], 0Bh
:1001C6CB        mov    [esp+78h+var_28], 0Dh
:1001C6D3        mov    [esp+78h+var_24], 11h
:1001C6DB        mov    [esp+78h+var_20], 13h
:1001C6E3        mov    [esp+78h+var_1C], 17h
:1001C6EB        mov    [esp+78h+var_18], 1Dh
:1001C6F3        mov    [esp+78h+var_14], 1Fh
:1001C6FB        mov    [esp+78h+var_10], 25h
:1001C703        mov    [esp+78h+var_C], 29h
:1001C70B        mov    [esp+78h+var_8], 2Bh
:1001C713        mov    [esp+78h+var_4], 2Fh
:1001C71B        call    _malloc
:1001C720        mov    esi, eax
:1001C722        push    5
:1001C724        push    ebx
:1001C725        push    esi
:1001C726        mov    [esp+84h+var_58], esi
:1001C72A        call    _strncpy
:1001C72F        push    5
:1001C731        lea    eax, [esp+88h+var_38]
:1001C735        push    ebx
:1001C736        push    eax
:1001C737        call    _strncpy
:1001C73C        mov    ecx, esi
:1001C73E        lea    eax, [esp+90h+var_38]
:1001C742        add    esp, 28h
:1001C745        xor    edx, edx
:1001C747        sub    ecx, eax
:1001C749        mov    [esp+68h+var_44], ecx
:1001C74D        lea    eax, [esp+edx+68h+var_38]
:1001C751        mov    bl, [ecx+eax]    ;取注册码字符
:1001C754        add    bl, 0D0h        ;字符转为数字
:1001C757        mov    [ecx+eax], bl
:1001C75A        mov    bl, [eax]        ;取注册码字符
:1001C75C        add    bl, 0D0h        ;字符转为数字
:1001C75F        inc    edx
:1001C760        cmp    edx, 5
:1001C763        mov    [eax], bl
:1001C765        jl      short loc_1001C74D

:1001C767        mov    ebx, 1
:1001C76C        push    ebp
:1001C76D        mov    [esp+6Ch+var_5C], ebx
:1001C771        xor    ebp, ebp        ;置初值
:1001C773        jmp    short loc_1001C779

:1001C775        mov    esi, [esp+6Ch+var_58]
:1001C779        mov    cl, [esi+ebp]    ;取注册码字符
:1001C77C        mov    byte ptr [esp+6Ch+var_54], cl
:1001C780        mov    ecx, 5
:1001C785        mov    edi, [esp+6Ch+var_54]
:1001C789        and    edi, 0FFh
:1001C78F        mov    eax, edi
:1001C791        cdq
:1001C792        idiv    ecx            ;整除5
:1001C794        cmp    ebp, 4          ;当前注册码是否是第5个
:1001C797        mov    byte ptr [esp+6Ch+var_50], dl    ;取余数值
:1001C79B        jge    short loc_1001C7A3    ;是则转
:1001C79D        mov    cl, [esi+ebp+1]  ;下一个注册码
:1001C7A1        jmp    short loc_1001C7A5
:1001C7A3        mov    cl, [esi]        ;取第1个注册码
:1001C7A5        mov    al, bl
:1001C7A7        mov    esi, 0Ah
:1001C7AC        imul    cl              ;al*cl
:1001C7AE        mov    dl, al          ;dl=al*cl
:1001C7B0        mov    eax, [esp+6Ch+var_50]
:1001C7B4        and    eax, 0FFh
:1001C7B9        mov    byte ptr [esp+6Ch+var_4C], dl
:1001C7BD        lea    ecx, [esp+eax+6Ch+var_38]
:1001C7C1        mov    al, [esp+eax+6Ch+var_38]  ;根据1001C797语句的dl值取进行变换的字符
:1001C7C5        add    al, dl          ;加上dl
:1001C7C7        and    eax, 0FFh        ;取低位
:1001C7CC        cdq
:1001C7CD        idiv    esi              ;整除10
:1001C7CF        xor    esi, esi
:1001C7D1        xor    eax, eax
:1001C7D3        mov    [ecx], dl        ;余数放到[ecx]
:1001C7D5        mov    ecx, 1

:1001C7DA        xor    edx, edx
:1001C7DC        mov    dl, [esp+eax+6Ch+var_38]  ;从变换的字符中取一个
:1001C7E0        imul    edx, ecx                  ;相乘
:1001C7E3        lea    ecx, [ecx+ecx*4]
:1001C7E6        add    esi, edx
:1001C7E8        shl    ecx, 1
:1001C7EA        cmp    eax, ebp
:1001C7EC        jnz    short loc_1001C7F2
:1001C7EE        mov    [esp+6Ch+var_48], ecx
:1001C7F2        inc    eax
:1001C7F3        cmp    eax, 5
:1001C7F6        jl      short loc_1001C7DA

:1001C7F8        mov    eax, ebp
:1001C7FA        mov    ebx, 0Bh
:1001C7FF        cdq
:1001C800        idiv    ebx
:1001C802        and    edx, 0FFh
:1001C808        mov    eax, [esp+edx*4+6Ch+var_30]  ;取1001C6BB-1001C713的赋值
:1001C80C        mov    edx, [esp+6Ch+var_4C]
:1001C810        and    edx, 0FFh
:1001C816        imul    eax, edi
:1001C819        imul    edx, [esp+6Ch+var_48]
:1001C81E        imul    edx, [esp+6Ch+var_5C]
:1001C823        add    edx, esi
:1001C825        mov    edi, 4
:1001C82A        lea    esi, [edx+eax]

:1001C82D        mov    eax, 66666667h            ;注意66666667h/100000000h=0.4
:1001C832        mov    ebx, 0Ah
:1001C837        imul    ecx
:1001C839        sar    edx, 2                    ;0.4/4=0.1,说明ecx每次循环的计算结果均*0.1
:1001C83C        mov    eax, edx
:1001C83E        shr    eax, 1Fh
:1001C841        add    edx, eax
:1001C843        mov    eax, esi
:1001C845        mov    ecx, edx                  ;即ecx=ecx/10 (语句1001C82D-1001C841的结果)
:1001C847        cdq
:1001C848        idiv    ecx                      ;eax=esi/ecx
:1001C84A        and    eax, 0FFh                ;取商的低位
:1001C84F        cdq
:1001C850        idiv    ebx                      ;整除10
:1001C852        mov    eax, esi
:1001C854        mov    [esp+edi+6Ch+var_38], dl  ;余数放回
:1001C858        cdq
:1001C859        idiv    ecx
:1001C85B        dec    edi                      ;计数器-1
:1001C85C        mov    esi, edx                  ;esi=esi mod ecx
:1001C85E        jns    short loc_1001C82D

:1001C860        mov    ebx, [esp+6Ch+var_5C]
:1001C864        neg    ebx                      ;ebx=-ebx
:1001C866        inc    ebp                      ;计数器加1
:1001C867        mov    [esp+6Ch+var_5C], ebx
:1001C86B        cmp    ebp, 5                    ;计算了5组了吗
:1001C86E        jl      loc_1001C775              ;没有则继续

:1001C874        mov    edx, [esp+6Ch+var_44]
:1001C878        xor    ebx, ebx
:1001C87A        xor    ecx, ecx
:1001C87C        lea    eax, [esp+ecx+6Ch+var_38] ;注册码经过5次循环变换计算的结果
:1001C880        add    byte ptr [edx+eax], 30h
:1001C884        add    byte ptr [eax], 30h      ;将变换后的字符变为数字
:1001C887        inc    ecx
:1001C888        cmp    ecx, 5
:1001C88B        jl      short loc_1001C87C
:1001C88D        mov    ecx, [esp+6Ch+var_58]
:1001C891        push    ecx
:1001C892        call    sub_1002651C
:1001C897        mov    ebp, [esp+70h+var_40]
:1001C89B        add    esp, 4
:1001C89E        xor    edi, edi
:1001C8A0        xor    eax, eax
:1001C8A2        mov    esi, [ebp+10h]
:1001C8A5        xor    edx, edx
:1001C8A7        xor    ecx, ecx
:1001C8A9        mov    dl, [esi+eax]            ;取"23675"字符串+第6个注册码字符的变换值
:1001C8AC        mov    cl, [esp+eax+6Ch+var_38]  ;前5个注册码变换生成的字符串
:1001C8B0        xor    edx, ecx                  ;如果相等,则异或为0
:1001C8B2        add    edi, edx                  ;异或结果累加
:1001C8B4        inc    eax
:1001C8B5        cmp    eax, 5
:1001C8B8        jl      short loc_1001C8A5
:1001C8BA        push    esi
:1001C8BB        call    _atol
:1001C8C0        mov    edx, [esp+70h+var_3C]
:1001C8C4        add    esp, 4
:1001C8C7        sub    eax, edx
:1001C8C9        push    eax
:1001C8CA        push    offset a05lu    ; "%05lu"
:1001C8CF        push    esi
:1001C8D0        call    _sprintf
:1001C8D5        add    esp, 0Ch
:1001C8D8        cmp    edi, ebx                  ;累加结果是否为0
:1001C8DA        jz      short loc_1001C907        ;如果为0则注册码正确
:1001C8DC        mov    eax, [ebp+5Ch]
:1001C8DF        mov    [ebp+6Ch], ebx
:1001C8E2        mov    ecx, [eax+0Ch]  ;取PhotoCaster Lingo Prefs_D8.PRF文件的注册次数,文件偏移量的0Ch处
:1001C8E5        inc    ecx                      ;注册次数加1
:1001C8E6        mov    [eax+0Ch], ecx
:1001C8E9        mov    edx, [ebp+5Ch]
:1001C8EC        cmp    dword ptr [edx+0Ch], 32h  ;注册次数是否大于50,小小的一暗桩
:1001C8F0        jg      short loc_1001C90E        ;大于则转,假装显示注册成功
:1001C8F2        mov    ecx, ebp
:1001C8F4        xor    esi, esi                  ;失败旗标
:1001C8F6        call    sub_1001B650              ;注册次数等信息写回文件(PhotoCaster Lingo Prefs_D8.PRF)
:1001C8FB        pop    ebp
:1001C8FC        mov    eax, esi  ;注册失败
:1001C8FE        pop    edi
:1001C8FF        pop    esi
:1001C900        pop    ebx
:1001C901        add    esp, 5Ch
:1001C904        retn    4
:1001C907        mov    dword ptr [ebp+6Ch], 1
:1001C90E        mov    eax, [ebp+5Ch]
:1001C911        mov    edi, [esp+6Ch+arg_0]  ;注册码
:1001C915        or      ecx, 0FFFFFFFFh
:1001C918        mov    [eax+14h], bl
:1001C91B        mov    edx, [ebp+5Ch]
:1001C91E        xor    eax, eax
:1001C920        add    edx, 14h
:1001C923        repne scasb
:1001C925        not    ecx
:1001C927        sub    edi, ecx
:1001C929        mov    eax, ecx
:1001C92B        mov    esi, edi
:1001C92D        mov    edi, edx
:1001C92F        shr    ecx, 2
:1001C932        repe movsd
:1001C934        mov    ecx, eax
:1001C936        and    ecx, 3
:1001C939        repe movsb
:1001C93B        mov    ecx, ebp
:1001C93D        mov    esi, 1          ;旗标
:1001C942        call    sub_1001B650    ;注册信息写回文件
:1001C947        pop    ebp
:1001C948        mov    eax, esi        ;注册成功
:1001C94A        pop    edi
:1001C94B        pop    esi
:1001C94C        pop    ebx
:1001C94D        add    esp, 5Ch
:1001C950        retn    4
:1001C950 sub_1001C640    endp

根据上面的计算程序,我用VB编了一个函数,传递参数为Unlock Code,返回值为计算结果,如果返回值等于常数+第6个字符的变换值就说明注册码正确。程序如下:
Function GetSerial(regcode As String)
    Dim Serial As String, i As Integer, j As Integer, k As Integer
    Dim ebp As Integer, al As Integer, bl As Integer, cl As Integer, dl As Integer
    Dim eax As Integer, ebx As Long, ecx As Long, edx As Long, esi As Long, edi As Long
    Serial = regcode
    bl = 1: ebp = 0
    For i = 1 To 5  '对5个注册码字符变换
        edi = CInt(Mid(regcode, i, 1)): cl = edi Mod 5
        j = i + 1
        If i = 5 Then j = 1
        eax = cl + 1: cl = CInt(Mid(regcode, j, 1)): dl = bl * cl
       
        If CInt(Mid(Serial, eax, 1)) + dl < 0 Then '检测变换的字符是否有“负值”,如果有则返回
                GetSerial = "负值!"    '表示该Unlock Code错误
                Exit Function
        End If
        Mid(Serial, eax, 1) = CStr((CInt(Mid(Serial, eax, 1)) + dl) Mod 10)
        dl = CInt("&H" + Right(Hex(dl), 2))  '取低位,即1001C7C7语句
'以上对应于程序的1001C775-1001C7D5语句

        ecx = 1: esi = 0
        For j = 1 To 5
            edx = CLng(Mid(Serial, j, 1))
            edx = edx * ecx: esi = esi + edx: ecx = ecx * 10
            If j = ebp + 1 Then ebx = ecx
        Next j
'以上对应于程序的1001C7DA-1001C7F6语句
       
        al = 2 * (ebp Mod 12) + 1
        al = CInt("&H" + Mid("070B0D1113171D1F25292B2F", al, 2))  '其实字符串取前5个就行了
        edx = dl * ebx * bl: esi = esi + al * edi + edx
'以上对应于程序的1001C7F8-1001C82A语句
       
        For j = 5 To 1 Step -1
            ecx = ecx / 10: ebx = esi: ebx = ebx \ ecx
            ebx = CLng("&H" + Right(Hex(ebx), 2)) Mod 10
            Mid(Serial, j, 1) = CStr(ebx)
            esi = esi Mod ecx
        Next j
'以上对应于程序的1001C82D-1001C85D语句

        bl = -bl: ebp = ebp + 1
    Next i
    GetSerial = Serial
End Function

根据上面的函数,我编了一个算码程序,共找到了15个注册码,介绍其中好记的一个:98567G

  • 标 题:PhotoCaster v3.0.3软件产品号(ProductID)生成部分的分析 (7千字)
  • 作 者:robot
  • 时 间:2001-11-22 23:09:05
  • 链 接:http://bbs.pediy.com

软件简介:见前面
前言:我将我最早找到的注册码240521和98567G给那位网上的朋友后,他回复说不能注册成功,那么就说明问题肯定是ProductID不同所致,这就否定了网上的那篇PhotoCaster v2破解文章所述的与“一个常数”异或运算的说法,这样就激起了我分析ProductID产生的雄心,还好,这次又让我给分析着了。于是我想,既然在我的机器上能生成该“常数”——23675,那么肯定有一段程序是生成它的。开始我想bpm该内存地址,看看那个地方给该地址赋值,但失败了,因为它在内存中的地址是变化的(事后想想,这样做虽然有点投机取巧,想省点事,但方法确实有点傻)。于是我只好从Director调入该xtra入手,来分析其生成过程。如果该xtra的设计者在生成这个常数之前绕来绕去,进行大量转换(如密码学),我想我会基本绝望并放弃的——除非这个xtra的ProductID对我来说,跟饭碗和生命相关,必须要分析出来。
过程:启动Director8.5,Ctrl+D激活SoftICE,设断:bpx loadlibrarya,选择Insert PhotoCaster,这时会被SoftICE拦截,进入到PhotoCaster领空后,F10跟踪,每次F10带过一个call后,都搜索一下内存中是否生成了23675,幸好在生成23675之前的call不太多,最后来到下面:
:1001A0C1        mov    ecx, esi
:1001A0C3        call    sub_10012FA0    ;F10带过这步后,产生常数23675和ProductID,追进去看看

10012FA0的call:
:10012FDD        push    1Eh
:10012FDF        push    edx
:10012FE0        push    0
:10012FE2        push    eax
:10012FE3        call    dword ptr [ecx+0Ch]    ;取Director的安装序列号,我的是WDW850-02044-87235-26475

:10012FE6        test    eax, eax              ;是否成功
:10012FE8        mov    [esp+4Ch+var_40], eax
:10012FEC        jnz    short loc_10012FFC    ;没有得到序列号则转
:10012FEE        lea    edi, [esp+4Ch+var_30]  ;Director序列号
:10012FF2        or      ecx, 0FFFFFFFFh
:10012FF5        repne scasb
:10012FF7        not    ecx
:10012FF9        dec    ecx
:10012FFA        jnz    short loc_1001302A
:10012FFC        mov    edi, offset aDrw80012345123 ; "DRW800-12345-12345-12345"

:10012FFC-10013028的语句:Director序列号用"DRW800-12345-12345-12345"代替,语句略

:1001302A        lea    edi, [esp+4Ch+var_30]  ;Director序列号
:1001302E        or      ecx, 0FFFFFFFFh
:10013031        xor    eax, eax
:10013033        repne scasb
:10013035        not    ecx
:10013037        dec    ecx
:10013038        cmp    ecx, 9
:1001303B        jge    short loc_10013049    ;序列号的长度大于等于9则转
:1001303D        pop    edi
:1001303E        pop    esi
:1001303F        mov    eax, 0FFFFFED0h        ;失败标志
:10013044        pop    ebp
:10013045        add    esp, 30h
:10013048        retn
:10013049        lea    esi, [esp+ecx+4Ch+var_31]  ;esi指向序列号的最后一位
:1001304D        mov    ecx, 1                    ;置初值
:10013052        lea    edx, [esp+4Ch+var_34]      ;存放要取的序列号
:10013056        mov    al, [esi]
:10013058        cmp    al, 30h
:1001305A        jl      short loc_10013064
:1001305C        cmp    al, 39h
:1001305E        jg      short loc_10013064        ;不是0-9的数字则取下一个字符
:10013060        mov    [edx], al                  ;取得的数字放此
:10013062        inc    ecx
:10013063        dec    edx
:10013064        dec    esi
:10013065        lea    eax, [esp+4Ch+var_30]
:10013069        cmp    esi, eax                  ;序列号全取完了吗?
:1001306B        jb      short loc_1001303D        ;取完了则转
:1001306D        cmp    ecx, 9                    ;取满了9个数字?
:10013070        jle    short loc_10013056        ;没有则转
10013049-10013070表示:从Director序列号的尾部开始取9个数字,我的结果为723526475

:10013072        lea    ecx, [esp+4Ch+var_40]
:10013076        lea    edx, [esp+4Ch+var_3C]      ;所取的9个数字
:1001307A        push    ecx
:1001307B        push    edx
:1001307C        mov    ecx, ebp
:1001307E        call    sub_10013210      ;根据这9个数字生成ProductID的部分字符
这个call的计算过程如下:
每次取3个数字,用a表示,则
(1) a*0.914285715/32;如:723*0.914285715=661,661\32=20
(2) a mod 23h,得到余数;如723 mod 23h=723 mod 35=23
根据这两个值从"0-9A-Z"中取相应的字符,则723经过计算取得的字符为:K和N
这样,共得到三组6个字符,我的是KNF1DK

:10013083        mov    esi, eax
:10013085        lea    eax, [esp+4Ch+var_40]
:10013089        push    eax
:1001308A        push    esi
:1001308B        mov    ecx, ebp
:1001308D        call    sub_100130E0      ;生成产品号
过程:PhotoCaster文件的第1个字符+6个字符的前2个+" - "+后4个字符,知道文件格式的人都清楚,xtra的第一个字符是M,这样,我的PhotoCaster的ProducID为:MKN - F1DK,共10个字符

:10013092        push    esi
:10013093        mov    ecx, ebp
:10013095        mov    [ebp+0Ch], eax
:10013098        call    sub_10012F80
:1001309D        mov    eax, [esp+4Ch+var_40]
:100130A1        test    eax, eax
:100130A3        jnz    short loc_100130CE
:100130A5        lea    ecx, [esp+4Ch+var_40]
:100130A9        lea    edx, [esp+4Ch+var_3C]
:100130AD        push    ecx                    ;所取的9个数字
:100130AE        push    0CBh                  ;换成二进制就是011001011b
:100130B3        push    edx
:100130B4        mov    ecx, ebp
:100130B6        call    sub_10013190          ;生成比较注册码正确与否的5个数字
该固定计算值的生成过程:取CBh为1的对应的数字,则从9个数字中分别取第2、3、6、8、9位的数字,就得到了23675

:100130BB        pop    edi
:100130BC        mov    [ebp+10h], eax
:100130BF        mov    eax, [esp+48h+var_40]
:100130C3        pop    esi
:100130C4        pop    ebp
:100130C5        add    esp, 30h
:100130C8        retn

这样,知道了ProductID和固定常数的生成过程,该软件的注册器就可以做了。

附记:PhotoCaster v2.0.5破解文章的作者说的“一个常数”其实也并没有错,虽然他尝试了很多机器,发现都是这个数,但我想,他做梦也不会想到PhotoCaster竟会用Director的序列号来生成自己的ProductID,但通过我这篇的分析文章就会明白,即使他尝试更多的机器,还会是相同的“一个常数”——安装时填入的Director的序列号都相同嘛。如果安装的Director是Z版的话,几乎是一机一号,那么PhotoCaster的ProductID也就可能出现不同了。那个作者在安装Director时手上要是多几个Director安装序列号的话,他的那篇文章就会能更深一步去分析ProductID的生成过程。还有,应该感谢网上的那个朋友(huihuicn),如果不是他回复说注册码不能用并说PhotoCaster v2在Director v8和v8.5的注册码是不同的话,如果他安装的Director不是v8而是v8.5并且安装序列号也和我的一样的话,我也可能会出现和PhotoCaster v2破解作者一样的观念。现在,他把这个机会让给我了。
    还有,我想做一个根据ProductID计算常数的程序,一般来讲,软件虽然升级,但通常注册码计算的改动却不一定太大,但我用这个程序就是找不出来v2.0.5的9个数字,于是我想,难道v3.0.3和v2.0.5的注册码的计算变化很大吗?于是只好请求网上的那位朋友将他手上的v2.0.5版给我,我反编译一看,注册码的计算过程与v3.0.3完全一样!注册码的生成过程也跟v3.0.3基本一样,不同的有两点:(1)v3.0.3版如果得不到Director的序列号则由DRW800-12345-12345-12345代替,而v2.0.5版如果得不到则产生错误信息并退出;(2) 取“常数”的部位不同,v2.0.5如下:

:10009BB4        push    14Eh    ;二进制就是101001110b,那么就是取9个数字的1、3、6、7、8位的数字
:10009BB9        push    edx
:10009BBA        mov    ecx, ebp
:10009BBC        call    sub_10009C90  ;生成常数

    于是我又把他的破解文章里的ProductID验算了一下,发现他写的MK9-RACA是错的,而应该是MK9-QACA,常数79043没错,他使用的Director的序列号应该是:WDW700-04074-07099-20430(这个序列号对于较早免费使用Director的人来说,应该是很熟悉的噢^_^),用我做的算号器搜索了一下v2的注册码(运算时间好长,将近1个小时,从注册码的运算过程来看,用逆算法反推出注册码基本是不可能的,谁若是会的话,教教我),共找到40组可用的注册码(还会找到更多!),但和v2破解文章不同的是,我搜索到的注册码中没有他的第3个注册码:653467,我想,他算的第3个应该是错的,不信,谁有条件试试,看我说的对不对。

  • 标 题:这将是我今年的最后一篇 (116字)
  • 作 者:robot
  • 时 间:2001-11-22 23:14:59
  • 链 接:http://bbs.pediy.com

工作太忙了,还有一本书的一章需要我去写,我还没有动笔。元旦我的大学同学全班要在北京毕业10周年聚会,也许都不能参加了。

  • 标 题:版本虽旧,但可以作此教程另一种破解之法的补丁之用! (8千字)
  • 作 者:1212
  • 时 间:2001-11-22 23:41:28

PhotoCaster 2拆解手记
PhotoCaster 2是著名的多媒体创作工具Director 7的一个内置插件,它可以直
接导入用PhotoShop制作的分层PSD图形,并按原样在舞台上自动排列。未注册
版会在导入的图形中加上很多蓝色的斜线,从而导致无法正常使用。
我拆这个插件的原因除了为减轻本公司制作人员的工作负担之外,更重要的是
我在网上找到的唯一一个由著名的SIGEG组织制作的破解版竟然未能完全拆开。
致使有很多网友不得不舍弃Director 7内置的PhotoCaster 2.0.3而去使用早
期功能尚不完备的PhotoCaster 1.1.2破解版。
为了能让网友们用上最新版本,我走上了漫漫破解之路(这是我花了二十余天
业余时间拆完之后回头一看才发现的:-D)
一、首先,由于SIGEG的破解版已能成功注册,并能够去除部分斜线,因此我决
定先研究一下SIGEG的破解方法:
1、将Director 7自带的PhotoCaster 2插件复制并更名为P1.x32,将SIGEG破解
的PhotoCaster 2插件复制并更名为P2.x32,切换到MS-DOS窗口,使用DOS的文件
比较命令FC进行分析:(在提示符下输入如下指令)
FC P1.x32 P2.x32/B >diff.txt
即按二进制文件格式比较两个文件,并将结果保存到diff.txt中。
2、返回Windows 98,在记事本中打开diff.txt文件,发现两个文件共有六处不
同,将其相应地址记下。
3、使用反汇编软件Wasm 8.93对该插件进行反编译(尽管插件的扩展名为x32,
其实是32位的EXE文件格式,故而能够成功进行反编译)
4、在刚才记录的相应地址进行察看,发现六处改动的语句相同,原来均为
XOR ECX,EDX(即将寄存器ECX和EDX进行异或运算),而此句之后除第一处为
ADD EDI,ECX之外,其余均为ADD EDI,EDX,可见密码运算结果应该与这两句
有关,SIGEG对它们的处理是将第一处的XOR ECX,EDX改为XOR ECX,ECX其余
几处均改为XOR EDX,EDX(或其等效语句),从而使与EDI相加的值恒为0,
这样以图达到使密码计算程序无效的目的,然而实践证明这样做虽能正常注
册,但却不能完全去掉加在图面上的斜线(只能去掉一部分,剩下的部分
和输入的密码有关)
二、通过试验,发现PhotoCaster 2能够记录已导入的图层数、已输入的密码
值等等,因此可以断定它一定有写文件或注册表的动作。那么我们可以看一
下:
1、如果是在注册表中搞鬼的话,通常我们可以使用注册监测软件RegMon来进
行监视(一定要设置好过滤选项,让它只显示你要拆解的软件,否则...)不过
PhotoCaster似乎不是在注册表中记录信息的。
2、如果是通过文件记录的话,可以通过非常简单的方法搞定,首先将你的系
统时间改为2000年(或者是将来随便的哪一年、哪一天),然后不要运行其它
软件,直接运行Director,并且不进行其它操作,直接调出PhotoCaster进行
注册(即使注册失败也不会有任何提示,只是将输入框清空,可见PhotoCaster
是有备而来的哦!)
3、在"开始"菜单中选"查找",并设定好查找2000年的文件,结果只有几个,其
中在C:\Windows系统目录下有一个名为PhotoCaster 2 Prefs_D7.PRF的文件,
不用说,一定和注册有关!用UltraEdit或其它十六进制编辑软件打开看看吧!
其内容大致如下:
FE ED AC DC 00 00 00 64 00 00 40 87 00 00 00 00
00 00 00 00 FF 00 00 00 00 00 00 00 00 00 00 00
经试验,发现前四位为特征字,第十位到第十二位(00 40 87)为界面上各选项
的选择记录,第十三位为已输入注册号的次数(当此数达到33即十进制的51时
无论你输入任何密码均会报告注册成功,而实际上根本没有注册,可见它对穷
举试验法也有所防范),第二行的头四位用来记录已导入的层数,如果是未注
册版,允许你正常导入十层单层图,超过十层时就会发出警告,并且即使你再
导入单层图也会加上斜线。第二行的第五位至第十位为密码记录位,当未注册
成功时为FF 00 00 00 00 00,注册后则为你输入的注册号(不要以为改了这里
就行了,PhotoCaster每次都会从这里读出密码再进行计算,如果不是真正的
注册号,仍然不能正常工作),怎么样,够劲儿吧?
4、看起来不动点真格的是不行了。要想彻底拆开,恐怕还是得使用Softice。
  首先涉及到的问题就是断点的设置。通常情况下,最常用的断点设置方法
是先在密码框中随便输入一个密码,然后按Ctrl+D激活Softice,并输入
bpx hmemcpy,即当程序读取密码时中断,按F5切回程序,按下"确定"或者
是"注册"之类的按钮进行注册,Softice弹出后,用bd *命令禁止所有断点,
以免发生重复中断,接下来按F12键直到发生错误为止,一定要记住按F12的
次数,再次按Ctrl+D激活Softice,用be *使断点再次生效,然后重复上面的
步骤,但要记得少按一次F12,然后改为按F10,直到发生错误为止,记住按
F10的次数,再重复一次上述操作,F10也要少按一次,一般说来,比较简单
的加密到这里就差不多可以破解了,从最后一句(这里通常情况下都是一个
CALL语句,是用来调用出错子程序显示信息的)向上找,通常都会找到这样
的语句之一:PUSH(将数据压入堆栈)、MOV(赋值)、LEA(调用有效地址)等等,
这些都是用于保存密码的,然后会有一个比较语句如CMP或XOR等,再接下来
是一个跳转语句如JMP、JZ、JNZ等等,而这些跳转语句通常都指向或跳过出
错子程序调用,如果是相等跳转JZ则改为无条件跳转JMP,如果是不等跳转,
则改为空操作NOP(因为一个NOP只占一个字节,通常要用几个NOP填满原位)。
可输入CODE ON命令,在代码窗口中显示语句的相应十六进制码,也就是它们
在程序源文件中实际存储的样子,记录后使用UltraEdit等软件更改源程序中
相应的代码即可完成破解。
    然而现在的许多软件为了防止被这样简单破掉,经常在比较注册码后再
做一些其它的事情,使得你在上述方法中最后一步向上察看时找不到真正比
较密码的地方。不幸的是,PhotoCaster正是这种情况,此类软件通常会表
现出如下症状:不弹出对话框,直接清空输入框(PhotoCaster就是如此)。
或者先清掉原先的窗口,再弹出对话框,而不是直接弹出对话框等等。
    虽然这些软件到处兜圈子,但是仍可以轻易找到密码比较程序之所在,
你只要注意到每次使用bpx hmemcpy中断后代码窗的情形就会明白:在程序
调用Windows功能的时候,代码窗口中的地址大多为XXXX:XXXX(即4位:4位)
的格式,在经过一次JMP XXXX:XXXXXXXX语句后,代码窗口中的地址则会变
为XXXX:XXXXXXXX(4位:8位)的格式,而代码窗口下面直线的中间或者上面
直线的左边也会显示出当前程序段所在位置,如PhotoCaster.text+XXXX的
字样,这就说明已经进入了PhotoCaster的地盘了,差不多每个加密者都会
先比较一下密码然后再去做别的,因此当你按F12到XXXX:XXXXXXXX形式的地
址时就应该注意改为用F10进行追踪了。当遇到一个CALL后出错时记下它的
位置,下次再追到此处时就可以分析一下这一句前面和后面的句子了,如果
走运的话,就可以找到刚才说的比较程序了(PUSH EDI、CMP EDI,EBX、JZ等)
照样一改就可以了,或者用?EDI等命令看一下被比较的寄存器或地址内的值
说不定可以直接看到注册码呢!
    可惜PhotoCaster又不是此类情况。
    拆到这儿,我觉得应该换一种断点试一下,于是就在选层导入的对话框
中设置了一个bpx destorywindow(即在注销窗口时中断),然后追到出现渲
染框为止,因为我觉得既然未册版单层可以正常导入,必定有一个判定的地
方,通过分析发现,导入单层和多层竟然不走一个子程序,强行跳过后则无
论选中多少层都只导入最后一层,而被跳过的部分中一连调用了好几层子程序
实在不容易分清究竟是哪个子程序给画面加上了蓝条条。
    最后,我不得不面对最根本的解决办法:破译加密子程序中所有算法,
自己算出注册码!
    经过多日苦战(光笔记本就用了三、四个),终于弄清了PhotoCaster的全部
算法:它的密码共有六位(这是第一处关卡,如果不对就会出错),首先它根据
程序的序号MK9-RACA算出一个常数79043,因为我试验了很多机器证实此序号
为PhotoCaster2的不变序号,因此就没有深究它是如何计算这个常数的。接下
来它取密码的最后一位与79043相加,所得的值保存起来。然后就进入了对其它
五位密码的大段计算过程,最后将计算结果与刚才的加和相比,如果相等则通
过(别想改此处的跳转,全部程序中用到密码的地方不下六处呢,还是老老实实
地算吧)。经过反复的试验和分析,终于搞懂了其余五位的加密算法。其中包括
位替换、逆序、与常数相乘、加和等等共计五大步二十小步的计算。
    下面是我按照所破译的算法用VB作的计算程序:
界面上共有一个按钮(执行计算用)、五个文本框,这些文本框为:
text1:输入测试初值(初始化为0)
text2:输入测试终值(初始化为10000)
text3:显示计算结果(初始化为0)
text4:输入比较值(即第6位+79043的值,初始化为79043)
text5:显示状态(是否成功)
其中只有按钮中有程序,内容如下:
Private Sub Command1_Click()
Dim s(4)
Dim d(4)
Dim num, restn, source, rests, fir, sec, result
Dim i, j, p, t
fir = Text1.Text
sec = Text2.Text
result = Text4.Text + 0
For source = fir To sec
rests = source
For i = 0 To 4
s(i) = Fix(rests / 10 ^ (4 - i))
rests = rests Mod 10 ^ (4 - i)
Next i
d(0) = s(0)
d(1) = s(1)
d(2) = s(2)
d(3) = s(3)
d(4) = s(4)
For i = 0 To 4
t = s(i) Mod 5
Select Case i
Case 0
d(t) = d(t) + s(1)
d(t) = Right(d(t), 1)
Case 1
d(t) = d(t) - s(2)
If d(t) < 0 Then
d(t) = 0
End If
Case 2
d(t) = d(t) + s(3)
d(t) = Right(d(t), 1)
Case 3
d(t) = d(t) - s(4)
d(t) = Right(d(t), 1)
If d(t) < 0 Then
d(t) = 0
End If
Case 4
d(t) = d(t) + s(0)
d(t) = Right(d(t), 1)
End Select
num = d(4) * 10000 + d(3) * 1000 + d(2) * 100 + d(1) * 10 + d(0)
Select Case i
Case 0
num = num + 10 * s(1) + 7 * s(0)
Case 1
num = num - 100 * (256 - s(2)) + 11 * s(1)
Case 2
num = num + 1000 * s(3) + 13 * s(2)
Case 3
num = num - 10000 * (256 - s(4)) + 17 * s(3)
Case 4
num = num + 100000 * s(0) + 19 * s(4)
End Select
restn = num
For j = 0 To 4
p = Fix(restn / 10 ^ (4 - j))
restn = num Mod 10 ^ (4 - j)
If p >= 0 Then
p = Right(p, 1)
d(j) = p
Else
p = Right(p, 1)
p = 16 - p
p = Right(p, 1)
d(j) = p
End If
Next j
num = d(0) * 10000 + d(1) * 1000 + d(2) * 100 + d(3) * 10 + d(4)
restn = num
For j = 0 To 4
d(4 - j) = Fix(restn / 10 ^ (4 - j))
restn = restn Mod 10 ^ (4 - j)
Next j
Next i
num = d(0) * 10000 + d(1) * 1000 + d(2) * 100 + d(3) * 10 + d(4)
If num = result Then
Text3.Text = source
Text5.Text = "成功了!"
Exit For
Else
Text3.Text = 0
Text5.Text = "没找到!"
End If
Next source
End Sub

有兴趣的网友可以研究一下具体的加密算法。
最后,我用它算出了三组有效的注册码:
868541
304323
653467
希望我的工作能为从事多媒体制作的网友们提供一点方便。
也希望我的拆解过程能给爱好Crack的网友一点帮助。


                          newx@21cn.com