BUUCTF逆向的一些WP(4)

目录

[GWCTF 2019]pyre

[ACTF新生赛2020]easyre

[ACTF新生赛2020]rome

CrackRTF

[FlareOn4]login

[2019红帽杯]easyRE



[GWCTF 2019]pyre

是pyc文件,可以用反编译工具,我这里是在线反编译,地址

#!/usr/bin/env python
# visit https://tool.lu/pyc/ for more information
# Version: Python 2.7

print "Welcome to Re World!"
print "Your input1 is your flag~"
l = len(input1)
for i in range(l):
    num = ((input1[i] + i) % 128 + 128) % 128
    code += num
for i in range(l - 1):
    code[i] = code[i] ^ code[i + 1]
print code
code = [
    "\x1f",
    "\x12",
    "\x1d",
    "(",
    "0",
    "4",
    "\x01",
    "\x06",
    "\x14",
    "4",
    ",",
    "\x1b",
    "U",
    "?",
    "o",
    "6",
    "*",
    ":",
    "\x01",
    "D",
    ";",
    "%",
    "\x13",
]

直接写脚本:

code = ["\x1f", "\x12", "\x1d", "(", "0", "4", "\x01", "\x06", "\x14", "4", ",", "\x1b", "U",
        "?", "o", "6", "*", ":", "\x01", "D", ";", "%", "\x13"]
le = len(code)
for i in range(-2, -le, -1):
    code[i] = chr(ord(code[i]) ^ ord(code[i + 1]))
# print(code)
flag = ''
for i in range(le):
    for j in range(128):
	    if((j + i) % 128 == ord(code[i])):
            flag += chr(j)
print(flag)
"""
第二个循环的另一种方式
for i in range(le):
        code[i] = chr((ord(code[i]) - i) % 128)
        flag += code[i]
"""

[ACTF新生赛2020]easyre

upx壳

脱掉后

int __cdecl main(int argc, const char **argv, const char **envp)
{
  _BYTE v4[12]; // [esp+12h] [ebp-2Eh] BYREF
  _DWORD v5[3]; // [esp+1Eh] [ebp-22h]
  _BYTE v6[5]; // [esp+2Ah] [ebp-16h] BYREF
  int v7; // [esp+2Fh] [ebp-11h]
  int v8; // [esp+33h] [ebp-Dh]
  int v9; // [esp+37h] [ebp-9h]
  char v10; // [esp+3Bh] [ebp-5h]
  int i; // [esp+3Ch] [ebp-4h]

  __main();
  qmemcpy(v4, "*F'\"N,\"(I?+@", sizeof(v4));
  printf("Please input:");
  scanf("%s", v6);
  if ( v6[0] != 'A' || v6[1] != 'C' || v6[2] != 'T' || v6[3] != 'F' || v6[4] != '{' || v10 != '}' )
    return 0;
  v5[0] = v7;
  v5[1] = v8;
  v5[2] = v9;
  for ( i = 0; i <= 11; ++i )
  {
    if ( v4[i] != _data_start__[*((char *)v5 + i) - 1] )
      return 0;
  }
  printf("You are correct!");
  return 0;
}

v6 = "ACTF{}",v5为括号中的内容,至于为什么是,我认为是靠猜和经验,伪代码本身不难。_data_start是字符串(这样伪代码才合理),其中有几个十六进制数要转为字符

脚本

v4 = "*F'\"N,\"(I?+@"
_data_start_ = "~}|{zyxwvutsrqponmlkjihgfedcba`_^]\[ZYXWVUTSRQPONMLKJIHGFEDCBA@?>=<;:9876543210/.-,+*)(\'&%$# !\""
v5 = ''
for i in v:
    v5 += chr(_data_start_.find(i) + 1)  # find字符查找,可规定范围
#    print(v5)
print(v5)

[ACTF新生赛2020]rome

关键在func()函数,v1为flag

