如何恶心CTF逆向选手 第一季

之前参加了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
来自菜鸡的代码乱写之旅。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值