hnctfRe

# Some reverse questions I didn’t have time to solve. (Or didn’t solve out…)

# Help_Me!

# My exp:
#include<bits/stdc++.h>
using namespace std;
int v12[1000]={0};
int v13[1000]={0};
long long maxn;
int v[1000];
void dfs(int cns,int n,int num,long long sum){
    if (num>200 || n==20)
    {
        if (n!=20)
        sum-=v12[n];
        cns--;
        if (sum>=maxn){
            maxn=sum;
            cout<<"sum  =  "<<sum<<"    :  "<<"cns="<<cns<<"   ";
            for (int i=0;i<cns;i++){
                cout<<v[i]<<" ";
            }
            cout<<endl;
        }
        return ;
    }
    
    for (int i=n+1;i<=20;i++){
        v[cns]=i;
        
        dfs(cns+1,i,num+v13[i],sum+v12[i]);
    }
}


int main(){
    
    v12[0] = 26;
    v12[1] = 59;
    v12[2] = 30;
    v12[3] = 19;
    v12[4] = 66;
    v12[5] = 85;
    v12[6] = 94;
    v12[7] = 8;
    v12[8] = 3;
    v12[9] = 44;
    v12[20] = 1000;
    v12[10] = 5;
    v12[11] = 1;
    v12[12] = 41;
    v12[13] = 82;
    v12[14] = 76;
    v12[15] = 1;
    v12[16] = 12;
    v12[17] = 81;
    v12[18] = 73;
    v12[19] = 32;
    v12[20]=1;
    v13[0] = 71;
    v13[1] = 34;
    v13[20] = 1000;
    v13[2] = 82;
    v13[3] = 23;
    v13[4] = 1;
    v13[5] = 88;
    v13[6] = 12;
    v13[7] = 57;
    v13[8] = 10;
    v13[9] = 68;
    v13[10] = 5;
    v13[11] = 33;
    v13[12] = 37;
    v13[13] = 69;
    v13[14] = 98;
    v13[15] = 24;
    v13[16] = 26;
    v13[17] = 83;
    v13[18] = 16;
    v13[19] = 26;
    v13[20]=1;
    maxn=0;
    dfs(0,-1,0,1);
}
# Official exp:
#include "iostream"
#include "stdio.h"
using namespace std;
int val[50]={26, 59, 30, 19, 66, 85, 94, 8, 3, 44, 5, 1, 41, 82, 76, 1,    12,    81,    73,    32},
w[50]={71, 34, 82, 23, 1,88,12,57, 10, 68, 5, 33,    37,    69,    98,    24, 26,    83, 16, 26};
int dp[21][301];
int main()
{
    int t=200;
    int m = 20;
    for(int i=1;i<=m;i++) 
        for(int j=t;j>=0;j--)  
    {
        if(j>=w[i])//容量可以放 
        {
            int tmp;
            dp[i][j]=max(dp[i-1][j-w[i]]+val[i],dp[i-1][j]);
        }  
        else
        {
            dp[i][j]=dp[i-1][j];
        }              
    }
    //dp[i][j] = max(dp[i-1][j-w[i]]+val[i],dp[i-1][j]);
    int x = 20;
    int y = 200;
    while(x!=0)
    {
        if(dp[x-1][y-w[x]]+val[x] > dp[x-1][y])        
        {
            printf("%d,",x); 
            y-=w[x];
        }
        x-=1;
    }
    return 0;
}

A 0-1 bag problem. And notice that you should ADD v12, not MULTIPLY, like in the ida…

# CM2

# Don’t understand how to debug to find the place… Though by guessing I work it out anyway.
# Officail wp:

cm2

# Try2debugPlusPlus

Nop the IsDebuggerPresent() (noticed that there are two of them) and get the key(remember to set breakpoint after printf() ):

debug2_1

Though is easy for us to decrypt the tea_encrypt, question setter seems to have forgotten something…

