吾愛破解 - LCG - LSG |安卓破解|病毒分析|huihengkj.com

 找回密碼
 注冊[Register]

QQ登錄

只需一步,快速開始

搜索
查看: 6476|回復: 44

[CTF] [網鼎杯 2020 青龍組]singal(補充)

  [復制鏈接]
樓主
Ginobili 發表于 2020-10-29 14:37 回帖獎勵

500彩票邀请码這個題目方法有很多,目的是練習算法的分析和vm練習,嘗試了靜態直接逆算法和動態靜態結合的方法

方法1

拖入IDA找到main函數

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v4; // [sp+18h] [bp-1D4h]@1

  __main();
  qmemcpy(&v4, &unk_403040, 0x1C8u);//拷貝opcode表
  vm_operad(&v4, 114);//關鍵函數
  puts("good,The answer format is:flag {}");
  return 0;
}

找到關鍵函數

int __cdecl vm_operad(int *a1, int a2)
{
  int result; // eax@2
  char v3[100]; // [sp+13h] [bp-E5h]@4
  char v4[100]; // [sp+77h] [bp-81h]@5
  char v5; // [sp+DBh] [bp-1Dh]@5
  int v6; // [sp+DCh] [bp-1Ch]@1
  int v7; // [sp+E0h] [bp-18h]@1
  int v8; // [sp+E4h] [bp-14h]@1
  int v9; // [sp+E8h] [bp-10h]@1
  int v10; // [sp+ECh] [bp-Ch]@1

  v10 = 0;
  v9 = 0;
  v8 = 0;
  v7 = 0;
  v6 = 0;
  while ( 1 )
  {
    result = v10;
    if ( v10 >= a2 )
      return result;
    switch ( a1[v10] )                          // a1是opcode  v10是索引
    {
      case 10:                                  // 輸入點
        read(v3);
        ++v10;
        break;
      case 1:
        v4[v7] = v5;
        ++v10;
        ++v7;
        ++v9;
        break;
      case 2:
        v5 = a1[v10 + 1] + v3[v9];
        v10 += 2;
        break;
      case 3:
        v5 = v3[v9] - LOBYTE(a1[v10 + 1]);
        v10 += 2;
        break;
      case 4:
        v5 = a1[v10 + 1] ^ v3[v9];
        v10 += 2;
        break;
      case 5:
        v5 = a1[v10 + 1] * v3[v9];
        v10 += 2;
        break;
      case 6:
        ++v10;
        break;
      case 7:
        if ( v4[v8] != a1[v10 + 1] )            // 最終判斷 a1->v5->v4
        {
          printf("what a shame...");
          exit(0);
        }
        ++v8;
        v10 += 2;
        break;
      case 11:
        v5 = v3[v9] - 1;
        ++v10;
        break;
      case 12:
        v5 = v3[v9] + 1;
        ++v10;
        break;
      case 8:
        v3[v6] = v5;
        ++v10;
        ++v6;
        break;
      default:
        continue;
    }
  }
}

可以判斷這是一個vm逆向,分析邏輯,輸入點是case 10最后的判斷位置是case 7。v4由v5得到,且必經過case 1,v5由opcode經過運算得到,所以我們可以模擬算法,先找出每次switch使用哪個索引值v10和整個過程中進行比較的opcode v4。
逆向算法如下

#include <stdio.h>
#include <string.h>
unsigned char opcode[] =
{
  0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x10, 0x00,
  0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
  0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00,
  0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
  0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
  0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
  0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x21, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00,
  0x08, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
  0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x51, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00,
  0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0B, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00,
  0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x08, 0x00,
  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x41, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x20, 0x00,
  0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
  0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x05, 0x00,
  0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
  0x02, 0x00, 0x00, 0x00, 0x25, 0x00, 0x00, 0x00, 0x01, 0x00,
  0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
  0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x20, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
  0x41, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x0C, 0x00,
  0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
  0x22, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3F, 0x00,
  0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
  0x07, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x07, 0x00,
  0x00, 0x00, 0x72, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
  0x33, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x18, 0x00,
  0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xA7, 0xFF, 0xFF, 0xFF,
  0x07, 0x00, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00,
  0x00, 0x00, 0xF1, 0xFF, 0xFF, 0xFF, 0x07, 0x00, 0x00, 0x00,
  0x28, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x84, 0xFF,
  0xFF, 0xFF, 0x07, 0x00, 0x00, 0x00, 0xC1, 0xFF, 0xFF, 0xFF,
  0x07, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x07, 0x00,
  0x00, 0x00, 0x7A, 0x00, 0x00,
};
int *a = (int *)opcode;

