2021.12.12基于用户名/序列号的软件保护机制分析

Posted ohmysoni_s_zhuo远

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021.12.12基于用户名/序列号的软件保护机制分析相关的知识,希望对你有一定的参考价值。


一、实验环境和开发工具

硬件环境:
处理器 i5-8300H
内存(RAM) 8G
硬盘 476G

软件环境:
Windows 10

开发工具:
Visual studio 2019
IDA PRO7.5

二、任务描述

针对所给的程序CM.exe,要求:

1.软件保护机制破解:序列号求解
(1)用户名为自己的班学号,如 19219101
(2)序列号形如:Q1T33-6IOYL-U6NQW-QCQM8-SNCJT

2.软件保护机制的安全性分析
(1)该保护机制是否存在问题,试分析之
(2)若保护机制存在问题,请提出改进的建议

3.写出注册机。(选做)

4.撰写详细的实验报告。

三. 分析过程

首先尝试了直接反汇编Crack.exe,无法识别,发现其大多内容是Unexplored,可读性不高,且开头有Rar的字段显示,推测其为压缩文件,对Crack.exe进行解压。

提示需要解压密码,根据同目录下的”你相信自己所看到的吗?”txt,推测解压密码就是”你相信自己所看到的吗?”解压成功得到CM.exe.使用IDA PRO反汇编CM.exe。

反汇编后的代码没有主函数,不知道代码在哪里开始,逆向思维反过来推,通过View-open subviews-Strings找到提示failure的字符串aFailure

再通过Search-Text找到调用该字符串地址的函数loc_401885

再通过Search-Text找到调用loc_401885的段loc_401831
发现跳转至loc_401885(输出验证失败)的条件是edi!=eax,输入验证成功的条件是edi==eax,而edi又与esi有关重点关注esi和****eax的由来

再通过Search-Text找到调用loc_401831的段,发现除了他自己以外没人调用他,那么就去分析前面的代码段。


易看出这段在检查输入的用户名是否为8位数字,判断输入的序列号是否为5段,并将去除‘-’的序列号作为sub_401700的参数,通过esi返回。

查看sub_401700,进行反编译

sub_401700将满足条件的字节(A~Z),进行减‘A’模26后+‘4’的操作。
即将输入的序列号中所有的字母进行了该处理,字母外的字符保持不变。
这就是esi的由来。

再返回loc_401831,查看得到eax的过程。发现eax首先存储着输入的用户名然后作为参数被sub_4018E0调用.

查看sub_4018E0

发现其调用了sub_401590,有五个参数,(输入的用户名,用户名位数8,明文数组,序列号25,和一个数组).查看该明文数组,其内部为 0xC8 ,0xED,0xBC,0xFE,0xB0 ,
0xB2,0xC8,0xAB,0xCA,0xB5,
0xD1,0xE9,0xBF,0xCE,0xB3,
0xCC,0x31,0x39,0x32,0x31,
0x39,0x31,0x2D,0x32,0x00 ;

查看sub_401590,发现为RC4算法,以输入的用户名为密钥

值得一提的是,与一般的RC4算法不同,一般的RC4算法密钥数组和状态数组是分开存储的,在这段代码中将长为512字节的数组前256位当成状态数组,后256位当成密钥数组.该密钥数组即重复填充用户名填充至256位形成的,加密也是一步到位。

我们知道RC4加密后的输出为明文与随机密钥流异或的结果,结果大概率不是我们想要的序列号。在RC4算法结束后,进行了如下图所示的操作

翻译成C++即

其中x[i]为RC4加密后的结果。最后将v8中的序列号每4个4个相加,将和存储在eax中返回。

分析至此,整个程序的逻辑框架就清晰了。程序将输入的用户名作为初始密钥与一固定明文进行RC4加密,再将得到的密文转换为字符串A,将A以4字节分组,再将每组相加在一起得到值x。将输入的序列号B进行转换成字符串C, 将C以4字节分组,再将每组相加在一起得到值y。若x=y,则验证通过;若x!=y,验证失败。

