吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 21734|回复: 77
收起左侧

[CTF] 2018上海CTF“骇极杯”逆向WP

  [复制链接]
一筐萝卜 发表于 2018-11-11 21:22
本帖最后由 一筐萝卜 于 2018-11-19 19:10 编辑

CPP
  • 题目是一个64位ELF文件
  • 拖入IDA中f5查看main函数伪代码
[Asm] 纯文本查看 复制代码
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char v4; // [rsp+0h] [rbp-80h]
  char v5; // [rsp+20h] [rbp-60h]
  char v6; // [rsp+40h] [rbp-40h]
  unsigned __int64 v7; // [rsp+68h] [rbp-18h]
 
  v7 = __readfsqword(0x28u);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v4, a2, a3);
  std::operator<<<std::char_traits<char>>(&std::cout, "input flag:");
  std::operator>><char,std::char_traits<char>,std::allocator<char>>(&std::cin, &v4);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v6, &v4);
  sub_4010A2((__int64)&v5, (__int64)&v6, (__int64)&v6);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v6);
  sub_401332((__int64)&v5);
  sub_40154E((__int64)&v5);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string(&v4);
  return 0LL;
}


  • 通过伪代码可以看出来,这是一个用C++写的
  • 这么长一串其实是引用了C++的字符串类,当时我也是没有看懂,也是第一次遇到

[Asm] 纯文本查看 复制代码
std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string


  • 通过引用字符串"input flag:"我们可以推断出“std::operator<<<std::char_traits<char>>(&std::cout, "input flag:");”是输出字符串的意思,而std::operator>><char,std::char_traits<char>,std::allocator<char>>(&std::cin, &v4)是输入字符串的意思
  • 通过动态调试可以发现std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(&v6, &v4);这个函数是将v4的值复制到v6
  • 然后进入sub_4010A2,通过看汇编代码可以发现,这个函数其实是传入两个参数“sub_4010A2(&v5,&v6)”

[Asm] 纯文本查看 复制代码
1
2
3
4
5
6
void __fastcall sub_4010A2(__int64 a1, __int64 a2, __int64 a3){
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(a1, a2, a3);
  std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator=(a1, a2);// 把a2的值复制到a1
  sub_40111A(a1);
  sub_40110E();
}


  • 在函数里面首先是将a2的值传入a1中,也就是将v6的值传入v5
  • 然后进入sub_40111A()

