吾愛破解 - LCG - LSG |安卓破解|病毒分析|破解軟件|www.mlqcje.live

 找回密碼
 注冊[Register]

QQ登錄

只需一步,快速開始

搜索
查看: 9169|回復: 60

[CTF] 2019騰訊游戲安全競賽PC版分析

  [復制鏈接]
二娃 發表于 2019-6-10 19:37
本帖最后由 二娃 于 2019-6-11 21:39 編輯

文章剛寫完,論壇剛好開了。。

首先感謝littleNA大佬的文章https://bbs.pediy.com/thread-230312.htm,看完這篇文章后事半功倍。

第一題 EasyJob

首先為了方便動靜結合調試,我先修改了PE文件頭部使之跳過重定位。使用工具DIE

這樣修改之后,使用IDA和其他調試工具的時候地址就能對得上了,告別aslr。

用ida打開程序后,看一下main函數,確定sub_45F650sub_460050分別為檢查request codeverification code的函數,做一下重命名。

值得注意的是check_request_codecheck_verification_code有同一個參數,我把它命名為req_code_stru。在后面的分析過程中,我得出該變量類型的結構體

struct req_code_stru
{
  vector request_code_part;
  __int64 num1;
  __int64 num2;
  __int64 num3;
  __int64 num4;
};

其中vector結構可以參考上面littleNA大佬的文章,在這個結構體中應該是vector<string>,每個成員的作用會在后面講到。

進入check_request_code函數后,發現字符串

很明顯是一個正則表達式,于是推測request code需要符合該正則表達式。打開程序隨便輸入了一個符合該表達式的request code,比如1111-2222-3333-4444,輸入后程序提示繼續輸入verification code,表明推測是正確的。通過調試后還發現該函數會把request code通過字符-分成4個string插入到req_code_stru->request_code_part中。

接下來看一下check_vertification_code。通過瀏覽后發現一個比較關鍵的函數sub_45F960,通過req_code_stru->request_code_part計算出4個num,具體代碼如下

