DASCTF x CBCTF 2024 Reverse

DASCTF x CBCTF 2024 Reverse

总结: 只做出三道Re,还是太菜了,这次勉强排到第十,今年保送路漫漫

在这里插入图片描述

prese

ida打开,代码真是依托答辩呐,可恶的ollvm,还好我有D810,直接启动,然后加密逻辑很简单,就是有个反调试,会改变密文

在这里插入图片描述

看到exp就很简单力

#include<iostream>
using namespace std;
//unsigned char tmp[256];
unsigned char tmp[256] =
{
  0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 
  0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 
  0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 
  0xFE, 0xFF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 
  0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 
  0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 
  0xDC, 0xDD, 0xDE, 0xDF, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 
  0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 
  0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 
  0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 0x80, 0x81, 0x82, 0x83, 
  0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 
  0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 
  0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0x60, 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, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 
  0x40, 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, 0x5B, 0x5C, 0x5D, 
  0x5E, 0x5F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 
  0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 
  0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 
  0x3C, 0x3D, 0x3E, 0x3F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 
  0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 
  0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 
  0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F
};  
unsigned char input[]="abcdefghijklmnopqrstuvwxyz012345";
unsigned char c[] =
{
  0x86, 0x83, 0x91, 0x81, 0x96, 0x84, 0xB9, 0xA5, 0xAD, 0xAD, 
  0xA6, 0x9D, 0xB6, 0xAA, 0xA7, 0x9D, 0xB0, 0xA7, 0x9D, 0xAB, 
  0xB1, 0x9D, 0xA7, 0xA3, 0xB1, 0xBB, 0xAA, 0xAA, 0xAA, 0xAA, 
  0xBF
};
int main()
{
    int len = 32;
    for(int i=0;i<32;i++)
    {
        c[i]^=34;
    }

    for(int i=0;i<32;i++)
    {
        for(int k=32;k<128;k++)
        {
            if(tmp[k]==c[i])
            {
                printf("%c",k);
            }
        }
    }
}

unwind

ida打开,流程非常地简单,一个标准XXTEA,看起来人畜无害,然后化身脚本小子,不出意外爆了个fake弗莱格出来

在这里插入图片描述

那么就是找反调试的存在,动调下,运行发现在int3断点处停了下来,可以看到有SEH的结构

在这里插入图片描述

那么显而易见sub_4E1424就是我们的Handler函数,点进去以后发现就是个XTEA加密,且加密轮数为36

在这里插入图片描述

那么可以猜想加密流程:

输入flag -> XXTEA加密 ->断点异常 -> XTEA加密 ->判断flag,多么天衣无缝的猜想啊。

然鹅当我们按照这个流程解密,会发现毛也解不出,这是为什么呢?做过DubheCTF 2024的Destination这题的小伙伴便会知道,有时候SEH的Handler函数会执行两次,这并不是因为触发了两次错误,而是因为栈展开这个操作的缘故,详细可看这位师傅的blog,(但其实我也没太搞得懂)

Windows-SEH学习笔记(2) - 云之君’s Blog (yunzh1jun.com)

那我们怎么知道有没有进行栈展开的操作呢,很简单动调康康就行了。

我们用x32dbg,因为他有ScyllaHide这个强大的反反调试插件,在sub_4E1960这个函数头下断点,会发现这个函数被调用了两次,因此正确的加密流程为:

输入flag -> XXTEA加密 ->断点异常 -> XTEA加密 ->栈展开 -> XTEA加密 -> 判断flag

也就是说最后经过了两次的XTEA加密,我们可以通过自己写一个加密流程和源程序动调结果对比以确认。

而这里还有一个坑点就是最后的密文会改变,这跟TLS里hook了比较函数有关,且也是前面加密流程改变的关键,这里就不细究了,通过x32dbg的动调可以直接发现正确的密文

在这里插入图片描述

exp:

XTEA

#include <stdio.h>
#include <stdint.h>
 #include<iostream>
 using namespace std;

//加密函数
void encrypt (uint32_t* v, uint32_t* k) {
    unsigned int  v0=v[0], v1=v[1], sum=0, i;     //v0,v1分别为字符串的低字节高字节     
    unsigned int delta=0x61C88647;                    
    int k0=k[0], k1=k[1], k2=k[2], k3=k[3]; 
    for (i=0; i < 36; i++) {               //加密32轮        
       v0 += (((v1 >> 5) ^ (16 * v1)) + v1) ^ (k[sum & 3] + sum);
        sum -= 1640531527;
        v1 += (((v0 >> 5) ^ (16 * v0)) + v0) ^ (k[(sum >> 11) & 3] + sum);
    }                                             
    v[0]=v0; v[1]=v1;//加密后再重新赋值
}



