前言:一个萌新re的wp 
 
PixelShooter 这个题最简单了,GG修改分数死了就直接出flag
 
flag:MRCTF{Unity_1S_Fun_233}
 
因为没有接触过安卓逆向,大佬都是直接把APK丢进反编译软件里的,只能用这种费力的方式了
该题官方下载链接 
 
lualu 这个题先翻了一下脚本发现看不懂,所以在线重写一遍
直接丢进IDA,提示用IDA64,那就是64位程序了
丢进IDA64加载完后翻了一下,卧槽居然没有main
 
不过不急shift+F12,顺便打开程序看一下
ctrl+F搜到了那个字符串
 
双击点进去,并按x查看引用
看到了这些汇编,不过咱也看不懂还是直接F5
一顿操作以后
 
这咋办呐,不急我们走动态看看
 
一样的做法先搜索字符串,定位关键代码
 
本来想找他的scanf,然后跟自己输入的假码,在他上面说的字符串之后下了断点,结果就发现了这个,分析如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 cmps={83 ,80 ,73 ,80 ,76 ,125 ,61 ,96 ,107 ,85 ,62 ,63 ,121 ,122 ,101 ,33 ,123 ,82 ,101 ,114 ,54 ,100 ,101 ,97 ,85 ,111 ,39 ,97 } print ("Give Me Your Flag LOL!:" )flag=io .read () if  string .len (flag)~=28  then  [[~是异或]] 	print ("Wrong flag!" ) 	os .exit () end for  i=1 ,string .len (flag) do 	local  x=string .byte (flag,i) 	if  i%2 ==0  then  [[第偶数个字符a[i]^=i 这里直接再^一次i就可以了]]  		x=x~i 	else  		x=x+6  [[不是偶数个则是奇数个a[i]+=6 逆向就是a[i]-=6]]  	end  	if  x~=cmps[i] then  [[处理完之后与上面给的字符串相比较]]  		print ("Wrong flag!" ) 		os .exit () 	end  end print ("Right flag!" )os .exit ()
 
exp如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include  <stdio.h>  int  main ()  {	char  a[]={0 ,83 ,80 ,73 ,80 ,76 ,125 ,61 ,96 ,107 ,85 ,62 ,63 ,121 , 	122 ,101 ,33 ,123 ,82 ,101 ,114 ,54 ,100 ,101 ,97 ,85 ,111 ,39 ,97 ,0 }; 	for (int  i=1 ;a[i];i++) 	{ 		if (i%2 ==0 ) 		{ 			a[i]^=i; 		}  		else  		{ 			a[i]-=6 ; 		} 	} 	printf ("%s" ,(char *)a+1 );  } 
 
flag:MRCTF{7he_83st_1u@_f0r_yOu!}
 
之后我尝试在内存里找到运行逻辑,失败告终,然后跑去问出题人,出题人说这个题目就是lua lu,内嵌了一个lua解释器(那么我可以改内存来达到盗用他的lua解释器!),大概就是不让那些喜欢静态调试的人好过吧
该题官方下载链接 
 
好了这题咱不吃上一题的亏 直接丢进x64dbg(x32dbg提示我的)
丢进去以后老规矩搜索字符串,定位关键点,在Give me your code下面发现strlen函数并且cmp 0x21
过了len的判断 再往下跟就到把input一顿xx的地方
怎么xx的先不管 再往下看看
一个一个cmp 不是就exit 很好这就是被xx过后的input 那么我们拿它来反xx就可以得到flag了
好了我摊牌了,听说re爷爷看汇编跟看小说一样,我…我是re孙子,咱们上神器IDA_F5
这次居然有main,要是没有main的话直接取后面的动态调试的地址ctrl+g过去找近的函数F5就OK了,要是被aslr了那就还是shift+f12找字符串过去吧,最后直接上exp,话说前面那个算法我分析了好久,以为我没弄错,是f5的错(f5经常抽风),害,我好菜,我菜死了
分析如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 for  ( i = 0 ; i <= 32 ; ++i ){   input[i] = input_[key_1[i]];     							   v4 = i;   input[i] ^= LOBYTE(key_1[i]); }			 for  ( j = 0 ; j <= 32 ; ++j ){   v4 = j;   if  ( key_2[j] != input[j] )   {     sub_40E640(argc, (__int64)argv, j, (__int64)"Wrong!\n" );     system(*(const  char  **)&argc);     exit (argc);   } } 
 