[Asm] 纯文本查看 复制代码
unsigned __int64 __fastcall sub_40111A(__int64 a1)
{
  _BYTE *v1; // rbx
  int v2; // er12
  const char *v3; // rax
  int i; // [rsp+1Ch] [rbp-54h]
  char s1; // [rsp+20h] [rbp-50h]
  char v7; // [rsp+21h] [rbp-4Fh]
  char v8; // [rsp+22h] [rbp-4Eh]
  char v9; // [rsp+23h] [rbp-4Dh]
  char v10; // [rsp+24h] [rbp-4Ch]
  char v11; // [rsp+25h] [rbp-4Bh]
  char v12; // [rsp+26h] [rbp-4Ah]
  char v13; // [rsp+27h] [rbp-49h]
  char v14; // [rsp+28h] [rbp-48h]
  char v15; // [rsp+29h] [rbp-47h]
  char v16; // [rsp+2Ah] [rbp-46h]
  char v17; // [rsp+2Bh] [rbp-45h]
  char v18; // [rsp+2Ch] [rbp-44h]
  char v19; // [rsp+2Dh] [rbp-43h]
  char v20; // [rsp+2Eh] [rbp-42h]
  char v21; // [rsp+2Fh] [rbp-41h]
  char v22; // [rsp+30h] [rbp-40h]
  char v23; // [rsp+31h] [rbp-3Fh]
  char v24; // [rsp+32h] [rbp-3Eh]
  char v25; // [rsp+33h] [rbp-3Dh]
  char v26; // [rsp+34h] [rbp-3Ch]
  char v27; // [rsp+35h] [rbp-3Bh]
  char v28; // [rsp+36h] [rbp-3Ah]
  char v29; // [rsp+37h] [rbp-39h]
  char v30; // [rsp+38h] [rbp-38h]
  char v31; // [rsp+39h] [rbp-37h]
  char v32; // [rsp+3Ah] [rbp-36h]
  char v33; // [rsp+3Bh] [rbp-35h]
  char v34; // [rsp+3Ch] [rbp-34h]
  char v35; // [rsp+3Dh] [rbp-33h]
  char v36; // [rsp+3Eh] [rbp-32h]
  char v37; // [rsp+3Fh] [rbp-31h]
  char v38; // [rsp+40h] [rbp-30h]
  char v39; // [rsp+41h] [rbp-2Fh]
  char v40; // [rsp+42h] [rbp-2Eh]
  char v41; // [rsp+43h] [rbp-2Dh]
  char v42; // [rsp+44h] [rbp-2Ch]
  char v43; // [rsp+45h] [rbp-2Bh]
  char v44; // [rsp+46h] [rbp-2Ah]
  char v45; // [rsp+47h] [rbp-29h]
  char v46; // [rsp+48h] [rbp-28h]
  char v47; // [rsp+49h] [rbp-27h]
  char v48; // [rsp+4Ah] [rbp-26h]
  unsigned __int64 v49; // [rsp+58h] [rbp-18h]
 
  v49 = __readfsqword(0x28u);
  s1 = 0x99u;
  v7 = 0xB0u;
  v8 = 0x87u;
  v9 = 0x9Eu;
  v10 = 0x84u;
  v11 = 0xA0u;
  v12 = 0xCBu;
  v13 = 0xEFu;
  v14 = 0x88u;
  v15 = 0x90u;
  v16 = 0xBBu;
  v17 = 0x8Eu;
  v18 = 0x91u;
  v19 = 0xE0u;
  v20 = 0xD2u;
  v21 = 0xAEu;
  v22 = 0xD4u;
  v23 = 0xC5u;
  v24 = 0x6F;
  v25 = 0xD7u;
  v26 = 0xC0u;
  v27 = 0x68;
  v28 = 0xC6u;
  v29 = 0x6A;
  v30 = 0x81u;
  v31 = 0xC9u;
  v32 = 0xB7u;
  v33 = 0xD7u;
  v34 = 0x61;
  v35 = 4;
  v36 = 0xDAu;
  v37 = 0xCFu;
  v38 = 0x3D;
  v39 = 0x5C;
  v40 = 0xD6u;
  v41 = 0xEFu;
  v42 = 0xD0u;
  v43 = 0x58;
  v44 = 0xEFu;
  v45 = 0xF2u;
  v46 = 0xADu;
  v47 = 0xADu;
  v48 = 0xDFu;
  for ( i = 0;
        i < (unsigned __int64)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::length(a1);
        ++i )
  {
    v1 = (_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](a1, i);// a1[i]
    v2 = 4 * *(char *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](a1, i);
    *v1 = ((*(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](a1, i) >> 6) | v2) ^ i;
  }
  v3 = (const char *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(a1);
  if ( strcmp(&s1, v3) == 0 )
  {
    sub_4012CE(a1);
    exit(0);
  }
  return __readfsqword(0x28u) ^ v49;
}


  • 然后经过调试发现std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[]的意思是取我们输入的第i个值,即a1


cpp1.png

[Asm] 纯文本查看 复制代码
.text:0000000000401234                 movzx   eax, byte ptr [rax]
.text:0000000000401237                 movsx   eax, al
.text:000000000040123A                 shl     eax, 2


  • 首先是先将a1[\i]进行左移两位
  • 然后将a1的值算术右移6,然后与v2进行或运算,即与“a<<2”进行或运算,随后与i的值进行异或
  • 这一轮下来,总结一下这个循环干了什么:a=(a<<2|a>>6)^i,该循环将a1的每一位都进行这样的操作
  • 当循环结束后,与s1进行比较,所以我们要把s1的数据提取出来,然后根据刚刚的运算进行反推,脚本如下:

[Python] 纯文本查看 复制代码
s1 = [0x99,0xB0,0x87,0x9E, 0x84, 0xA0, 0xCB, 0xEF, 0x88, 0x90, 0xBB, 0x8E, 0x91, 0xE0, 0xD2, 0xAE, 0xD4, 0xC5, 0x6F, 0xD7, 0xC0, 0x68, 0xC6, 0x6A, 0x81, 0xC9, 0xB7, 0xD7, 0x61, 4, 0xDA, 0xCF, 0x3D, 0x5C, 0xD6, 0xEF, 0xD0, 0x58, 0xEF, 0xF2, 0xAD, 0xAD, 0xDF]
flag = ""
for x in range(0,len(s1)):
        for asc in range(32,128):
                if (asc>>6|asc<<2)&0xff==s1[x]^x:
                        flag+= chr(asc)
                        break
print flag


  • 我在写脚本的时候也遇到一个错误,就是我之前没遇到过,最开始我的脚本是这样的,“(asc>>6|asc<<2)==s1[x]^x”,然后死活也跑不出来东西,经过查阅之后发现还需要加一个&0xff的操作
  • 该脚本跑出来的是“flag is: flag{7h15_15_4_f4k3_F14G_=3=_rua!}”
  • 当比较成功之后会进入sub_4012CE()函数里面,这个函数的作用就是输出两句话:“good job,but .....”,很显然,题目并没有这个简单,接着往下面分析
  • std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::~basic_string,该函数应该是把刚刚分配的变量v6给撤销掉,然后进入sub_401332

[Asm] 纯文本查看 复制代码
unsigned __int64 __fastcall sub_401332(__int64 a1)
{
  _BYTE *v1; // r12
  int v2; // ebx
  char v3; // r13
  const char *v4; // rax
  signed int i; // [rsp+18h] [rbp-58h]
  signed int j; // [rsp+1Ch] [rbp-54h]
  char s[8]; // [rsp+20h] [rbp-50h]
  __int64 v9; // [rsp+28h] [rbp-48h]
  __int64 v10; // [rsp+30h] [rbp-40h]
  __int64 v11; // [rsp+38h] [rbp-38h]
  char v12; // [rsp+40h] [rbp-30h]
  unsigned __int64 v13; // [rsp+48h] [rbp-28h]
 
  v13 = __readfsqword(0x28u);
  v12 = 0;
  s[0] = -103;
  s[1] = -80;
  s[2] = -121;
  s[3] = -98;
  s[4] = 112;
  s[5] = -24;
  s[6] = 65;
  s[7] = 68;
  v9 = 0x5855BC749A8B0405LL;
  v10 = -1920493130842480203LL;
  v11 = -3031743088776258207LL;
  for ( i = 0; i <= 3; ++i )
  {
    for ( j = 1; j < strlen(s); ++j )
    {
      v1 = (_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](a1, j);
      v2 = *(unsigned __int8 *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                                 a1,
                                 j);
      v3 = *(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](a1, j - 1) | v2;
      LOBYTE(v2) = *(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                               a1,
                               j);
      *v1 = v3 & ~(v2 & *(_BYTE *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::operator[](
                                    a1,
                                    j - 1));
    }
  }
  v4 = (const char *)std::__cxx11::basic_string<char,std::char_traits<char>,std::allocator<char>>::c_str(a1);
  if ( strncmp(v4, s, 32uLL) == 0 )
    sub_401522();
  return __readfsqword(0x28u) ^ v13;
}


  • 进入函数之后,先进行一系列的赋值操作,然后是一个大循环,循环次数为4次,在这个大循环中套着一个小循环,小循环的次数是我们字符串s的长度,经过在上一个check函数跳过的坑,所以我们对这一个check函数直接看汇编代码
  • 程序先将a[j]取出来,再将a[j-1]|a[j],当我调试的时候发现第一次或运算的两个数是0xc0和0xc5,这个我就很是纳闷了,我之前输入的是012345....,怎么变成了0xc0、0xc5了呢,然后回头一想,我们的字符串在之前的check函数进行过一次处理

[Python] 纯文本查看 复制代码
str1 = "0123456789abcdef"
i=0
for x in str1:
        print hex(((ord(x)<<2)|(ord(x)>>6))&0xff^i),
        i+=1
         
         
 
out:0xc0 0xc5 0xca 0xcf 0xd4 0xd1 0xde 0xdb 0xe8 0xed 0x8f 0x82 0x81 0x9c 0x9b 0x96

  • emmmmmmm,这就对上了,第一次“0xc5|0xc0=0xc5”,然后将计算的结果和a[j-1]进行&运算"0xc5 & 0xc0 = 0xc0 ",然后再将结果进去not(按位取反)“~0xc0”,然后和a[j]进行and操作,最后得到的结果为5,综上所述,这个多的运算其实是将a[j]=a[j]^a[j-1],验证一下:

[Python] 纯文本查看 复制代码
print 0xc0^0xc5
 
out:5


  • 和刚刚的到的结果吻合,所以我们的推测是对的,根据刚刚的调试简化sub_401332这个函数

[Python] 纯文本查看 复制代码
for i in range(4):
        for j in range(32):
                a[j] = a[j]^a[j-1]


  • 当循环结束后,把处理过的字符串和s进行比较,如果正确的话,就会输出“you got it!”,看来这才是真正的flag,脚本如下:

[Python] 纯文本查看 复制代码
list1 = [0x99,0xB0,0x87,0x9E,0x70,0xE8,0x41,0x44,5,4,0x8B,0x9A,0x74,0xBC,0x55,0x58,0xB5,0x61,0x8E,0x36,0xAC,9,0x59,0xE5,0x61,0xDD,0x3E,0x3F,0xB9,0x15,0xED,0xD5]
 
for x in range(4):
        for x in range(31,0,-1):
                list1[x] = list1[x]^list1[x-1]
flag = ""
for x in range(len(list1)):
        flag+=chr(((list1[x]^x)<<6|(list1[x]^x)>>2)&0xff)
print flag


  • 写这个脚本的时候我还是有点迷,迷在他们之间的关系,check2异或运算是从第2个开始的加密

[Asm] 纯文本查看 复制代码
加密:
a[0]= 4   a[1] = 5   a[2] = 6
a[1] = a[1]^a[0] = 5^4 = 1
a[2] = a[2]^a[1] = 6^1 = 7
 
解密:
a[2] = a[2]^a[1] = 7^1 = 6
a[1] = a[1]^a[0] = 1^4 = 5


What’s_it
  • 该题目为32位PE文件,使用PEID检测发现没有加壳
  • 拖入IDA中,查看main函数

[Asm] 纯文本查看 复制代码
int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned __int8 v4[4]; // [esp+1Ch] [ebp-48h]
  unsigned __int8 v5[4]; // [esp+20h] [ebp-44h]
  char v6[32]; // [esp+24h] [ebp-40h]
  char v7; // [esp+44h] [ebp-20h]
  char Str[4]; // [esp+45h] [ebp-1Fh]
  __int16 v9; // [esp+49h] [ebp-1Bh]
  char v10; // [esp+4Bh] [ebp-19h]
  int k; // [esp+4Ch] [ebp-18h]
  int j; // [esp+50h] [ebp-14h]
  int i; // [esp+54h] [ebp-10h]
  int v14; // [esp+58h] [ebp-Ch]
  int v15; // [esp+5Ch] [ebp-8h]
 
  __main();
  *(_DWORD *)Str = 0;
  v9 = 0;
  v10 = 0;
  memset(v6, 0, sizeof(v6));
  v7 = 0;
  *(_DWORD *)v5 = 0;
  *(_DWORD *)v4 = 0;
  v15 = 0;
  v14 = 0;
  printf("Please input your luck string:");
  scanf("%s", Str);
  if ( strlen(Str) != 6 )
    return 0;
  for ( i = 0; i <= 5; ++i )
  {
    if ( Str[i] <= '`' || Str[i] > 'z' )        // 限制string全部为小写
      return 0;
  }
  getMD5(Str, v6);                              // v6是加密后的ozulmt
  for ( j = 0; j <= 31; ++j )
  {
    if ( v6[j] == '0' )
    {
      ++v15;
      v14 += j;
    }
  }
  if ( 10 * v15 + v14 == 403 )
  {
    for ( k = 0; k <= 3; ++k )
    {
      v5[k] = v6[k];                            // v5是md5的前四位
      v4[k] = v6[k + 28];                       // v4是md5的后四位
    }
    decode(v4);
  }
  check(v5);
  return 0;
}


  • 该程序首先让你输入一个luck string,规定长度为6,必须全部是小写字母,并且md5加密后,0的个数和所在的下标满足“ 10 * 个数 + 下标和 == 403”
  • 接下来就是用python写脚本进行爆破


[Python] 纯文本查看 复制代码
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
false[/color]]
# coding:utf-8
import hashlib
def md5encode(str1):
        a = hashlib.md5()
        a.update(str1)
        return a.hexdigest()
 
