LOADING

加载过慢请开启缓存 浏览器默认开启

CtfNewStar Week4 Writeup

Reverse

(・∀・)つ原题

MazE

在main函数里找到加密逻辑,父子进程进行用v12和pipedes管道通信。根据分析依次找到函数
1
getmap用于获取地图
2
getbin获取二进制值到显示的3X3地图
3
dec用于获取获取当前坐标的二进制值
4
dec1从被压缩解密的迷宫地图中获取当前坐标对应的char字符
后面的位移运算用于提取出当前位置的二进制值
5
对地图进行解密,简单的异或,那么omap就是被压缩的地图
最后进行MD5加密即可
用脚本解密脚本如下

#include <stdio.h>

unsigned char omap[1226] = {
    ...
};
unsigned int map[99][99];

unsigned char getmap(unsigned char* omap, int index) {
    unsigned char key[] = "tgrddf55";
    return omap[index] ^ key[index % 8];
}

unsigned int getbin(int x,int y, unsigned char *omap) {
    int index1,index2;
    unsigned int tmpmap,bin;
    index1 = (99 * x + y) / 8;
    tmpmap = getmap(omap, index1);
    bin = tmpmap >> (7 - (99 * x + y) % 8) & 1;
    return bin;
}

int main() {
    for (int x = 0; x < 99; x++) {
        for (int y = 0; y < 99; y++) {
            map[x][y] = getbin(x, y, omap);
            printf("%d,", map[x][y]);
        }
    }
    return 0;
}
//在官方那里搜刮的脚本
#include <iostream>
using namespace std;
int Map[] = { 1,1,1,1,1,1,1,1,1,1,1,1,...}
int map[99][99];
char t[10000];
int r = 0;
int dx[4] = { -1, 0, 1, 0 };
int dy[4] = { 0, 1, 0, -1 };
char op[4] = { 'w', 'd', 's', 'a' };
bool check(int qx, int qy) {
    if (qx >= 0 && qx < 99 && qy >= 0 && qy < 99) {
        if (map[qx][qy] == 0) return 1;
    }
    return 0;
}
void dfs(int x, int y) {
    // printf("%d %d\n",x,y);
    if (x == 97 && y == 97) {
        // printf("yes!");
        for (int i = 0; i < r; i++) cout << t[i];
        cout << endl;
        cout << r;
        cout << endl;
        return;
    }
    for (int i = 0; i < 4; i++) {
        int qx = dx[i] + x;
        int qy = dy[i] + y;
        if (check(qx, qy)) {
            t[r] = op[i];
            r++;
            map[x][y] = 1;
            dfs(qx, qy);
            map[x][y] = 0;
            r--;
            t[r] = 0;
        }
    }
}
int main() {
    for (int i = 0; i < 99; i++) {
        for (int j = 0; j < 99; j++) {
            map[i][j] = Map[99 * i + j];
        }
    }
    dfs(1, 1);
    return 0;
}

洞OVO

看提示,函数的修改之处是新增了3行汇编代码,想到用bindffi对比分析,去网上查找资料,发现修复版本是6.23,于是下载安装6.23然后提取出WinRAR.exe.与附件中的进行对比.
想先猜猜看,以相似度排序就在第二个相似度为0.99的sub_1400EF508函数发现了不同刚好是三行汇编,于是直接输入函数的地址
flag{00000001400EF508}


Easygui

先查看winmain函数,发现窗口在收到输入的数据时会调用WndClass.lpfnWndProc方法,然后调用加密函数,分析第一层加密函数,最上面是一个反调试的检测,下面调用了真正的加密函数,后面是把密文逐一对比判断,很显然这就是密文,手动提取下来。
分析真正的加密函数,首先是把输入的值赋给了src,然后以src的元素作为下标在初始盒中映射,然后把src进行位运算,后面再初始化一个ascii的码表盒,进行rc4加密。
那么解密思路就是,先进行rc4解密,然后反向进行位运算,再把元素与初始盒映射为下标。
Tips:提取初始盒注意顺序,显示的顺序不一定是索引顺序。这里key的使用在ida中的代码让人疑惑,因为他存储的位置是src的长度之外,推测是存到了别的内存,于是进行动调,先在if ( !IsDebuggerPresent() )的jz处下断点,然后修改zf的值跳过反调试,然后观察src的值,发现末尾就是一大串key的值,看了一下其他的数组,发现就是v13的值,所以key被存到了v13里,这里我直接ida提取出来了。
按思路解密就行,脚本如下。

#include <stdio.h>