int __thiscall sub_45F960(req_code_stru *req_code_stru)
{
  char *v1_part1; // esi
  char *v2_part1; // eax
  char *v3_part4; // ebx
  char *v4_part4; // ecx
  int v5; // edi
  char *v6_part1; // ecx
  char *v7_part4; // eax
  int v8; // edi
  char *v9_part1; // edx
  char *v10_part4; // eax
  int v11; // edi
  char *v12_part1; // edx
  char *v13_part4; // eax
  char *v14_part2; // eax
  char *v15_part3; // edi
  char *v16_part3; // ecx
  char *v17_part2; // edx
  char *v18_part3; // eax
  char *v19_part2; // edx
  char *v20_part3; // eax
  char *v21_part1; // edx
  char *v22_part3; // eax
  char *v23_part1; // edx
  char *v24_part1; // eax
  char *v25_part2; // eax
  char *v26_part2; // edx
  char *v27_part3; // ecx
  char *v28_part3; // eax
  int v29; // ecx
  char *v30_part4; // edx
  char *v31_part4; // eax
  char *v32_part1; // ecx
  char *v33_part4; // eax
  char *v34_part2; // edx
  char *v35_part3; // eax
  char *v36_part2; // eax
  int v37; // eax
  int v38; // edx
  int v39; // ST24_4
  int v40; // ecx
  __int64 v41; // rdi
  bool v42; // cf
  unsigned __int64 v43; // rt0
  __int64 v44; // kr38_8
  __int64 v45; // rax
  unsigned int v46; // ecx
  unsigned int v47; // ecx
  signed __int64 v48; // rax
  int v49; // ecx
  req_code_stru *v51_reg_code_stru; // [esp+10h] [ebp-18h]
  int v52; // [esp+14h] [ebp-14h]
  int v53; // [esp+14h] [ebp-14h]
  int v54; // [esp+18h] [ebp-10h]
  int v55; // [esp+1Ch] [ebp-Ch]
  int v56; // [esp+24h] [ebp-4h]
  int v57; // [esp+24h] [ebp-4h]
  int v58; // [esp+24h] [ebp-4h]
  int v59; // [esp+24h] [ebp-4h]
  int v60; // [esp+24h] [ebp-4h]
  int v61; // [esp+24h] [ebp-4h]

  v1_part1 = req_code_stru->request_code_part._Myfirst;
  v51_reg_code_stru = req_code_stru;
  if ( *((_DWORD *)req_code_stru->request_code_part._Myfirst + 5) < 0x10u )
    v2_part1 = req_code_stru->request_code_part._Myfirst;
  else
    v2_part1 = *(char **)v1_part1;
  v3_part4 = v1_part1 + 72;
  if ( *((_DWORD *)v1_part1 + 23) < 0x10u )
    v4_part4 = v1_part1 + 72;
  else
    v4_part4 = *(char **)v3_part4;
  v5 = (*v4_part4 ^ *v2_part1) << 8;            // v5=(part4[0]^part1[0])<<8
  if ( *((_DWORD *)v1_part1 + 5) < 0x10u )
    v6_part1 = v1_part1;
  else
    v6_part1 = *(char **)v1_part1;
  if ( *((_DWORD *)v1_part1 + 23) < 0x10u )
    v7_part4 = v1_part1 + 72;
  else
    v7_part4 = *(char **)v3_part4;
  v8 = v6_part1[1] * v7_part4[1] + v5;          // v8=part1[1]*part4[1]+v5
  if ( *((_DWORD *)v1_part1 + 5) < 0x10u )
    v9_part1 = v1_part1;
  else
    v9_part1 = *(char **)v1_part1;
  if ( *((_DWORD *)v1_part1 + 23) < 0x10u )
    v10_part4 = v1_part1 + 72;
  else
    v10_part4 = *(char **)v3_part4;
  v11 = v9_part1[2] / v10_part4[2] + v8;        // v11=part1[2]/part4[2]+v8
  if ( *((_DWORD *)v1_part1 + 5) < 0x10u )
    v12_part1 = v1_part1;
  else
    v12_part1 = *(char **)v1_part1;
  if ( *((_DWORD *)v1_part1 + 23) < 0x10u )
    v13_part4 = v1_part1 + 72;
  else
    v13_part4 = *(char **)v3_part4;
  v54 = v11 + v12_part1[2] % v13_part4[2];      // v54=part1[2]%part4[2]+v11
  v14_part2 = v1_part1 + 24;
  if ( *((_DWORD *)v1_part1 + 11) >= 0x10u )
    v14_part2 = *(char **)v14_part2;
  v15_part3 = v1_part1 + 48;
  if ( *((_DWORD *)v1_part1 + 17) < 0x10u )
    v16_part3 = v1_part1 + 48;
  else
    v16_part3 = *(char **)v15_part3;
  v56 = *v16_part3 * *v14_part2 << 8;           // v56=part3[0]*part2[0]<<8
  if ( *((_DWORD *)v1_part1 + 11) < 0x10u )
    v17_part2 = v1_part1 + 24;
  else
    v17_part2 = (char *)*((_DWORD *)v1_part1 + 6);
  if ( *((_DWORD *)v1_part1 + 17) < 0x10u )
    v18_part3 = v1_part1 + 48;
  else
    v18_part3 = *(char **)v15_part3;
  v57 = (char)(v17_part2[1] ^ v18_part3[1]) + v56;// v57=(part2[1]^part3[1])+v56
  if ( *((_DWORD *)v1_part1 + 11) < 0x10u )
    v19_part2 = v1_part1 + 24;
  else
    v19_part2 = (char *)*((_DWORD *)v1_part1 + 6);
  if ( *((_DWORD *)v1_part1 + 17) < 0x10u )
    v20_part3 = v1_part1 + 48;
  else
    v20_part3 = *(char **)v15_part3;
  v58 = v19_part2[2] % v20_part3[2] + 32 + v57; // v58=part2[2]%part3[2]+32+v57
  if ( *((_DWORD *)v1_part1 + 5) < 0x10u )
    v21_part1 = v1_part1;
  else
    v21_part1 = *(char **)v1_part1;
  if ( *((_DWORD *)v1_part1 + 17) < 0x10u )
    v22_part3 = v1_part1 + 48;
  else
    v22_part3 = *(char **)v15_part3;
  v55 = v58 + v21_part1[2] / v22_part3[2];      // v55=part1[2]/part3[2]+v58
  if ( *((_DWORD *)v1_part1 + 5) < 0x10u )
    v23_part1 = v1_part1;
  else
    v23_part1 = *(char **)v1_part1;
  if ( *((_DWORD *)v1_part1 + 5) < 0x10u )
    v24_part1 = v1_part1;
  else
    v24_part1 = *(char **)v1_part1;
  v59 = v23_part1[1] % v24_part1[3] << 8;       // v59=part1[1]%part1[3]<<8
  v25_part2 = v1_part1 + 24;
  if ( *((_DWORD *)v1_part1 + 11) < 0x10u )
    v26_part2 = v1_part1 + 24;
  else
    v26_part2 = *(char **)v25_part2;
  if ( *((_DWORD *)v1_part1 + 11) >= 0x10u )
    v25_part2 = *(char **)v25_part2;
  v60 = v26_part2[1] / v25_part2[3] + v59;      // v60=part2[1]/part2[3]+v59
  if ( *((_DWORD *)v1_part1 + 17) < 0x10u )
    v27_part3 = v1_part1 + 48;
  else
    v27_part3 = *(char **)v15_part3;
  if ( *((_DWORD *)v1_part1 + 17) < 0x10u )
    v28_part3 = v1_part1 + 48;
  else
    v28_part3 = *(char **)v15_part3;
  v29 = v27_part3[1] * v28_part3[3] + 8 + v60;  // v29=part3[1]*part3[3]+8+v60
  if ( *((_DWORD *)v1_part1 + 23) < 0x10u )
    v30_part4 = v1_part1 + 72;
  else
    v30_part4 = *(char **)v3_part4;
  if ( *((_DWORD *)v1_part1 + 23) < 0x10u )
    v31_part4 = v1_part1 + 72;
  else
    v31_part4 = *(char **)v3_part4;
  v61 = v29 + (char)(v30_part4[1] ^ v31_part4[3]);// v61=(part4[1]^part4[3])+v29
  if ( *((_DWORD *)v1_part1 + 5) < 0x10u )
    v32_part1 = v1_part1;
  else
    v32_part1 = *(char **)v1_part1;
  if ( *((_DWORD *)v1_part1 + 23) < 0x10u )
    v33_part4 = v1_part1 + 72;
  else
    v33_part4 = *(char **)v3_part4;
  v52 = (*v32_part1 ^ v33_part4[3]) << 8;       // v52=(part1[0]^part4[3])<<8
  if ( *((_DWORD *)v1_part1 + 11) < 0x10u )
    v34_part2 = v1_part1 + 24;
  else
    v34_part2 = (char *)*((_DWORD *)v1_part1 + 6);
  if ( *((_DWORD *)v1_part1 + 17) < 0x10u )
    v35_part3 = v1_part1 + 48;
  else
    v35_part3 = *(char **)v15_part3;
  v53 = v34_part2[1] / v35_part3[2] + v52;      // v53=part2[1]/part3[2]+v52
  if ( *((_DWORD *)v1_part1 + 17) >= 0x10u )
    v15_part3 = *(char **)v15_part3;
  v36_part2 = v1_part1 + 24;
  if ( *((_DWORD *)v1_part1 + 11) >= 0x10u )
    v36_part2 = *(char **)v36_part2;
  v37 = v15_part3[2] * v36_part2[1];            // v37=part3[2]*part2[1]
  if ( *((_DWORD *)v1_part1 + 23) >= 0x10u )
    v3_part4 = *(char **)v3_part4;
  if ( *((_DWORD *)v1_part1 + 5) >= 0x10u )
    v1_part1 = *(char **)v1_part1;
  v38 = v37 + v53 + v3_part4[3] % *v1_part1;    // v38=part4[3]%part1[0]+v37+v53
  v39 = v38;
  v40 = (unsigned __int64)(unsigned int)v38 >> 29;
  LODWORD(v41) = 20 * (v54 + 2 * v61);          // v41=20*(v54+2*v61)
  v42 = __CFADD__(8 * v38, v38);
  v38 *= 9;
  HIDWORD(v43) = v40 + v42;
  LODWORD(v43) = v38;
  HIDWORD(v41) = v43 >> 29;
  v51_reg_code_stru->num1 = 30i64 * (unsigned int)v55
                          + __PAIR__(20 * ((unsigned int)v54 + 2 * (unsigned __int64)(unsigned int)v61) >> 32, 8 * v38)
                          + v41;                // num1=30*v55+20*(v54+2*v61)+72*v38
  v51_reg_code_stru->num2 = 23i64 * (unsigned int)v54
                          + 32i64 * (unsigned int)v55
                          + 42i64 * (unsigned int)v61
                          + 54i64 * (unsigned int)v39;// num2=23*v54+32*v55+42*v61+54*v39
  LODWORD(v41) = 25 * v55 + 38 * v61 + 67 * v39;
  HIDWORD(v43) = ((unsigned __int64)(unsigned int)v54 >> 29) + __CFADD__(8 * v54, v54);
  LODWORD(v43) = 9 * v54;
  HIDWORD(v41) = v43 >> 31;
  v44 = __PAIR__(
          (25i64 * (unsigned int)v55 + 38i64 * (unsigned int)v61 + 67 * (unsigned __int64)(unsigned int)v39) >> 32,
          18 * v54)
      + v41;
  HIDWORD(v41) = v51_reg_code_stru;
  v51_reg_code_stru->num3 = v44;
  HIDWORD(v43) = ((unsigned __int64)(unsigned int)v54 >> 31) + __CFADD__(2 * v54, v54);
  LODWORD(v43) = 3 * v54;
  LODWORD(v41) = (__PAIR__(v43 >> 30, 12 * v54)
                + 45i64 * (unsigned int)v55
                + 33i64 * (unsigned int)v39
                + ((unsigned __int64)(unsigned int)v61 << 6)) >> 32;
  v45 = SHIDWORD(v51_reg_code_stru->num1);
  v46 = HIDWORD(v45) ^ LODWORD(v51_reg_code_stru->num1);
  v42 = v46 < HIDWORD(v45);
  LODWORD(v51_reg_code_stru->num1) = v46 - HIDWORD(v45);
  v47 = v51_reg_code_stru->num2;
  HIDWORD(v51_reg_code_stru->num1) = (HIDWORD(v45) ^ v45) - (v42 + HIDWORD(v45));
  *(_QWORD *)(HIDWORD(v41) + 24) = __PAIR__(
                                     (*(_DWORD *)(HIDWORD(v41) + 28) >> 31) ^ *(_DWORD *)(HIDWORD(v41) + 28),
                                     (*(_DWORD *)(HIDWORD(v41) + 28) >> 31) ^ v47)
                                 - __PAIR__(*(_DWORD *)(HIDWORD(v41) + 28) >> 31, *(_DWORD *)(HIDWORD(v41) + 28) >> 31);
  v51_reg_code_stru->num3 = (signed __int64)(__PAIR__(
                                               (*(_DWORD *)(HIDWORD(v41) + 36) >> 31) ^ *(_DWORD *)(HIDWORD(v41) + 36),
                                               (*(_DWORD *)(HIDWORD(v41) + 36) >> 31) ^ *(_DWORD *)(HIDWORD(v41) + 32))
                                           - __PAIR__(
                                               *(_DWORD *)(HIDWORD(v41) + 36) >> 31,
                                               *(_DWORD *)(HIDWORD(v41) + 36) >> 31))
                          % 20;                 // num3=(25*v55+38*v61+67*v39+18*v54)%20
  v48 = (signed __int64)(__PAIR__(
                           ((signed int)v41 >> 31) ^ (unsigned int)v41,
                           ((signed int)v41 >> 31) ^ (unsigned int)(12 * v54 + 45 * v55 + 33 * v39 + (v61 << 6)))
                       - __PAIR__((signed int)v41 >> 31, (signed int)v41 >> 31))
      % 20;                                     // num4=(12*v54+45*v55+33*v39+v61*64)%20
  v51_reg_code_stru->num4 = v48;
  if ( SHIDWORD(v51_reg_code_stru->num3) >= SHIDWORD(v48) )
  {
    if ( SHIDWORD(v51_reg_code_stru->num3) > SHIDWORD(v48)// 如果num3>num4就互相交換
      || (LODWORD(v48) = v51_reg_code_stru->num3, (unsigned int)v48 > LODWORD(v51_reg_code_stru->num4)) )
    {
      v49 = v51_reg_code_stru->num3;
      HIDWORD(v48) = HIDWORD(v51_reg_code_stru->num3);
      LODWORD(v51_reg_code_stru->num3) = v51_reg_code_stru->num4;
      LODWORD(v48) = HIDWORD(v51_reg_code_stru->num4);
      HIDWORD(v51_reg_code_stru->num3) = v48;
      LODWORD(v51_reg_code_stru->num4) = v49;
      HIDWORD(v51_reg_code_stru->num4) = HIDWORD(v48);
    }
  }
  return v48;
}