def seek_luckstring():
        string = ""
        for a in range(97,123):
                for b in range(97,123):
                        for c in range(97,123):
                                for d in range(97,123):
                                        for e in range(97,123):
                                                for f in range(97,123):
                                                        string = chr(a)+chr(b)+chr(c)+chr(d)+chr(e)+chr(f)
                                                        md5_string = md5encode(string)
                                                        v14=0
                                                        v15=0
                                                        for x in range(len(md5_string)):
                                                                if md5_string[x]=="0":
                                                                        v15+=1
                                                                        v14+=x
                                                        if 10*v15+v14==403:
                                                                print string
                                                                exit(0)
 
seek_luckstring()

  • 最后得出luckstring是“ozulmt”
  • 然后通过循环分别给v4,v5赋值def seek_v4v5():

[Python] 纯文本查看 复制代码
def seek_v4v5():
        luckstring = md5encode("ozulmt")
        print luckstring
        v4 = [""]*4
        v5 = [""]*4
        for x in range(4):
                v5[x] = luckstring[x]
                v4[x] = luckstring[x+28]
        print v4    #['0', '1', '0', 'e']后四位
        print v5          #['0', 'e', 'c', '4']前四位


  • 然后进入decode函数

[Asm] 纯文本查看 复制代码
// write access to const memory has been detected, the output may be wrong!
signed int __cdecl decode(unsigned __int8 *a1)
{
  signed int result; // eax
  signed int j; // [esp+24h] [ebp-14h]
  signed int i; // [esp+28h] [ebp-10h]
  unsigned int Seed; // [esp+2Ch] [ebp-Ch]
 
  Seed = 0;
  for ( i = 0; i <= 3; ++i )
    Seed += a1[i];                              // seed = 246
  srand(Seed);
  *(_BYTE *)check ^= 0x96u;                     // check = 0xc3
  for ( j = 1; ; ++j )
  {
    result = j;
    if ( j >= 305 )
      break;
    *((_BYTE *)check + j) ^= rand();
  }
  return result;
}


  • 这个函数是对check函数的字节码进行操作,将数组v4中的每一个值得ASCII码加起来作为srand的种子,然后将check的每一个字节码都与rand()产生的随机数进行异或
  • 我们知道,当srand()的种子一样的情况下,生成的rand()随机值也是一样的,所以check函数是可以恢复的
  • 我动态调试的时候,check函数确实恢复成功了