整个程序流程图如下:

四、序列号求解

4.1 不构造注册机

如果不构造注册机,我们只要首先截取到RC4加密转化得到的字符串,并将该字符串进行逆转化(即将字符串中的-‘A’+’4’再加26直至该字节大于’A’),便能得到所需的序列号。

调试运行到这一行时能看到RC4加密转化得到的字符串

我输入的是19219119,在这里截取到的字符串为BQ06JRTDVYD6A1PIOFDAS69D8

进行逆转化得到OD06W-EGQIL-Q6N1C-V0SQN-F69Q8
进行输入

求解成功

4.2 构造注册机

构造注册机,就要关注输入用户名后的加密过程。首先实现RC4算法

void RC4(int *C, char* key)

    int S[256];//状态数组
    int T[256];
    int  count = 256;
    for (int i = 0; i < 256; i++)
    
        S[i] = i;
        int tmp = i % count;
        T[i] = key[tmp];
    
    int j = 0;
    for (int i = 0; i < 256; i++) 
        j = (j + S[i] + T[i]) % 256;
        int tmp;
        tmp = S[j];
        S[j] = S[i];
        S[i] = tmp;
    
    int length = 25;
    int i;
    i = 0, j = 0;
    for (int p = 0; p < length; p++)
        i = (i + 1) % 256;
        j = (j + S[i]) % 256;
        int tmp;
        tmp = S[j];
        S[j] = S[i];
        S[i] = tmp;
        int k= (S[(S[i] + S[j]) % 256]);
        C[p] = C[p] ^ k;
    


然后构造明文数组, int plaintext[25] = 0xC8 ,0xED,0xBC,0xFE,0xB0 ,
0xB2,0xC8,0xAB,0xCA,0xB5,
0xD1,0xE9,0xBF,0xCE,0xB3,
0xCC,0x31,0x39,0x32,0x31,
0x39,0x31,0x2D,0x32,0x00 ;
初始密钥为从键盘输入.然后将RC4加密后的结果进行
某操作
然后进行逆转化
char* fun(char a1[], int a2)

char v3 = 0;
char v6;
char* v2 = a1;
for (int i = 0; i < a2; i++)

v3 = v2[i];
if ((v3 < ‘A’ || v3 > ‘Z’))
arr[i] = v3;
continue;

else
v6 = ‘\\0’;
v6 = v3 - ‘A’ + ‘4’;
while (v6 < ‘A’)
v6 = v6 + 26;

arr[i] = v6;
continue;


return arr;

运行注册机

得到的结果与上述不构造注册机得到的结果是相同的,自然验证也是通过的。

2.5 安全性分析

2.5.1 保护机制分析

暂无

2.5.2 改进建议

一、放弃RC4算法,现阶段对RC4的攻击方法已有很多。

二、改进验证算法。不应该采取比较字节和这种对应多种输入的算法,应该考虑使用Hash算法,比如SHA-256,实现单向性和抗碰撞,避免被破解者获取到关键的相关信息。

2.6 注册机源代码

#include<iostream>
#include<string>
#include <Windows.h> 
using namespace std;
char arr[26];
char tmp[31];
char* fun(char a1[], int a2)

    char v3 = 0;
    char v6;
    char* v2 = a1;
 
    for (int i = 0; i < a2; i++)
    
        v3 = v2[i];
        if ((v3 < 'A' || v3 > 'Z')) 
            arr[i] = v3;
            continue;
        
        else 
            v6 = '\\0';

            v6 = v3 - 'A' + '4';
            while (v6 < 'A') 
                v6 = v6 + 26;
            
            arr[i] = v6;
            continue;
        
    

    return arr;