F5后看起來很亂,需要結合匯編一起分析。

接下來在sub_45FE00計算出num5

num5等于request_code每個數字相加,比如1111-2222-3333-4444計算出來的num5就是40

接下來回到check_request_code中把得到的4個part和5個num拼接

這里看F5可能看不太懂,可以看匯編

這里是先把4個operator<<的參數全部壓棧再一個個call,應該是編譯器優化的結果。得到的結果就是一個字符串s=part1+part2+part3+part4+num1+num2+num3+num4+num5

最后將得到的字符串再做一個以part1keyhmac_sha256計算

因為用ida看了字符串發現OpenSSL 1.0.2m  2 Nov 2017的字樣所以事先用Rizzo打上了OpenSSL的符號。關于OpenSSL編譯可以看https://cloud.tencent.com/developer/article/1343632。不過遺憾的是打上了符號也沒辦法識別出這個hmac使用的hash算法是什么,后來我是通過動態調試獲得一組輸入和輸出暴力跑出了hashsha256,做完hmac_sha256后得到的結果即為verification_code

附上python3注冊機代碼

import re
import hmac

def hmac_sha256(key,s):
    return hmac.new(key.encode('utf-8'), s.encode('utf-8'), 'SHA256').hexdigest()

def get_verification_code(part1,part2,part3,part4):
    v5=(ord(part4[0])^ord(part1[0]))<<8
    v8=ord(part1[1])*ord(part4[1])+v5
    v11=ord(part1[2])//ord(part4[2])+v8
    v54=ord(part1[2])%ord(part4[2])+v11
    v56=ord(part3[0])*ord(part2[0])<<8
    v57=(ord(part2[1])^ord(part3[1]))+v56
    v58=ord(part2[2])%ord(part3[2])+32+v57
    v55=ord(part1[2])//ord(part3[2])+v58
    v59=ord(part1[1])%ord(part1[3])<<8
    v60=ord(part2[1])//ord(part2[3])+v59
    v29=ord(part3[1])*ord(part3[3])+8+v60
    v61=(ord(part4[1])^ord(part4[3]))+v29
    v52=(ord(part1[0])^ord(part4[3]))<<8
    v53=ord(part2[1])//ord(part3[2])+v52
    v37=ord(part3[2])*ord(part2[1])
    v38=ord(part4[3])%ord(part1[0])+v37+v53
    v39=v38
    v38=v38*9
    v41=12*v54+45*v55+33*v39
    num1=30*v55+20*(v54+2*v61)+8*v38
    num2=23*v54+32*v55+42*v61+54*v39
    num3=(25*v55+38*v61+67*v39+18*v54)%20
    num4=(12*v54+45*v55+33*v39+v61*64)%20
    if num3>num4:
        num3,num4=num4,num3
    num5=0
    for i in range(4):
        num5+=int(part1[i])+int(part2[i])+int(part3[i])+int(part4[i])
    code=part1+part2+part3+part4+str(num1)+str(num2)+str(num3)+str(num4)+str(num5)
    return hmac_sha256(part1,code)