void dealboxdec(unsigned char* Src, unsigned char* box, int len) {
    unsigned _int16 v11;
    for (int k = 0; k < len; k += 4) {
        v11 = Src[k + 3];
        
        Src[k + 3] = (Src[k + 3] << 3) | (Src[k + 0] >> 5);
        Src[k + 0] = (Src[k + 0] << 3) | (Src[k + 1] >> 5);
        Src[k + 1] = (Src[k + 1] << 3) | (Src[k + 2] >> 5);
        Src[k + 2] = (Src[k + 2] << 3) | (v11 >> 5);
    }

    for (int j = 0; j < 44; ++j) {
        for (int i = 0; i < 256; i++) {
            if (box[i] == Src[j]) {
                Src[j] = i;
                break;
            }
        }
    }
}


void dec1(char *key,unsigned char *box, unsigned char *Src) {
    int i = 0;
    do
    {
        box[i] = i;
        ++i;
    } while (i < 256);
    
}

void dec2(unsigned char* box) {
    int v21,v22,v17,v18,v16;
    char* v23;
    unsigned __int64 result;
    unsigned char v31[256] = {
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,  //v13实际是key的填充,动调提取出来的
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
     0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49, 0x65, 0x61, 0x73, 0x79, 0x5F, 0x47, 0x55, 0x49,
    };
    v21 = 0;
    v16 = 256;
    v17 = 0;
    do
    {
        v22 = *((unsigned __int8*)box + v21);
        v17 = (v22 + v31[v21] + v17) % 256;
        v23 = (char*)box + v17;
        result = (unsigned __int8)*v23;
        box[v21++] = result;
        *v23 = v22;
        --v16;
    } while (v16);
}

void dec3(unsigned char* box,unsigned char* Src) {
    int v25,v6,v27;
    v25 = 0;
    int len = 44;
    v6 = 0;

    for (int m = 0; m < len; ++m)
    {
        v6 = (v6 + 1) % 256;
        v27 = *((unsigned __int8*)box + v6);
        v25 = (v27 + v25) % 256;
        box[v6] = box[v25];
        box[v25] = v27;
        Src[m] ^= box[(unsigned _int8)(v27 + box[v6])];
    }
}

void print(unsigned char* data) {
    for (int i = 0; i < 44; i++) {
        printf("%c",data[i]);
    }
}
unsigned char Src[256];
char out[256] = {-33,-57,77,20,-63,-20,8,-28,95,63,3,-76,-112,74,-71,-113,-113,-6,113,67,-57,-15,-99,-35,79,-64,18,68,92,-99,-120,54,45,22,29,-19,-68,-17,-69,91,-97,119,-21,88 };
char key[] = "easy_GUI";  //key被填充到v13,这里v13我是动调提取的

int main() {

    unsigned char box0[256];
    unsigned char box[256] = {
    0x31, 0x74, 0x54, 0x20, 0x03, 0x53, 0x78, 0x70, 0x3A, 0x35, 0x65, 0x42, 0x04, 0x6B, 0x1F, 0x43,
    0x06, 0x37, 0x00, 0x76, 0x21, 0x08, 0x0B, 0x13, 0x52, 0x4B, 0x2F, 0x1A, 0x59, 0x2C, 0x56, 0x51,
    0x7F, 0x3B, 0x0E, 0x05, 0x26, 0x15, 0x25, 0x63, 0x64, 0x7A, 0x3C, 0x29, 0x41, 0x2A, 0x12, 0x17,
    0x2E, 0x39, 0x57, 0x3D, 0x66, 0x33, 0x44, 0x6C, 0x6F, 0x47, 0x16, 0x71, 0x5F, 0x1C, 0x14, 0x5A,
    0x0C, 0x4F, 0x01, 0x30, 0x1B, 0x68, 0x0F, 0x62, 0x3F, 0x18, 0x69, 0x6D, 0x7E, 0x5D, 0x6A, 0x28,
    0x22, 0x5B, 0x55, 0x72, 0x09, 0x5E, 0x02, 0x3E, 0x50, 0x7B, 0x46, 0x45, 0x38, 0x10, 0x48, 0x79,
    0x60, 0x36, 0x61, 0x6E, 0x2D, 0x49, 0x7C, 0x2B, 0x34, 0x27, 0x11, 0x7D, 0x0D, 0x0A, 0x77, 0x73,
    0x58, 0x5C, 0x4C, 0x32, 0x4D, 0x1E, 0x24, 0x40, 0x67, 0x4A, 0x4E, 0x1D, 0x07, 0x75, 0x19, 0x23
    };
    int len = 44;
    for (int i = 0; i < 44; ++i) {
         Src[i] = out[i]; 
    }
    dec1(key, box0, Src); //rc4解密
    dec2(box0);
    dec3(box0, Src);
    dealboxdec(Src, box, len); //反向位运算和与盒的下标映射
    print((unsigned char*)Src);
    return 0;
}

PLZdebugme

有虚拟机检测,启动frida调试。
先hook虚拟机检测函数,修改返回值绕过。脚本如下。在mainactivity有一个Blowfish加密,然后有一个获得密钥的方法g4tk4y在点击按钮时被调用,于是hook方法获取key。在写完两个hook后我们发现运行脚本是报错的