//解密函数
void decrypt ( unsigned int* v, unsigned int* k) {
    unsigned int v0=v[0], v1=v[1];
    unsigned int delta=1640531527;  
    unsigned int sum=0, i;      
    for(int i=0;i<36;i++)
    {
        sum-=1640531527;
                   }               
    for (i=0; i<36; i++) {      
        v1 -= (((v0 >> 5) ^ (16 * v0)) + v0) ^ (k[(sum >> 11) & 3] + sum);
        sum += delta;    
        v0 -= (((v1 >> 5) ^ (16 * v1)) + v1) ^ (k[sum & 3] + sum);             //解密时将加密算法的顺序倒过来,还有+=变为-=



    }                                              
    v[0]=v0; v[1]=v1;//解密后再重新赋值
}

   unsigned char enc[] =
{
   0xC1,0xA7,0xAA,0x87,0xB6,0x21,0x73,0x85,0x8C,0xD2,0x71,0x0E,0xF2,0x39,0xDF,0xCA,0x14,0xCA,0xEF,0x58,0xD8,0xD9,0xE7,0xD7,0x5D,0x5C,0x9F,0xF2,0x5E,0xD4,0x5E,0x5F
};


int main()
{

  unsigned int v[8];
      for(int i=0;i<8;i++)
    {
        v[i]= *((uint32_t*)&enc[i*4]);
        //printf("%x ",v[i]);
     } 


    unsigned int k[4]={'D','A','S','!'};
    for(int i=0;i<8;i+=2)
    {
        decrypt(v+i, k);
        decrypt(v+i, k);

    }
    unsigned char a;
    for(int i=0;i<8;i++)
    {
        for(int k=0;k<4;k++)
        {

            a= *((unsigned char *)(&v[i])+k); 
            printf("0x%x,",a);
        }
     } 

    return 0;
}

XXTEA

#include <stdio.h>
#include <stdint.h>
#define DELTA -1640531527
#define MX (((z>>5^y<<2) + (y>>3^z<<4)) ^ ((sum^y) + (key[(p&3)^e] ^ z)))

void btea(uint32_t *v, int n, uint32_t const key[4])
{
    uint32_t y, z, sum;
    unsigned p, rounds, e;
    if (n > 1)            /* Coding Part */
    {
        rounds = 6 + 52/n;

        sum = 0;
        z = v[n-1];
        do
        {
            sum += DELTA;
            e = (sum >> 2) & 3;
            for (p=0; p<n-1; p++)
            {
                y = v[p+1];
                z = v[p] += MX;
            }
            y = v[0];
            z = v[n-1] += MX;
        }
        while (--rounds);
    }
    else if (n < -1)      /* Decoding Part */
    {
        n = -n;
        rounds = 6 + 52/n;
        sum = rounds*DELTA;
        //printf("%d\n",rounds);
        y = v[0];
        do
        {
            e = (sum >> 2) & 3;
            for (p=n-1; p>0; p--)
            {
                z = v[p-1];
                y = v[p] -= MX;
            }
            z = v[n-1];
            y = v[0] -= MX;
            sum -= DELTA;
        }
        while (--rounds);
    }
}

unsigned char enc[]={0x27,0x74,0xbe,0x58,0x3a,0xcd,0x94,0x33,0x5f,0x22,0x24,0x93,0xbb,0x2d,0xb8,0x1,0x90,0x4b,0x7d,0x4b,0x8f,0xbf,0xef,0xde,0x96,0x6e,0x1b,0x91,0x88,0x92,0x6,0x38}; 
int main()
{
    uint32_t v[11];
    for(int i=0;i<11;i++)
    {
        v[i]=*((uint32_t *)&enc[i*4]);
        //printf("%x ",v[i]);
    }
    //printf("\n");

    uint32_t const k[4]= {'D','A','S','!'};
    int n= 8; 
    btea(v, -n, k);
    unsigned char a;
    for(int i=0;i<8;i++)
    {
        for(int k=0;k<4;k++)
        {
            a=*(((unsigned char *)&v[i])+k);
            printf("%c",a);
        }
    }
    return 0;
}

ezvm

先点开exe康康,发现输入key,输入flag后加载了一个dll,通过check.dll里的check函数进行对比

而题目附件里给的dll直接解出来是个fake flag,也是预料之中了。

在这里插入图片描述

我们先看sub_7FF7200911E5这个函数,前面判断了key必须由数字组成,而这个函数则是将key的每两位组成一个数

比如我输入key为1234567890123456,那么就是他就会转化为8个数:12,34,56,78,90,12,34,56

然后再看sub_7FF7200912D5这个函数,不难发现就是一个很简单的VM,然后直接copy代码,小做修改