void RC4(int *C, char* key)
    int S[256];
    int T[256];
    int  count = 256;

    for (int i = 0; i < 256; i++)
    
        S[i] = i;
        int tmp = i % count;
        T[i] = key[tmp];
    
    int j = 0;
    for (int i = 0; i < 256; i++)
        j = (j + S[i] + T[i]) % 256;
        int tmp;
        tmp = S[j];
        S[j] = S[i];
        S[i] = tmp;
    
    int length = 25;
    int i;
    i = 0, j = 0;
    for (int p = 0; p < length; p++)
        i = (i + 1) % 256;
        j = (j + S[i]) % 256;
        int tmp;
        tmp = S[j];
        S[j] = S[i];
        S[i] = tmp;
        int k= (S[(S[i] + S[j]) % 256]);
        C[p] = C[p] ^ k;
     

int main() 
    char *s;
    //char a[26] =  "BQ06JRTDVYD6A1PIOFDAS69D8" ;
    // OD06W-EGQIL-Q6N1C-V0SQN-F69Q8
    cout << "请输入用户名:";
    char Key[9];
    cin >> Key;
    char key[256] =  "" ;
    for (int i = 0; i < 256; i = i + 8) 
        key[i] = Key[0];
        key[i + 1] = Key[1];
        key[i + 2] = Key[2];
        key[i + 3] = Key[3];
        key[i + 4] = Key[4];
        key[i + 5] = Key[5];
        key[i + 6] = Key[6];
        key[i + 7] = Key[7];
    
    int x[25] =  0xC8 ,0xED,0xBC,0xFE,0xB0 ,
             0xB2,0xC8,0xAB,0xCA,0xB5,
             0xD1,0xE9,0xBF,0xCE,0xB3,
             0xCC,0x31,0x39,0x32,0x31,
             0x39,0x31,0x2D,0x32,0x00 ;
    RC4(x, key);
    char v8[26];
    char v4;
    balabalabla
    v8[25] = '\\0';
    cout << endl;
    cout << "RC4加密后转化后的字符串为:" << v8 << endl;
    cout << endl;
    int j = 0;
    for (int i = 0; i < 25; i=i+5,j++) 
    
            tmp[i+j] = v8[i]; //tmp[0]=v8[0] tmp[5]=v8[4] tmp[11]=v8[9]
            tmp[i+j + 1] = v8[i + 1];
            tmp[i+j + 2] = v8[i + 2];
            tmp[i+j + 3] = v8[i + 3];
            tmp[i+j + 4] = v8[i + 4];
            tmp[i+j + 5] = '-';
    
    tmp[29] = '\\0';
    s = fun(tmp, 31);
    s[29] = '\\0';  
    cout << "序列号为:"<<s;
    return 0;

总结

逆向分析是信息安全专业学习必须掌握的基本技能。通过本次实验
1) 阅读汇编代码的能力得到提高。汇编语言看似枯燥无味,可读性不高,但寄存器的各种操作都接近代码的本质,只要稍加分析,问题的脉络,漏洞的原理就清晰了。一开始是有些生疏,各种跳转指令都得去搜索,各种mov lea指令也是不能理解。但经过一段时间的精心阅读和断点调试,我逐渐摸到逆向分析的门道。正着走不通的路就反过来走,动态分析前尽可能静态分析找到问题所在。对逆向分析的运用,随着实验的深入,是越来越深刻,而且各种思路也是层出不穷。
2) 思考的能力得到提高。单一用户名对应的序列号究竟有多少种?我本以为只是6个分组的置换,但通过动手实践我可以交换他们对应位置上的数,可以A处增加B处所减少的值…我发现问题并没有要求的那么简单,只要深入去思考,花时间去学习,能够看清很多表面下的东西。

以上是关于2021.12.12基于用户名/序列号的软件保护机制分析的主要内容,如果未能解决你的问题,请参考以下文章

主机为ubuntu12.04,虚拟机为windows7旗舰版,在win7中映射网络驱动器如何实现不输用户名和密码?

一机一码的软件?是怎么回事?可以在另一台电脑上用吗?

搞机助手重制版和原版区别

第 1 题 连续问题

一机一码怎么设置?"

Python的数值类型与序列类型