int __cdecl vm_operad(int *opcode, int a2)
{
  int order[114] = {};//執行順序
  char flag[100]; // [esp+13h] [ebp-E5h]
  char v4[100]; // [esp+77h] [ebp-81h]
  char v5; // [esp+DBh] [ebp-1Dh]
  int m; // [esp+DCh] [ebp-1Ch]
  int z; // [esp+E0h] [ebp-18h]
  int y; // [esp+E4h] [ebp-14h]
  int x; // [esp+E8h] [ebp-10h]
  int i; // [esp+ECh] [ebp-Ch]
  int s = 0;
  i = 0;
  x = 0;
  y = 0;
  z = 0;
  m = 0;
  while (1)
  {
    if ( i >= a2 )//繼續循環
      break;
    switch ( opcode[i] )
    {
      case 1:
        v4[z] = v5;
        ++i;
        ++z;
        ++x;
        break;
      case 2:
        v5 = opcode[i + 1] + flag[x];
        i += 2;
        break;
      case 3:
        v5 = flag[x] - opcode[i + 1];
        i += 2;
        break;
      case 4:
        v5 = opcode[i + 1] ^ flag[x];
        i += 2;
        break;

      case 5:
        v5 = opcode[i + 1] * flag[x];
        i += 2;
        break;
      case 6:
        ++i;
      case 7:
        v4[y] = opcode[i + 1];
        printf("%#x, ",v4[y]);//打印比較的opcode
        ++y;
        i += 2;
        break;
      case 8:
        flag[m] = v5;
        ++i;
        ++m;
        break;
      case 10:
        scanf("%s",flag);//輸入點
        ++i;
        break;
      case 11:
        v5 = flag[x] - 1;
        ++i;
        break;
      case 12:
        v5 = flag[x] + 1;
        ++i;
        break;
    }
    //printf("%d, ",i);//打印每次使用的索引
  }
}

int main()
{
    vm_operad(a,114);
    return 0;
}

得到

  char order[100] = {1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 17, 18, 19, 20, 22, 23, 25, 26, 28, 29, 30, 31, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, 46, 47, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73, 75, 76, 78, 79, 81, 82, 83, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114};
  unsigned char v4[] = {0x22, 0x3f, 0x34, 0x32, 0x72, 0x33, 0x18, 0xffffffa7, 0x31, 0xfffffff1, 0x28, 0xffffff84, 0xffffffc1, 0x1e, 0x7a};

確定了中間結果根據算法寫逆向算法,注意:正向的算法逆回去需要從后往前來走

int __cdecl vm_decode(int *opcode,int len_114)
{
  char order[100] = {1, 3, 4, 6, 7, 9, 10, 12, 13, 15, 16, 17, 18, 19, 20, 22, 23, 25, 26, 28, 29, 30, 31, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45, 46, 47, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 64, 66, 67, 69, 70, 72, 73, 75, 76, 78, 79, 81, 82, 83, 84, 86, 88, 90, 92, 94, 96, 98, 100, 102, 104, 106, 108, 110, 112, 114};
  unsigned char v4[] = {0x22, 0x3f, 0x34, 0x32, 0x72, 0x33, 0x18, 0xffffffa7, 0x31, 0xfffffff1, 0x28, 0xffffff84, 0xffffffc1, 0x1e, 0x7a};
  unsigned char flag[100] = {}; // [esp+13h] [ebp-E5h]
  int v5; // [esp+DBh] [ebp-1Dh]
  int m; // [esp+DCh] [ebp-1Ch]
  int z; // [esp+E0h] [ebp-18h]
  int x; // [esp+E8h] [ebp-10h]
  int i; // [esp+ECh] [ebp-Ch]
  x = 15;
  z = 15;
  m = 15;
  for(int k=strlen(order) - 1;k>=0;k--)//從后往前
  {
    i = order[k];
    switch ( opcode[i] )
    {
      case 1:
        --x;
        --z;
        v5 = v4[z];
        break;
      case 2:
        flag[x] = v5 - opcode[i + 1];
        break;
      case 3:
        flag[x] = v5 + opcode[i + 1];
        break;
      case 4:
        flag[x] = v5 ^ opcode[i + 1];
        break;
      case 5:
        flag[x] = v5 / opcode[i + 1];
        break;
      case 6:
        break;
      case 8:
        v5 = flag[--m];
        break;
      case 11:
        flag[x] = v5 + 1;
        break;
      case 12:
        flag[x] = v5 - 1;
        break;
    }
  }
  printf("%s",flag);
  return 0;
}