what2.png

  • 然后转成伪C代码:

what.png
  • 通过观察,我们可以知道这个函数首先让你输入一个flag,然后进入到checkht函数中

[Asm] 纯文本查看 复制代码
char *__cdecl checkht(char *Dest)
{
  int v1; // eax
  char Source[48]; // [esp+1Ch] [ebp-4Ch]
  __int16 v4; // [esp+4Ch] [ebp-1Ch]
  int v5; // [esp+4Eh] [ebp-1Ah]
  __int16 v6; // [esp+52h] [ebp-16h]
  int j; // [esp+54h] [ebp-14h]
  int i; // [esp+58h] [ebp-10h]
  int v9; // [esp+5Ch] [ebp-Ch]
 
  v5 = 1734437990;
  v6 = 123;
  memset(Source, 0, sizeof(Source));
  v4 = 0;
  v9 = 5;
  for ( i = 0; ; ++i )
  {
    v1 = strlens(Dest);
    if ( v1 - 1 <= v9 )
      break;
    if ( Dest[v9] == '-' )                      // 去掉我们输入字符串中的"-",新字符串存在source中
      --i;
    else
      Source[i] = Dest[v9];
    ++v9;
  }
  for ( j = 0; j <= 4; ++j )
  {
    if ( Dest[j] != *((_BYTE *)&v5 + j) )       // 前5位位 "flag{"
    {
      printfs(0);
      exit(0);
    }
  }
  if ( Dest[13] != Dest[28] )                   //  Dest[13] == Dest[28]
  {
    printfs(0);
    exit(0);
  }
  if ( Dest[13] != Dest[18] )
  {
    printfs(0);
    exit(0);
  }
  if ( Dest[13] != Dest[23] )
  {
    printfs(0);
    exit(0);
  }
  if ( Dest[13] != 45 )
  {
    printfs(0);
    exit(0);
  }
  if ( Dest[41] != '}' )
  {
    printfs(0);
    exit(0);
  }
  return strcpy(Dest, Source);
}


  • 该函数首先把我们输入的字符串进行去除“-”,然后有几个判断,如果有一个不满足则会退出程序:

