[原创]牛刀小试
2020-04-19 00:21:04 Author: bbs.pediy.com(查看原文) 阅读量:345 收藏

一个小萌新

记一次令人难忘的密码学keygenme!

友情提示! 如果看不清的话,可以下载附件!

这是一个可以让你加密算法突飞猛进的keygenme,如果你也想试试这个的话,那就先不要看我的分析过程,可以在附件里下载keygenme,然后自己试试,下面我们开始详细分析!

工具:PEID,OD,RSATool,vc6.0。

1. 进行PEID查壳,显然这是vc6编译的控制台程序,没有加壳,使用插件KANAL得知程序里面疑似使用了密码学算法,如MD5,AES , SHA-512等。

 

2. 运行keygenme,可以看出就是简单的控制台程序。先输入假码试试(name:lixu  sn:12345678),可以看到 Bad sn. 这样的提示信息。有提示信息就好办,这样容易找到判断的地方。

 

3. 进OD,查找参考文本字符串,找到Bad sn. 回车之后可以定位到第二幅图地址为401503的位置。第二幅图很明显,在第一部分对name进行判断是否满足3=< name <=20,不满足就错误,然后对sn进行长度判断,len (sn) ?= 64,不满足也跳转。

更换假sn(11111111aaaaaaaa11111111aaaaaaaa11111111aaaaaaaa11111111aaaaaaaa)

 

 

4. 根据跳转,我们很容易找到了401380这个算法函数;

 

5. 跟进函数,如下图所示它又进行name和sn字符个数的判断,和上一次判断条件相同,不满足也会提示错误!我们主要看函数 push进去的参数:eax是一个buffer,0x40是sn的字符个数,ecx是输入的sn。所以这个call对sn进行处理;跟进之后,慢慢执行,并观察buffer中数据的变化,得知sn变为大写,并且根据下图可以得知只允许0-9,a-f,A-F,

所以很显然sn必须是16进制的字符串。

总结函数1的功能:判断输入的字符串是否为16进制,同时进行小写转大写的操作。

 

 

6. 执行到算法2:看堆栈push进去的数据,0x0,0x80=128,4190D0里面有128bit数据,18FDE4里面是垃圾数据,18FDC4是算法1执行后的数据,所以猜测将算法1执行后的数据再次进行处理,并存入18FDE4。

 

跟进算法2(地址4013DF处的函数4010F0):执行到函数4053A0,看堆栈,push进去4190D0,0x80=128,18FC80一个缓冲区,这个函数将4190D0的16字节的sbox进行扩展,扩展成为176字节的扩展sbox,根据扩展密匙和PEID的插件结果可以确定是AES-128,执行到地址401157处的函数404FB0,看堆栈,push进去了4个参数(分别为0x0,sbox,18FDE4,18FDC4),所以就可以确定是AES-ecb-128模式,因为其他模式需要的参数和参数顺序不同,参数0x0确定是解密数据(0x1是加密数据),18FC80里面存着扩展之后sbox,18FDE4是缓冲区,18FC80是我们转为大写的sn;所以就是对sn进行解密操作,因为这个模式每次只能对128 bit数据进行加解密,而sn有256 bit,所以需要进行两次解密。解密结果存入18FDE4。

 

 

7. 执行到算法3(地址4013F0处的401210函数):看堆栈;18FDA4是一个缓冲区,0x20说明数据有32个字节,18FDE4是AES解密后的数据。

 

跟进算法3(地址4013F0处的401210函数):进去之后会发现先压入了一堆数据,也就是公匙n,然后是四个变量的初始化n,e,c,m,紧接着函数406930 是AES解密之后的值转大数得到m,函数4068D0是对公钥e 0x10001转大数(也是从这里猜测使用RSA,结合前面四个变量的初始化,还有后面的powmod函数才确定使用RSA),然后是公匙n转大数(这里要注意大数在内存中的存储格式,以便后面的注册机的编写),然后进行函数406D30 powmod 求c( c = m ^ e mod n),然后将c由大数转换为16进制数放入缓冲区,c就是name cal函数执行的结果前面拼接上16字节的数据的一个32字节的数据。

将c存入18FDA4。后面是四个BN_free和一个BN_CTX_free。

 

 

