吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 21312|回复: 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] 纯文本查看 复制代码
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] 纯文本查看 复制代码
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,0x19,  8,0x36,0x47,0x52,0x13,0x57,0x7C,0x39,0x54,0x4B,  5,  5,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, 2024-11-16 14:37

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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