最后修改时间是2019/04/11 22:10:34。 猜测大概2019/04/11
22点11分之前,某一刻用户运行了genprik,生成了密钥加密文件。
加密算法已知,通过分析密文,只能得出密钥第1个字节是0x25第2个字节是0x61。 求完整的16个字节的密钥。
刚开始用IDA打开,IDA会提示exe的入口点在其他的位置,应该是有壳,先对它进行查壳脱壳:
int __cdecl main(int argc, const char **argv, const char **envp)
{
__time32_t v3; // ST40_4@1
DWORD Seed; // ST38_4@1
int v5; // eax@2
signed int v7; // [sp+24h] [bp-34h]@1
v3 = time32(0i64); //0i64表示int64_t类型的0,time3()表示返回自1970年1月1日午夜起经过的秒数,或者在出现错误时返回-1
Seed = GetCurrentProcessId() ^ v3; //获取当前进程的唯一标识
srand(Seed);
v7 = 0;
do
{
v5 = rand();
printf(Format, (v5 >> 7) & 0xFF);
++v7;
}
while ( v7 < 16 );
return 0;
}
这就是代码的关键部分,获取当前系统时间和进程号,来进行运算生产16字节的密钥,所以我们将时间调整到2019/04/11 22:10:34之前进行爆破,因为我们是进行爆破,所以时间不用太准确,只要在这之前都行,当然这里的2019/04/11 22:10:34可能是老师给的hint:设置密钥的正确事件。我将当前时间设为了2019/04/11 22:10:00开始,先将它转换成从1970年1月1日以来持续时间的秒数:
源代码:time.c
#include
#include
/**
struct tm {
int tm_sec;// 秒 - 取值区间为[0,59] /
int tm_min; // 分 - 取值区间为[0,59] /
int tm_hour; / 时 - 取值区间为[0,23] /
int tm_mday; / 一个月中的日期 - 取值区间为[1,31] /
int tm_mon; / 月份 (从一月开始,0代表一月) - 取值区间为[0,11] /
int tm_year; / 年份,其值等于实际年份减去1900 /
int tm_wday; / 星期 - 取值区间为[0,6],其中0代表星期天,1代表星期一,以此类推 /
int tm_yday; / 从每年的1月1日开始的天数 - 取值区间为[0,365],其中0代表1月1日,1代表1月2日,以此类推 /
int tm_isdst; / 夏令时标识符,实行夏令时的时候,tm_isdst为正。不实行夏令时的时候,tm_isdst为0;不了解情况时,tm_isdst()为负. /
};
*/
int main(void)
{
//获取时间差
struct tm tnNormal;
///开始的时间:2019/04/11 22:10:00
tnNormal.tm_year = 2019-1900;
tnNormal.tm_mon = 4-1;
tnNormal.tm_mday = 11;
tnNormal.tm_hour = 22;
tnNormal.tm_min = 10;
tnNormal.tm_sec = 0;
tnNormal.tm_isdst = 0;
time_t tmNormal;
tmNormal = mktime(&tnNormal);
///开始的时间:2019/04/11 22:10:00转化为的秒数:1554991800
printf("开始的时间:2019/04/11 22:10:00转化为的秒数:%d\n",tmNormal);
return 0;
}
将2019/04/11 22:10:00转化为的秒数是:1554991800。接下来从1554991800开始递加,当前进程ID从0到100000循环,爆破直到得出的密钥第1个字节是0x25第2个字节是0x61:
下面是源代码:key.c
#include
#include
/**
0~100000和0~500的第一个解
0x25 0x61 0x6c 0xd5 0x1d 0xd3 0x4b 0xcb 0xe7 0x34 0x97 0x93 0xa4 0x92 0x53 0x1f
当前时间:1554991800,当前进程:32375
*/
int main()
{
///开始的时间:2019/04/11 22:09:00
__time32_t v3 = 1554991800; // ST40_4@1
long Seed; // ST38_4@1
int v5; // eax@2
signed int v7; // [sp+24h] [bp-34h]@1
int i;
int flag = 0;
int id;
__time32_t now;
///0x25和0x61
__int8 v4,v6;
///时间递加
for(i = 0; i< 500; i++){
now = v3+i;
///获取当前进程的唯一标识GetCurrentProcessId()
for(id = 0 ; id < 100000 ; id++){
Seed = id ^ now;
///设置时间种子
srand(Seed);
v7 = 0;
__int8 temp = 0;
do
{
///根据时间种子生成随机数
v5 = rand();
temp = (v5 >> 7) & 0xFF;
///第一个数是0x25
if( v7 ==0 && 0x25 == temp){
v4 = 0x25;
///第一个数不是0x25
}else if(v7 == 0 && temp !=0x25){
v4 = 0;
break ;
}
///第二个数是0x61
if(v7 == 1 && 0x61 == temp){
v6 = 0x61;
printf("0x25 ");
///第二个数不是0x61
}else if(v7 == 1 && 0x61 != temp){
v4 = 0;
v6 = 0;
break;
}
///如果找到结果
if(v4 == 0x25 && v6 ==0x61){
printf("0x%x ",temp & 0xFF);
}
++v7;
}while( v7 < 16 );
if(v7 == 16){
///输出正确答案
printf("\n当前时间:%d,当前进程:%d\n",now,id);
flag = 1;
break;
}
}
if(flag == 1){
printf("flag\n");
break;
}
}
return 0;
}
最后破解的16字节密钥为:0x25 0x61 0x6c 0xd5 0x1d 0xd3 0x4b 0xcb 0xe7 0x34 0x97 0x93 0xa4 0x92 0x53 0x1f。
到这破解的密钥已经得到的,但是我们发现当前时间:1554991800,当前进程:32375,因为随机种子是由当前时间和当前进程异或得到的,所以随着当前时间和当前进程递增,会出现多个时间和进程的异或结果相同,然后它们都最终得到这同一个密钥:0x25 0x61 0x6c 0xd5 0x1d 0xd3 0x4b 0xcb 0xe7 0x34 0x97 0x93 0xa4 0x92 0x53 0x1f。
我们来输出一下增加的时间0500以内和进程ID在0100000以内的所有解:
源代码:
#include
#include
int main()
{
///开始的时间:2019/04/11 22:10:00
__time32_t v3 = 1554991800; // ST40_4@1
long Seed; // ST38_4@1
int v5; // eax@2
signed int v7; // [sp+24h] [bp-34h]@1
int i;
int flag = 0;
int id;
__time32_t now;
///0x25和0x61
__int8 v4,v6;
///时间递加
for(i = 0; i< 500; i++){
now = v3+i;
///获取当前进程的唯一标识GetCurrentProcessId()
for(id = 0 ; id < 100000 ; id++){
Seed = id ^ now;
///设置时间种子
srand(Seed);
v7 = 0;
__int8 temp = 0;
do
{
///根据时间种子生成随机数
v5 = rand();
temp = (v5 >> 7) & 0xff;
///第一个数是0x25
if( v7 ==0 && 0x25 == temp){
v4 = 0x25;
///第一个数不是0x25
}else if(v7 == 0 && temp !=0x25){
v4 = 0;
break ;
}
///第二个数是0x61
if(v7 == 1 && 0x61 == temp){
v6 = 0x61;
printf("0x25 ");
///第二个数不是0x61
}else if(v7 == 1 && 0x61 != temp){
v4 = 0;
v6 = 0;
break;
}
///如果找到结果
if(v4 == 0x25 && v6 ==0x61){
printf("0x%x ",temp & 0xff);
}
++v7;
}while( v7 < 16 );
if(v7 == 16){
///输出正确答案
printf("\n当前时间:%d,当前进程:%d,异或的结果随机种子:%d\n",now,id,now^id);
v4 = 0;
v6 = 0;
}
}
}
return 0;
}
这是因为源文件算法中异或这个限制条件不够苛刻,又没有其他的条件来限制多解,才会出现这样的情况,题目的主要用意也是让我们明白以当前系统时间作为随机种子并不是安全的。