8. 这里进行了三次比较,因为要进行RSA加密,m必须小于n,所以将name call(就是将name进行加密的函数)执行结果前面拼接了第一个字节为0,第二个字节为2,中间3-15是随机数,第十六个字节为0的这样一组16个字节的数据,这样就能保证m<n;同时也算是作者的一个加密小心思。当这三个字节的数据为真时,才会进行name cal函数。(大家看到我的数据不满足,因为真的sn输入太费劲,所以我就输了假码的,然后改变零标志位才执行到name call函数,大家在刚开始也可以这么做)。

 

9. 执行到函数 401190 也就是name call,看堆栈,18FEC5是一个变量,18FD94是缓冲区,0x4是name字符个数,18FE20是输入的字符。然后进入name call,这里重复执行了同一个4010B0函数。但是数据和拼接的数据不同,也就是第一次是将原始数据name进行拼接(拼接内容0xDE, 0xED, 0xBE, 0xEF)后进行一次4010B0加密,将加密结果再进行一次拼接(0xB9, 0x79, 0x37, 0x9E)然后再进行一次函数4010B0加密。

 

 进入函数 4010B0;可以看到4019B0函数和401560函数。

进入函数4019B0;看到这里很显然这是sha-512散列算法,因为这一组数据是sha-512散列时使用的加密常数,同时结合PEID的结果更加确定进行sha-512进行散列。

 

进入函数401560,再进入函数4080F0里面进行常数的初始化,很显然这是MD5散列需要用到的常数,同时结合PEID的结果更加确定是MD5散列。

 

 

name call 加密函数总结:name拼接数据进行 sha-512散列得到A,将A结果再进行MD5散列得到B,将B拼接字符后再进行sha-512散列产生C,将C再进行MD5散列产生一个16字节的数据D。

10. 最后进行比较,将RSA加密后的后16字节和D进行比较,相同的话就正确。

 

11. 总结上述过程:

(1) 先进行name和sn字符数的判断,不满足条件就跳转结束。

(2) 对sn进行判断,是否是16进制,是的话就转为大写SN,不是16进制就跳转结束。

(3) SN进行AES_ecb_128_decrypt 解密计算产生m。

(4) m进行RSA计算产生c。(c = m ^ e mod n)

(5) 对于c的前16个字节A进行判断,是否满足第一字节为0,第二字节为2,第16字节为0,有一项不满足就跳转结束。后16字节为B。

(6) 对name进行两次sha-512_MD5 离散加密产生C。

(7) 然后B和C进行比较,相同就成功,不同就跳转结束。

 

12. 注册机思路:

(1) 将输入的name先进行字符串拼接,然后进行 sha-512散列加密A,将A结果再进行MD-5加密B,将B拼接字符后再进行sha产生C,将C再进行MD5散列产生一个16字节的数据D。

(2) 将D前面拼接一个第一个字节为0,第二个字节为2,中间3-15是随机数,第十六个字节为0的这样一组16个字节的数据,产生E这样一个 32字节。

(3) 在算法3(地址4013F0处的401210函数)我们得知了公匙n,利用RSATool工具进行n的因式分解,产生两个大素数p和q,便能计算出 φ(n) = (p-1) (q-1) ,使用欧几里德扩展算法,很容易就能求出d。利用RSATool,输入e后,单击“Calc.D”也可以计算出d,因为我电脑配置较低,因式分解了一个小时左右。使用公式F = E^d mod n,计算出F,大小32字节。

n:69823028577465AB3991DF045146F91D556DEE8870845D8EE1CD3CF77E4A0C39  (256 bit)

d:390A684CB713378FFD5CCE8C4000B5D6A2BB9F29B63D395E6BE6E9DD941527BD  (256 bit)

(4) 然后F进行AES加密产生G  32字节。

(5) 最后G就是注册码。

(6) 公式如下图

 

13. 

注册机源码:

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

#include <windows.h>

extern "C" {

#include "openssl/bn.h"

#include "openssl/md5.h"

#include "openssl/sha.h"

#include "openssl/aes.h"

}

#pragma comment(lib, "ssleay32.lib")

#pragma comment(lib, "libeay32.lib")

void keygen()

