2023 nb-ctf 逆向 wp(未写完)

0x01 py逆向

给了一个main.py和out.txt 的文件,打开分析一下

import random

key1 = random.choices(range(256), k=20)  #0到255里取20个随机数
key2 = list(range(256))  #0到255的列表
random.shuffle(key2) #随机排列
flag = open('flag.txt', 'rb').read()    #读取flag,设置成input

def enc(n): #加密函数
    q = key2[n]
    w = key1[q % 20]
    n ^= q
    return n, w

x = 0x00000000
for i, c in enumerate(flag):  #枚举遍历,第一个是下标,第二个是里面的东西
    x <<= 8  #x左移动8位,一个字节
    n, w = enc(c)  #根据密钥和flag出两个数
    if i % 2:   #如果是奇数下标,swap交换
        n, w = w, n
    x |= n
    x |= (w << ((2 * i + 1) * 8) ) #按位运算的优先级高于赋值运算

print(key1)
print(key2)
print(x)

然后手动调试一下前几项,很容易发现x 的组成是由w和n交替排列的,大概的规律就是

             w34n33......w2n1w0n0w1n2......w33n34

根据output文件里的x的值,转码成十六进制

(0x)ea0149929a24de3a6ab642ebeaa16a02571f572449b67f1f57c4a4b67fc542027feb6a0eeaab4d9c42ab6a3e5124deab6a045724de244db69a16ea016ab6571f57a1a2fe4290

总共有70个字符,flag推算出是35位

根据这坨玩意写个抽象的脚本分离出w和n

enc="ea0149929a24de3a6ab642ebeaa16a02571f572449b67f1f57c4a4b67fc542027feb6a0eeaab4d9c42ab6a3e5124deab6a045724de244db69a16ea016ab6571f57a1a2fe4290"
enc_l=[]
enc_r=[]
print(len(enc))
for i in range(0,70):
    enc_l.append(enc[i])
for i in range(70,140):
    enc_r.append(enc[i])
n_o=[0xea,0x49,0x9a,0xde,0x6a,0x42,0xea,0x6a,0x57,0x57,0x49,0x7f,0x57,0xa4,0x7f,0x42,0x7f,0x6a]
n_j=[0xea,0x4d,0x42,0x6a,0x51,0xde,0x6a,0x57,0xde,0x4d,0x9a,0xea,0x6a,0x57,0x57,0xa2,0x42]
w_o=[0x0e,0xab,0x9c,0xab,0x3e,0x24,0xab,0x04,0x24,0x24,0xb6,0x16,0x01,0xb6,0x1f,0xa1,0xfe,0x90]
w_j=[0x01,0x92,0x24,0x3a,0xb6,0xeb,0xa1,0x02,0x1f,0x24,0xb6,0x1f,0xc4,0xb6,0xc5,0x02,0xeb]
#n的偶数部分,34到0
for i in range(0,70,4):
    print("0x",end="")
    print(enc_l[i],end="")
    print(enc_l[i+1],end="")
    print(",",end="")
print("\n")
#n的奇数部分,1到33
for i in range(2,70,4):
    print("0x",end="")
    print(enc_r[i],end="")
    print(enc_r[i+1],end="")
    print(",",end="")
#w的奇数部分,33到1,
print("\n")
for i in range(2,70,4):
    print("0x",end="")
    print(enc_l[i],end="")
    print(enc_l[i+1],end="")
    print(",",end="")
#w的偶数部分 ,0到34
print("\n")
for i in range(0,70,4):
    print("0x",end="")
    print(enc_r[i],end="")
    print(enc_r[i+1],end="")
    print(",",end="")
print("\n")
n=[]
w=[]
j=17
for i in range(0,17):
    n.append(n_o[j])
    n.append(n_j[i])
    j-=1
print(n)
j=16
for i in range(18):
    w.append(w_o[i])
    w.append(w_j[j])
    j-=1
print(w)
_n = [106, 234, 127, 77, 66, 66, 127, 106, 164, 81, 87, 222, 127,
    106, 73, 87, 87, 222, 87, 77, 106,
    154, 234, 234, 66, 106, 106, 87, 222, 87, 154, 162, 73, 66,234]
_w = [14, 235, 171, 2, 156, 197, 171, 182, 62, 196, 36, 31, 171,
    182, 4, 36, 36, 31, 36, 2, 182,
    161, 22, 235, 1, 182, 182, 58, 31, 36, 161, 146, 254, 1, 144]

