之前参加了NPUCTF,当时的一道re题目可以说是印象颇深了,第一道就开始了操作,而且接下来好几道题都是照搬这种操作,于是乎我从中得到启发,开发出了一种BT操作恶心做题人。
先来看看npuctf的第一道题目长啥样吧。
看到了嘛,超级多的函数和一些杂乱无章的逻辑操作,看起来复杂的要命,让人看都不想看,但是其实慢慢分析发现好多if都是假的,好多位运算就是加减乘除运算,垃圾代码一大堆。
于是乎我在想这种插入垃圾代码不就是为了隐藏代码么,而且这么多if不就是迷惑选手么,然后我在想有什么办法能够强化这种感官呢?
我觉得对于re选手来说,点开sub其实是很烦的,那么多sub,点了一层有一层岂不是让人头疼么。
于是乎,垃圾代码v1.0就出现了,我们可以把sub疯狂嵌套,再嵌套中塞入对flag的修改代码,当然手写太累了,在这里不讨论。
所以使用树就可以了,类似于树,re就变成了选手走迷宫23333
代码片段:
void buildRandomTree(Tree *node)
{
node->branch_num=0;
node->data=data++;
if(remain_size<=0)
return;
int x=rand()%3+2;
if(remain_size<=x)
x=remain_size;
remain_size-=x;
for(int i=0;i<x;i++)
{
Tree *node_=(Tree*)malloc(sizeof(Tree));
node->branch[node->branch_num++]=node_;
buildRandomTree(node_);
}
}
void genRandomTree(int size_)
{
remain_size=size_;
size=size_;
data=0;
Tree *head_=(Tree*)malloc(sizeof(Tree));
head=head_;
buildRandomTree(head_);
}
这里的树采取随机生成,(当然可能会不平衡,不管了),然后就是根据树来生成代码,这里不赘述了,对着树遍历一遍就好了。
当然,仅仅只有sub嵌套没点用,交叉应用一下就找到了关键代码,所以我么可以试着加入一大堆垃圾代码修改flag数组,但是使得最后结果不变。
于是这里就可以使用加密解密技术了,这里就先用最简单的加减异或吧,毕竟加对应减,异或对应异或,比较简单,我们可以随机生成序号,来对flag数字的对应元素进行修改,然后随机进行加密解密。
这里加密与机密是需要满足栈操作的,所以用栈写就好了
代码片段
void genOperates(int max_size)
{
int ptr=0;
for(int i=0;i<max_size;i++)
{
Operate *op=(Operate*)malloc(sizeof(Operate));
OpOrig[i]=op;
op->op_type=rand()%MAX_OP_TYPE;
int t=rand()%var_num;
op->var_info=&list[t];
for(int j=0;j<=6;j++)
op->op_args[j]=rand();
op->recover=0;
}
while(ptr<max_size)
{
int choice=rand()%2;
if(choice || st_size==0)
{
OpList[Ops++]=OpOrig[ptr];
push(ptr++);
continue;
}
else if(!choice && st_size>0)
{
int x=pop();
Operate *op=getRecoverOp(OpOrig[x]);
OpList[Ops++]=op;
if(st_size==0)
{
printf("[OBFU_MAKER]: Find a function %s_%d() that can put real code intoo..\n",sub_name,Ops-1);
cleanSub.push_back(Ops-1);
}
continue;
}
}
int t=st_size;
for(int i=0;i<t;i++)
{
int x=pop();
Operate *op=getRecoverOp(OpOrig[x]);
OpList[Ops++]=op;
}
#ifdef DEBUG_OP
dumpOpList();
#endif
}
这里,第一个循环随机取加密方式,第二个根据栈生成加密解密顺序,使得结果不变,大概就可以了,加入亿点点细节。
效果图:
这里按照树的遍历生成代码,保证了执行完代码flag数组不会发生改变
看起来复杂,然而可以用angr跑,为了杜绝这一点,我们可以再生成CPP代码时加入一点点细节,引入全局变量(不透明谓词)
最后的结果:
虽然不知道对抗angr有没有用,恶心人就完事了,对此代码就写完了,但是你可能觉得,这些代码有啥用呢,动调就发现对flag啥也没操作。。。
然而,这波可以来一个真真假假 假假真真,你塞入自己的代码就是了,谁知道时垃圾代码还是有用的代码呢?所以对应的,我写了一个功能,计算出来了在哪里函数可以加入你的代码,哪里就会有个注释
/* put your code here! */
到这里,恶心之旅就结束了,虽然不知道能不能恶心到人,还是把代码放上吧。
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<cstring>
#include<vector>
#include<string>
#include<windows.h>
#define INT 0
#define LLONG 1
#define CHAR 2
#define SHORT 3
#define MAX_OP_TYPE 2
#define MAX_CODE_LEN 1000
#define MAX_DEOBFU_CODE_NUM 1000
//#define DEBUG_CPP_CODE
#define OUPUT_CONSOLE
using namespace std;
struct VarInfo
{
int data_type;
char name[30];
};
int var_num;
VarInfo list[10000];
struct Operate
{
int op_type;
VarInfo *var_info;
int op_args[10];
int recover;
};
struct Op2ObfuCode
{
char *code;
int arg_num;
};
struct Op2DeObfuCode
{
char *deobfu_code[MAX_DEOBFU_CODE_NUM];
int arg_num;
int equal_code_num=0;
};
int Ops=0;
Operate *OpList[10000],*OpOrig[10000];
Op2ObfuCode op_table[10000];
Op2DeObfuCode deop_table[10000];
int RunTimeStack[10000]={0},st_size=0;
void push(int val)
{
RunTimeStack[st_size++]=val;
}
int pop()
{
return RunTimeStack[--st_size];
}
void addDeObfuCode(const char *code,int type,int arg_num)
{
Op2DeObfuCode op2code=deop_table[type];
op2code.deobfu_code[op2code.equal_code_num++]=(char*)code;
op2code.arg_num=arg_num;
deop_table[type]=op2code;
}
void addObfuCode(const char *code,int type,int arg_num)
{
Op2ObfuCode op2code;
op2code.code=(char*)code;
op2code.arg_num=arg_num;
op_table[type]=op2code;
}
void addVar(const char *name,int type)
{
VarInfo vi;
strcpy(vi.name,name);
vi.data_type=type;
list[var_num++]=vi;
}
Operate *getRecoverOp(Operate *a)
{
Operate *op=(Operate*)malloc(sizeof(Operate));
for(int i=0;i<=6;i++)
op->op_args[i]=a->op_args[i];
op->op_type=a->op_type;
op->recover=1;
op->var_info=a->var_info;
return op;
}
void dumpOpList()
{
for(int i=0;i<Ops;i++)
printf("dump:%04X VarName:%s OpType:%d RecoverFlag:%s\n\n",i,OpList[i]->var_info->name,OpList[i]->op_type,OpList[i]->recover==1?"True":"False");
}
const char sub_name[]="sub";
vector<int> cleanSub;
void genOperates(int max_size)
{
int ptr=0;
for(int i=0;i<max_size;i++)
{
Operate *op=(Operate*)malloc(sizeof(Operate));
OpOrig[i]=op;
op->op_type=rand()%MAX_OP_TYPE;
int t=rand()%var_num;
op->var_info=&list[t];
for(int j=0;j<=6;j++)
op->op_args[j]=rand();
op->recover=0;
}
while(ptr<max_size)
{
int choice=rand()%2;
if(choice || st_size==0)
{
OpList[Ops++]=OpOrig[ptr];
push(ptr++);
continue;
}
else if(!choice && st_size>0)
{
int x=pop();
Operate *op=getRecoverOp(OpOrig[x]);
OpList[Ops++]=op;
if(st_size==0)
{
printf("[OBFU_MAKER]: Find a function %s_%d() that can put real code intoo..\n",sub_name,Ops-1);
cleanSub.push_back(Ops-1);
}
continue;
}
}
int t=st_size;
for(int i=0;i<t;i++)
{
int x=pop();
Operate *op=getRecoverOp(OpOrig[x]);
OpList[Ops++]=op;
}
#ifdef DEBUG_OP
dumpOpList();
#endif
}
vector<string> codeList;
void genCppCode()
{
for(int i=0;i<Ops;i++)
{
char *str=(char*)malloc(MAX_CODE_LEN+10);
char *buf=(char*)malloc(MAX_CODE_LEN+10);
memset(str,0,sizeof(str));
memset(buf,0,sizeof(buf));
if(!OpList[i]->recover)
{
Op2ObfuCode *c=&op_table[OpList[i]->op_type];
if(c->code==NULL)
continue;
int ptr=0,rep=0;
for(int j=0;j<strlen(c->code);j++)
{
if(c->code[j]=='%' && c->code[j+1]=='s')
{
int len=strlen(OpList[i]->var_info->name);
for(int k=0;k<len;k++)
str[ptr+k]=OpList[i]->var_info->name[k];
ptr+=len;
j+=1;
continue;
}
str[ptr++]=c->code[j];
}
str[ptr]='\0';
sprintf(buf,str,OpList[i]->op_args[0],OpList[i]->op_args[1],OpList[i]->op_args[2],OpList[i]->op_args[3],OpList[i]->op_args[4],OpList[i]->op_args[5],OpList[i]->op_args[6]);
for(int j=0;j<strlen(buf);j++)
str[j]=buf[j];
str[strlen(buf)]='\0';
#ifdef DEBUG_CPP_CODE
printf("Encode Code:\n %s\n",str);
#endif
}
else
{
Op2DeObfuCode *c=&deop_table[OpList[i]->op_type];
if(c->equal_code_num==0)
continue;
char *code=c->deobfu_code[rand()%c->equal_code_num];
int ptr=0,rep=0;
for(int j=0;j<strlen(code);j++)
{
if(code[j]=='%' && code[j+1]=='s')
{
int len=strlen(OpList[i]->var_info->name);
for(int k=0;k<len;k++)
str[ptr+k]=OpList[i]->var_info->name[k];
ptr+=len;
j+=1;
continue;
}
str[ptr++]=code[j];
}
str[ptr]='\0';
sprintf(buf,str,OpList[i]->op_args[0],OpList[i]->op_args[1],OpList[i]->op_args[2],OpList[i]->op_args[3],OpList[i]->op_args[4],OpList[i]->op_args[5],OpList[i]->op_args[6]);
for(int j=0;j<strlen(buf);j++)
str[j]=buf[j];
str[strlen(buf)]='\0';
#ifdef DEBUG_CPP_CODE
printf("Decode Code:\n %s\n",str);
#endif
}
string s(str);
codeList.push_back(s);
free(buf);
free(str);
}
}
struct Tree
{
int data;
Tree* branch[20];
int branch_num;
};
int remain_size,data,size;
Tree *head;
void buildRandomTree(Tree *node)
{
node->branch_num=0;
node->data=data++;
if(remain_size<=0)
return;
int x=rand()%3+2;
if(remain_size<=x)
x=remain_size;
remain_size-=x;
for(int i=0;i<x;i++)
{
Tree *node_=(Tree*)malloc(sizeof(Tree));
node->branch[node->branch_num++]=node_;
buildRandomTree(node_);
}
}
void genRandomTree(int size_)
{
remain_size=size_;
size=size_;
data=0;
Tree *head_=(Tree*)malloc(sizeof(Tree));
head=head_;
buildRandomTree(head_);
}
vector<string> finalCode;
void genSubCode(Tree *node,int depth)
{
char *code=(char*)malloc(5000);
int data=node->data;
int x=0;
x+=sprintf(code,"void %s_%d()\t//depth: %d\n{\n",sub_name,data,depth);
VarInfo *vi=&list[rand()%var_num];
x+=sprintf(code+x,"\tx=(x&%s)^(y|%s)^(x<<1);\n",vi->name,vi->name);
x+=sprintf(code+x,"\ty=(y&%s)^(x|%s)^(y<<1);\n",vi->name,vi->name);
x+=sprintf(code+x,"\t%s",codeList[data].c_str());
for(int i=0;i<cleanSub.size();i++)
if(data==cleanSub[i])
x+=sprintf(code+x,"\n\t/* put your code here! */");
for(int i=0;i<node->branch_num;i++)
{
int p=rand()%100;
if(p<=20)
{
x+=sprintf(code+x,"\n\tif((x+y)^2>=0 && (x-y)^2>=0)");
x+=sprintf(code+x,"\n\t\t%s_%d();",sub_name,node->branch[i]->data);
x+=sprintf(code+x,"\n\telse");
x+=sprintf(code+x,"\n\t\texit(0);");
}
else if(p<=40)
{
x+=sprintf(code+x,"\n\tif((x*(x-1)%2+2*y)^((x*(x-1)%2+2*y)|1)==1)");
x+=sprintf(code+x,"\n\t\t%s_%d();",sub_name,node->branch[i]->data);
x+=sprintf(code+x,"\n\telse");
x+=sprintf(code+x,"\n\t\texit(0);");
}
else if(p<=60)
{
x+=sprintf(code+x,"\n\tif((x-y)==(~(~x+y)))");
x+=sprintf(code+x,"\n\t\t%s_%d();",sub_name,node->branch[i]->data);
x+=sprintf(code+x,"\n\telse");
x+=sprintf(code+x,"\n\t\texit(0);");
}
else
x+=sprintf(code+x,"\n\t%s_%d();",sub_name,node->branch[i]->data);
}
x+=sprintf(code+x,"\n}\n");
string cx(code);
free(code);
finalCode.push_back(cx);
#ifdef OUPUT_CONSOLE
printf("%s\n",cx.c_str());
#endif
}
void walkTree(Tree *node,int depth)
{
int x=node->branch_num;
genSubCode(node,depth);
for(int i=0;i<x;i++)
{
Tree *n=node->branch[i];
walkTree(n,depth+1);
}
}
void spawnRubbisCode(int size)
{
printf("[OBFU_MAKER]: This program is used to generate rubbish code in order to disgust others..\n");
printf("[OBFU_MAKER]: Press any key to continue..\n");
getchar();
printf("[OBFU_MAKER]: Start to generate rubbish codes..\n");
// Sleep(1000);
printf("[OBFU_MAKER]: Code size: %d..\n",size);
genOperates(size);
// Sleep(3000);
printf("[OBFU_MAKER]: Operation Generated..\n");
// Sleep(1000);
printf("[OBFU_MAKER]: Translate into cpp code..\n");
genCppCode();
// Sleep(2000);
printf("[OBFU_MAKER]: Done..\n");
// Sleep(1000);
printf("[OBFU_MAKER]: Generate logic tree..\n");
// Sleep(2000);
genRandomTree(size*2-1);
printf("[OBFU_MAKER]: Done..\n");
// Sleep(1000);
printf("[OBFU_MAKER]: Generate final codes..\n");
Sleep(3000);
walkTree(head,1);
printf("[OBFU_MAKER]: Done!\n");
Sleep(2000);
}
void writeToFile(const char *filename)
{
FILE *fp=fopen(filename,"w");
int x=rand()+1,y=rand()+1;
if(x>y)
swap(x,y);
fprintf(fp,"unsigned int x=%d,y=%d;\n",x,y);
for(int i=finalCode.size()-1;i>=0;i--)
fprintf(fp,"%s",finalCode[i].c_str());
fclose(fp);
printf("[OBFU_MAKER]: Write to file %s..\n",filename);
}
void test()
{
addVar("flag[0]",1); //添加变量
addVar("flag[1]",1);
addVar("flag[2]",1);
addVar("flag[3]",1);
addVar("flag[4]",1);
addVar("flag[5]",1);
addVar("flag[6]",1);
addVar("flag[7]",1);
addVar("flag[8]",1);
addVar("flag[9]",1);
addVar("flag[10]",1);
addVar("flag[11]",1);
addVar("flag[12]",1);
addVar("flag[13]",1);
addVar("flag[14]",1);
addVar("flag[15]",1);
addVar("flag[16]",1);
addVar("flag[17]",1);
addVar("flag[18]",1);
addVar("flag[19]",1);
addVar("flag[20]",1);
addVar("flag[21]",1);
addObfuCode("%s=~(~%s+%d%12);",0,1); //添加加解密块,一个加密可以对应多种解密的写法
addDeObfuCode("%s=%s+%d%12;",0,1);
addDeObfuCode("%s=~(~%s-%d%12);",0,1);
addObfuCode("unsigned char a=%s,b=%d&0xff,c=%d&0xff;\n\t%s=(a|b|c)^(a&b)^(b&c)^(c&a)^(a&b&c);",1,2);
addDeObfuCode("%s=%s^(%d&0xff)^(%d&0xff);",1,2);
//addObfuCode("unsigned char a=%s,x=%d%8;\n\t%s=(a>>x) | (a<<(8-x));",2,1);
//addDeObfuCode("unsigned char a=%s,x=%d%8;\n\t%s=(a<<x) | (a>>(8-x));",2,1);
spawnRubbisCode(2000); //生成个有4000个节点的代码
//some bug occured here which cause the argument size that can't be larger than 1000
writeToFile("obfus.cpp");
}
int main()
{
srand(time(0));
test();
return 0;
}
github: https://github.com/za233/NestingObfuscator
来自菜鸡的代码乱写之旅。