{

char name[25] = {0};

printf(" input name (3 <= len(name) <= 20): \n");

scanf("%s",name);

    int name_len = strlen(name);

unsigned char magic2[] = {0xde, 0xed, 0xbe, 0xef};

    unsigned char m[0x40], n[0x20], o[0x20], out[0x20];

    unsigned char *p;

int i;

    BIGNUM *b_n, *b_e, *b_c, *b_m, *b_d, *b_c1;

    BN_CTX *bn_ctx;

memcpy(name+name_len, magic2, 4);   

SHA512((unsigned char *)name, name_len+4, m);

printf("sha512: ");

for(i=0; i<0x40; i++)

{

printf("%x",m[i]);

}

    printf("\n");

printf("MD5: ");

MD5(m, 0x40, n);

for(i=0; i<0x10; i++)

{

printf("%x",n[i]);

}

    printf("\n\n");

unsigned char magic1[] = {0xb9, 0x79, 0x37, 0x9e};

memset(o, 0, 0x20);

memcpy(o, n, 0x10);

memcpy(o+16, magic1, 4);

printf("sha512: ");

    SHA512(o, 0x14, m);

for(i=0; i<0x40; i++)

{

printf("%x",m[i]);

}

    printf("\n");

printf("MD5: ");

MD5(m, 0x40, n+0x10);

for(i=0; i<0x10; i++)

{

printf("%x",n[i+0x10]);

}

    printf("\n\n");

    n[0] = 0;

    n[1] = 2;

    n[15] = 0;

    for (i=2; i<15; i++)

    {

        n[i] = rand();

    }

    bn_ctx = BN_CTX_new();

    b_n = BN_new(); b_e = BN_new(); b_c = BN_new(); b_m = BN_new(); b_d = BN_new();b_c1 = BN_new();

    // p: 979BE0C9EECE7426C9FD28C2D6E7772B

    // q: B22831D15714EB91CD83340B4837182B

    // e: 10001

    // d: 390A684CB713378FFD5CCE8C4000B5D6A2BB9F29B63D395E6BE6E9DD941527BD

    // n: 69823028577465AB3991DF045146F91D556DEE8870845D8EE1CD3CF77E4A0C39

    BN_bin2bn(n, 0x20, b_c);

    BN_hex2bn(&b_d, "390A684CB713378FFD5CCE8C4000B5D6A2BB9F29B63D395E6BE6E9DD941527BD");

    BN_hex2bn(&b_n, "69823028577465AB3991DF045146F91D556DEE8870845D8EE1CD3CF77E4A0C39");

BN_mod_exp(b_m, b_c, b_d, b_n, bn_ctx);

    p = (unsigned char *)b_m->d;

    for (i=0; i<0x20; i++)

    {

        out[i] = p[0x20-i-1];

    }

    printf("m = c ^ d(mod n) = :\n");

    for (i=0; i<0x20; i++)

    {

        printf("%02X ", out[i] & 0xFF);

    }

printf("\n");

    BN_free(b_n); BN_free(b_e); BN_free(b_c); BN_free(b_m);

    BN_CTX_free(bn_ctx);

unsigned char ucEncryptKey[16] = {0};

unsigned char ucEncryptOutData[32] = {0};

unsigned char key[] =

{0x48,0x0B,0x62,0xC3,0xAC,0xD6,0xC8,0xA3,0x6B,0x18,0xD9,0xE9,0x06,0xCD,0x90,0xD2};

AES_KEY aesEncryptKey;

memcpy(ucEncryptKey,key,0x10);

printf("\n\n");

AES_set_encrypt_key(ucEncryptKey, 128, &aesEncryptKey);

for (i = 0; i < 2; i++)

{

AES_ecb_encrypt(out + 16 * i, ucEncryptOutData + 16 * i, &aesEncryptKey, AES_ENCRYPT);

}

printf("after encrypt = ");

for (i = 0; i < 32; i++) {

printf("%02x ", ucEncryptOutData[i]);

}

printf("\n\n");

}

int main(int argc, char *argv[])

{

    srand(time(NULL));

    keygen();

    return 0;

}

[求职]想求职找工作,请来看雪招聘投递简历!

最后于 1分钟前 被kanxue编辑 ,原因:


文章来源: https://bbs.pediy.com/thread-258883.htm
如有侵权请联系:admin#unsafe.sh