debug2_2

# What_1in_D11

Repair the upx’s features(easy in this problem, to study more you can refer to this blog)

And upx -d to unpacked the .dll, check it in ida, we got:

dll

# Official writeup:
# (A btea encrypt, which I am not fimilar with yet. Shall get more study about encryption and decryption later.)
#include <stdio.h>  
#include <stdint.h>  
#define DELTA 0x9e3779b9  
#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;  
        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);  
    }  
}  
int main()  
{  
    unsigned int enc[8]={0x22a577c1,0x1c12c03,0xc74c3ebd,0xa9d03c85,0xadb8ffb3};
    uint32_t const k[4]= {55,66,77,88};  
    int n= 5; //n的绝对值表示v的长度,取正表示加密,取负表示解密  
    // v为要加密的数据是两个32位无符号整数  
    // k为加密解密密钥,为4个32位无符号整数,即密钥长度为128位  
    btea(enc, -n, k);  
    for(int i=0;i<5;i++)
    {
            printf("%x",enc[i]);
        }
    return 0;  
}  

# Mazes

# A bit complex, wait until later…

# stub

# Never seen before, later…

# ez_maze

We get a .exe file, from its’ icon we know that we should uncompile it to .pyc with pyinstxtractor

ez_maze

Then, we got a package _extracted :

# complex it is…

extracted

Find a file named maze (without a suffix one!) Change its suffix into .pyc and Try compyle6 -o maze.py maze.pyc , it went wrong:

compyle6

That’s when you find out that pyinstxtractor didn’t fix your .pyc file’s magic number.

cmake

The link problem setter gives you, which does no help at all…The magic number of the file struct is destroy as well…

magicNumber_1

I been trying tools like uncompyle6 (which does not support python 3.10, and install python 3.9 does not help at all. Later I should look into this problem and try to solved it…) and pycdc (which is said to support high edition, but can not identify the magic number as well) However, when I tried to add some magic number from other python edition(Python 3.8b2, if I remember it right), and put it in an online uncompiler. It just worked…

a blog from my classmate that may help(though I do not really think so…)

# some magic number:
enum PycMagic {
    MAGIC_1_0 = 0x00999902,
    MAGIC_1_1 = 0x00999903, /* Also covers 1.2 */
    MAGIC_1_3 = 0x0A0D2E89,
    MAGIC_1_4 = 0x0A0D1704,
    MAGIC_1_5 = 0x0A0D4E99,
    MAGIC_1_6 = 0x0A0DC4FC,

    MAGIC_2_0 = 0x0A0DC687,
    MAGIC_2_1 = 0x0A0DEB2A,
    MAGIC_2_2 = 0x0A0DED2D,
    MAGIC_2_3 = 0x0A0DF23B,
    MAGIC_2_4 = 0x0A0DF26D,
    MAGIC_2_5 = 0x0A0DF2B3,
    MAGIC_2_6 = 0x0A0DF2D1,
    MAGIC_2_7 = 0x0A0DF303,

    MAGIC_3_0 = 0x0A0D0C3A,
    MAGIC_3_1 = 0x0A0D0C4E,
    MAGIC_3_2 = 0x0A0D0C6C,
    MAGIC_3_3 = 0x0A0D0C9E,
    MAGIC_3_4 = 0x0A0D0CEE,
    MAGIC_3_5 = 0x0A0D0D16,
    MAGIC_3_5_3 = 0x0A0D0D17,
    MAGIC_3_6 = 0x0A0D0D33,
    MAGIC_3_7 = 0x0A0D0D42,
    MAGIC_3_8 = 0x0A0D0D55,
    MAGIC_3_9 = 0x0A0D0D61,
};

uncompyle:(to long the maze is, I didn’t put it here)

uncompyle

Dfs script(From official wp):

