over 3 years ago

Reversing 250, 原題目 https://github.com/csie217/ctf/raw/master/codegate2014pre/clone_technique.exe
PE 檔,執行的話會一直分出新的 process 執行自己。分析後發現

  1. 執行時需要 3 個參數 a1, a2, counter,如果沒有的話有預設值 (a1=0xA8276BFA,a2=0x92F837ED,counter=1)
  2. 參數 a1, a2 傳入一個 decrypt 函式,會試著解密一個固定的字串 (0x00407030)
  3. a1 符合某個條件時,不會 fork 而是直接回傳一個值;否則依底下的方式 fork,結束後回傳 0。
  4. 接下來會 fork 出新的 process 執行自己,會等 child 結束再開下一個,最多 400-counter 次 (如果 child 回傳 0 就提前結束) 。傳入 child 的參數是 a1, a2 和上一個 child 的回傳值運算過的結果、還有 counter-1

因為只要有任何一個 child 回傳 0,就會全部結束掉了;但如果 child 的回傳值不是 0,意思是這個 child 沒有再往下分枝。另外每往下一層 counter 都會減 1。所以最差情況是: 前 399-counter 次的 child 回傳值非 0,最後一次是 0。這樣每層最多 401 個 process,乘上總層數就大約只有 160000 次。

但 process 數可能因為太多開不起來,而且太慢。因此我們重新寫了一遍,所做的事情完全一樣只是不 fork 而只是遞迴函式。另外原本的程式中有個計算 a^n 的函式 pow(a,n) 是直接用迴圈乘 n 次所以太慢了,我們把它換成了快速幂。還有重新寫成 C 時要注意,原本的 imul, div 這些都會直接 overflow,要注意變數是 signed 還是 unsigned。

重新寫成 C code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const char *str="\x0F\x8E\x9E\x39\x3D\x5E\x3F\xA8"\
                 "\x7A\x68\x0C\x3D\x8B\xAD\xC5\xD0"\
                 "\x7B\x09\x34\xB6\xA3\xA0\x3E\x67\x5D\xD6";

inline int ROL(int a,int b){
    return (a<<b)|((unsigned)a>>(32-b));
}

char* decode(const char *a1,int a2,int a3){
    int i;
    int size;
    char *v8;

    size = strlen(a1);
    v8 = (char*)malloc(size+1);
    memset(v8,0,size);
    for(i = 0;i < (size - 1);i += 2){
        v8[i] = a2 ^ a1[i];
        a2 = ROL(a2,5)^0x2f;
        if(!a1[i + 1]){
            break;
        }
        v8[i + 1] = a3 ^ a1[i + 1];
        a3 = ((unsigned char)a2)^ROL(a3,11);
    }

    return v8;
}

inline int pow(int a,unsigned int b){
    if(b==0) return 1;
    int d = pow(a,b/2);
    if(b%2){
        return d*d*a;
    }else{
        return d*d;
    }
}

int calc(int a1,int a2,int ct){
    printf("%s\n",decode(str,a1,a2));
    a1^=0xB72AF098u;
    a2 = (a1*a2)^a2;
    int v1 = 29*a1+7*pow(a1,2);
    int v6 = pow(v1^a2,((unsigned)v1%2)+5);
    if((unsigned int)a1<=0xD0000000u){
        int ret;
        do{
            if(ct>400) return 0;
            ct++;
            ret = calc(v1,v6,ct);
            v1 = ret;
            v6 = pow(ret^v6,ret%30);
        }while(ret);
        return 0;
    }else{
        return (13*((unsigned)v1/27))^0x1f2a990d;
    }
}

int main(){
    calc(0xA8276BFA,0x92F837ED,1);
    return 0;
}

執行後解出 key

$ ./clone | strings -n 10
And Now His Watch is Ended
← Olympic CTF 2014 Nopsleigh 300 Echof Writeup Codegate CTF Preliminary 2014 Angry Doraemon Writeup →