if __name__=='__main__':
    request_code=input('input request code: ')
    reobj=re.fullmatch('(\\d{4})-(\\d{4})-(\\d{4})-(\\d{4})',request_code)
    if reobj!=None:
        part1=reobj.group(1)
        part2=reobj.group(2)
        part3=reobj.group(3)
        part4=reobj.group(4)
        print(get_verification_code(part1,part2,part3,part4))
    else:
        print('[+] Invalid code!')

第二題 Rotate

第二題和第一題相差不大,不同的地方是多了一個num6以及hmac_sha256key不同。看一下導入表,發現一些opencv的函數

為了方便分析,去摳了一下cv::Mat的結構體。

struct cvMat
{
  int flags;
  int dims;
  int rows;
  int cols;
  unsigned __int8 *data;
  unsigned __int8 *datastart;
  unsigned __int8 *dataend;
  unsigned __int8 *datalimit;
  void *allocator;
  void *u;
  int *size_p;
  unsigned __int64 *step_p;
  unsigned __int64 step_buf[2];
};

其中MatStepMatSize被我拆了,影響不是很大。

首先在check_verification_code中使用cv::imread讀取了題目目錄下的flag.jpg

這里應該是個cv::Mat構造函數,被編譯器優化成內聯函數了。

接下來在sub_14007A2C0中進行圖像的旋轉。

