BGM!

第二、三代?

  • 第一代实际上是我第一次使用的版本,由于不兼容Linux,爆零。
    Bugs:

    • 不兼容Linux
    • 使用复杂
    • 数据文件名格式不标准会出错
  • 第二代就是这篇文章的版本。
    修改:

    • 使用基于字符串而不是数据的对比
    • 兼容Linux

    Bugs:

    • 数据文件名格式不标准会出错
    • 在某个输入是另一个输入的前缀时会出错
  • 第三代是我在今天的模拟赛趁着老师不在时写的。
    修改:

    • 加入基于随机数的(半自动)混淆
    • 修复在某个输入是另一个输入的前缀时的出错

    Bugs:

    • 数据文件名格式不标准会出错
  • 然后我推出了——第四代!
    修改:

    • 直接枚举文件而不是枚举测试点,使用std::set存储
    • 不再使用std::ostringstream格式化字符串
    • 依赖于io.h(Windows)和dirent.h(Linux)

代码

lemon-autoac.cpp: 未混淆的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <fstream>
#include <iostream>
#include <set>

#ifdef _WIN32
#include <io.h>
const char* datapath = "..\\..\\data\\";
std::set<std::string> scan(const std::string& dirname) {
_finddata_t file;
std::set<std::string> ret;
intptr_t dir = _findfirst((dirname + "\\*").c_str(), &file);
while (!_findnext(dir, &file)) {
std::string filename = file.name;
if (filename.length() > 3 && filename.substr(filename.length() - 3) == ".in") {
std::string path = dirname + '\\' + filename;
ret.insert(path.substr(0, path.length() - 3));
}
}
_findclose(dir);
return ret;
}
#else
#include <dirent.h>
const char* datapath = "../../data/";
std::set<std::string> scan(const std::string& dirname) {
DIR* dir = opendir(dirname.c_str());
std::set<std::string> ret;
for (dirent* file = readdir(dir); file != NULL; file = readdir(dir)) {
std::string filename = file->d_name;
if (filename.length() > 3 && filename.substr(filename.length() - 3) == ".in") {
std::string path = dirname + '/' + filename;
ret.insert(path.substr(0, path.length() - 3));
}
}
closedir(dir);
return ret;
}
#endif
const std::string probname = "$PROBLEM_NAME$";
bool check_stream(std::istream& fin, std::istream& tin) {
fin.seekg(0);
for (char ch = fin.get(), ch2 = tin.get(); ch != -1 || ch2 != -1; ch = fin.get(), ch2 = tin.get()) {
if (ch != ch2) {
return false;
}
}
return true;
}
int main() {
std::ifstream fin((probname + ".in").c_str());
std::ofstream fout((probname + ".out").c_str());
std::set<std::string> filelist = scan(datapath + probname);
for (std::set<std::string>::iterator i = filelist.begin(), i_ = filelist.end(); i != i_; i++) {
std::ifstream tin((*i + ".in").c_str());
if (check_stream(fin, tin)) {
std::ifstream tout((*i + ".out").c_str());
for (char ch = tout.get(); ch != -1; ch = tout.get()) {
fout << ch;
}
return 0;
}
}
return 1;
}

lemon-autoac-obf.cpp: 半混淆的代码,配合混淆器使用
ps: 虽然直接编译也能用
ps2: C++没找到什么好用的混淆器,所以这是我人肉混淆的

1
2
3
4
5
6
7
8
9
10
11
#include<fstream>
#include<iostream>
#include<set>
#ifdef _WIN32
#include<io.h>
const char*$1$="..\\..\\data\\";std::set<std::string>$2$(const std::string&$3$){_finddata_t $4$;std::set<std::string>$5$;intptr_t $6$=_findfirst(($3$+"\\*").c_str(),&$4$);while(!_findnext($6$,&$4$)){std::string $7$=$4$.name;if($7$.length()>3&&$7$.substr($7$.length()-3)==".in"){std::string $14$=$3$+'\\'+$7$;$5$.insert($14$.substr(0,$14$.length()-3));}}_findclose($6$);return $5$;}
#else
#include <dirent.h>
const char*$1$="../../data/";std::set<std::string>$2$(const std::string&$8$){DIR*$9$=opendir($8$.c_str());std::set<std::string>$10$;for(dirent*$11$=readdir($9$);$11$!=NULL;$11$=readdir($9$)){std::string $12$=$11$->d_name;if($12$.length()>3&&$12$.substr($12$.length()-3)==".in"){std::string $13$=$8$+'/'+$12$;$10$.insert($13$.substr(0,$13$.length()-3));}}closedir($9$);return $10$;}
#endif
const std::string $15$="$PROBLEM_NAME$";bool $16$(std::istream&$17$,std::istream&$18$){$17$.seekg(0);for(char $19$=$17$.get(),$20$=$18$.get();$19$!=-1||$20$!=-1;$19$=$17$.get(),$20$=$18$.get()){if($19$!=$20$){return false;}}return true;}int main(){std::ifstream $21$(($15$+".in").c_str());std::ofstream $22$(($15$+".out").c_str());std::set<std::string>$23$=$2$($1$+$15$);for(std::set<std::string>::iterator $25$=$23$.begin(),$26$=$23$.end();$25$!=$26$;$25$++){std::ifstream $24$((*$25$+".in").c_str());if($16$($21$,$24$)){std::ifstream $27$((*$25$+".out").c_str());for(char $28$=$27$.get();$28$!=-1;$28$=$27$.get()){$22$<<$28$;}return 0;}}return 1;}

lemon-autoac-make.cpp: 混淆器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <chrono>
#include <fstream>
#include <iostream>
#include <random>
#include <sstream>
#include <string>

std::default_random_engine rnd;
std::uniform_int_distribution<int> uni(0, 15);
std::uniform_int_distribution<int> uni2(10, 15);
std::string uuid() {
std::ostringstream str;
char ch = uni2(rnd);
for (int i = 0; i < 32; i++) {
if (ch < 10) {
str << static_cast<char>(ch + '0');
} else {
str << static_cast<char>(ch - 10 + 'a');
}
ch = uni(rnd);
}
return str.str();
}
std::string read(std::istream& ins) {
std::ostringstream str;
str << ins.rdbuf();
return str.str();
}
int main() {
rnd.seed(std::chrono::steady_clock::now().time_since_epoch().count());
std::ifstream fin("lemon-autoac-obf.cpp");
std::string content = read(fin);
std::string probname;
std::cout << "Problem Name: ";
std::getline(std::cin, probname);
std::ofstream fout(probname + ".cpp");
content.replace(content.find("$PROBLEM_NAME$"), 14, probname);
for (int i = 1;; i++) {
std::ostringstream str;
str << '$' << i << '$';
std::string pattern = str.str();
std::string replacement = uuid();
int plen = pattern.length();
int pos = content.find(pattern);
if (pos == -1) {
break;
}
while (pos != -1) {
content.replace(pos, plen, replacement);
pos = content.find(pattern);
}
}
fout << content;
return 0;
}