• 标 题:再贴一篇,进阶篇,比上面那个难一些!! (6千字)
  • 作 者:TAE!
  • 时 间:2001-7-19 11:20:48
  • 链 接:http://bbs.pediy.com

原著:raZZia
翻译:TAE![CCG]
软件 2: Ize 2.04 from Gadgetware
    Ize from Gadgetware 是一个聪明得小程序,它放了一双眼睛在你的屏幕上,
    它会跟着你的鼠标得,它有输入姓名和注册码得注册功,对付这个软件得策略
    仍然和上面一个相同:找出我们输入的数据在内存中的地址。

    步骤 1: 运行 Ize。 选择注册并输入名字和注册码,我用的是:“razzia”
    和“12345”

    步骤 2: 进入 (CTRL-D) Softice 并且在 GetDlgItemTextA 上设置断点。

    步骤 3: 离开 SoftIce 并且点击 OK。这将带你回到 Softice。你将回到函数
    GetDlgItemTextA 中。按 F11 离开函数,你将看到以下代码:
 
    mov esi, [esp + 0C]
    push 00000064
    push 0040C3A0      ;<--我们的姓名被存放到内存中的这个地方!
    mov edi, [USER32!GetDlgItemTextA]  ;<-- edi 载入一个地址 GetDlgItemTextA
    push 00004EE9
    push esi
    call edi          ;<-- 执行 GetDlgItemTextA
    push 00000064            ;<-- (你应该在这里)
    push 0040C210      ;<--我们输入的注册码将被放在这里
    push 00004EEA
    push esi
    call edi          ;<-- 再次执行 GetDlgItemTextA

    我们看见在这个代码段中函数 GetDlgItemTextA 被呼叫了两次,第一次的Call已经
    执行,用 ED 40C3A0 命令我们可以看到我们在内存中的名字。
 
    我们跟着程序来读取输入的注册码把,键入 G 并且回车,现在,我们再一次回到函数
    GetDlgItemTextA 中现在按F11离开这里,我们看看内存 40C210 中有啥,哦,是我们
    输入的注册码!
 
    现在我们知道我们输入的名字和注册码被放到哪里了,我们把这些记下来!
     
    步骤 4: 好的, 下一步呢?我们已经知道了名字和假注册码被放在哪里了,我们需要
    找出程序用这些数据干了什么,通常我们需要下断点来找出程序的那些地方读取了这
    些数,可是在这里却不需要。
    看看以下代码段:

    push 0040C210  ;<--保存我们输入的注册码 (作为下面那个Call的参数)
    call 00404490  ;<--这个CALL是干什么的呢?
    add esp, 00000004
    mov edi, eax  ;<-- 保存 EAX  (hmmmm)

    我们进如Call看看它究竟做了什么,但那是不必的,依你经验来猜猜看这个Call是干什么
    的?它计算假注册码放入了 EAX。我们继续按 F10直到我们通过了CAll并且看看 EAX 中
    的内容,用 ?EAX 命令,在我这里它显示:00003039    0000012345 "09"。

    知道了 EDI 是我们的注册码,那么我们继续:

    push 0040C3A0 ;<-- 保存我们输入的名字 (作为下个Call的参数)
    push 00409080 ;<-- 保存我们未知的内存地址 (作为下个Call的参数)
    call 004043B0 ;<-- 执行我们未知的函数
    add esp, 00000008
    cmp edi, eax  ;<--比较 EDI (我们输入的注册码) 和 EAX (未知)
    jne 004018A1  ;<--如果不同就跳

    我们看见那个CALL有两个入口参数。一个是我们输入名字的地址,另一个我们还不知道是什么,
    但我们可以用ED 409080 找到它。我们看见文本‘Ize’。这个函数使用这两个参数计算正确的
    注册码,如果你仅仅想破解它,那么你只需要在Call后面设置断点,然后查看EAX的值就可以了
    它将显示出正确的注册码来,但我们想知道它是如何计算出注册码的,所以我们跟踪到函数内部
    之后,我们将在那儿试着找到 EAX 的内容。
   
    步骤 5: 一旦进入了有趣的函数你将发现一段相当长的执行过程。对我们来说是不需要的,不需
    要列出完整的CALL清单,因为这对我们做注册码来说根本不需要。

    但是一旦找出哪一部分是对我们计算注册码所必要的时候,你就应该一步步的跟踪它,并且仔细
    的把它记下来!

    在做了这些以后,我们发现函数的第一部分计算出一些“Key”,之后这些“key”被保存在内存
    中并且带入了函数的第二部分。

    函数的第二部分计算出正确的注册码,但这是基于“Key”和我们输入的名字的!

    下面的代码是我们作出注册机所必要的:

    (在跟踪下面代码之前不要忘了记录,使用到的寄存器是下面几个:EBX指向我们输入名字的第一
    个字母,EDX 是 0,EBP 是 0,至于“key”我们说得简单些,它存放在内存地址 0040B828 中,
    它最初的值是 0xA4CC )

    :00404425 movsx byte ptr edi, [ebx + edx]  ;<-- 把名字的第一个字母放入 EDI
    :00404429 lea esi, [edx+01] ;<-- ESI 是字母所在的位数 "letter-number"
    :0040442C call 00404470  ;<-- 一个Call
    :00404431 imul edi, eax  ;<-- EDI=EDI*EAX (EAX是上面那个CAll返回的一个值)
    :00404434 call 00404470  ;<-- 又Call了
    :00404439 mov edx, esi
    :0040443B mov ecx, FFFFFFFF
    :00404440 imul edi, eax  ;<-- EDI=EDI*EAX (EAX是上面那个call返回的一个值)
    :00404443 imul edi, esi  ;<-- EDI=EDI*ESI (ESI 是字母所在的位数)
    :00404446 add ebp, edi  ;<-- EBP=EBP+EDI  (注意 EBP 最后将是我们正确的注册码)
    :00404448 mov edi, ebx  ;<--这几行计算我们输入名字的长度
    :0040444A sub eax, eax  ;<--这几行计算我们输入名字的长度
    :0040444C repnz        ;<--这几行计算我们输入名字的长度
    :0040444D scasb        ;<--这几行计算我们输入名字的长度
    :0040444E not ecx      ;<--这几行计算我们输入名字的长度
    :00404450 dec ecx      ;<-- ECX 现在已经是我们名字的长度了
    :00404451 cmp ecx, esi
    :00404453 ja 00404425  ;<-- 如果不是最后一个字母,继续循环
    :00404455 mov eax, ebp  ;<--  将 EBP 存入 EAX !!!!
    :00404457 pop ebp
    :00404458 pop edi
    :00404459 pop esi
    :0040445A pop ebx
    :0040445B ret
           
    去看看那个CALL是干什么的!
   
    :00404470 mov eax, [0040B828]    ;<-- "key" 放入 EAX
    :00404475 mul eax, eax, 015A4E35  ;<-- EAX=EAX * 15A4E35
    :0040447B inc eax                ;<-- EAX=EAX + 1
    :0040447C mov [0040B828], eax    ;<-- 用EAX中的值代替 "key"
    :00404481 and eax, 7FFF0000      ;<-- EAX=EAX && 7FFF0000
    :00404486 shr eax, 10            ;<-- EAX=EAX >>10
    :00404489 ret

    上面的循环代码对我们输入名字的所有字母进行了操作。每个字母计算出了一些值,所有的这些
    值相加到 EBP 中,之后这个值放入了 EAX,并且函数返回 EAX,那就是我们要找的,我们要知道
    的是 EAX 是如何得到这个值的!

    步骤 6: 可以做注册机了,我们把上面那段计算注册码的代码翻译为 C 语言吧!
    下面就是注册机代码: (注意:我是一个差劲的 C 程序员 :)

    #include <stdio.h>
    #include <string.h>
    main() {
        char Name[100];
        int NameLength,Offset;
        unsigned long Letter,DummyA;
        unsigned long Key = 0xa4cc;
        unsigned long Number = 0;
        printf("Ize 2.04 crack by razzia\n");
        printf("Enter your name: ");
        gets(Name);
        NameLength=strlen(Name);
        for (Offset=0;Offset<NameLength;Offset=Offset+1) {
          Letter=Name[Offset];
          DummyA=Key;
          DummyA=DummyA*0x15a4e35;
          DummyA=DummyA+1;
          Key=DummyA;
          DummyA=DummyA & 0x7fff0000;
          DummyA=DummyA >> 0x10;
          Letter=Letter*DummyA;
          DummyA=Key;
          DummyA=DummyA*0x15a4e35;
          DummyA=DummyA+1;
          Key=DummyA;
          DummyA=DummyA & 0x7fff0000;
          DummyA=DummyA >> 0x10;
          Letter=Letter*DummyA;
          Letter=Letter*(Offset+1);
          Number=Number+Letter;
        }
        printf("\nYour registration number is : %lu\n",Number);
    }