picoCTF-RE-otp WP

今天被大佬学弟带着做了一道国外的ctf,质量果然和国内的套娃题不一样,挺有意思的,写个wp纪念一下。
64位elf文件,无壳,用ida64打开,主要算法代码如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  char v4; // al
  char v5; // dl
  unsigned int v6; // eax
  int i; // [rsp+18h] [rbp-E8h]
  int j; // [rsp+1Ch] [rbp-E4h]
  char dest[100]; // [rsp+20h] [rbp-E0h]
  char v10; // [rsp+84h] [rbp-7Ch]
  char s1[104]; // [rsp+90h] [rbp-70h]
  unsigned __int64 v12; // [rsp+F8h] [rbp-8h]

  v12 = __readfsqword(0x28u);
  if ( argc > 1 )
  {
    strncpy(dest, argv[1], 0x64uLL);
    v10 = 0;
    for ( i = 0; (unsigned int)valid_char((unsigned int)dest[i]); ++i )
    {
      if ( i )
      {
        v4 = jumble((unsigned int)dest[i]);
        v5 = s1[i - 1] + v4;
        v6 = (unsigned int)((s1[i - 1] + v4) >> 31) >> 28;
        s1[i] = ((v6 + v5) & 0xF) - v6;
      }
      else
      {
        s1[0] = (char)jumble((unsigned int)dest[0]) % 16;
      }
    }
    for ( j = 0; j < i; ++j )
      s1[j] += 97;
    if ( i == 100
      && !strncmp(
            s1,
            "adpjkoadapekldmpbjhjhbaghlfldbhjdalgnbeedheenfoeddabpmdnliokcahomdphbcleipfgibjdcgmjcmadaomiakpdjcni",
            0x64uLL) )
    {
      puts("You got the key, congrats! Now xor it with the flag!");
      result = 0;
    }
    else
    {
      puts("Invalid key!");
      result = 1;
    }
  }
  else
  {
    printf("USAGE: %s [KEY]\n", *argv, envp, argv);
    result = 1;
  }
  return result;
}

大体看一下整体的正向思路是先检查输入的合法化,然后进行第一层加密,最后+97与一串字符串比较,然后根据给的提示把输入与给的字符串进行异或得到flag。接下来一层一层分析加密。
先看vaild_char函数:

signed __int64 __fastcall valid_char(char a1)
{
  if ( a1 > 47 && a1 <= 57 )
    return 1LL;
  if ( a1 <= 96 || a1 > 102 )
    return 0LL;
  return 1LL;
}

这里是限制输入为十六进制0~f
然后看一下jumble函数:

__int64 __fastcall jumble(char a1)
{
  char v2; // [rsp+0h] [rbp-4h]
  unsigned __int8 v3; // [rsp+0h] [rbp-4h]

  v2 = a1;
  if ( a1 > 96 )
    v2 = a1 + 9;
  v3 = 2 * (v2 % 16);
  if ( (char)v3 > 15 )
    ++v3;
  return v3;
}

传入的参数为我们的输入,这里做的变化经过分析发现是在干以下事情:
如果a1<=7,返回2a1,如果7<a1<=0x0Fh,返回2a1+1。
接下来分析第一层加密函数:

 for ( i = 0; (unsigned int)valid_char(dest[i]); ++i )
    {
      if ( i )
      {
        v4 = jumble(dest[i]);
        v5 = s1[i - 1] + v4;
        v6 = (unsigned int)((s1[i - 1] + v4) >> 31) >> 28;
        s1[i] = ((v6 + v5) & 0xF) - v6;
      }
      else
      {
        s1[0] = (char)jumble(dest[0]) % 16;
      }
    }

v4取出jumble返回的值,v5将输入的s[i-1]与s[i]相加,v6看似很迷茫,但经过动态调试发现v6一直为0,这里我就不管他了。有了v6看作0,s1[i]=v5&0xF,其实这个就等价于s1[i]=v5%16,这样这个加密就很清晰了。
这里还要注意s1[0] = (char)jumble(dest[0]) % 16这个,分析出s1[0]的值是很关键的,下面字符串第一个为a,‘a’-97=0,所以jumble(dest[0]) % 16应该是0,也就是我们第一个输入为0。
第二层加密是一个全体+97,很简单不多赘述。
接下来我写下我的解密脚本:

#include <iostream>
using namespace std;

int rejumble(int a)
{
    int v2 = a;
    unsigned __int8 v3;
    if (v2 < 15) {
        v3 = v2 / 2;
    }
    else {
        v3 = (v2 - 1) / 2;
    }
    return v3;
}

int main()
{
    char a[] = { "adpjkoadapekldmpbjhjhbaghlfldbhjdalgnbeedheenfoeddabpmdnliokcahomdphbcleipfgibjdcgmjcmadaomiakpdjcni" };
    int b[100] = { 0 };//-97后 0 3 15 9 10 14 0 3 0 15 4 10 11 3 12 15 1 9 7 9 7 1 0 6 7 11 5 11 3 1 7 9 3 0 11 6 13 1 4 4 3 7 4 4 13 5 14 4 3 3 0 1 15 12 3 13 11 8 14 10 2 0 7 14 12 3 15 7 1 2 11 4 8 15 5 6 8 1 9 3 2 6 12 9 2 12 0 3 0 14 12 8 0 10 15 3 9 2 13 8
    int c[100] = { 0 };

    for (int i = 0; i < 100; i++) {
        b[i] = (int)(a[i] - 97);
    }

    b[0] = 0;
    for (int i = 1; i < 100; i++) {
        if (b[i] - b[i - 1] < 0 || (b[i] - b[i - 1]) % 2 != 0) {
            if (b[i] - b[i - 1] < 0 && (b[i] - b[i - 1]) % 2 != 0) c[i] = rejumble(b[i] + 32 - b[i - 1]);
            else c[i] = rejumble(b[i] + 16 - b[i - 1]);
        }
        else {
            c[i] = rejumble(b[i] - b[i - 1]);
        }
    }

    for (int i = 0; i < 100; i++) {
        cout << hex << c[i];
    }
}

得到
09658219efa384c9147175f3825347315eddb290f2e0c4c3f0e87eb57e3647bb7b6458cc2b381c45f23ec529e77645a23cdd,与flag异或就是flag。

import binascii
Ans1 = "790ce176acf7c2b277040687b23e185b2bb0d0fcc1939bf782db10c1210218dc4b2b3c931a5c2f04ad5aa711d04175920aa0"
Ans2 = "09658219efa384c9147175f3825347315eddb290f2e0c4c3f0e87eb57e3647bb7b6458cc2b381c45f23ec529e77645a23cdd"
ans1 = binascii.unhexlify(Ans1)
ans2 = binascii.unhexlify(Ans2)
key = ""
for i in range(len(ans1)):
    key += chr(ans1[i] ^ ans2[i])
print(key)

flag:picoCTF{cust0m_jumbl3s_4r3nt_4_g0Od_1d3A_db877006}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值