#include<iostream>
using namespace std;
int opcode[100];
int main()
{
     int eip_ = 0;
  int v8 = 0;
  opcode[0] = 162;
  opcode[1] = 0;
  opcode[2] = 132;
  opcode[3] = 163;
  opcode[4] = 8;
  opcode[5] = 0;
  opcode[6] = 163;
  opcode[7] = 8;
  opcode[8] = 1;
  opcode[9] = 176;
  opcode[10] = 8;
  opcode[11] = 316;
  opcode[12] = 178;
  opcode[13] = 163;
  opcode[14] = 9;
  opcode[15] = 1;
  opcode[16] = 163;
  opcode[17] = 9;
  opcode[18] = 2;
  opcode[19] = 163;
  opcode[20] = 9;
  opcode[21] = 3;
  opcode[22] = 176;
  opcode[23] = 9;
  opcode[24] = 158;
  opcode[25] = 178;
  opcode[26] = 166;
  opcode[27] = 4;
  opcode[28] = 22;
  opcode[29] = 163;
  opcode[30] = 0;
  opcode[31] = 4;
  opcode[32] = 176;
  opcode[33] = 0;
  opcode[34] = 889;
  opcode[35] = 178;
  opcode[36] = 164;
  opcode[37] = 5;
  opcode[38] = 11;
  opcode[39] = 161;
  opcode[40] = 8;
  opcode[41] = 5;
  opcode[42] = 163;
  opcode[43] = 8;
  opcode[44] = 6;
  opcode[45] = 176;
  opcode[46] = 8;
  opcode[47] = 38;
  opcode[48] = 178;
  opcode[49] = 163;
  opcode[50] = 7;
  opcode[51] = 6;
  opcode[52] = 176;
  opcode[53] = 7;
  opcode[54] = 96;
  opcode[55] = 178;
  opcode[56] = 161;
  opcode[57] = 9;
  opcode[58] = 1;
  opcode[59] = 163;
  opcode[60] = 9;
  opcode[61] = 2;
  opcode[62] = 165;
  opcode[63] = 9;
  opcode[64] = 5;
  opcode[65] = 176;
  opcode[66] = 9;
  opcode[67] = 111;
  opcode[68] = 178;
  opcode[69] = 166;
  opcode[70] = 5;
  opcode[71] = 7;
  opcode[72] = 161;
  opcode[73] = 8;
  opcode[74] = 0;
  opcode[75] = 165;
  opcode[76] = 8;
  opcode[77] = 6;
  opcode[78] = 163;
  opcode[79] = 8;
  opcode[80] = 5;
  opcode[81] = 176;
  opcode[82] = 8;
  opcode[83] = 859;
  opcode[84] = 178;
  opcode[85] = 163;
  opcode[86] = 3;
  opcode[87] = 4;
  opcode[88] = 176;
  opcode[89] = 3;
  opcode[90] = 706;
  opcode[91] = 178;
  opcode[92] = 192;

  int num;
  do
  {
      int a,b;

      //printf("%d;",opcode[eip_]);
    if ( opcode[eip_] == 160 )
    {
        a= opcode[eip_ + 1];
          b= opcode[eip_ + 2];
      //encs[opcode[eip_ + 1]] = opcode[eip_ + 2];
      printf("encs[%d] = %d \n",a,b);
      eip_ += 3;
      //continue;
    }
    if ( opcode[eip_] == 161 )
    {
        a= opcode[eip_ + 1];
      b= opcode[eip_ + 2];
      //encs[opcode[eip_ + 1]] = encs[opcode[eip_ + 2]];
      printf("encs[%d] = encs[%d] \n",a,b);
      eip_ += 3;
      //continue;
    }
    if ( opcode[eip_] == 162 )
    {
        a= opcode[eip_ + 1];
      b= opcode[eip_ + 2];
      //encs[opcode[eip_ + 1]] += opcode[eip_ + 2];
      printf("encs[%d] += %d \n",a,b);
      eip_ += 3;
     // continue;
    }
    if ( opcode[eip_] == 163 )
    {
        a= opcode[eip_ + 1];
      b= opcode[eip_ + 2];
      //encs[opcode[eip_ + 1]] += encs[opcode[eip_ + 2]];
      printf("encs[%d] += encs[%d] \n",a,b);
      eip_ += 3;
      //continue;
    }
    if ( opcode[eip_] == 164 )
    {
        a= opcode[eip_ + 1];
      b= opcode[eip_ + 2];
      //encs[opcode[eip_ + 1]] -= opcode[eip_ + 2];
      printf("encs[%d] -= %d \n",a,b);
      eip_ += 3;
      //continue;
    }
    if ( opcode[eip_] == 165 )
    {
        a= opcode[eip_ + 1];
      b= opcode[eip_ + 2];
      //encs[opcode[eip_ + 1]] -= encs[opcode[eip_ + 2]];
      printf("encs[%d] -= encs[%d] \n",a,b);
      eip_ += 3;
     // continue;
    }
    if ( opcode[eip_] == 166 )
    {
        a= opcode[eip_ + 1];
      b= opcode[eip_ + 2];
      //encs[opcode[eip_ + 1]] *= opcode[eip_ + 2];
      printf("encs[%d] *= %d \n",a,b);
      eip_ += 3;
     // continue;
    }
    if ( opcode[eip_] == 167 )
    {
        a= opcode[eip_ + 1];
      b= opcode[eip_ + 2];
      //encs[opcode[eip_ + 1]] *= encs[opcode[eip_ + 2]];
      printf("encs[%d] *= encs[%d] \n",a,b);
      eip_ += 3;
      //continue;
    }
    if ( opcode[eip_] == 176 )
    {
        a= opcode[eip_ + 1];
      b= opcode[eip_ + 2];
      //v12 = encs[opcode[eip_ + 1]] == opcode[eip_ + 2];
      //v8 = v12;
      //printf("v8 = encs[%d] == %d \n",a,b);
      printf("s.add(encs[%d] == %d) \n",a,b);
      //printf("v8 = ")
      eip_ += 3;
      //continue;
    }
    if ( opcode[eip_] == 177 )
    {
        a= opcode[eip_ + 1];
      b= opcode[eip_ + 2];
      //v12 = encs[opcode[eip_ + 1]] == encs[opcode[eip_ + 2]];
      //v8 = v12;
      //printf("v8 = encs[%d] == %d \n",a,b);
      printf("s.add(encs[%d] == %d) \n",a,b);
      eip_ += 3;
      //continue;
    }
    if ( opcode[eip_] == 178 )
    {

      //printf("if ( !v8 ) return \n");//if ( !v8 )
      //printf("")
        //return 0i64;
      ++eip_;
      //continue;
    }
  }
  while ( opcode[eip_] != 192 );
}