通過cv::getRotationMatrix2D獲得旋轉的矩陣再用cv::warpAffine進行仿射變換。

sub_140079C50進行圖像的處理并計算num6,首先是圖像的處理

分別調用了cv::cvtColor cv::resizecv::dct

然后根據圖像的數據計算num6

  v9 = *Dst.step_p;                             // 一行元素的字節數
  v10 = &v45;
  v11 = (double *)(Dst.data + 24);
  do
  {
    v12 = *(v11 - 3);
    *((double *)v10 - 1) = v12;
    v13 = *(v11 - 2);
    *(double *)v10 = v13;
    v14 = *(v11 - 1);
    *((double *)v10 + 1) = v14;
    v15 = *v11;
    v10[2] = *(_QWORD *)v11;
    v16 = v11[1];
    *((double *)v10 + 3) = v16;
    v17 = v11[2];
    *((double *)v10 + 4) = v17;
    v18 = v11[3];
    *((double *)v10 + 5) = v18;
    v19 = v11[4];
    *((double *)v10 + 6) = v19;
    v5 = v12 * 0.015625                         // 圖像數據的平均數
       + v5
       + v13 * 0.015625
       + v14 * 0.015625
       + v15 * 0.015625
       + v16 * 0.015625
       + v17 * 0.015625
       + v18 * 0.015625
       + v19 * 0.015625;
    v10 += 8;
    v11 = (double *)((char *)v11 + v9);         // 下一行
    --v8;
  }
  while ( v8 );                                 // 8次
  v20 = 0i64;
  do                                            // 圖像每一點的數據與平均數對比
                                                // 如果數值小于平均數則為0否則為1
                                                // 得到一個長度為64格式為二進制的字符串
  {
    if ( *((double *)&v44 + v20) < v5 )
    {
      if ( v4->_Myres < 0x10 )
        v22 = v4;
      else
        v22 = *(string **)v4->_Buf;
      v22->_Buf[v20] = '0';
    }
    else
    {
      if ( v4->_Myres < 0x10 )
        v21 = v4;
      else
        v21 = *(string **)v4->_Buf;
      v21->_Buf[v20] = '1';
    }
    if ( *((double *)&v45 + v20) < v5 )
    {
      if ( v4->_Myres < 0x10 )
        v24 = v4;
      else
        v24 = *(string **)v4->_Buf;
      v24->_Buf[v20 + 1] = '0';
    }
    else
    {
      if ( v4->_Myres < 0x10 )
        v23 = v4;
      else
        v23 = *(string **)v4->_Buf;
      v23->_Buf[v20 + 1] = '1';
    }
    if ( *((double *)&v46 + v20) < v5 )
    {
      if ( v4->_Myres < 0x10 )
        v26 = v4;
      else
        v26 = *(string **)v4->_Buf;
      v26->_Buf[v20 + 2] = '0';
    }
    else
    {
      if ( v4->_Myres < 0x10 )
        v25 = v4;
      else
        v25 = *(string **)v4->_Buf;
      v25->_Buf[v20 + 2] = '1';
    }
    if ( *(double *)&v47[v20] < v5 )
    {
      if ( v4->_Myres < 0x10 )
        v28 = v4;
      else
        v28 = *(string **)v4->_Buf;
      v28->_Buf[v20 + 3] = '0';
    }
    else
    {
      if ( v4->_Myres < 0x10 )
        v27 = v4;
      else
        v27 = *(string **)v4->_Buf;
      v27->_Buf[v20 + 3] = '1';
    }
    v20 += 4i64;
  }
  while ( v20 < 64 );                           // 總共有8*8=64個數