int func()
{
  int result; // eax
  int v1[4]; // [esp+14h] [ebp-44h]
  unsigned __int8 v2; // [esp+24h] [ebp-34h] BYREF
  unsigned __int8 v3; // [esp+25h] [ebp-33h]
  unsigned __int8 v4; // [esp+26h] [ebp-32h]
  unsigned __int8 v5; // [esp+27h] [ebp-31h]
  unsigned __int8 v6; // [esp+28h] [ebp-30h]
  int v7; // [esp+29h] [ebp-2Fh]
  int v8; // [esp+2Dh] [ebp-2Bh]
  int v9; // [esp+31h] [ebp-27h]
  int v10; // [esp+35h] [ebp-23h]
  unsigned __int8 v11; // [esp+39h] [ebp-1Fh]
  char v12[29]; // [esp+3Bh] [ebp-1Dh] BYREF

  strcpy(v12, "Qsw3sj_lz4_Ujw@l");
  printf("Please input:");
  scanf("%s", &v2);
  result = v2;
  if ( v2 == 'A' )
  {
    result = v3;
    if ( v3 == 'C' )
    {
      result = v4;
      if ( v4 == 'T' )
      {
        result = v5;
        if ( v5 == 'F' )
        {
          result = v6;
          if ( v6 == '{' )
          {
            result = v11;
            if ( v11 == '}' )                   // 有十六位
            {
              v1[0] = v7;
              v1[1] = v8;
              v1[2] = v9;
              v1[3] = v10;
              *(_DWORD *)&v12[17] = 0;          // 初始化,可以看作取i=0
              while ( *(int *)&v12[17] <= 15 )  //可以看作i<=15
              {                                 //可以看作v1[i]>64&&v1[i]<=90
                if ( *((char *)v1 + *(_DWORD *)&v12[17]) > 64 && *((char *)v1 + *(_DWORD *)&v12[17]) <= 90 )
                  *((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 51) % 26 + 65;
                if ( *((char *)v1 + *(_DWORD *)&v12[17]) > 96 && *((char *)v1 + *(_DWORD *)&v12[17]) <= 122 )
                  *((_BYTE *)v1 + *(_DWORD *)&v12[17]) = (*((char *)v1 + *(_DWORD *)&v12[17]) - 79) % 26 + 97;
                ++*(_DWORD *)&v12[17];
              }
              *(_DWORD *)&v12[17] = 0;
              while ( *(int *)&v12[17] <= 15 )//这里是在验证加密后v1是否
                                              //等于"Qsw3sj_lz4_Ujw@l"
              {
                result = (unsigned __int8)v12[*(_DWORD *)&v12[17]];
                if ( *((_BYTE *)v1 + *(_DWORD *)&v12[17]) != (_BYTE)result )
                  return result;
                ++*(_DWORD *)&v12[17];
              }
              result = printf("You are correct!");
            }
          }
        }
      }
    }
  }
  return result;
}

python脚本

v1 = "Qsw3sj_lz4_Ujw@l"
flag = ''
for i in v1:
    if(ord(i) >= 97 and ord(i) < 123):
        for j in range(97, 123):
            if((j - 79) % 26 + 97 == ord(i)):
                flag += chr(j)
    elif(ord(i) >= 65 and ord(i) < 91):
        for j in range(65, 91):
            if((j - 51) % 26 + 65 == ord(i)):
                flag += chr(j)
    else:
        flag += i
print("flag{" + flag + "}")

CrackRTF

运行一下,对程序有了一个初步认识。查壳发现是32位无壳,直接放进IDA。查看String window跟进字符串 pls input the first passwd(1):反汇编得到伪代码。

函数先后共获取了两段字符串,第一段经过检验后与@DBApp连接到一起并进入函数sub_40100A进行SHA加密再验证结果,然后与第二段字符串连接进入函数sub_401019进行MD5加密再验证结果,这就是函数的大致流程了。介绍一种取巧的方法,直接把用来验证MD5加密的值用在线MD5解密网站解出就可以得到所求的字符串,虽然MD5理论上不可解密,但终究是有方法的。运行程序先后输入123321与~!3a@0可得到一个rtf文件,flag就在里面

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  DWORD v3; // eax
  DWORD v4; // eax
  char Str[260]; // [esp+4Ch] [ebp-310h] BYREF
  int v7; // [esp+150h] [ebp-20Ch]
  char String1[260]; // [esp+154h] [ebp-208h] BYREF
  char Destination[260]; // [esp+258h] [ebp-104h] BYREF

  memset(Destination, 0, sizeof(Destination));  // 初始化
  memset(String1, 0, sizeof(String1));
  v7 = 0;
  printf("pls input the first passwd(1): ");
  scanf("%s", Destination);                     //  
  if ( strlen(Destination) != 6 )
  {
    printf("Must be 6 characters!\n");          // 6位
    ExitProcess(0);
  }
  v7 = atoi(Destination);                       // 把参数 str 所指向的字符串转换为一个整数
  if ( v7 < 100000 )
    ExitProcess(0);
  strcat(Destination, "@DBApp");                // 连接,12位
  v3 = strlen(Destination);
  sub_40100A((BYTE *)Destination, v3, String1); // 加密
  if ( !_strcmpi(String1, "6E32D0943418C2C33385BC35A1470250DD8923A9") )// 比较
  {
    printf("continue...\n\n");
    printf("pls input the first passwd(2): ");
    memset(Str, 0, sizeof(Str));
    scanf("%s", Str);                           //  
    if ( strlen(Str) != 6 )
    {
      printf("Must be 6 characters!\n");        // 6位
      ExitProcess(0);
    }
    strcat(Str, Destination);                   // 连接
    memset(String1, 0, sizeof(String1));        // 初始化
    v4 = strlen(Str);
    sub_401019((BYTE *)Str, v4, String1);       // 加密
    if ( !_strcmpi("27019e688a4e62a649fd99cadaafdb4e", String1) )// 比较
    {
      if ( !(unsigned __int8)sub_40100F(Str) )
      {
        printf("Error!!\n");
        ExitProcess(0);
      }
      printf("bye ~~\n");
    }
  }
  return 0;
}