exp如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include  <stdio.h>  int  main ()  {	char  key_1[]={9 ,10 ,15 ,23 ,7 ,24 ,12 ,6 ,1 ,16 ,3 ,17 ,32 ,29 ,11 ,30 ,                  27 ,22 ,4 ,13 ,19 ,20 ,21 ,2 ,25 ,5 ,31 ,8 ,18 ,26 ,28 ,14 ,0 }; 	char  key_2[]="gy{\x7Fu+<RSyW^]B{-*fB~LWyAk~e<\\EobM" ; 	char  flag[34 ]={0 }; 	for (int  i=32 ;i>=0 ;i--) 	{ 		flag[key_1[i]] = key_2[i]; 		flag[key_1[i]] ^= key_1[i]; 	} 	printf ("%s" ,flag);  } 
 
flag:MRCTF{Tr4nsp0sltiON_Clph3r_1s_3z}
 
该题官方下载链接 
 
Junk 拿到题目先丢进ida,shift+f12看到了 “give me your flag! “ ,然后交叉引用,然后f5,然后欸?怎么f5不了?f5报错了居然!不急不急,按空格切换视图,记下左边的地址,401390 
然后咱把它丢进od,ctrl+g跳到这个地方,再一顿改名分析,发现了关键call
enter进去看看
 
好了len出来了,往下继续看
 
这个call,噫,有点意思,call指令分为两步,先push下一条指令的地址入栈,然后jmp去一个地方,这里过来直接把入栈的地址加了一,后面retn就是到了之前入栈的下一个地址的+1的地方,意思就是说下面那里根本不是一个call,前面那一位E8是根本不会走的,那么我们把它nop掉,这样就能看见了
这里说一下怎么nop他,在下面的数据窗口ctrl+g输入401256 即那个call的地址,再双击第一个E8改成90就是nop了
这样逻辑很清晰了,这里才几句用不着看F5也可以知道这里的算法是把每一位都^3
异或完了可以看见他直接跳过了这里,那么我们就可以料想这两个call一点P用都没有,再往下看看
什么?je跳过去的线断了?其实并不是,只是od把这里以为是个call其实他是个屁call,他直接je到了401280 ,而这个call的地址是40127F ,也就是说这里是一个假call,那咱再把他nop掉
因为上面那个xor,zf标志寄存器一定会为1,je一定会跳过,那么我们直接把je也一块nop了,下面也有一个一毛一样的结构,于是我顺手就nop完了,好了我们现在下面就是剩下的程序了,但是菜鸡看不懂汇编呢,现在能f5了吗?试试看?右键->复制到可执行文件->所有修改,文件->右键->保存文件
我们来对比一下
 之前是这个样子的,点进去并不能F5
 之后是这个样子的,点进去可以F5
分析一波如下
 
