<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    level1-5

    感謝LeadroyaLshi師傅!

    Level1

    Java 層什么都沒有,直接看 native;native 里包含了一些數學計算。

    有 init_array ,但里面主要是一些線程相關操作的初始化,沒有JNI_OnLoad。

    int __fastcall Java_com_didictf_guesskey2018one_MainActivity_stringFromJNI(JNIEnv *a1, jobject a2, jstring a3)
    
    {
    
      i = 0;
    
      bInput = (*a1)->GetStringUTFChars(a1, a3, 0);
    
      j_j_GetTicks();
    
      do
    
     v10 = j_j_gpower(i++);
      while ( i != 32 );
    
      j_j_GetTicks();
    
      fromBytes((String *)&p_string, bInput);
    
      v5 = (String *)fromString((String *)&cp_string, (String *)&p_string);
    
      ret = j_j_j__Z20__aeabi_wind_cpp_prjSs((int)v5);
    
      finiString((int *)(cp_string - 12));
    
      finiString((int *)(p_string - 12));
    
      return ret;
    
    }

    上來先算了32次平方,不知道想干嘛,調用2次GetTicks,不知道想干嘛。之后把輸入轉為std::string類型,進入check函數。
    首先檢測長度是否為36,以及與 const-data 進行 xor。

        while ( 1 )
        {
          if ( v13 >= 1 && currentOff < input_len )
          {
            v3 = 0;
            if ( v10[10] != *v10 )
              break;
          }
          ++currentOff;
          ++v10;

    這個地方校驗第010、第1120、第2130、第3040是否一模一樣。

    最后的檢測是

              if ( v24 )
                goto LABEL_40;                      // if a%b == 0
              v26 = j_j_j___aeabi_uldivmod(divisor, dividend);
              v3 = 1;
              v25 = (unsigned int)dividend >= (unsigned int)v26;
              LODWORD(v26) = 1;
              if ( v25 )
                LODWORD(v26) = 0;
              v27 = 1;
              if ( HIDWORD(dividend) >= HIDWORD(v26) )
                v27 = 0;
              if ( HIDWORD(dividend) != HIDWORD(v26) )
                LODWORD(v26) = v27;
              if ( !(_DWORD)v26 )
    LABEL_40:
                v3 = 0;
              finiString((int *)v30 - 3);

    復制

    這里v3最后被返回了,要求是前者能夠整除后者,而且會有除數和商的大小比較,只有除數大于上時候才有可能返回1。

    dividend = j_j_atoll((const char *)a1->ptr);

    往上翻,發現輸入僅與除數有關。

    被除數是由兩個字符串算出來的,怎么算出來的我也看不大懂,好像是重新組合成一個字符串,拼接字符什么樣的,應該可以直接 dump。

    【后來看某位老哥寫的 writeup,發現是通過2個字符串取 index 得到的】

    https://blog.csdn.net/dydxdz/article/detai...

    map1 = {}
    str1 = 'deknmgqipbjthfasolrc'
    for i in range(len(str1)):
        map1[str1[i]] = i/2
    str2 = 'jlocpnmbmbhikcjgrla'
    k = []
    for i in range(len(str2)):
        print map1[str2[i]],

    先創建 map<char,int> ,第 i 個 char對應的數字是i/2 ,剛好得到每個 char 對應 [0,10) 的數字;再查詢 str2里每個 char 所對應的下標,將這個下標加上 ‘0’ ,拼起來,得到新的十進制的字符串。

    綜上,拿到數字 5889412424631952987 ,將它分解了, 5889412424631952987=1499419583*3927794789 ,輸入就是偏大的數字, 1499419583 ,再 xor 一下常量就行了。

    最后 flag 是 d5axivcw6ggfswpxg80estgc58h7yghqogbm 。

    Level2

    看起來使用的是 Robust 的熱更新框架,沒有做太特殊的處理,在 assets 里存放了 GeekTan.BMP ,其實是個 zip 包,里面放著 Robust 的 patch 文件。

    有簡單的方法,也有復雜的方法,復雜的就是肉眼去看,把代碼運行一遍即可,是個約瑟夫問題,也可以直接求解,跟我以前出的用棧寫約瑟夫很像。

    簡單的方法嘛,直接上 xposed

    input text DDCTF{2517299225169920}
    XposedHelpers.findAndHookMethod("cn.chaitin.geektan.crackme.MainActivity", loader, "Joseph", int.class, int.class, new XC_MethodHook() {
        @Override
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            super.beforeHookedMethod(param);
            new Exception().printStackTrace();
            Log.d(TAG, "======== before hook =======");
            Log.d(TAG, "with " + (int) param.args[0] + " and" + (int) param.args[1]);
        }
    
        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
            super.afterHookedMethod(param);
            Log.d(TAG, "======== after hook =======");
            Log.d(TAG, "result is " + param.getResult());
            }
    });

    LeveL3

    Java 層什么都沒有,直接看 native。 init_array應該是初始化一些東西,沒有過多操作。 沒有JNI_OnLoad。 直接看 JNI的方法,進入之后先將輸入轉化為 std::string ,再使用 str2ll轉為int64。 長得比較丑,看起來是做divmod(int64, int64),循環終止的條件是i==int64(input),最后檢測余數是否和預期相等。 debug 一下,大概就是左移1bit,mod 一下,左移1bit,mod 一下這樣,寫段 python 爆破即可。

    DDCTF{ddctf-android2-KEY}
    p = 0x17A904F1B91290
    mod = 0xDBDEE7AE5A90
    In [23]: i = 1
        ...: remain = 1
        ...: while True:
        ...:     remain = ((remain << 1) & 0xFFFFFFFFFFFFFFFF) % 0x17A904F1B91290
        ...:     if remain == 0xDBDEE7AE5A90:
        ...:         print i, remain, hex(remain >> 32), hex(remain & 0xFFFFFFFF)
        ...:         break
        ...:     i += 1
        ...:
    595887 241750416186000 0xdbdeL 0xe7ae5a90L

    不知道這題想干嘛。。。

    LeveL4

    這次只有 java 層,沒有 native 層,看起來使用了公開的第三方庫 spongycastle,所以丟到網站上 deguard 一下,得到一個非常優美的結果~

    官方說是10位以內的數字,所以是暗示爆破,而且 ECC 么,除了爆破也沒有辦法。

        public MainActivity() {
            super();
            this.editText = "00C3632B69D3FC1DD8D80C288C44281B67F4828DC77E37EE338E830E66DC71972A008835BA3156353815DFEDEB4330B48B454F35A88D83DA6260C206E4A619753F97";
        }
    
        public void onClickTest(View arg24) {
            this.outputView.setText("Empty Input");
            TextView v1 = this.preview;
            this = this;
            String v4 = v1.getText().toString();
            String v5 = v4;
            if(v4.length() == 0) {
                v5 = "1";
            }
    
            new R$id().init();
            ECPoint v11 = SECNamedCurves.getByName("secp256k1").getG().multiply(new BigInteger(v5.getBytes()));
            BigInteger v8 = v11.getXCoord().toBigInteger();
            BigInteger v13 = v11.getYCoord().toBigInteger();
            byte[] v14 = v8.toByteArray();
            byte[] v15 = v13.toByteArray();
            byte[] v9 = new byte[v14.length + v15.length];
            int v6;
            for(v6 = 0; v6 < v9.length; ++v6) {
                byte v17 = v6 < v14.length ? v14[v6] : v15[v6 - v14.length];
                v9[v6] = v17;
            }
    
            StringBuilder v18 = new StringBuilder();
            v6 = v9.length;
            int v16;
            for(v16 = 0; v16 < v6; ++v16) {
                v18.append(String.format("%02X", Byte.valueOf(v9[v16])));
            }
    
            if(v18.toString().equals(this.editText)) {
                this.outputView.setText("Correct");
                return;
            }
    
            this.outputView.setText("Wrong");
        }

    使用的是 ECC 加密算法,使用secp256k1曲線,先拿到 G 點,與輸入進行橢圓域上的相乘,得到新的點,去校驗計算出來的點是否是預先規定好的那個點,是的話就 return true 。

    這個沒什么操作,就是按照描述去爆破,一開始懶得寫 java 代碼,直接在手機上爆破的(原諒我腦殘),發現速度簡直慢到炸,手機燙了一晚上也沒跑多少數據。

    然后想著優化,但發現這個 API 似乎很不好用, G+G+G 和 G*3 不相等,以及各種神奇的表現,可能是我不大會用API吧,按理說加法比乘法好做很多,每次加一比每次乘法應該要快,但優化時候老是算出來的不一樣,就懶得優化了。 最后在 PC上寫個爆破腳本,早上起來就看到了 flag,DDCTF{54135710}。

    LeveL5

    這題就是反調試的大集合,亂七八糟的方式什么都有,Java 層沒有東西,直接看 native。

    init_array 沒有特殊操作,是 C++的初始化。

    JNI_OnLoad里動態注冊了 JNI 函數,沒有額外操作。

    直接看了哈,最原始的長這樣

    int __fastcall Java_check(const char *b_input)
    {
      void *v2; // r0@1
      void *v3; // r5@1
      int i; // r2@4
      char v6[32]; // [sp+4h] [bp+0h]@1
    
      memset(v6, 0, 0x20u);
      v2 = dlopen("libc.so", 0);
      v3 = v2;
      if ( v2 )
      {
        open = (int (__fastcall *)(_DWORD, _DWORD, _DWORD))dlsym(v2, "open");
        close = (int (__fastcall *)(int))dlsym(v3, "close");
        read = (int (__fastcall *)(_DWORD, _DWORD, _DWORD))dlsym(v3, "read");
        strncmp = (int (__fastcall *)(_DWORD, _DWORD, _DWORD))dlsym(v3, "strncmp");
        strstr = (int)dlsym(v3, "strstr");
      }
      isTraced = 0;
      setValue(dword_EF2B5024);
      maybe_antidebug_1();
      some_encrypt_2(dword_EF2B5024, v6);
      if ( strlen(b_input) == 32 )
      {
        i = 0;
        do
        {
          v6[i] ^= b_input[i];
          ++i;
        }
        while ( i != 32 );
        memcpy(&unk_11100, &v7, 0x20u);
    // return strncmp(xx, xx, 32);
    // patch by LeadroyaL
      }
      return -1;
    }

    將輸入操作一下,xor 一下,返回的是 strncmp 的結果,這不是送分題么?直接上去調試,斷下來,發現答案并不對。。。有幾個反調試的函數,把 xor_key 給修改了。

    sub_3c54是第一個函數,先做一些不知道什么的操作,再檢測 tracerPid那行的 strlen ,可以繞過,然后去從sha256_table里取一些值,不知道想干嘛。內層還有一堆不知道在干嘛的函數,估計藏了一些反調試,而且會對 global 的值進行一些操作,亂七八糟的。

    反正每次都會被測到反調試,于是懶得搞了,我認輸,ok?

    patch 一下binary文件,因為是簡單的 xor,所以只要能拿到xor_key 即可,在最后一句他是strncmp,如果把它 patch為memcpy的話,在正常運行過程中,就可以將算出來的密文保存下來。之后想辦法dump內存,就能拿到密文,與輸入進行xor,就拿到了 key。

    經過一番努力,終于patch成功了。。。如上圖的最后一個 memcpy。

    先運行,讓它算一遍,再attach,斷在最開始,就能拿到明密文對了。

    最后算出來是DDCTF{GoodJob,Congratulations!!}。

    本文章首發在 網安wangan.com 網站上。

    上一篇 下一篇
    討論數量: 0
    只看當前版本


    暫無話題~
    亚洲 欧美 自拍 唯美 另类