编译器C-Free V352注册算法分析

编译器C-Free V352注册算法分析

作者:prince

ASPack 2.12 脱壳很简单,ASPackDie也可以轻松对付。无自校验,脱壳后可直接运行。注册情况:机器码给出,输入用户名prince和序列号8764321,确定,提示重启验证。
说到重启验证,最简单最直接的就想到注册表,确认一下,打开注册表搜索用户名prince,果然找到在\HKEY_LOCAL_MACHINE\SOFTWARE\C-Free\3.5下,同样列在其中的还有我们输入的假码87654321和我机器上的机器码(MachineCode)2781318776。这下我们就可以确定了软件确实是通过注册表来进行重启验证的。目标如此明确,载入脱壳后的程序,下断点RegQueryValueA,恩?没有,再下RegQueryValueExA,呵呵,可以了。F9运行,马上被断下,看堆栈,ValueName = "layout text",不是我们想要的,继续F9,注意断点不能取消,因为后面的对注册表的读取还是要靠这个函数,再次断下,还不是,再运行...,大约81次,堆栈中显示ValueName
= "MachineCode",这就是要读取机器码了,注意。再次F9,断在读取RegistryCode也就是假码的地方,呵呵,敏感。再接下来是读取UserName,即用户名。到这里,计算注册码的准备工作就做完了,可是在那里计算的呢?作者在软件启动的时候将所有的配置信息连同机器码,用户名和注册码一起读出,而且也没有读出后立即计算注册码继续比较,这就给我们定位注册码计算造成了困难。这个时候我们该怎么办?两个办法,一个就是下面都进行单步跟踪,直到找到关键函数为止,毕竟软件在启动前肯定会计算注册码的;另外一个办法就是在内存中搜索假码然后下内存断点,这个方法倒是即快又方便,但是要掌握时机,具体什么时候搜内存要看代码的动作。我通常都是先搜内存,不行的话只好一步一步的单跟了,做Cracker要有耐心。当你在堆栈中看到ValueName = "EditorTabWidth"的时候,小心,呵呵,我们到了藏有宝藏的秘密入口了。

----------------------------------------------------------------------------------------
00419654  |.>MOV WORD PTR DS:[EBX+10],4B8
0041965A  |.>MOV EDX,unpacked.005DAA4C        ;  ASCII "EditorTabWidth"
0041965F  |.>LEA EAX,DWORD PTR SS:[EBP-620]
00419665  |.>CALL unpacked.0058D308
0041966A  |.>INC DWORD PTR DS:[EBX+1C]
0041966D  |.>MOV EDX,DWORD PTR DS:[EAX]
0041966F  |.>MOV EAX,ESI
00419671  |.>CALL unpacked.004E936C
00419676  |.>MOV ECX,DWORD PTR DS:[EDI]
00419678  |.>MOV EDX,2
0041967D  |.>MOV DWORD PTR DS:[ECX+A0C],EAX
00419683  |.>LEA EAX,DWORD PTR SS:[EBP-620]
00419689  |.>DEC DWORD PTR DS:[EBX+1C]
0041968C  |.>CALL unpacked.0058D520
00419691  |.>MOV ECX,DWORD PTR DS:[EDI]
00419693  |.>MOV BYTE PTR DS:[ECX+8F4],0
0041969A  |.>MOV EAX,DWORD PTR DS:[EDI]
0041969C  |.>INC DWORD PTR DS:[EAX+8F0]
004196A2  |.>CALL unpacked.00462F18           ;  取机器码送EAX
004196A7  |.>MOV DWORD PTR SS:[EBP-764],EAX
004196AD  |.>MOV WORD PTR DS:[EBX+10],98
004196B3  |.>MOV EDX,DWORD PTR DS:[EDI]
004196B5  |.>MOV ECX,DWORD PTR DS:[EDX+8E4]
004196BB  |.>CMP ECX,DWORD PTR SS:[EBP-764]
004196C1  |.>JNZ unpacked.00419772
004196C7  |.>LEA EAX,DWORD PTR SS:[EBP-884]
004196CD  |.>PUSH EAX                         ; /Arg2
004196CE  |.>MOV EDX,DWORD PTR SS:[EBP-764]   ; |[EBP-764]==机器码
004196D4  |.>PUSH EDX                         ; |Arg1
004196D5  |.>CALL unpacked.00462F70           ; \关键CALL,跟进
004196DA  |.>MOV WORD PTR DS:[EBX+10],4C4     ;  上面的这个CALL计算真码,存放在EAX中(哎!又是明文

)
004196E0  |.>ADD ESP,8
004196E3  |.>LEA EDX,DWORD PTR SS:[EBP-884]   ;  真码地址送入EDX
004196E9  |.>LEA EAX,DWORD PTR SS:[EBP-624]
004196EF  |.>CALL unpacked.0058D308
004196F4  |.>INC DWORD PTR DS:[EBX+1C]
004196F7  |.>MOV EDX,DWORD PTR DS:[EAX]
004196F9  |.>MOV EAX,DWORD PTR DS:[EDI]
004196FB  |.>MOV EAX,DWORD PTR DS:[EAX+8E8]   ;  假码送入EAX,呵呵,准备比较了哦
00419701  |.>CALL unpacked.004ECED4           ;  比较函数,嘿嘿。
00419706  |.>TEST EAX,EAX
00419708  |.>LEA EAX,DWORD PTR SS:[EBP-624]