代码有点乱,大概思路就是先把70个字符分成两部分,然后分别从左到右分离出下标奇数部分的n和w,下标偶数部分的w和n,然后把这些n和w排好顺序,分别交替输出得到n和w 的数组。

这个过程读者可以自行完成,我这个只是参考,而且感觉很慢。最后得到的_n和_w便是结果

(脚本里输出的n和w有些问题,分别多了一项和少了一项,笔者懒得去修正了然后手动修改了一下得到正确的n和w,然后这个_n和_w也是有些问题的,当时看题时把main.py里的下标是奇数时,n和w替换,看成了偶数时才替换,所以这里得到的_n其实是实际的w,_w是实际上的n,搞反了,不过万幸的是不影响后面的爆破,只要换一下就好了)

接下来把得到的n和w拿来爆破出c就可以组合成flag了,至福~

key1=[127, 81, 241, 40, 222, 128, 45, 87, 27, 154,
      66,162, 73, 176, 172, 164, 106, 234, 77, 5]
key2=[155, 117, 124, 113, 104, 46, 151, 71, 144, 229, 152, 240,
      199, 88, 103, 105, 245, 209, 13, 82, 166, 9, 201, 233, 228,
      154, 19, 5, 30, 141, 81, 206, 246, 232, 107, 29, 208, 253,
      187, 116, 98, 160, 60, 7, 220, 143, 80, 239, 52, 15, 94, 50,
      149, 241, 57, 92, 230, 100, 31, 51, 36, 24, 39, 14, 25, 90, 101,
      55, 194, 225, 157, 102, 2, 26, 148, 161, 180, 120, 223, 165, 32,
      146, 185, 243, 119, 210, 172, 244, 1, 125, 44, 35, 169, 179, 188,
      64, 207, 33, 137, 200, 142, 182, 250, 195, 28, 4, 79, 191, 86, 215,
      96, 236, 91, 122, 196, 87, 118, 231, 126, 97, 147, 67, 132, 190, 234,
      237, 43, 193, 252, 18, 212, 163, 56, 73, 123, 176, 162, 23, 192, 49,
      21, 242, 171, 112, 153, 238, 203, 134, 167, 93, 115, 95, 8, 12, 65,
      217, 248, 168, 219, 47, 211, 108, 76, 129, 145, 62, 156, 34, 218, 135,
      48, 70, 75, 3, 249, 72, 202, 133, 183, 38, 37, 227, 164, 173, 159, 251,
      0, 174, 54, 20, 136, 53, 138, 99, 226, 178, 42, 66, 150, 205, 204, 214,
      197, 235, 110, 216, 63, 45, 184, 74, 41, 177, 27, 69, 130, 89, 61, 247,
      255, 17, 254, 181, 131, 22, 224, 83, 189, 59, 114, 139, 111,
      68, 6, 84,11, 127, 221, 106, 77, 109, 158,
      170, 16, 121, 222, 186, 10, 58, 175, 40, 128, 198, 78, 85, 213, 140]
x = 3449711664888782790334923396354433085218951813669043815144799745483347584183883892868078716490762334737115401929391994359609927294549975954045314661787321463018287415952
w=[106, 234, 127, 77, 66, 66, 127, 106, 164, 81, 87, 222, 127,
    106, 73, 87, 87, 222, 87, 77, 106,
    154, 234, 234, 66, 106, 106, 87, 222, 87, 154, 162, 73, 66,234]
n=[14, 235, 171, 2, 156, 197, 171, 182, 62, 196, 36, 31, 171,
    182, 4, 36, 36, 31, 36, 2, 182,
    161, 22, 235, 1, 182, 182, 58, 31, 36, 161, 146, 254, 1, 144]
flag=""
def enc(n): #加密函数
    q = key2[n]
    w = key1[q % 20]
    n ^= q
    return n, w
for i in range(35):
      for j in range(32, 127):
            a , b = enc(j)
            if a==n[i] and b==w[i]:
                  flag += chr(j)
                  break
print(flag)


nbctf{cr15s_cr0ss_str4wb3rry_s4uz3}

0x02 sands

看介绍就知道是迷宫题目,打开ida分析一下