关于两种加密算法的识别:

(1)SHA加密 ,里面调用了一堆win32的API函数,查看官方文档发现关键函数是CryptCreateHash,其中的0x8004为SHA算法的特征值

int __cdecl sub_401230(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
  int result; // eax
  DWORD i; // [esp+4Ch] [ebp-28h]
  CHAR String2[4]; // [esp+50h] [ebp-24h] BYREF
  BYTE v6[20]; // [esp+54h] [ebp-20h] BYREF
  DWORD pdwDataLen; // [esp+68h] [ebp-Ch] BYREF
  HCRYPTHASH phHash; // [esp+6Ch] [ebp-8h] BYREF
  HCRYPTPROV phProv; // [esp+70h] [ebp-4h] BYREF

  if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )
    return 0;
  if ( CryptCreateHash(phProv, 0x8004u, 0, 0, &phHash) )// 创建加密服务,提供程序(CSP)哈希对象的句柄
  {
    if ( CryptHashData(phHash, pbData, dwDataLen, 0) )
    {
      CryptGetHashParam(phHash, 2u, v6, &pdwDataLen, 0);// 检索控制散列对象操作的数据
      *lpString1 = 0;
      for ( i = 0; i < pdwDataLen; ++i )
      {
        wsprintfA(String2, "%02X", v6[i]);      // 将字符和数值输入到缓冲区
        lstrcatA(lpString1, String2);           // 该函数将一个字符串附加在另一个字符串后面。
      }
      CryptDestroyHash(phHash);
      CryptReleaseContext(phProv, 0);
      result = 1;
    }
    else
    {
      CryptDestroyHash(phHash);
      CryptReleaseContext(phProv, 0);
      result = 0;
    }
  }
  else
  {
    CryptReleaseContext(phProv, 0);
    result = 0;
  }
  return result;
}

 (2)MD5加密,与上一个差不多,不过CryptCreateHash函数中的值变为0x8003,是MD5加密的特征值

