虎符ctf的wp&红明谷杯的wp

大型比赛的题确实有难度,赛后看wp学习了一下,不得不说自己做的时候真的很懵逼。吐槽一下go语言题太多了。。。

gocrypt

输入的flag要满足go语言的regexp_MustCompileregexp___Regexp__FindStringSubmatch规定的格式,然后两轮xtea加密。

动调来看看整个过程。

cherk函数,go语言相关知识点https://ask.csdn.net/questions/1032787https://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}