得到flag{757515121f3d478}

方法2

使用動態調試的方法得到opcode和算法。
回到vm算法,可以把斷點斷在case 7 的cmp上,這樣可以根據eax和edx的值一步一步定位使用的opcode值和算法
在這里插入圖片描述
在這里插入圖片描述

地址是004016E2,動態調試,使用od修改jz為jmp觀察這個位置的寄存器數據為0x22
在這里插入圖片描述

依次運行得到v4的值
unsigned char v4[] = {0x22, 0x3f, 0x34, 0x32, 0x72, 0x33, 0x18, 0xffffffa7, 0x31, 0xfffffff1, 0x28, 0xffffff84, 0xffffffc1, 0x1e, 0x7a};
下面分析算法,用ida從輸入點進行分析,下面舉第一個字符的例子:
1.首先走到case10讀取字符串
在這里插入圖片描述
2.走到case4執行抑或操作抑或的對象是第一個字符
在這里插入圖片描述
在這里插入圖片描述
3.再往下到case8賦值操作
在這里插入圖片描述
4.再往后到了case3,相減操作操作數是5
在這里插入圖片描述
在這里插入圖片描述
5.再往下到case1 case1是每個opcode的進行判段前的賦值,在這里結束第一個字符進入第二個字符
在這里插入圖片描述
這五步的計算結果就是0x22 = (flag[0] ^ 0xa) - 5
500彩票邀请码 依次動態調試分析出15個算法分別為

0x22 = (flag[0] ^ 0xa) - 5

0x3f = (flag[1] ^ 0x20) * 3

0x34 = (flag[2] - 2) - 1

0x32 = (flag[3] + 1) ^ 4

0x72 = (flag[4] * 3) - 0x21

0x33 = (flag[5] - 1) - 1 

0x18 = (flag[6] ^ 9) - 0x20

0xffffffa7 = (flag[7] + 0x51) ^ 0x24

0x31 = (flag[8] +1 ) - 1

0xfffffff1 = (flag[9] * 2) + 0x25

0x28 = (flag[10] + 0x36) ^ 0x41

0xffffff84 = (flag[11] + 0x20) * 1

0xffffffc1 = (flag[12] *3) + 0x25

0x1e = (flag[13] ^ 9) - 0x20

0x7a = (flag[14] + 0x41) + 1 

可以寫腳本得到每個的值拼接