game3分析如下
逆向算法思路:从key反推是table几,保存起来,在反base64算法,再反game2,最后反game1,完事
exp如下
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 #include  <stdio.h>  int  main ()  {	char  table[]={0x41 ,0x42 ,0x43 ,0x44 ,0x45 ,0x46 ,0x47 ,0x48 ,0x49 ,0x4A , 			0x4B ,0x4C ,0x4D ,0x4E ,0x4F ,0x50 ,0x51 ,0x52 ,0x53 ,0x54 ,0x55 , 			0x56 ,0x57 ,0x58 ,0x59 ,0x5A ,0x61 ,0x62 ,0x63 ,0x64 ,0x65 ,0x66 , 			0x67 ,0x68 ,0x69 ,0x6A ,0x6B ,0x6C ,0x6D ,0x6E ,0x6F ,0x70 ,0x71 , 			0x72 ,0x73 ,0x74 ,0x75 ,0x76 ,0x77 ,0x78 ,0x79 ,0x7A ,0x29 ,0x21 , 			0x40 ,0x23 ,0x24 ,0x25 ,0x5E ,0x26 ,0x2A ,0x28 ,0x2B ,0x2F }; 	char  key[]={0x25 ,0x42 ,0x55 ,0x45 ,0x64 ,0x56 ,0x53 ,0x48 ,0x6C ,0x6D , 			0x66 ,0x57 ,0x68 ,0x70 ,0x5A ,0x6E ,0x21 ,0x6F ,0x61 ,0x57 ,0x5A , 			0x28 ,0x61 ,0x47 ,0x42 ,0x73 ,0x5A ,0x40 ,0x5A ,0x70 ,0x5A ,0x6E , 			0x21 ,0x6F ,0x61 ,0x57 ,0x5A ,0x28 ,0x61 ,0x47 ,0x42 ,0x73 ,0x5A , 			0x40 ,0x5A ,0x70 ,0x5A ,0x6E ,0x21 ,0x6F ,0x59 ,0x47 ,0x78 ,0x6E , 			0x5A ,0x6D ,0x25 ,0x77 ,0x2E ,0x2E }; 	unsigned  char  flag[60 ]={0 }; 	int  temp[60 ]={0 }; 	for (int  i=0 ;key[i];i++) 	{ 		for (int  j=0 ;table[j];j++) 			if (key[i]==table[j]) 			{  				temp[i]=j; 				break ; 			} 	} 	for (int  i=0 ,j=0 ;i<43 ;i+=3 ,j+=4 ) 	{ 		flag[i] = ((temp[j] & 0x3F ) << 2 ) + ((temp[j+1 ] & 0x30 ) >> 4 ); 		flag[i+1 ] = ((temp[j+1 ] & 0xF ) << 4 ) + ((temp[j+2 ] & 0x3C ) >> 2 ); 		flag[i+2 ] = ((temp[j+2 ] & 3 ) << 6 ) + (temp[j+3 ] & 0x3F ); 	} 	for (int  i=0 ;i<43 ;i++) 	{ 		flag[i] = flag[i] % 16  * 16  + flag[i] / 16 ; 		flag[i] ^= 3 ; 	} 	printf ("%s" ,flag); } 
 
flag:MRCTF{junkjunkjunkcodejunkjunkcodejunkcode}
 
该题官方下载链接 
 
shit 丢进IDA里容易发现这个,不过呢这里点过去不能F5,因为他做了混淆,我们动态跑进去把他混淆的地方nop掉
然后右键补丁,效果如下
反汇编分析如下
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 char  __cdecl sub_4012F0 (const  char  *input)  {  unsigned  int  v1;    int  v2;    int  v4;    signed  int  i;    int  v6;    v4 = 0 ;   for  ( i = 0 ; i < strlen (input); i += 4  )   {     v1 = input[i + 3 ] | (input[i + 2 ] << 8 ) | (input[i + 1 ] << 16 ) | (input[i] << 24 );     v2 = (v1 << (32  - *(&key_1 + 4  * (i / 4 )))) | (v1 >> *(&key_1 + i / 4 ));     sub_4013CA();     v6 = ((v2 << 16 ) | ~HIWORD(v2)) ^ (1  << *(&key_1 + i / 4 ));     sub_40140F();     if  ( i > 0  )       v6 ^= v4;     v4 = v6;     if  ( v6 != key_2[i / 4 ] )       return  0 ;   }   return  1 ; } 
 
逆向思路:
key_2 ^=上一个key_2( 如果有的话), 
再拿结果 ^=1<<key_1[i/4] 的后四位取反 ,再把前后4位换位置 , 
再循环左移key_1[i/4]位 ,这个时候的结果转换成char输出就是flag了 
 