[Asm] 纯文本查看 复制代码
条件:
前5位位 "flag{"
Dest[13] == Dest[28] = "-"
Dest[13] == Dest[18] = "-"
Dest[13] == Dest[23] = "-"
Dest[13] == 45 = "-"
最后一个为'}'


  • 最后将处理后的字符串返回,接着看check函数,又是利用随机值来生成一个正确的flag,饭后与我们的输入进行比较

[Python] 纯文本查看 复制代码
def check2():
        # v5 = ['0', 'e', 'c', '4']
        # v10 = 0
        # for x in range(len(v5)):
        #         v10 += ord(v5[x])
        # print v10
        # v10 = 300
        ASCII = "0123456789abcdef"
        rand = [1018,20977,6537,12471,23035,10168,24596,6071,15959,21200,27017,8546,23669,16211,26282,7236,26183,16060,27924,26961,4987,23116,4119,365,8438,9389,31685,29746,25726,20182,17993,17389]
        flag = ""
        for x in range(len(rand)):
                flag+=ASCII[rand[x]%16]
        print flag
        #a197b847709253a47c41bc7d6d52e69d


  • 我们可以得到一串字符串a197b847709253a47c41bc7d6d52e69d,然后根据之前的那几个限制条件,得出最终的flag:flag{a197b847-7092-53a4-7c41-bc7d6d52e69d}