#coding=utf-8
b=[34, 63, 52, 50,   114, 51, 24, 256-89,  49,256-15, 40,256-124,  256-63,  30,  122]#(負數取了最低位,因此,直接取后面那部分)
flag=[0]*15
flag[0]=(b[0]+5)^0x10
flag[1]=(b[1]//3)^0x20
flag[2]=(b[2]+3)
flag[3]=(b[3]^4)-1
flag[4]=(b[4]+0x21)//3
flag[5]=b[5]+2
flag[6]=(b[6]+0x20)^9
flag[7]=(b[7]^0x24)-0x51
flag[8]=b[8]
flag[9]=(b[9]-0x25)//2
flag[10]=(b[10]^0x41)-0x36
flag[11]=(b[11]-0x20)
flag[12]=(b[12]-0x25)//3
flag[13]=(b[13]+0x20)^9
flag[14]=(b[14]-1)-0x41
result = ""
for i in range(len(flag)):
        result += chr(flag[i])
print(result)

500彩票邀请码得到flag{757515121f3d478}

另外的思想

關鍵點在case 7和case1 指向了v4的來源
在case7中a1[v10]等于7,所以a1[v10 + 1]就是opcode中數值為7的下一個數字(int為四個字節)
在這里插入圖片描述
可以直接靜態分析出使用的opcode
除此之外,希望看點騷操作的師傅請移步李師傅的吾愛論壇還有小弟的論壇簡單的符號執行講解
http://huihengkj.com/thread-1176826-1-1.html
http://huihengkj.com/thread-1187493-1-1.html

免費評分

參與人數 25威望 +2 吾愛幣 +123 熱心值 +22 收起 理由
0615 + 1 + 1 我很贊同!
zy349898114 + 1 + 1 鼓勵轉貼優秀軟件安全工具和文檔!
burpliu + 1 我很贊同!
最心疼的玩笑 + 1 + 1 熱心回復!
封心以絕塵 + 1 + 1 我很贊同!
Jedis + 1 + 1 熱心回復!
p1kachuu + 1 謝謝@Thanks!
哭泣泣 + 1 + 1 用心討論,共獲提升!
小橙子Top + 1 + 1 用心討論,共獲提升!
Jetset + 1 + 1 很好地思路
DarkRide + 1 + 1 我很贊同!
活到lao學到lao + 1 + 1 謝謝@Thanks!
siuhoapdou + 1 + 1 用心討論,共獲提升!
soyiC + 1 + 1 謝謝@Thanks!
Cinwa + 1 用心討論,共獲提升!
hzpqvq + 1 謝謝@Thanks!
fengbolee + 1 + 1 用心討論,共獲提升!
tflyr + 2 + 1 謝謝@Thanks!
Hmily + 2 + 100 + 1 感謝發布原創作品,吾愛破解論壇因你更精彩!
JIAN_ + 1 + 1 用心討論,共獲提升!
duhao1027 + 1 + 1 熱心回復!
小朋友呢 + 2 + 1 我很贊同!
w516258928 + 1 熱心回復!
wmsuper + 1 + 1 用心討論,共獲提升!
Mr-墨云 + 1 用心討論,共獲提升!

查看全部評分

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

推薦
小朋友呢 發表于 2020-11-2 15:46
樓主,我照著你的代碼用python跑了一下,發現下面這個地方我們有點不同
[C] 純文本查看 復制代碼
      case 2:
        flag[x] = v5 - opcode[i + 1]; //逆運算時,這里應該是 - 1 
        break;
      case 3:
        flag[x] = v5 + opcode[i + 1];
        break;
      case 4:
        flag[x] = v5 ^ opcode[i + 1];
        break;
      case 5:
        flag[x] = v5 / opcode[i + 1];
        break;
推薦
csw曾哥 發表于 2020-11-4 10:38
不錯嘛,感謝分享,之前這道題沒做出來,現在看了一下,感覺有點惋惜
4#
熙曉川 發表于 2020-10-29 14:45
5#
panhaihaihai 發表于 2020-10-29 14:47
額,是個不錯的操作我挺喜歡的。
6#
一線碼農 發表于 2020-10-29 14:50

看不懂。。。占個樓
7#
aa77ss55dd 發表于 2020-10-29 14:53
這操作我喜歡
8#
cydhr 發表于 2020-10-29 16:02
看不懂 但是很羨慕
9#
莫丶小白 發表于 2020-10-29 16:05
完全看不懂。。。好尷尬。
10#
timefy 發表于 2020-10-29 16:34
這個操作666
11#
Baipg 發表于 2020-10-29 23:33
這才是高端操作
12#
我是新手123 發表于 2020-10-30 09:03
這個是C語言逆向匯編?眼熟。。。
您需要登錄后才可以回帖 登錄 | 注冊[Register]

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

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

RSS訂閱|小黑屋|處罰記錄|聯系我們|吾愛破解 - LCG - LSG ( )

GMT+8, 2020-11-27 03:06

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回復 返回頂部 返回列表

500彩票邀請碼-彩經網