map1=[...]
map2 =  [[0 for i in range(len(map1))] for i in range(len(map1)) ]
flag=""
def DFS(x,y):
    global flag
    if x == len(map1) - 2 and y == len(map1) - 2: #判断边界
        print(flag)
    if map1[x+1][y] == 0 and map2[x+1][y] == 0:
        map2[x][y] = 1
        flag += 's'
        DFS(x+1,y)
        flag = flag[:-1]
        map2[x][y] = 0
    if map1[x-1][y] == 0 and map2[x-1][y] == 0:
        map2[x][y] = 1
        flag += 'w'
        DFS(x-1,y)
        flag = flag[:-1]
        map2[x][y] = 0
    if map1[x][y+1] == 0 and map2[x][y+1] == 0:
        map2[x][y] = 1
        flag += 'd'
        DFS(x,y+1)
        flag = flag[:-1]
        map2[x][y] = 0
    if map1[x][y-1] == 0 and map2[x][y-1] == 0:
        map2[x][y] = 1
        flag += 'a'
        DFS(x,y-1)
        flag = flag[:-1]
        map2[x][y] = 0
y=1
x=1
DFS(x,y)

# findit

Gets a .exe file, check whether shell exists.

shell

# Open it with ida.

ida1
ida2
ida3

A complex encryption. The key and the flag both needs to be brute forces out. I shall do a emersion later…