问题就在run这个方法里,我们到run中查看,是一个执行杀死进程的方法,判断GetPPid()是否为0;不多说,我们直接hook下来把,返回值改为0;然后在点击一下提交按钮,这样我们就获得了密钥jRLgC/Pi。
接下来就是分析so里面的check函数,是一个移位加密,直接写出解密脚本获得base64编码的密文,拿着密钥解密去cy解密就行,注意有个偏移量要设为0.

Java.perform(function(){
let App = Java.use("work.pangbai.tool.App");
App["GetPPid"].implementation = function () {
    console.log(`App.GetPPid is called`);
  //  let result = this["GetPPid"]();
    console.log(`App.GetPPid result=`);
    return "0";
};
});





Java.perform(function(){
let MainActivity = Java.use("work.pangbai.debugme.MainActivity");
MainActivity["isEmu"].implementation = function () {
    console.log(`MainActivity.isEmu is called`);
    return false;
};


MainActivity["g4tk4y"].implementation = function () {
    console.log(`MainActivity.g4tk4y is called`);
    let result = this["g4tk4y"]();
    console.log(`MainActivity.g4tk4y result=${result}`);
    return result;
};
})
#include <stdio.h>


void dec1(unsigned int* input,int v6) {
    int v11,index,len;
    char v15;
    unsigned int v16,v1213,v16q,v16h;
    len = 12;
    index = len -1;
    v11 = 11;
    
    do
    {
        v15 = 32 - v11;
        v1213 = input[index] ^ input[index - 1];
        if (v6)
        {
            v16h = v1213 >> v15;
            v16q = v1213 << index;
        }
        else
        {
            v16h = v1213 << v15;
            v16q = v1213 >> index;
        }
        v16 = v16q | v16h;
        input[index] = v16 ^ input[index-1];
        index--;
        v11--;
        
        
    } while (index != 0);
}

int dec2(unsigned int*input,int v6) {
    unsigned int index,v21;

    unsigned int v17,v17q,v17h;
    char v18;
    int len;
    len = 12;
    v18 = 32 - len;
    index = 11;
    v21 = input[0] ^ input[index];  //注意v21的数据类型不能是char,会变成负数影响下面的操作
    if (v6)
    {
        v17h = v21 >> v18;
        v17q = v21 << len;
    }
    else
    {
        v17h = v21 << v18;
        v17q = v21 >> len;
    }
    v17 = v17q | v17h;
    input[0] = v17 ^ input[index];
    return 0;
}

void print(unsigned char* a) {
    for (int x = 0; x < 48; x++) {
        printf("%c", a[x]);
    }
}

unsigned int cc[12] = {
    0x9C5F5508, 0x40561970, 0x58676904, 0xC13E5285, 0x75DC2D4C, 0x06F06EAF, 0x6E7B5DA5, 0xE37EAE2A,
    0xF1B9FEFD, 0x06966BAC, 0x4A21BF43, 0x47DBF512
};

int main() {
    int v6;
    v6 = 0;
    dec2((unsigned int*)cc,v6);
    dec1((unsigned int*)cc,v6);
    printf("\n");
    print((unsigned char*)cc);
    //printf("%s", cc);

    return 0;
}

ezrust

rust语言比较新,用高版本的ida打开会更好(IDA9)。

进来就看到一堆,感觉v6就是密文。

发现下面这个函数里面有一串字符”loverust“,推测这就是加密的函数,但是进入函数后发现是一堆史。想到我们已经知道密钥的位置,所以我们在密钥处下硬件断点,进行动调来寻找逻辑。

发现断点在这个位置,分析一下,这个位置对密钥进行了读取,主要是和一个数据进行了异或操作。于是我们往前看看交叉引用,我们发现,在一个函数中对这个函数进行了多次的循环调用,那么我们就可以确定这是一个加密函数了。

我们回到密钥操作的地方分析。看逻辑就是一个异或,我们很容易知道a1是密钥,v5是偏移量,a3是我们输入的数据。再往上分析v5的值,v5的值是由v7-a2%v6,的来的v7的值是7恒等,v6是8,a2是每次的下标,且v5的初始值是7,那么一开始异或的就是密钥的最后一位,而a2是递增的,推测这个代码的逻辑就是

a3 ^ key[7-2a%8]

写出解密脚本就行了。

v6 = [18, 31, 20, 21, 30, 15, 95, 57, 43, 51, 7, 65, 58, 79, 95, 3, 16, 44, 53, 6, 58, 4, 26, 31, 0, 14, 26]

key = "loverust"
a = 0
result = ""
for x in v6:
    index = 7-a%8
    x ^= ord(key[index])
    result += chr(x)
    a+=1
print(result)