得到的長度為64的字符串即為num6

最后拼接partnum得到字符串s=part1+part2+part3+part4+num1+num2+num3+num4+num5+num6再以num6keyhmac_sha256得到verification code,相關代碼與第一題一致。

附上python3注冊機

import re
import hmac
import cv2
import numpy as np

def hmac_sha256(key,s):
    return hmac.new(key.encode('utf-8'), s.encode('utf-8'), 'SHA256').hexdigest()

def get_num6(part1,part2,part3,part4):
    img=cv2.imread('.\\flag.jpg',cv2.IMREAD_COLOR)
    center=(img.shape[1]*0.5,img.shape[0]*0.5)
    angle=0
    for i in range(4):
        angle+=int(part1[i])+int(part2[i])+int(part3[i])+int(part4[i])
    scale=1.0
    trans=cv2.getRotationMatrix2D(center,angle,scale)
    dsize=(img.shape[1],img.shape[0])
    img=cv2.warpAffine(img,trans,dsize)
    img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    img=np.float64(img)
    dsize=(8,8)
    img=cv2.resize(img,dsize)
    img=cv2.dct(img)
    total=0
    for i in img:
        for j in i:
            total+=j
    average=total/64
    num6=''
    for i in img:
        for j in i:
            if j<average:
                num6+='0'
            else:
                num6+='1'
    return num6

def get_verification_code(part1,part2,part3,part4):
    v5=(ord(part4[0])^ord(part1[0]))<<8
    v8=ord(part1[1])*ord(part4[1])+v5
    v11=ord(part1[2])//ord(part4[2])+v8
    v54=ord(part1[2])%ord(part4[2])+v11
    v56=ord(part3[0])*ord(part2[0])<<8
    v57=(ord(part2[1])^ord(part3[1]))+v56
    v58=ord(part2[2])%ord(part3[2])+32+v57
    v55=ord(part1[2])//ord(part3[2])+v58
    v59=ord(part1[1])%ord(part1[3])<<8
    v60=ord(part2[1])//ord(part2[3])+v59
    v29=ord(part3[1])*ord(part3[3])+8+v60
    v61=(ord(part4[1])^ord(part4[3]))+v29
    v52=(ord(part1[0])^ord(part4[3]))<<8
    v53=ord(part2[1])//ord(part3[2])+v52
    v37=ord(part3[2])*ord(part2[1])
    v38=ord(part4[3])%ord(part1[0])+v37+v53
    v39=v38
    v38=v38*9
    v41=12*v54+45*v55+33*v39
    num1=30*v55+20*(v54+2*v61)+8*v38
    num2=23*v54+32*v55+42*v61+54*v39
    num3=(25*v55+38*v61+67*v39+18*v54)%20
    num4=(12*v54+45*v55+33*v39+v61*64)%20
    if num3>num4:
        num3,num4=num4,num3
    num5=0
    for i in range(4):
        num5+=int(part1[i])+int(part2[i])+int(part3[i])+int(part4[i])
    num6=get_num6(part1,part2,part3,part4)
    code=part1+part2+part3+part4+str(num1)+str(num2)+str(num3)+str(num4)+str(num5)+num6
    return hmac_sha256(num6,code)

if __name__=='__main__':
    request_code=input('input request code: ')
    reobj=re.fullmatch('(\\d{4})-(\\d{4})-(\\d{4})-(\\d{4})',request_code)
    if reobj!=None:
        part1=reobj.group(1)
        part2=reobj.group(2)
        part3=reobj.group(3)
        part4=reobj.group(4)
        print(get_verification_code(part1,part2,part3,part4))
    else:
        print('[+] Invalid code!')

