虎符ctf的wp&红明谷杯的wp
大型比赛的题确实有难度,赛后看wp学习了一下,不得不说自己做的时候真的很懵逼。吐槽一下go语言题太多了。。。
gocrypt
输入的flag要满足go语言的regexp_MustCompileregexp___Regexp__FindStringSubmatch规定的格式,然后两轮xtea加密。
动调来看看整个过程。
cherk函数,go语言相关知识点https://ask.csdn.net/questions/1032787,https://blog.csdn.net/weiyuefei/article/details/78589764
所以说flag要满足类似的形式flag{aaaaaaaa-aaaa-aaaa-1111-111111111111},继续往下看
然后回到主函数,继续调几步就是变了delat的xtea加密函数
进入函数,会很明显的知道这是个xtea加密,所以只需要找到key和delat和加密后的数据存放的位置就可以了
在后面下一个断点,看看加密数据存放的位置
后面又有一组xtea加密,就直接跳过了,加密后的数据也是在那个位置。
就直接看比较函数了。
exp
//flag{aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}
//flag{aaaaaaaa-aaaa-aaaa-1111-111111111111}
//flag{11111111-1111-1111-1111-111111111111} C00005E190,C000060780
void decrypt(unsigned int r ,unsigned int *code ,unsigned int *key)
{
unsigned int v0,v1,i,delta=0x12345678;
unsigned int sum=delta*r;
v0=code[0];
v1=code[1];
for(i=0;i<r;i++)
{
v1-=( ((v0<<4) ^(v0>>5)) +v0 ) ^ ( sum + key[ (sum>>11)&3 ]);
sum-=delta;
v0-=( ((v1<<4) ^ (v1>>5)) +v1 ) ^ ( sum + key[sum&3] );
}
code[0]=v0;
code[1]=v1;
}
#include<stdio.h>
int main()
{
unsigned int key[4]={0x10203,0x4050607,0x8090a0b,0xc0d0e0f};
unsigned int r=32;
unsigned int code[2]={F011C30E,F39AC745};// 0x10D9F5ED,0xCB022754
decrypt(r,code,key);
printf("%02x %02x",code[0],code[1]);
}
//flag{6ab84d0a-d2f3-le6f-15fd147da714fd6f}
注意小端序就ok
re
mips架构,32位IDA7.5打开,反编译后会看到很多c++写的东西,里面主要的函数也就是pre和server_check_redemption_code,然后数据也就两个字符串,pre函数内部实际上是和主函数差不多的。下面先看看一些c++反编译出来的怎么理解。
std::operator<<<std::char_traits<char>>(&std::cout, "your redemption code: ", envp);
//这种有&std::cout,是输出,类似printf
std::getline<char,std::char_traits<char>,std::allocator<char>>(&std::cin, v11);
//这种有&std::cin的是输入
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(a,b,c)
//即 strncpy(a, b, lenth) 第三个参数可选
std::allocator<char>::allocator(a)
//相当于malloc(a),a是大小
std::allocator<char>::~allocator(a)
//相当于free(a)
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length(a1)
//求字符串长度,类似strlen(a1)
*(char *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](a2, i)
//相当于a2[i]
下面对主函数进行分析
下面对server_check_redemption_code进行分析
刚开始我还因为有多解,因为如果只需要函数值返回7的话,那么只需要让k=20,所以前13个字符随便取前面的都ok了,但是忽略了pre这个函数,这个函数里面的server_check_redemption_code函数不能返回-1,这就意味着,flag一定在pre函数的那个字符串中有。所以flag只会有一个。
所以直接在”I Love Ninja Must Die”这个字符串中找和”Ninja Must Die 3 Is A Cruel Game, So Hard For Me”相同的部分就是flag
得到flag{Ninja Must Die}
C语言写的server_check_redemption_code大致过程
#include<stdio.h>
#include<string.h>
int main(void)
{
int i,j,v4,v7;
int s[0xe][256]={0,};
int flag[14]={'a','a','a','a','a','a','a','a','a','a','a','a','a','a',};
char table[]="I Love Ninja Must Die 3. Beautiful Art And Motive Operation Is Creative.";
memset(s, 0,0xe*256);
s[0][flag[0]]=1;
v4=0;
for(i=1;i<0xE;i++)
{
for(j=0;j<256;j++)
{
if(j==flag[i])
s[i][j]=s[v4][j];
else
s[i][j]=i+1;
}
v4=s[v4][(flag[i])];
}
v7=0;
for(i=0;i<strlen();i++)
{
v7=s[v7][table[k]]
if(v7=14)
rerutn k-14+1;
}
}
CrackMe
动调,flag有17个,通过两个while(1)被分为7,和10。我测试的flag是abcdefghijklmnopq
第一个while
第二个while
然后需要输入一个值
爆破,参考了别人的脚本,爆破的是商,然后*12379+余数,确实比较好,思路很6逼,大大减少了爆破的时间
#include<stdio.h>
#include<math.h>
double sub_7FF6110D1360(double a,double b)
{
double c;
c=pow(a,b-1);
return c/exp(a);
}
int main()
{
int i=0,j=0;
double v19;
double v22;
double v16=0.0;
while(1)
{
double v17=0.0;
double v18=0.0;
int v20;
v19 = (double)i + 1.0;
do
{
v17= v17 + sub_7FF6110D1360(v18, v19)* 0.001;
v18 = v18 + 0.001;
}while(v18 <= 100.0);
v20 = (int)(v17 + v17 + 3.0);
if(v20==0x13b03)
{
//printf("%d",i);
break;
}
else
{
//printf("%d",i);
}
i++;
}
while(1)
{
double v21=0.0;
v16=0.0;
v22=j+1.0;
do
{
v16 = v16 + sub_7FF6110D1360(v21, v22) * 0.001;
v21 = v21 + 0.001;
}while( v21 <= 100.0 );
if((int)(v16 + v16 + 3.0)==0x5a2)
{
//printf("%d",j);
break;
}
j++;
}
printf("%d",i*12379+j);
}
//99038
继续看后面,异或处理了前7个字符。
先解出来
#include<stdio.h>
int main()
{
int code[7]={0x08, 0x4D, 0x59, 0x06, 0x73, 0x02, 0x40};
int key[7]={0x39,0x39,0x30,0x33,0x38,0x31,0x39};
int flag[7];
int i ;
for(i=0;i<7;i++)
{
flag[i]=code[i]^key[i];
printf("%c",flag[i]);
}
}
//1ti5K3y
然后是rc4加密后面10个字符,key为前7个,没有魔改
网上脚本直接解
//程序开始
#include<stdio.h>
#include<string.h>
typedef unsigned longULONG;
/*初始化函数*/
void rc4_init(unsigned char*s, unsigned char*key, unsigned long Len)
{
int i = 0, j = 0;
char k[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i<256; i++)
{
s[i] = i;//做一道re的题时这里就改为了,s[i]=256-i
k[i] = key[i%Len];
}
for (i = 0; i<256; i++)
{
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j];//交换s[i]和s[j]
s[j] = tmp;
}
}
void rc4_crypt(unsigned char*s, unsigned char*Data, unsigned long Len)
{
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k<Len; k++)
{
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];//交换s[x]和s[y]
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] ^= s[t];
}
}
int main()
{
unsigned char s[256] = { 0 }, s2[256] = { 0 };//S-box
char key[256] = { "1ti5K3y" };
char pData[512] = {0xB2, 0xD6, 0x8E, 0x3F, 0xAA, 0x14, 0x53, 0x54, 0xC6};
unsigned long len = strlen(pData);
int i;
rc4_init(s, (unsigned char*)key, strlen(key));//已经完成了初始化
for (i = 0; i<256; i++)//用s2[i]暂时保留经过初始化的s[i],很重要的!!!
{
s2[i] = s[i];
}
//rc4_init(s,(unsignedchar*)key,strlen(key));//初始化密钥
rc4_crypt(s2, (unsigned char*)pData, len);//解密
printf("pData=%s", pData);
return 0;
}
//RC4_crypt
得到flag{1ti5K3yRC4_crypt}
g0
go语言写的exe,用了脚本后还是看不到main_main函数,估计是用了什么东西吧,但是动调可以找到相关函数的位置,然后进去p键一些地方,点进去一些函数又点出来,多试几次就可以看到反编译出来的逻辑了。
先看大致流程
首先是交换位置,将20个字符分为4组,每组5个,假设序号为1234,就变为了2431。
然后就是base58加密,特征有长度,和变表
进入main_Encode
特征1:长度
特征2:表
最后就是跳过if检测,进入比较函数看密文了
进入main_main_func1
exp
import base58
code='2GVdudkYo2CBXoQii7gfpkjTc4gT'
table1='12Nrst6CDquvG7BefghJKLMEFHPQZabRSTUVmyzno89ApwxWXYcdkij345'
table2='123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
code2=''
flag1=''
flag=''
for i in range(len(code)):
for j in range(len(table1)):
if code[i]==table1[j]:
code2+=table2[j]
break
print(code2)
flag1=base58.b58decode(code2)
flag=flag1[15:20]+flag1[0:5]+flag1[10:15]+flag1[5:10]
print(flag)
//flag{We1CTFc0m_2345}