int __cdecl sub_401040(BYTE *pbData, DWORD dwDataLen, LPSTR lpString1)
{
  int result; // eax
  DWORD i; // [esp+4Ch] [ebp-24h]
  CHAR String2[4]; // [esp+50h] [ebp-20h] BYREF
  BYTE v6[16]; // [esp+54h] [ebp-1Ch] BYREF
  DWORD pdwDataLen; // [esp+64h] [ebp-Ch] BYREF
  HCRYPTHASH phHash; // [esp+68h] [ebp-8h] BYREF
  HCRYPTPROV phProv; // [esp+6Ch] [ebp-4h] BYREF

  if ( !CryptAcquireContextA(&phProv, 0, 0, 1u, 0xF0000000) )
    return 0;
  if ( CryptCreateHash(phProv, 0x8003u, 0, 0, &phHash) )
  {
    if ( CryptHashData(phHash, pbData, dwDataLen, 0) )
    {
      CryptGetHashParam(phHash, 2u, v6, &pdwDataLen, 0);
      *lpString1 = 0;
      for ( i = 0; i < pdwDataLen; ++i )
      {
        wsprintfA(String2, "%02X", v6[i]);      // 将数据写入缓存区
        lstrcatA(lpString1, String2);           // 连接
      }
      CryptDestroyHash(phHash);
      CryptReleaseContext(phProv, 0);
      result = 1;
    }
    else
    {
      CryptDestroyHash(phHash);
      CryptReleaseContext(phProv, 0);
      result = 0;
    }
  }
  else
  {
    CryptReleaseContext(phProv, 0);
    result = 0;
  }
  return result;
}

[FlareOn4]login

html文件

<!DOCTYPE Html />
<html>
    <head>
        <title>FLARE On 2017</title>
    </head>
    <body>
        <input type="text" name="flag" id="flag" value="Enter the flag" />
        <input type="button" id="prompt" value="Click to check the flag" />
        <script type="text/javascript">
            document.getElementById("prompt").onclick = function () {
                var flag = document.getElementById("flag").value;//获取flag元素的value
                var rotFlag = flag.replace(/[a-zA-Z]/g, function(c){return String.fromCharCode((c <= "Z" ? 90 : 122) >= (c = c.charCodeAt(0) + 13) ? c : c - 26);});
            //replace(a,b)将a指定的内容用b替换,此处的a为正则表达式,[a-zA-Z]表示匹配a-z与A-Z中的所有字符,g表示进行全局匹配,即比较flag中的每个字符,未加g时匹配到一个指定字符便会停止;
            //fromCharCode()将 Unicode 编码的值转换为一个字符串;
            //b? x : y为三目运算符,b为真返回x否则返回y;
            //charCodeAt(a)返回字符串第a个字符的 Unicode 编码。
            //经分析,此为Rot13加密算法
                if ("PyvragFvqrYbtvafNerRnfl@syner-ba.pbz" == rotFlag) {
                    alert("Correct flag!");
                } else {
                    alert("Incorrect flag, rot again");
                }
            }
        </script>
    </body>
</html>

python脚本

rotflag = 'PyvragFvqrYbtvafNerRnfl@syner-ba.pbz'
flag = ''
for i in rotflag:
    if i >= 'a' and i <= 'z':
        flag += chr((ord(i) - ord('a') + 13) % 26 + ord('a'))
    elif i >= 'A' and i <= 'Z':
        flag += chr((ord(i) - ord('A') + 13) % 26 + ord('A'))
    else:
        flag += i