第三題 Invisiable

該題目錄下的文件

打開Invisiable.exe,首先看到有個tlscallback反調試

通過NtQueryInformationProcess查了DebugPort,如果程序被調試了這個DebugPort就不為0。檢測到沒有被調試后程序修改了某個函數,把這個函數填充成nop。當然這個反調試沒什么影響,因為我用了SharpOD插件,所以基本不用管。

看一下main中的主要部分

驗證輸入的函數并不是直接調用的,ida看起來也不方便,所以我選擇通過動態調試獲取了3個step的驗證輸入的函數

first step

sub_404180中判斷number-2019000是否為水仙花數。

如果是的話就就執行接下來的操作

讀取題目目錄下的invisiable.mp3文件數據,然后通過sub_401C20解密得到真實的文件decode.py

解密函數sub_401C20如下

將原文件數據的每個字節與字符串ReverseEngineerIsEasy的每個字符循環異或,得到真實文件數據,然后寫回硬盤上。

然后對目錄下的文件flag也做了同樣的事情,解密后得到nice.png文件。

最后做了一個謎之操作,修改了main.dll

看了一下main.dll偏移0x46c處正是它的導出函數的位置,這里修改成了ret C

到這里first step就結束了

second step

首先判斷了一下輸入

如果符合條件就執行接下來的操作

讀取目錄下1.txt的數據

然后判斷文本內容

也就是說1.txt的文本內容格式需要為xxxx-xxxxpart1長度暫時未知。

然后對這兩個part進行了和first step解密文件很像的操作,buf1buf2分別為part1part2

然后將得到的結果做base64encode計算,得到的結果需要是VFxQXkljVFo=

VFxQXkljVFo=base64decode后得到T\P^IcTZ,也就是說我們的part1長度必須為8。

然后就是寫腳本跑一個符合該要求的輸入出來了

def xor_asc(l):
    s='T\\P^IcTZ'
    a=[ord(x) for x in s]
    b=[0 for i in range(8)]
    for i in range(8):
        b[i]=a[i]^l[i%len(l)]
    return b

def inc_asc(l):
    for i in range(len(l)):
        l[len(l)-i-1]+=1
        if l[len(l)-i-1]==ord('-'):
            l[len(l)-i-1]+=1
        if l[len(l)-i-1]>126:
            l[len(l)-i-1]=33
        else:
            break
    return l

def check_asc(l):
    for i in l:
        if i>126 or i<33:
            return False
    return True

def create_txt():
    part1=[]
    part2=[33,33,33,33]
    while(True):
        part1=xor_asc(part2)
        if check_asc(part1):
            s1=''.join([chr(x) for x in part1])
            s2=''.join([chr(x) for x in part2])
            s=s1+'-'+s2
            print(s)
        part2=inc_asc(part2)

if __name__=='__main__':
    create_txt()
    #u}6uhB2q-!!f+

為了方便輸入,所以只取了可視的ascii字符(不包括空格)。這個腳本不會停,所以請自行ctrl+c強制結束。隨便取了一個結果u}6uhB2q-!!f+保存到1.txt后,再輸入一個符合要求的number,second step就結束了。

third step

sub_404220中對輸入的number進行了驗證

需要輸入一個長度為6的數字,然后每兩個為一組拆成3個2位數,這3個數需要符合一個方程,解的結果為744110

輸入正確結果后,程序加載main.dll

如果僅僅是這樣,那么恭喜,程序崩潰。

還記得在first step中修改了main.dll的導出函數嗎?這就是程序崩潰的原因。為了能夠得到正確結果,在third step輸入數字前需要把未修改的main.dll替換回去。最后符合題目要求,程序輸出了flag。

但是我感覺這里過于詭異,所以不知道算不算正解。又看了一遍題目,要求輸出的flag形式為flag{xxxxxxx}。。。也就是說1.txtpart1的前五位是可以確定的,但是我不想改了,就這樣吧。

還有一個詭異的地方就是,first step中創建的decode.pynice.png在后面沒用到,貼一下decode.py的代碼

# coding=utf-8
import cv2
import numpy as np
import random
import os
from argparse import ArgumentParser
ALPHA = 5

def build_parser():
    parser = ArgumentParser()
    parser.add_argument('--original', dest='ori', required=True)
    parser.add_argument('--image', dest='img', required=True)
    parser.add_argument('--result', dest='res', required=True)
    parser.add_argument('--alpha', dest='alpha', default=ALPHA)
    return parser

def main():
    parser = build_parser()
    options = parser.parse_args()
    ori = options.ori
    img = options.img
    res = options.res
    alpha = float(options.alpha)
    if not os.path.isfile(ori):
        parser.error("original image %s does not exist." % ori)
    if not os.path.isfile(img):
        parser.error("image %s does not exist." % img)
    decode(ori, img, res, alpha)