有这几个关键的语句,和普通迷宫不同的是,这个迷宫会有‘S’,沙子没回合都会根据step移动来阻挡你的路线,如果沙子移动后正好和你的位置重合,会直接导致游戏失败(逻辑是沙子先动人后动),因为沙子撞人的判断是人移动后的,所以在这个迷宫中,只要你在沙子撞到你后移动走,也是能继续游戏的,这个是关键,知道这个小技巧后走迷宫就简单许多了。

比赛时为了节省时间可以省去不必要的东西,只保留关键的能帮助解题的判断语句,赛后我把迷宫完善了不少,已经能当做一个小游戏玩了。

#include<iostream>
int map[100]={38, 35, 35, 35, 48, 48, 48, 48, 48, 35,
            48, 48, 35, 83, 48, 35, 35, 48, 48, 35, 35,
          48, 83, 35, 48, 35, 83, 83, 48, 35, 35, 48,
           48, 35, 48, 35, 48, 48, 35, 35, 48, 83, 48, 
           35, 48, 35, 48, 83, 83, 48, 48, 35, 35, 35, 
           48, 35, 48, 83, 48, 48, 48, 48, 48, 35, 48,
            35, 48, 48, 48, 83, 35, 35, 48, 35, 48, 35, 
            35, 35, 35, 48, 48, 83, 48, 83, 48, 35, 48,
             48, 83, 48, 48, 48, 83, 48, 48, 35, 76, 83, 
             48, 48};
int i=0,j=0,step=0;
char ch;
char step_arr[50]={0};
//i是横坐标,j是纵坐标
void move(char ch){
    switch(ch){
        case 'a':
            if(i!=0){
                if(map[10*j+i-1]=='0'||map[10*j+i-1]=='L'){
                    map[10*j+i-1]='&';
                    if(map[10*j+i]!='S'){
                    	map[10*j+i]='0';
					}
                    i--;
                }
            }
            break;
        case 'd':
            if(i!=9){
                if(map[10*j+i+1]=='0'||map[10*j+i+1]=='L'){
                    map[10*j+i+1]='&';
                    if(map[10*j+i]!='S'){
                    	map[10*j+i]='0';
					}
                    i++;
                }
            }
            break;
        case 's':
            if(j!=9){
                if(map[10*j+10+i]=='0'||map[10*j+10+i]=='L'){
                    map[10*j+10+i]='&';
                    if(map[10*j+i]!='S'){
                    	map[10*j+i]='0';
					}
                    j++;
                }
            }
            break;
        case 'w':
            if(j!=0){
                if(map[10*j-10+i]=='0'||map[10*j+i-10]=='L'){
                    map[10*j-10+i]='&';
                    if(map[10*j+i]!='S'){
                    	map[10*j+i]='0';
					}
                    j--;
                }
            }
            break;
    }
}
void set_map(){
    int k;
    for(k=0;k<100;k++){
        if(k%10==0){
            std::cout<<"\n";
        }
        printf("%c",map[k]);
    }
}
void sand_move(){
    int x,y;
    switch(step%4){
        case 3:
            for(x=9;x>=0;x--){
                for(y=0;y<=9;y++){
                    if(map[10*y+x]=='S'&&x!=9){
                        if(map[10*y+x+1]=='0'||map[10*y+x+1]=='&'){
                            map[10*y+x+1]='S';
                            map[10*y+x]='0';
                        }  
                    }
                }
            }
            break;
        case 2:
            for(y=0;y<=9;y++){
                for(x=0;x<=9;x++){
                    if(map[10*y+x]=='S'&&y!=0){
                        if(map[10*y+x-10]=='0'||map[10*y+x-10]=='&'){
                            map[10*y+x-10]='S';
                            map[10*y+x]='0';
                        }
                    }
                }
            }
            break;
        case 1:
            for(x=0;x<=9;x++){
                for(y=0;y<=9;y++){
                    if(map[10*y+x]=='S'&&x!=0){
                        if(map[10*y+x-1]=='0'||map[10*y+x-1]=='&'){
                            map[10*y+x-1]='S';
                            map[10*y+x]='0';
                        }
                    }
                }
            }
            break;
        case 0:
            for(y=9;y>=0;y--){
                for(x=0;x<=9;x++){
                    if(map[10*y+x]=='S'&&y!=9){
                        if(map[10*y+x+10]=='0'||map[10/y+x+10]=='&'){
                            map[10*y+x+10]='S';
                            map[10*y+x]='0';
                        }
                    }
                }
            }
            break;
    }
}
int main(){  
    while(step<=49&&map[10*j+i]!='S'){
        set_map();
        std::cout<<"\n"<<"step:"<<step;
        std::cout<<"\n"<<"sand de is "<<(step%4);
        std::cout<<"\n"<<"(0是下移动,1是左移动,2是上移动,3是右移动)";
        std::cout<<"\n"<<"enter your direcation:";
		std::cin>>ch;
        sand_move();
        step_arr[step]=ch;
        step++;
        move(ch);
        if(j==9&&i==6){
        	std::cout<<"yep"<<"\n";
        	for(int l=0;l<step;l++){
            	std::cout<<step_arr[l];
        	}
        	break;
		}
    }
    return 0;
}