----------------------------------------------------------------------------------------

我们要找算法的计算过程,所以004196D5 处跟进:

----------------------------------------------------------------------------------------
00462F70  /$>PUSH EBP
00462F71  |.>MOV EBP,ESP
00462F73  |.>ADD ESP,-0C
00462F76  |.>XOR EDX,EDX
00462F78  |.>PUSH EBX
00462F79  |.>PUSH ESI
00462F7A  |.>PUSH EDI
00462F7B  |.>MOV EBX,25                       ;  EBX=0x25
00462F80  |.>MOV ECX,DWORD PTR SS:[EBP+8]     ;  [EBP+8]==机器码
00462F83  |.>XOR ECX,90909090                 ;  机器码异或90909090,ECX==35571EE8
00462F89  |.>MOV EAX,ECX
00462F8B  |.>DIV EBX                          ;  上面异或的结果除以25
00462F8D  |.>MOV EAX,EDX                      ;  余数送EAX
00462F8F  |.>CMP EAX,11                       ;  余数同0x11比较
00462F92  |.>JGE SHORT unpacked.00462F97      ;  大于等于就直接压栈准备函数调用
00462F94  |.>ADD EAX,11                       ;  否则余数+0x11,然后再入栈
00462F97  |>>PUSH EAX                         ; /Arg3 余数入栈
00462F98  |.>LEA EDX,DWORD PTR SS:[EBP-C]     ; |
00462F9B  |.>PUSH EDX                         ; |Arg2
00462F9C  |.>PUSH ECX                         ; |Arg1 上面异或结果入栈
00462F9D  |.>CALL unpacked.005861F0           ; \跟进
00462FA2  |.>MOV ECX,DWORD PTR SS:[EBP+C]
00462FA5  |.>ADD ESP,0C
00462FA8  |.>MOV ESI,ECX
00462FAA  |.>XOR EAX,EAX
00462FAC  |.>LEA EDI,DWORD PTR SS:[EBP-C]

----------------------------------------------------------------------------------------

00462F9D 处继续跟进:

----------------------------------------------------------------------------------------
005861F0  /$>PUSH EBP
005861F1  |.>MOV EBP,ESP
005861F3  |.>MOV EAX,DWORD PTR SS:[EBP+10]
005861F6  |.>MOV EDX,DWORD PTR SS:[EBP+8]
005861F9  |.>CMP EAX,0A                       ;  余数同0A比较
005861FC  |.>PUSH 61
005861FE  |.>SETE CL                          ;  条件为假,所以CL清零
00586201  |.>AND ECX,1
00586204  |.>CMP EAX,0A                       ;  仍然同0A比较
00586207  |.>PUSH ECX
00586208  |.>PUSH EAX
00586209  |.>MOV ECX,DWORD PTR SS:[EBP+C]
0058620C  |.>PUSH ECX
0058620D  |.>JNZ SHORT unpacked.00586213
0058620F  |.>MOV EAX,EDX
00586211  |.>JMP SHORT unpacked.00586215
00586213  |>>MOV EAX,EDX
00586215  |>>PUSH EAX                         ; |Arg1
00586216  |.>CALL unpacked.00586160           ; \跟进
0058621B  |.>ADD ESP,14
0058621E  |.>POP EBP
0058621F  \.>RETN

----------------------------------------------------------------------------------------

没有结果,00586216处继续跟进:

----------------------------------------------------------------------------------------
00586160  /$>PUSH EBP
00586161  |.>MOV EBP,ESP
00586163  |.>ADD ESP,-24
00586166  |.>PUSH EBX
00586167  |.>PUSH ESI
00586168  |.>PUSH EDI
00586169  |.>MOV EDI,DWORD PTR SS:[EBP+10]    ;  [EBP+10]为前面压栈的余数
0058616C  |.>MOV ESI,DWORD PTR SS:[EBP+8]     ;  [EBP+8]为机器码异或90909090的结果
0058616F  |.>MOV EBX,DWORD PTR SS:[EBP+C]
00586172  |.>CMP EDI,2                        ;  余数同0x2比较
00586175  |.>JL SHORT unpacked.005861C4       ;  小于跳
00586177  |.>CMP EDI,24                       ;  同0x24比较
0058617A  |.>JG SHORT unpacked.005861C4       ;  大于则跳
0058617C  |.>TEST ESI,ESI
0058617E  |.>JGE SHORT unpacked.0058618C
00586180  |.>CMP BYTE PTR SS:[EBP+14],0
00586184  |.>JE SHORT unpacked.0058618C
00586186  |.>MOV BYTE PTR DS:[EBX],2D
00586189  |.>INC EBX
0058618A  |.>NEG ESI
0058618C  |>>LEA ECX,DWORD PTR SS:[EBP-24]    ;  下面为关键循环
0058618F  |>>/MOV EAX,ESI                     ;  ESI==机器码异或结果
00586191  |.>|XOR EDX,EDX                     ;  EDX清零
00586193  |.>|DIV EDI                         ;  将上面异或结果除以压栈的余数
00586195  |.>|MOV BYTE PTR DS:[ECX],DL        ;  上面计算的余数的一个字节写入内存
00586197  |.>|INC ECX
00586198  |.>|MOV EAX,ESI
0058619A  |.>|XOR EDX,EDX
0058619C  |.>|DIV EDI
0058619E  |.>|MOV ESI,EAX                     ;  又做了一次相同的计算,商送入ESI
005861A0  |.>|TEST EAX,EAX                    ;  直到EAX==0为止
005861A2  |.>\JNZ SHORT unpacked.0058618F     ;  不为0则继续循环
005861A4  |.>JMP SHORT unpacked.005861BD
005861A6  |>>/DEC ECX
005861A7  |.>|MOV AL,BYTE PTR DS:[ECX]        ;  内存[ECX]的值送入AL
005861A9  |.>|CMP AL,0A                       ;  同0A比较
005861AB  |.>|JGE SHORT unpacked.005861B5     ;  大于等于跳到下面进行另外的计算
005861AD  |.>|ADD EAX,30                      ;  该值加上0x30
005861B0  |.>|MOV BYTE PTR DS:[EBX],AL        ;  这个值就是注册码的第i个值,写入内存保存起来
005861B2  |.>|INC EBX                         ;  继续下一步
005861B3  |.>|JMP SHORT unpacked.005861BD
005861B5  |>>|ADD AL,BYTE PTR SS:[EBP+18]     ;  上面如果大于0A,则加上[EBP+18]==61,
005861B8  |.>|ADD AL,0F6                      ;  再加上0F6
005861BA  |.>|MOV BYTE PTR DS:[EBX],AL        ;  作为注册码的第i个值写入内存
005861BC  |.>|INC EBX
005861BD  |>> LEA EDX,DWORD PTR SS:[EBP-24]   ;  取地址[EBP-24]
005861C0  |.>|CMP ECX,EDX                     ;  比较是否结束循环
005861C2  |.>\JNZ SHORT unpacked.005861A6     ;  没有结束则继续
005861C4  |>>MOV BYTE PTR DS:[EBX],0
005861C7  |.>MOV EAX,DWORD PTR SS:[EBP+C]
005861CA  |.>POP EDI
005861CB  |.>POP ESI
005861CC  |.>POP EBX
005861CD  |.>MOV ESP,EBP
005861CF  |.>POP EBP
005861D0  \.>RETN

----------------------------------------------------------------------------------------

呵呵,过程清晰明了吧?第一次循环:机器码异或0x90909090的结果除以前面求得的压栈的余数,然后这个过程的余数写入内存保留,商作为下一次循环的变量继续循环。第二次循环:将第一次循环中写入内存的值逆序读取出来,同0xA比较,小于就直接加上0x30,作为注册码的第i个字符写入内存;大于等于则加上61,再加0xF6,取低字节作为注册码的第i个字符写入内存。用户名没有参与计算。也不知道我说明白了没有,还是看程序来得直接,C源码的注册机:

-----------------------------------------------------------------------------------------

#include "stdafx.h"
#include "stdlib.h"
#include "stdio.h"

int main(int argc, char* argv[])
{
        char chKey[128] = {0};
        unsigned int unXORCode, unRemainder, unQuotient, unTmp, unMachineCode;
        printf("Please Key in the Machine Code:\n");
        scanf("%d", &unMachineCode);

        unXORCode   = unMachineCode ^ 0x90909090;
        unRemainder = unXORCode % 0x25;
        unQuotient  = unXORCode;
        if (unRemainder < 0x11)
        {
                unRemainder += 0x11;
        }

        int i;
        i = 0;
        while (unQuotient != 0)
        {
                unTmp          = unQuotient % unRemainder;
                unQuotient /= unRemainder;
                if (unTmp >= 0xa)
                {
                        unTmp = unTmp + 0x61 + 0xf6;
                        unTmp &= 0x0ff;
                        chKey[i] = unTmp;
                }
                else
                {
                        chKey[i] = unTmp + 0x30;
                }
                i++;
        }
        printf("Key is: \n");
        while (i >= 0)
        {
                printf("%c", chKey[i]);
                i--;
        }
        printf("\n");

        return 0;
}

-----------------------------------------------------------------------------------------

 

你可能感兴趣的:(编译器C-Free V352注册算法分析)