print(flag)

[2019红帽杯]easyRE

查壳,64位无壳。

IDA打开,先看字符串

看到

"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

应该是base64加密,跟进到sub_400E44(),确实是base64加密。

再跟进"You found me!!!",找到sub_4009C6,F5。

前半部分,用脚本解密

 

 伪代码中的字符串出错了,还得看汇编代码,本来还没问题的,就很后怕,还是要以汇编代码为主才行。

v15 = ""
v13 = "y.i"
v14 = "d`3w}wek9{iy=~yL@EC"
print(len(v14))
v12 = "Iodl>Qnb(ocy"
for i in range(len(v12)):
    v15 += chr(ord(v12[i]) ^ i)
v15 += chr(127 ^ 12)
for i in range(len(v13)):
    v15 += chr(ord(v13[i]) ^ (i + 13))
v15 += chr(127 ^ 16)
for i in range(len(v14)):
    v15 += chr(ord(v14[i]) ^ (i + 17)) 
print(v15)

得到

再解密后半部分,函数sub_400360功能类似于比较运算符。

 最终得到一网址https://bbs.pediy.com/thread-254172.htm,点进去是看雪的一个帖子,然后发现被坑了......

回去再看看字符串,其中/$nb<'THl$nr<2E[很特别,地址在Vm0wd2VHUXhTWGhpUm1SWVYwZDRWVll3Wkc5WFJsbDNXa1pPVlUxV2NIcFhhMk0xV下方跟进sub_4009C6

unsigned __int64 sub_400D35()
{
  unsigned __int64 result; // rax
  unsigned int v1; // [rsp+Ch] [rbp-24h]
  int i; // [rsp+10h] [rbp-20h]
  int j; // [rsp+14h] [rbp-1Ch]
  unsigned int v4; // [rsp+24h] [rbp-Ch]
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  v1 = sub_43FD20(0LL) - qword_6CEE38;
  for ( i = 0; i <= 1233; ++i )
  {
    sub_40F790(v1);
    sub_40FE60();
    sub_40FE60();
    v1 = sub_40FE60() ^ 0x98765432;
  }
  v4 = v1;
  if ( ((unsigned __int8)v1 ^ byte_6CC0A0[0]) == 'f' && (HIBYTE(v4) ^ (unsigned __int8)byte_6CC0A3) == 'g' )
  {
    for ( j = 0; j <= 24; ++j )
      sub_410E90((unsigned __int8)(byte_6CC0A0[j] ^ *((_BYTE *)&v4 + j % 4)));
  }
  result = __readfsqword(0x28u) ^ v5;
  if ( result )
    sub_444020();
  return result;
}

f与g是第一个与第三个也正好是flag的第一个和第三个字符,因此flag就在这了,这是有理有据的猜。

python脚本

lis = [0x40, 0x35, 0x20, 0x56, 0x5d, 0x18, 0x22, 0x45, 0x17, 
        0x2f, 0x24, 0x6e, 0x62, 0x3c, 0x27,0x54, 0x48, 0x6c,
        0x24, 0x6e, 0x72, 0x3c, 0x32, 0x45, 0x5b]
str = 'flag'
v4 = []
for i in range(4):
    v4.append(ord(str[i]) ^ lis[i])
flag = ''
for i in range(25):
    flag += chr(lis[i] ^ v4[i % 4])
print(flag)

得到flag{Act1ve_Defen5e_Test}

虽然自己没做出来,但仍然收获很大。

  • 汇编代码很重要,反汇编代码与字符串的异常需要通过汇编代码矫正,有些线索也在汇编代码中
  • 某些位置的自定义函数与标准函数功能相同,如memset下的sub_4406E0与scanf功能相同,if判断条件中的sub_424BA0与strlen功能相同以及sub_400360与strcmp功能相同,大部分时候都是这样的
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

∪v

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值