这样很浪费时间,不过赛后无所谓了。

然后这题还要连一下官方给的靶机,在linux虚拟机里面的命令行打入

nc 【ip】【端口】

连接成功后ls罗列一下文件,cat可以查看文件

0x03 twostep

这个题的逻辑就是反复调用两个不同的函数,然后分块加密,函数1中有几个子函数,每个子函数加密一块,函数2同理,然后调用法则是函数1到函数2,函数2再到函数1,每次调用时step(计数器)+1,然后case不同的step分块加密,最后根据一个数组word_404090[]知道这个加密块在整体flag具体的位置

#472036518
#123456789

下面是步数step,上面是位置。

函数1

__int64 __fastcall encry1(_BYTE *input)
{
  __int64 result; // rax
  char one; // [rsp+19h] [rbp-A7h]
  __int16 v3; // [rsp+1Ah] [rbp-A6h]
  __int16 v4; // [rsp+1Ch] [rbp-A4h]
  __int16 v5; // [rsp+1Eh] [rbp-A2h]
  unsigned __int64 i; // [rsp+20h] [rbp-A0h]
  unsigned __int64 k; // [rsp+20h] [rbp-A0h]
  __int64 v8; // [rsp+20h] [rbp-A0h]
  unsigned __int64 m; // [rsp+20h] [rbp-A0h]
  unsigned __int64 j; // [rsp+28h] [rbp-98h]
  _BYTE *v11; // [rsp+30h] [rbp-90h]
  _BYTE *v12; // [rsp+30h] [rbp-90h]
  _BYTE *v13; // [rsp+30h] [rbp-90h]
  __int16 v14[4]; // [rsp+38h] [rbp-88h]
  __int64 v15[6]; // [rsp+40h] [rbp-80h]
  __int64 v16[8]; // [rsp+70h] [rbp-50h]
  __int16 v17; // [rsp+B6h] [rbp-Ah]
  unsigned __int64 v18; // [rsp+B8h] [rbp-8h] 472036518
                        //                    123456789

  v18 = __readfsqword(0x28u);
  v14[0] = 0x4808;
  v14[1] = 0xC40;
  v14[2] = 0x480C;
  v14[3] = 0x408C;
  v16[0] = 1LL;
  v16[1] = 16LL;
  v16[2] = 32LL;
  v16[3] = 512LL;
  v16[4] = 1024LL;
  v16[5] = 2048LL;
  v16[6] = 0x2000LL;
  v16[7] = 0x4000LL;
  v15[0] = 177LL;
  v15[1] = 166LL;
  v15[2] = 183LL;
  v15[3] = 182LL;
  v15[4] = 177LL;
  v15[5] = 0xADLL;
  v17 = 170;
  switch ( step )
  {
    case 0LL:
      if ( *input == 'n' && input[1] == 'b' && input[2] == 'c' && input[3] == 't' && input[4] == 'f' && input[5] == '{' )
        result = encry2(input + 6);             // step=0
      else
        result = 0LL;
      break;
    case 1LL:
      v11 = step_change(input);
      for ( i = 0LL; i <= 3; ++i )              // step=2
      {
        one = v11[i];
        v4 = 0;
        for ( j = 0LL; j <= 3; ++j )
        {
          v4 |= (one & 3) << (4 * j + 2);
          one >>= 2;
        }
        if ( v4 != v14[i] )
          return 0LL;
      }
      result = encry2(input);
      break;
    case 3LL:
      v12 = step_change(input);
      for ( k = 0LL; k <= 4; ++k )              // step=4
        v100[k] = v12[k];
      result = encry2(input);
      break;
    case 6LL:
      v3 = *step_change(input);
      v8 = 0LL;                                 // step=7
      while ( v3 )
      {
        v5 = v3 & -v3;                          // 等于1
        if ( v8 == 8 )
          return 0LL;
        if ( v16[v8] == v5 )
          ++v8;
        v3 ^= v5;
      }
      result = v8 == 8 && encry2(input);
      break;
    case 8LL:
      v13 = step_change(input);
      for ( m = 0LL; m <= 5; ++m )              // step=9
        LOBYTE(v17) = v13[m] ^ v15[m];          // 去调试找找v17是多少
      result = 1LL;
      break;
    default:
      result = 0LL;
      break;
  }
  return result;
}