# Official wp:
mid=[32,59,121,60,125,45,32,17,38,0,43,45,48,48,28,42,11,20,49,100,63,119,103,53,173,46,136,158,49,78,78,176]
enc=[29,70,92,84,87,19,61,43,62,60,29,9,18,63,6,6,42,14,124,110,109,60,105,191,7,162,64,104,92,61,223,179]
flag=[0]*32
flag[31]=179
flag[30]=223
print(flag)
#th1s_3ncryt_is_s0_e@sy!1
key = [ord(i) for i in "HN_CTF"]
def decrypt(arr):
    for i in range(0,len(arr)//4):
        tmp = arr[4*i:4*i+4]
        st = ""
        st+=chr(tmp[3]^key[i%6])
        st+=chr(tmp[0]^key[i%6])
        st+=chr(tmp[2]^key[i%6])
        st+=chr(tmp[1]^key[i%6])
        print(st,end='')
    print()   
    pass
def dfs(deep):
    global flag
    if(deep==0):
        decrypt(flag)
    else:
        for i in range(0,0xff):
            if( enc[deep-1] == (0x12+deep-1)^ ((i+12)%24 )^ i ^ flag[deep]):
                flag[deep-1] = i
                dfs(deep-1)
dfs(31)

# flower

From ida, there are a lot of junkcodes inside, and we have to patch them before I can analyze the code.

junkcode

# Some script to nop the junkcodes:

junkcode1

# scripts1:
import idc
import idautils
start =0x00401000
end = 0x00401401
bad=[0x75,0x02,0x74,0x01,0xc7]

for i in range(start,end):
    if idc.get_wide_byte(i) == 0x75:
        flag=1
        for j in range(len(bad)):
            if(idc.get_wide_byte(i+j)!= bad[j]):
                flag=0
                break
        if flag==1:
            for j in range(len(bad)):
                patch_byte(i+j,0x90)
                print("success")
# scripts2:

junkcode2

import idc
import idautils
start =0x00401000
end = 0x00401401
bad=[0xe8,0x01,0x00,0x00,0x00,0xe8,0x36,0x83,0x04,0x24,0x08,0xc3,0xe8]

for i in range(start,end):
    if idc.get_wide_byte(i) == 0xe8:
        flag=1
        for j in range(len(bad)):
            if(idc.get_wide_byte(i+j)!= bad[j]):
                flag=0
                break
        if flag==1:
            for j in range(len(bad)):
                patch_byte(i+j,0x90)
                print("success")
# Some interlude:

PDF

# Anyway, the uncompilation shall look like this:

ida

# The encrypt function:

encrypt

# Now we get the encryption, we can know that it is a RC4- encryption, and here comes the official exp:
#include <iostream>
using namespace std;
unsigned int ar[40] = { 0x4d,0xffffffe6,0x49,0xffffff95,0x3,0x2d,0x2b,0xffffffba,0xffffffea,0x6d,0xffffffff,0x59,0x70,0x0,0x1b,0xffffffa9,0x2c,0xffffffb0,0x32,0xffffff98,0x6f,0xffffff8c,0x56,0xffffffa2,0x4c,0x79,0x7f };
// c[i]_ = c[i]^c[(i+1)%27]
// c[26]_ = c[26]^c_[0]
unsigned char st[16] = "Hello_Ctfers!!!";
void O0oo00OOo00o0(unsigned char *m, int mlen, int keylen) {
        unsigned char s[256];
        unsigned char t[256];
        int i;
        for ( i = 0; i < 256; i++) { //初始化s和t向量 
                s[i] = i;
                t[i] = st[i%keylen];
        }        
        int j = 0;
        for ( i = 0; i < 256; i++) {
                j = (j + s[i] + t[i]) % 256;
                swap(s[i],s[j]);
                //根据t向量打乱s盒 
        }
        unsigned char k[64];//保存秘钥流,或者直接进行异或 
        i = 0; j = 0; 
        int tmp;
        int index ;
        for ( index = 0; index < mlen; index++) {   //生成与明文长度一致的秘钥流 
                i = (i + 3) % 256;
                j = (j + s[i]+1) % 256;
                swap(s[i],s[j]); 
                tmp = (s[i] + s[j]) % 256;
                k[index] = s[tmp];//保存秘钥 
        }
        for (i = 0; i < mlen; i++)
        {
                m[i] = m[i] ^ k[i];//主要进行了一步异或,加密的逆过程就是解密 
        }
}
int main()
{
        char c[27]={0x4d,0xffffffe6,0x49,0xffffff95,0x3,0x2d,0x2b,0xffffffba,0xffffffea,0x6d,0xffffffff,0x59,0x70,0x0,0x1b,0xffffffa9,0x2c,0xffffffb0,0x32,0xffffff98,0x6f,0xffffff8c,0x56,0xffffffa2,0x4c,0x79,0x7f };
        int i;
        for(i=26;i>=0;i--)
        {
                c[i] = c[(i+1)%27]^c[i];
        }
        for(i=0;i<27;i++)
        {
                printf("0x%x,",c[i]) ;
        }
        O0oo00OOo00o0((unsigned char *)c,27,16);
        printf("\n");
                for(i=0;i<27;i++)
        {
                printf("%c",c[i]&0xff) ;
        }
}

It base on static crack here, but a bro told me that since there is only ONE XOR that really do something to your flag, you can dynamic debug to get the array v4 . (in the upper encryption photograth) Which is really genius because whether you know how to decrypt rc4 or not you can solve this problem and it do save you tons of work!

# Just input a series of ‘1’ and look into the ECX register!

dynamic_1
dynamic_2
dynamic_3
dynamic_4
dynamic_5

# And so on…
###### Two thinkings(should be tried out later):
###### Can `angr` solve `rc4` immediately?
###### Can I add a `hook` so it prints the `ECX` rightaway and I don't need to check it once and once again?
######                                                                                          --2022.11.4

From here, the last few problems are quite hard…

# MAZE

We found thounds of junkcodes here, and we nop them:

import idc
import idautils
start =0x00140001000
end = 0x00140CEF1F0
bad=[0x50,0x48,0x0f,0xc7,0xf0,0x58]

for i in range(start,end):
    if idc.get_wide_byte(i) == 0x50:
        flag=1
        for j in range(len(bad)):
            if(idc.get_wide_byte(i+j)!= bad[j]):
                flag=0
                break
        if flag==1:
            for j in range(len(bad)):
                patch_byte(i+j,0x90)
                print("success")