新人发的第一个C++写的CM,按要求重发
本帖最后由 copper187 于 2020-2-24 14:02 编辑感谢 @iTruth 大佬的详细分析,现在放上代码,注意经测试,VS20xx不能编译!请使用GCC编译器!
源码下载链接https://pan.baidu.com/s/1vxTNm3asV1m4yr66sIHm-w
提取码:bhwg
公布下密钥:
easy: 0101011
medium: 10020102030405060708091012
hard: 9999132016638975202418150841035437583359700447190986618337016136262846840888905453047143721704956797654181737314954297329217897392895530763726416356544673036995112282316092584575626926344567728484071940111122312322334442322413441331441244312243243233424144223422122134412234224214212411434324341443334
之前的帖子(https://www.52pojie.cn/thread-1112587-1-1.html)被删,理由是缺少成功截图,现在补上。使用C++编写,GCC编译.,无壳无花无反调试。
压缩包里有七个文件,分别为:加密程序(Encode.exe),解密程序(Decode.exe),密钥生成器(Keycreater.exe),三个加密好的文件(新增medium难度,easy,hard一样),以及解密好的hard文件重编码(因为音乐文件无法截图体现哈。)
通过三个程序可以用随机密钥对任何文件进行加解密,所以你可以自己生成明密文对比较,这里就不提供例子了哈。
目标是通过三个程序。分析加解密文件,最终解密预先提供的三个文件。
easy和medium的密钥是有规律的,所以应该可以破解。hard的密钥是完全随机的,本人还不清楚是否能破解,如果大佬们发现无解还请原谅哈。
注意,有个小bug,形如01XXXX+之后含有3的密钥(例1:0101563,例2:01052211333,例3:01118112324123211)程序加密会有错误,有的会直接卡死,应避免使用,我会很快修复
下载链接 https://pan.baidu.com/s/1Rh6oaATt3HFLGVB6VBj--g 提取码9kkv
成功截图
注:gif中的keycreater是64位的,度盘链接已经换成32位的,所以两个文件的大小不一样,但不影响破解的!
easy为txt文件
https://i.loli.net/2020/02/23/1k53iNmARsb2zG9.gif
medium为png文件
https://i.loli.net/2020/02/23/uiKEBhLrbzak3yH.gif
hard为ogg文件
https://i.loli.net/2020/02/23/XLamsZgpewBbcu9.gif
另外,浏览器直接新建标签页打开链接可以直接听hard的音乐
http://bdfile.net/hard.mp3
或者
http://nhnkr.bdfile.net/hard.mp3
本帖最后由 iTruth 于 2020-2-23 18:13 编辑
在你的上一个帖子没删之前我就已经分析过你的加密算法了,说真的我真不知道这个算法到底是否存在漏洞.现在把我分析的成果拿出来和大家一起研究此加密算法
我也大概的说一下,他的算法不会直接加密数据而是在有些时候会写入垃圾数据
另外如果有分析错或者不到位的地方务必提出来
这时对Encode.exe的分析
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
bool v4; // eax
char *v5; // eax
std::ostream::sentry *v6; // eax
std::ostream::sentry *v7; // eax
std::string *v8; // eax
_BOOL1 v9; // al
int v10; // eax
void *v11; // esp
void *v12; // esp
_BYTE *v13; // eax
char *v14; // eax
std::ostream::sentry *v15; // eax
std::ostream::sentry *v16; // eax
std::string *filename; //
char v19; //
int v20; //
struct _Unwind_Exception *v21; //
std::string **v22; //
bool v23; //
struct _Unwind_Exception *lpuexcpt; //
struct SjLj_Function_Context fctx; //
void *v26; //
std::string **v27; //
char v28; //
char v29; //
int v30; //
char v31; //
char v32; //
char *Enc_Filename; //
int v34; //
char v35; //
int *v36; //
v36 = &argc;
lpuexcpt = (struct _Unwind_Exception *)&argc;
fctx.personality = (_Unwind_Personality_Fn)__gxx_personality_sj0;
fctx.lsda = dword_476924;
fctx.jbuf = &v35;
v26 = &loc_40248E;
v27 = &filename;
_Unwind_SjLj_Register(&fctx);
__main();
v22 = &filename;
v3 = time(0);
srand(v3); // 用当前时间取随机数种子
if ( LODWORD(lpuexcpt->exception_class) == 1 )
{
printf("Error 1.You should drag and drop a file...\n");
fctx.call_site = 1;
system("pause");
lpuexcpt = 0;
v4 = 0;
}
else
{
filename = *(std::string **)(HIDWORD(lpuexcpt->exception_class) + 4);
fctx.call_site = 1;
std::string::operator=(filename);
printf("Please enter a key to encode file(s)\n");
std::operator>><char,std::char_traits<char>,std::allocator<char>>((std::istream::sentry *)&std::cin, (int)&key);
v5 = (char *)std::string::c_str(filename);
std::basic_ifstream<char,std::char_traits<char>>::basic_ifstream(v5, 4);
fctx.call_site = 2;
lpuexcpt = (struct _Unwind_Exception *)std::string::c_str(filename);
v6 = (std::ostream::sentry *)std::operator<<<std::char_traits<char>>(
(std::ostream::sentry *)&std::cout,
"Encode \"");
v7 = (std::ostream::sentry *)std::operator<<<std::char_traits<char>>(v6, (char *)lpuexcpt);
std::operator<<<std::char_traits<char>>(v7, "\" ,please wait...");
std::ostream::operator<<(std::endl<char,std::char_traits<char>>);
v8 = (std::string *)std::string::c_str(filename);
std::string::operator=(v8);
filenamelong = std::string::length(filename);
for ( i = filenamelong - 1; i >= 0; --i ) // 不能加密.enc文件,i记录了'.'的位置
{
++k; // k表示后缀名长度
fctx.call_site = 2;
if ( *(_BYTE *)std::string::operator[](i) == 46 )
{
v9 = *(_BYTE *)std::string::operator[](i + 1) == 101
&& *(_BYTE *)std::string::operator[](i + 2) == 110
&& *(_BYTE *)std::string::operator[](i + 3) == 99;
if ( v9 )
{
fctx.call_site = 2;
std::operator<<<std::char_traits<char>>(
(std::ostream::sentry *)&std::cout,
"Error 3. Can not encode a enc file...");
std::ostream::operator<<(std::endl<char,std::char_traits<char>>);
system("pause");
lpuexcpt = 0;
v23 = 0;
goto LABEL_64;
}
break;
}
}
if ( i == -1 ) // -1代表待加密的文件无拓展名
k = 0;
v34 = filenamelong + --k + 99;
v10 = 16 * ((filenamelong + k + 115) / 0x10u);
v11 = alloca(v10);
v12 = alloca(v10);
Enc_Filename = (char *)&v20;
memset(&v20, 0, filenamelong + k + 100);
for ( i = 0; filenamelong - 2 - k >= i; ++i )
{
lpuexcpt = (struct _Unwind_Exception *)i;
fctx.call_site = 2;
v13 = (_BYTE *)std::string::operator[](i);
*((_BYTE *)&lpuexcpt->exception_class + (_DWORD)Enc_Filename) = *v13;
}
Enc_Filename = 46;
Enc_Filename = 101;
Enc_Filename = 110;
Enc_Filename = 99;
fctx.call_site = 2;
std::basic_ofstream<char,std::char_traits<char>>::basic_ofstream(Enc_Filename, 4);// 将后缀名改成.enc后打开文件
if ( k <= 99 )
{ // 开始构造加密文件头
if ( k <= 9 && k != -1 ) // 如果后缀名长度k<=9并且有后缀的话就写入0后跟后缀名长度
{
lpuexcpt = (struct _Unwind_Exception *)k;
fctx.call_site = 3;
std::operator<<<std::char_traits<char>>((std::ostream::sentry *)&v28, "0");
std::ostream::operator<<(lpuexcpt);
}
if ( k > 9 && k <= 99 ) // 如果后缀名长度大于9那么直接写入后缀名长度
{
fctx.call_site = 3;
std::ostream::operator<<(k);
}
if ( k == -1 ) // 如果无后缀那么写入00
{
fctx.call_site = 3;
std::operator<<<std::char_traits<char>>((std::ostream::sentry *)&v28, "00");
}
if ( k ) // 如果有后缀名那么写入后缀名
{
for ( i = filenamelong - k; filenamelong - 1 >= i; ++i )
{
fctx.call_site = 3;
v14 = (char *)std::string::operator[](i);
std::ostream::put((std::ostream *)*v14, v19);
}
}
if ( !k ) // 如果后缀长度是0那么写0
{
fctx.call_site = 3;
std::operator<<<std::char_traits<char>>((std::ostream::sentry *)&v28, "0");
}
fctx.call_site = 3;
v32 = *(_BYTE *)std::string::operator[](0);// 加密文件头构造结束,现在处理密匙
v31 = *(_BYTE *)std::string::operator[](1);
if ( v32 == 48 )
codelong = v31 - 48;
else
codelong = v31 - 48 + 10 * (v32 - 48);
fctx.call_site = 3;
v32 = *(_BYTE *)std::string::operator[](2);
v31 = *(_BYTE *)std::string::operator[](3);
if ( v32 == 48 )
typelong = v31 - 48;
else
typelong = v31 - 48 + 10 * (v32 - 48);// codelong就是生成密匙时输入的第一个数,typelong就是输入的第二个数
j = 0;
for ( i = 4; 2 * codelong + 3 >= i; i += 2 )// code用于存放code段的随机数,两个个位数组合成一个十位数
{
fctx.call_site = 3;
v32 = *(_BYTE *)std::string::operator[](i);
v31 = *(_BYTE *)std::string::operator[](i + 1);
if ( v32 == 48 )
code = v31 - 48;
else
code = v31 - 48 + 10 * (v32 - 48);
++j;
}
i = 0;
for ( j = 2 * (codelong + 2); typelong + 2 * (codelong + 2) >= j; ++j )// type用于存放type段的随机数,单个个位数
{
fctx.call_site = 3;
v32 = *(_BYTE *)std::string::operator[](j);
type = v32 - 48;
}
codetag = 0; // codetag代表写入了多少字节的垃圾数据
typetag = 0;
while ( 1 )
{
fctx.call_site = 3;
if ( !((unsigned __int8)std::basic_ios<char,std::char_traits<char>>::eof(&v30) ^ 1) )
break;
if ( entypeok != 1 )
entype(type); // 构造workcode
if ( workcode == puttag ) // puttag是直接写入文件的字符数,在此情况下写入垃圾数据
{
c = rand();
fctx.call_site = 3;
std::ostream::put((std::ostream *)c, v19);// 生成垃圾数据后写入
++codetag;
puttag = 0;
if ( codetag >= codelong )
{
codetag = 0;
memset(workcode, 0, 0x1B4u);
if ( ++typetag >= typelong )
typetag = 0;
entypeok = 0;
}
}
else // 否则写入正常的数据
{
fctx.call_site = 3;
std::istream::get((std::istream *)&c, *(char **)&v19);
std::ostream::put((std::ostream *)c, v19);
++puttag;
}
}
lpuexcpt = (struct _Unwind_Exception *)Enc_Filename;
v15 = (std::ostream::sentry *)std::operator<<<std::char_traits<char>>(
(std::ostream::sentry *)&std::cout,
"Complete! Encode file name is \"");
v16 = (std::ostream::sentry *)std::operator<<<std::char_traits<char>>(v15, (char *)lpuexcpt);
std::operator<<<std::char_traits<char>>(v16, "\"");
std::ostream::operator<<(std::endl<char,std::char_traits<char>>);
system("pause");
v23 = 1;
lpuexcpt = v21;
}
else
{
fctx.call_site = 3;
std::operator<<<std::char_traits<char>>(
(std::ostream::sentry *)&std::cout,
"Error 2. This encode program is only can support 0~99 byte(s) long filename extension...");
std::ostream::operator<<(std::endl<char,std::char_traits<char>>);
system("pause");
lpuexcpt = 0;
v23 = 0;
}
fctx.call_site = 2;
std::basic_ofstream<char,std::char_traits<char>>::~basic_ofstream(&v28);
v23 = v23 == 1;
LABEL_64:
fctx.call_site = 1;
std::basic_ifstream<char,std::char_traits<char>>::~basic_ifstream(&v29);
v4 = v23 == 1;
}
if ( v4 == 1 )
lpuexcpt = 0;
_Unwind_SjLj_Unregister(&fctx);
return (int)lpuexcpt;
}
这是对Encode.exe里一个关键函数的分析:
int __cdecl entype(int a1)
{
int result; // eax
switch ( a1 )
{
case 1:
for ( i = 0; ; ++i ) // 模式1: 复制code到workcode
{
result = i;
if ( codelong - 1 < i )
break;
workcode = code;
}
entypeok = 1;
break;
case 2:
j = 0;
for ( i = codelong - 1; ; --i ) // 模式2: 从code尾部往前复制到workcode
{
result = i;
if ( i < 0 )
break;
workcode = code;
}
entypeok = 1;
break;
case 3:
if ( codelong % 2 == 1 ) // 模式3: 如果codelong是奇数那么把code的后半部分复制到workcode的前半部分,
// 把code的前半部分复制到workcode的后半部分
// 和codelong是偶数的情况不同的是在codelong是奇数的情况下workcodelong = codelong+1来凑到偶数继续计算
{
workcodelong = codelong + 1;
j = 0;
for ( i = (codelong + 1) / 2; codelong - 1 >= i; ++i )
workcode = code;
j = workcodelong / 2;
for ( i = 0; workcodelong / 2 - 1 >= i; ++i )
workcode = code;
}
result = codelong & 1;
if ( !(codelong & 1) ) // 如果codelong是偶数的情况
{
workcodelong = codelong;
j = 0;
for ( i = codelong / 2; codelong - 1 >= i; ++i )
workcode = code;
j = workcodelong / 2;
for ( i = 0; ; ++i )
{
result = i;
if ( workcodelong / 2 - 1 < i )
break;
workcode = code;
}
}
entypeok = 1;
break;
case 4:
if ( codelong % 2 == 1 ) // 模式4: 如果codelong是奇数那么将code的前半部分反着拷贝到workcode的前半部分,
// 将code的后半部分反着拷贝到workcode的后半部分
// 和codelong是偶数的情况不同的是在codelong是奇数的情况下workcodelong = codelong+1来凑到偶数继续计算
{
workcodelong = codelong + 1;
j = 0;
for ( i = (codelong + 1) / 2 - 1; i >= 0; --i )
workcode = code;
j = workcodelong / 2;
for ( i = codelong - 1; workcodelong / 2 <= i; --i )
workcode = code;
}
result = codelong & 1;
if ( !(codelong & 1) ) // 如果codelong是偶数的情况
{
workcodelong = codelong;
j = 0;
for ( i = codelong / 2 - 1; i >= 0; --i )
workcode = code;
j = workcodelong / 2;
for ( i = codelong - 1; ; --i )
{
result = i;
if ( workcodelong / 2 > i )
break;
workcode = code;
}
}
entypeok = 1;
break;
}
return result;
}
这时生成密匙的C++代码,我重写了一下能直观些:
#include <iostream>
#include <ctime>
#include <cmath>
using namespace std;
int main()
{
srand(time(nullptr));
int a,b;
cout<<"Please enter two numbers, each number should between 1 to 99."<<endl;
cin>>a>>b;
if(a>9){
cout<<endl<<"----------Begin the key----------"<<endl<<a;
}else{
cout<<endl<<"----------Begin the key----------"<<endl<<"0"<<a;
}
if(b>9){
cout<<b;
}else{
cout<<"0"<<b;
}
for (int i = 0; i < 2*a; ++i) {
cout<<rand()%10;
}
for (int i = 0; i < b; ++i) {
cout<<rand()%4+1;
}
cout<<endl<<"----------End the key----------"<<endl;
return 0;
}
拜读源码来学习一下 支持一下,虽然我是走C++编程竞赛的,这些有点看不懂 加密算法是要求有数学基础的,自己研究的算法一是限于无法反编译(需要一个安全的运行环境,如安全芯片等),一旦能反编译安全性也就没有了;二是随机性不够(强度不够) 本帖最后由 copper187 于 2020-2-23 21:37 编辑
iTruth 发表于 2020-2-23 18:11
在你的上一个帖子没删之前我就已经分析过你的加密算法了,说真的我真不知道这个算法到底是否存在漏洞.现在把 ...
谢谢大佬的分析!另外我想问一下,这个算法即使知道了过程,对于用9999开头的无规律密钥加密的文件可以在不知道密钥的情况下暴力破解吗? iTruth大佬厉害!我从开始构建算法到完善程序花了五六天的时间,没想到大佬一天不到就破解了! copper187 发表于 2020-2-23 21:30
谢谢大佬的分析!另外我想问一下,这个算法即使知道了过程,对于用9999开头的无规律密钥加密的文件可以在 ...
你说的暴力破解如果指的是枚举法那确实是可以解出来的,而且速度肯定不会慢。因为你的算法过于依赖时间和随机数。只要知道了你大概何时创建的密钥那么枚举那段时间的时间戳即可轻松得到密钥。如果是针对特例来触发算法漏洞的话。。那抱歉,我也说了我还不知道你的算法是否存在漏洞。。 iTruth 发表于 2020-2-23 21:59
你说的暴力破解如果指的是枚举法那确实是可以解出来的,而且速度肯定不会慢。因为你的算法过于依赖时间和 ...
嗯,谢谢。那如果密钥的生成依赖更加随机化(不依赖时间),那暴力破解的难度是否就大幅上升了呢{:1_904:} copper187 发表于 2020-2-23 22:15
嗯,谢谢。那如果密钥的生成依赖更加随机化(不依赖时间),那暴力破解的难度是否就大幅上升了呢{:1_904 ...
如果你只是不依赖时间的话其实依然非常简单。。如果你的密钥是类似PGP那样生成的话确实会提高难度。但问题在于你的算法本身感觉还有问题,有些时候加密了都能直接看到文档内容。可以先去学好密码学再继续尝试