def decode(ori_path, img_path, res_path, alpha):
    ori = cv2.imread(ori_path)
    img = cv2.imread(img_path)
    ori_f = np.fft.fft2(ori)
    img_f = np.fft.fft2(img)
    height, width = ori.shape[0], ori.shape[1]
    watermark = (ori_f - img_f) / alpha
    watermark = np.real(watermark)
    res = np.zeros(watermark.shape)
    random.seed(height + width)
    x = range(height / 2)
    y = range(width)
    random.shuffle(x)
    random.shuffle(y)
    for i in range(height / 2):
        for j in range(width):
            res[x][y[j]] = watermark[i][j]
    cv2.imwrite(res_path, res, [int(cv2.IMWRITE_JPEG_QUALITY), 100])

if __name__ == '__main__':
    main()

這就超出我的能力范圍了(怎么還有快速傅里葉變換呢)。試著用了一下這個腳本

py -2 decode.py --original nice.png --image out.png --result res.png

得到圖片

nice.pngout.png分別為

看了這幾張圖,總感覺第三題還有一點門路。然而小弟能力有限,只能到這了,希望大佬們能指導指導。



題目太大,上傳不了,有興趣的可以自行去下載。

免費評分

參與人數 20吾愛幣 +22 熱心值 +20 收起 理由
xuzhouwangchen + 1 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
殺豬用牛刀 + 1 + 1 [email protected]
Cloudx + 1 + 1 [email protected]
迷霧v + 1 [email protected]
orangewinnie + 1 + 1 [email protected]
大大帶師兄 + 1 歡迎分析討論交流,吾愛破解論壇有你更精彩!
can0a + 1 用心討論,共獲提升!
yixi + 1 + 1 [email protected]
fu90 + 1 + 1 熱心回復!
s3568753 + 1 + 1 一臉懵逼來,一臉懵逼去,大神威武!
凌霄公子 + 1 + 1 用心討論,共獲提升!
夢游槍手 + 2 + 1 用心討論,共獲提升!
洛熏 + 1 熱心回復!
笙若 + 1 + 1 [email protected]
XhyEax + 2 + 1 熱心回復!
靜一靜 + 1 + 1 用心討論,共獲提升!
65302666 + 2 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
海盜小K + 3 + 1 雖然看不懂,但分還是要給的。
陳世界 + 1 + 1 用心討論,共獲提升!
Smallhorse + 2 + 1 用心討論,共獲提升!

查看全部評分

發帖前要善用論壇搜索功能,那里可能會有你要找的答案或者已經有人發布過相同內容了,請勿重復發帖。

 樓主| 二娃 發表于 2019-6-12 16:59
焰火 發表于 2019-6-10 20:35
雖然沒看過題目,但是樓主思路表達了出來,看有人說看不懂,感覺是樓主沒有說明白一些細節問題,因為所以就 ...

嗯 是有挺多細節沒講的 這篇文章主要還是給看過題目的人看的
kikkki 發表于 2019-7-4 23:49
膜拜大神,小白我看來這輩子都學不會。。太強大了。去培訓了4個月的JAVA,到頭來,連最基本的增刪改都不會。我這豬腦,啥時候能變得和樓主一樣優秀,有一門拿得出手的技術。
 樓主| 二娃 發表于 2019-6-10 19:42
Checkon 發表于 2019-6-10 19:58
完全就根本
看不懂
不訴離殤 發表于 2019-6-10 19:59
完全看不懂。。
57558938 發表于 2019-6-10 20:06
看不懂+1
 樓主| 二娃 發表于 2019-6-10 20:09
對不起我的表達能力太差了。如果沒看過題目的話可能確實是看不懂 = =
_小白 發表于 2019-6-10 20:19
頂一下樓主
chiao 發表于 2019-6-10 20:28
厲害,頂一下
vethenc 發表于 2019-6-10 20:33
村長喊你回來放牛
焰火 發表于 2019-6-10 20:35
雖然沒看過題目,但是樓主思路表達了出來,看有人說看不懂,感覺是樓主沒有說明白一些細節問題,因為所以就直接跳轉過去講了
您需要登錄后才可以回帖 登錄 | 注冊[Register]

本版積分規則 警告:禁止回復與主題無關內容,違者重罰!

快速回復 收藏帖子 返回列表 搜索

RSS訂閱|小黑屋|聯系我們|吾愛破解 - LCG - LSG ( 京ICP備16042023號 | 京公網安備 11010502030087號 )

GMT+8, 2019-7-6 21:50

Powered by Discuz!

© 2001-2017 Comsenz Inc.

快速回復 返回頂部 返回列表
内蒙古11选5开奖查询百度