在这里插入图片描述

然后加密流程也很简单,这里就直接用z3解

from z3 import *

s = Solver()
encs=[Int("x[%d]" %i) for i in range(10)]
#encs=enc[:]
encs[8]=0
encs[9]=0
encs[0] += 132
encs[8] += encs[0]
encs[8] += encs[1]
s.add(encs[8] == 316)
encs[9] += encs[1]
encs[9] += encs[2]
encs[9] += encs[3]
s.add(encs[9] == 158)
encs[4] *= 22
encs[0] += encs[4]
s.add(encs[0] == 889)
encs[5] -= 11
encs[8] = encs[5]
encs[8] += encs[6]
s.add(encs[8] == 38)
encs[7] += encs[6]
s.add(encs[7] == 96)
encs[9] = encs[1]
encs[9] += encs[2]
encs[9] -= encs[5]
s.add(encs[9] == 111)
encs[5] *= 7
encs[8] = encs[0]
encs[8] -= encs[6]
encs[8] += encs[5]
s.add(encs[8] == 859)
encs[3] += encs[4]
s.add(encs[3] == 706)
if s.check()==sat:
    ans = s.model()
    print(ans)

解出来得到key为 9787254630123759

运行程序程序,输入key,验证通过,最后返回正确值,然后进入到sub_7FF720091217,这个函数根据输入的key对data进行修改,从而得到正确的加密流程,非预期的做法则是通过观察data文件结合PE文件的知识直接拿到异或的key值

在这里插入图片描述

我们直接输入正确的key后运行就可以得到正确加密流程的dll了,一个非常简单的加密

在这里插入图片描述

exp:
#include<iostream>
using namespace std;
unsigned char enc[]={   0x0D, 0x08, 0x1A, 0x0A, 0x1D, 0x0F, 0x32, 0x78, 0x2A, 0x7B, 
  0x2A, 0x7B, 0x7C, 0x7D, 0x71, 0x64, 0x7A, 0x2C, 0x7B, 0x7D, 
  0x64, 0x28, 0x7D, 0x71, 0x2C, 0x64, 0x78, 0x78, 0x7D, 0x7A, 
  0x64, 0x28, 0x7A, 0x7D, 0x70, 0x7F, 0x28, 0x7A, 0x2B, 0x7E, 
  0x7D, 0x79, 0x79, 0x34};
int main()
{
    unsigned char a;
    for(int i=0;i<44;i++)
    {
        a=enc[i]^0x49;
        printf("%c",a);
    }
}

YunV2

假如我是Retard就好了

  • 19
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值