[Asm] 纯文本查看 复制代码
def check2():
        # v5 = ['0', 'e', 'c', '4']
        # v10 = 0
        # for x in range(len(v5)):
        #         v10 += ord(v5[x])
        # print v10
        # v10 = 300
        ASCII = "0123456789abcdef"
        rand = [1018,20977,6537,12471,23035,10168,24596,6071,15959,21200,27017,8546,23669,16211,26282,7236,26183,16060,27924,26961,4987,23116,4119,365,8438,9389,31685,29746,25726,20182,17993,17389]
        flag = ""
        for x in range(len(rand)):
                flag+=ASCII[rand[x]%16]
        print flag
        #a197b847709253a47c41bc7d6d52e69d


cyvm
  • 同样拖入IDA中查看伪C代码,发现是循环里套switch,代码非常多,我还以为是算法题,然后我分析了好长时间没分析出来,根据官方WP,这道题是一个VM题
  • WP上有一句话值得我记住,就是“while循环套switch,不是迷宫就是vm”,这也许就是经验只谈吧,我这个没经验的人看起来这个题是算法题emmmmmmm
  • 判断是VM:1.看到这么多case,那一定是vm了 2.根据传入函数的a2和每次goto前的”+=”运算可以很容易的确定v5是读字节码的操作
  • 根据WP分析出:

[Python] 纯文本查看 复制代码
s2reg    1
reg2s    2
mov      3
add      4
sub      5
xor      6
and      7
jmp      9
cmp      10
or       11
jnz      12
putc     13
ipt      15
movs     16
adds     17
inc      18


  • 这里贴上官方WP上出的VM指令

cyvm.png

  • 总结算法就是:

[Python] 纯文本查看 复制代码
input[i]=input[i+1]^i


  • 解题脚本如下:

[Python] 纯文本查看 复制代码
#coding:utf-8
s1 = [0x0A ,0x0C, 4,0x1F,0x48,0x5A,0x5F,3,0x62,0x67,0x0E,0x61,0x1E,0x198,0x36,0x47,0x52,0x13,0x57,0x7C,0x39,0x54,0x4B55,0x45,0x77,0x15,0x26,0x0E,0x62,0]
for x in range(31,-1,-1):
        s1[x] ^= s1[x+1]^x
flag =""
for x in range(len(s1)):
        flag+= chr(s1[x])
print flag
#flag{7h15_15_MY_f1rs7_s1mpl3_Vm}

免费评分

参与人数 26吾爱币 +25 热心值 +23 收起 理由
meking + 1 + 1 谢谢@Thanks!
开心糖 + 2 + 1 我很赞同!
boyce_wang + 1 谢谢@Thanks!
永远喜欢英梨梨 + 1 + 1 我很赞同!
shajiaqin + 1 我很赞同!
Mr.mao + 1 + 1 用心讨论,共获提升!
不见001 + 1 + 1 谢谢@Thanks!
daxingshen + 1 + 1 热心回复!
1127363108 + 1 + 1 我很赞同!
刺心 + 2 + 1 用心讨论,共获提升!
TwilightHome + 1 谢谢@Thanks!
清夜无尘 + 1 谢谢@Thanks!
Mr.Mcdull + 1 + 1 热心回复!
youhen233 + 1 + 1 谢谢@Thanks!
Boommi6 + 1 + 1 我很赞同!
usermargin + 1 + 1 用心讨论,共获提升!
靓仔胖哥 + 1 + 1 我很赞同!
莽撞人 + 1 + 1 热心回复!
chkds + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Jerry_bean + 1 + 1 用心讨论,共获提升!
2ing + 1 用心讨论,共获提升!
liphily + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
tiandu0jin + 1 + 1 谢谢@Thanks!
静叶流云 + 1 + 1 谢谢@Thanks!
lbwb2 + 1 + 1 谢谢@Thanks!
lihaohua + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| 一筐萝卜 发表于 2018-11-12 18:25
N0nE_Seana 发表于 2018-11-12 12:21
不知道楼主能否分享以下题目源文件?

https://github.com/hacker-mao/ctf_repo/tree/master/%E7%AC%AC%E5%9B%9B%E5%B1%8A%E4%B8%8A%E6%B5%B7%E5%B8%82%E5%A4%A7%E5%AD%A6%E7%94%9F%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E5%A4%A7%E8%B5%9B

免费评分

参与人数 1吾爱币 +1 收起 理由
N0nE_Seana + 1 谢谢@Thanks!

查看全部评分

Q火流星 发表于 2018-11-20 22:22
lbwb2 发表于 2018-11-11 21:52
头像被屏蔽
HasaH 发表于 2018-11-11 22:18
来了就发帖,支持新人,,
GLSakura 发表于 2018-11-11 22:47 来自手机
我看不懂,给你点赞
超大的橙子 发表于 2018-11-11 22:50
想问下lz知道怎么把What’s_it解密后的creck函数dump出来吗
壹万叁 发表于 2018-11-11 23:00
谢谢分享,学习了
 楼主| 一筐萝卜 发表于 2018-11-11 23:35 来自手机
我的排班有问题,第一次发,不是太美观,抱歉啦,谢谢支持
 楼主| 一筐萝卜 发表于 2018-11-11 23:42 来自手机
超大的橙子 发表于 2018-11-11 22:50
想问下lz知道怎么把What’s_it解密后的creck函数dump出来吗

在od中按照程序的流程走,当走到check函数的时候就会有的,在ida中check函数中是没有的

免费评分

参与人数 1吾爱币 +1 热心值 +1 收起 理由
wwwio + 1 + 1 用心讨论,共获提升!

查看全部评分

 楼主| 一筐萝卜 发表于 2018-11-11 23:48 来自手机
大家可以去我的博客上看这篇文章,https://luobuming.github.io,博客上比较清晰
btctw 发表于 2018-11-12 00:28
谢谢分享,学习了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-12 05:51

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表