完整exp如下
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 #include  <stdio.h>  #define  rol( a , o ) \ ((a<<(o%0x20 )) | (a>>(0x20  - (o%0x20 )))) #define  ror( a , o ) \ ((a>>(o%0x20 )) | (a<<(0x20  - (o%0x20 )))) int  main ()  {	int  key1[]={0x3 ,0x10 ,0xD ,0x4 ,0x13 ,0xB }; 	int  key2[]={0x8C2C133A ,0xF74CB3F6 ,0xFEDFA6F2 ,0xAB293E3B ,0x26CF8A2A ,0x88A1F279 }; 	unsigned  int  v1,v2,v3,v4; 	char  input[]={0x66 ,0x6C ,0x61 ,0x67 ,0x7B ,0x61 ,0x5F ,0x33 ,0x61 ,0x32 ,0x79 , 	0x5F ,0x72 ,0x65 ,0x5F ,0x66 ,0x6F ,0x72 ,0x5F ,0x74 ,0x65 ,0x73 ,0x74 ,0x7D ,0 }; 	for (int  i=0 ;i<6 ;i++) 	{ 		v3 = key2[i]; 		if (i) 			v3 ^= key2[i-1 ]; 		v3 ^= (1  << key1[i]); 		v2 = ((v3& 0xFFFF0000 ) >> 16 ) + (((~v3) & 0xFFFF ) << 16 ); 		 		v1 = (v2 >> (32  - key1[i])) | (v2 << key1[i]); 		printf ("%c%c%c%c" ,*((char *)&v1+3 ),*((char *)&v1+2 ),*((char *)&v1+1 ),*(char *)&v1); 	}  } 
 
flag:flag{a_3a2y_re_for_test}
 
官方下载链接 
 
撒花完结~这次就写了这么几个题,主要是因为IDA里的反汇编太不熟悉了,以及对基本算法逆向的不熟悉,需要多多做题,多多写wp
 
virtual_tree (再补一道题)
这个题动态静态双管齐下
大体结构是这样的
第一个函数进去乍一看以为是个算法,仔细一看发现根本不会走,判断直接跳过了,然后len=16
这个树着实麻烦,不过关键代码只有一句,在这里下断点跑一次就可以拿到所有的数据
这个game其实挺简单的,不过这是经过处理之后的如果是直接静态来看,会直接懵B
 
作者坏坏的把名字改了,而且还有一点混淆(作者说这是编译器的问题)
具体处理方法是这样的,不过其实没有必要,动态走一遍记录一下跑的地方和参数也是一样的,主要是迷惑性很强
exp及部分解析 唠叨
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 #include  <stdio.h>  char  key1[]={0x17 ,0x63 ,0x77 ,0x3 ,0x52 ,0x2E ,0x4A ,0x28 ,0x52 ,0x1B ,0x17 ,0x12 ,0x3A ,0x0A ,0x6C ,0x62 };void  uadd (char  a,char  b)  {	key1[b]-=a; } void  uxor (char  a,char  b)  {	key1[b]^=key1[a];  }    void  usub (char  a,char  b)   { 	  	  	 	key1[b]=-key1[b]+key1[a];  } int  main ()  {	char  input[17 ]={0 };  	uadd(2 ,0xf ); 	uxor(0xf ,0xe ); 	usub(2 ,0xc ); 	uxor(0xc ,0xb ); 	usub(7 ,0xa ); 	usub(8 ,9 ); 	uxor(7 ,8 ); 	uadd(3 ,7 ); 	usub(1 ,6 ); 	uxor(5 ,4 ); 	usub(7 ,3 ); 	uadd(7 ,2 ); 	uxor(2 ,1 ); 	uadd(0xa ,0 ); 	char  key2[]={0x4D ,0x4C ,0x47 ,0x50 ,0x4F ,0x4B ,0x46 ,0x43 ,0x4A ,0x45 ,0x4E ,0x49 ,0x48 ,0x44 ,0x42 ,0x41 };  	for (int  i=0 ;i<16 ;i++) 		input[i]=key1[i]^key2[i]; 	printf ("%s" ,input); } 
 
flag:flag{@_7r3e_f0r_fuNN!}