函数2

__int64 __fastcall encry2(_BYTE *input)
{
  __int64 result; // rax
  unsigned __int64 i; // [rsp+18h] [rbp-E8h]
  unsigned __int64 j; // [rsp+18h] [rbp-E8h]
  unsigned __int64 k; // [rsp+18h] [rbp-E8h]
  __int64 v5; // [rsp+20h] [rbp-E0h]
  _BYTE *v6; // [rsp+28h] [rbp-D8h]
  _BYTE *v7; // [rsp+28h] [rbp-D8h]
  _BYTE *v8; // [rsp+28h] [rbp-D8h]
  _BYTE *v9; // [rsp+28h] [rbp-D8h]
  __int64 v10[6]; // [rsp+30h] [rbp-D0h]
  __int64 v11[6]; // [rsp+60h] [rbp-A0h]
  __int64 v12[6]; // [rsp+90h] [rbp-70h]
  __int64 v13[8]; // [rsp+C0h] [rbp-40h] 472036518
                  //                     123456789

  v13[7] = __readfsqword(0x28u);
  v13[0] = 54515LL;
  v13[1] = 15689LL;
  v13[2] = 4219LL;
  v13[3] = 50297LL;
  v13[4] = 43652LL;
  v13[5] = 38919LL;
  v10[0] = 14668LL;
  v10[1] = 24063LL;
  v10[2] = 37349LL;
  v10[3] = 50716LL;
  v10[4] = 61563LL;
  v11[0] = 1843061LL;
  v11[1] = 222420LL;
  v11[2] = 5184810LL;
  v11[3] = 4590105LL;
  v11[4] = 2184197LL;
  v5 = 0LL;
  v12[0] = 1073741834LL;
  v12[1] = 2415919110LL;
  v12[2] = 939524099LL;
  v12[3] = 536870913LL;
  v12[4] = 1845493760LL;
  switch ( step )
  {
    case 0LL:
      v6 = step_change(input);
      if ( (*v6 ^ 95) == v6[1]                  // step=1
        && (v6[1] ^ 117) == v6[2]
        && (v6[2] ^ 18) == v6[3]
        && (v6[3] ^ 56) == *v6
        && *v6 == 108 )
      {
        result = encry1(input);
      }
      else
      {
        result = 0LL;
      }
      break;
    case 2LL:
      v7 = step_change(input);
      for ( i = 0LL; i <= 4; ++i )              // step=3
      {
        if ( v13[i] + v13[i + 1] * v7[i] != v11[i] )
          return 0LL;
      }
      result = encry1(input);
      break;
    case 4LL:
      v8 = step_change(input);
      for ( j = 0LL; j <= 4; ++j )              // step=5
      {
        v5 += v100[j] + (v8[j] << 7);
        if ( v5 != v10[j] )
          return 0LL;
      }
      result = encry2(input);
      break;
    case 5LL:
      v9 = step_change(input);
      for ( k = 0LL; k <= 4; ++k )              // step=6
      {
        if ( v12[k] != ((v9[k] >> (k + 3)) | (v9[k] << (32 - (k + 3)))) )
          return 0LL;
      }
      result = encry1(input);
      break;
    case 7LL:
      qword_404308 = *step_change(input);
      result = *&qword_404308 == 3.325947034342098e151 && encry1(input);// step=8
      break;
    default:
      result = 0LL;
      break;
  }
  return result;
}

大概思路就是这样,剩下逐个攻破加密块,按照word_404090里一一对应的关系排列组合,就能得出flag了。(调试太痛苦了,最后不调试还不知道,摆了)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值