首页
社区
课程
招聘
[原创][原创]牛刀小试
发表于: 2020-4-15 21:14 7803

[原创][原创]牛刀小试

2020-4-15 21:14
7803

一个小萌新

记一次令人难忘的密码学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;


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2020-4-18 23:19 被kanxue编辑 ,原因:
上传的附件:
收藏
免费 2
支持
分享
最新回复 (4)
雪    币: 95
活跃值: (291)
能力值: ( LV6,RANK:87 )
在线值:
发帖
回帖
粉丝
2
key:KCTF
sn:0c5ce379f2102313d7420ec4883e25d07b935f98e2b0d9819275450cfda7086e
上传的附件:
2020-4-16 16:13
0
雪    币:
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
3
分析的很详细啊
2020-6-10 11:24
0
雪    币: 259
活跃值: (283)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
mark
2020-6-10 11:47
0
雪    币: 258
活跃值: (124)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
406
5
大佬
2020-6-15 22:19
0
游客
登录 | 注册 方可回帖
返回
//