帕鲁杯复现

1、茶:


1、经典查壳:

64位无壳软件:

2、ida启动:

main函数还是比较复杂的,他的流程大概就是输入flag,然后经过加密,在经过if语句判断与密文进行对比,我们使用ida的查询加密算法的插件来进行查询一下他的加密算法:

他给出的是salsa20加密,但是结果并不是这个加密算法,所以还是要靠自己的经验来观察

这个函数就是我们所要进行解密的函数,这个加密其实是chacha20加密,经过搜索可以得到chacha20加密他需要一个密钥key,一个偏移量ponce,,其中偏移量是一个12位的,但是他给的是16位,经过动调发现,他只取了前12位,下面给出解密代码(使用python的库进行解密,这个Crypto是真的难安装)

from Cryptodome.Cipher import ChaCha20

enc_data = bytes.fromhex("f568c48912eed6dc520c7164f44b6378e1d0d3e248914fa8847b405a131f")


key = b"SGludDogSW1wcm92ZSBvZiBTYWxzYTIw"
nonce = b"Is_This_"

cc = ChaCha20.new(key=key, nonce=nonce)

print(cc.decrypt(enc_data))

flag就是flag{But_I_Like_ChaCha20_More}

chacha20加密参考文章

[]: 【密码学】一文读懂ChaCha20-阿里云开发者社区

2、Pypl:

记录一下这个python逆向的解决

1、解包

1、首先可以观察到这个是一个python进行打包的程序

所以使用pyinstxtractor解包,我刚开始使用的是低于2.0的版本,这个解包出来的pyc不带后缀,而且也没有自动填补程序头

首先将所要解包的程序放在与其pyinstxtractor放在一起,然后打开终端运行

python pyinstxtractor.py 解包.exe   (格式)

执行完毕会看到successful,而且会生成一个文件夹

上面的文件夹。就存放着我们解包的pyc文件,我们往他里去找根文件夹名字相同的文件,然后如果没有pyc的后缀,我们手动添加,最后拉入到010editor中进行查看文件头

如果缺少magic头,我们就应该重新修改,所以我们还是使用2.0以上的版本吧,方便快捷

此时我们可以通过一个dll文件进行查询他的python版本

所以这是一个python311的版本

贴一下各个版本的magic头

enum PycMagic {
    MAGIC_1_0 = 0x00999902,
    MAGIC_1_1 = 0x00999903, /* Also covers 1.2 */
    MAGIC_1_3 = 0x0A0D2E89,
    MAGIC_1_4 = 0x0A0D1704,
    MAGIC_1_5 = 0x0A0D4E99,
    MAGIC_1_6 = 0x0A0DC4FC,
 
    MAGIC_2_0 = 0x0A0DC687,
    MAGIC_2_1 = 0x0A0DEB2A,
    MAGIC_2_2 = 0x0A0DED2D,
    MAGIC_2_3 = 0x0A0DF23B,
    MAGIC_2_4 = 0x0A0DF26D,
    MAGIC_2_5 = 0x0A0DF2B3,
    MAGIC_2_6 = 0x0A0DF2D1,
    MAGIC_2_7 = 0x0A0DF303,
 
    MAGIC_3_0 = 0x0A0D0C3A,
    MAGIC_3_1 = 0x0A0D0C4E,
    MAGIC_3_2 = 0x0A0D0C6C,
    MAGIC_3_3 = 0x0A0D0C9E,
    MAGIC_3_4 = 0x0A0D0CEE,
    MAGIC_3_5 = 0x0A0D0D16,
    MAGIC_3_5_3 = 0x0A0D0D17,
    MAGIC_3_6 = 0x0A0D0D33,
    MAGIC_3_7 = 0x0A0D0D42,
    MAGIC_3_8 = 0x0A0D0D55,
    MAGIC_3_9 = 0x0A0D0D61,
    MAGIC_3_10 = 0x0A0D0D6F,
    MAGIC_3_11 = 0x0A0D0DA7,
    MAGIC_3_12 = 0x0A0D0DCB,
 
    INVALID = 0,
};

https://blog.csdn.net/OrientalGlass/article/details/134612786

接下来就是准备将pyc反编译为python

2、反编译:
 

这里我尝试使用uncompyle6进行反编译,但是一直报错,我要不知道什么原因导致的,贴个图记录一下错误看看以后可以解决吗:

所以我还是使用在线网站将其反编译了

https://tool.lu/pyc/

使用这个可以解决

得到代码:

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

from Crypto.Util.number import bytes_to_long

def enc(key):
    R = bytes_to_long(b'Welcome To PaluCTF!')
    MOD = 2 ** 418
    R = R ^ R - 60 >> 24
    R = R ^ R - 60 << 88
    R ^= key
    R = -R ** 2 * 2024 % MOD
    R = R * key % MOD
    return R

flag = input('Welcome To PaluCTF!\nInput FLAG:')
m = bytes_to_long(flag.encode())
cor = 0x2E441F765514CCA89173554726494D37E9FBE774B6F807BC5F6E71117530CE3D7DB5F70554C03CD9055F4E42969600904DF1F4DB8L
if enc(m) == cor:
    print('Congratulation!')
    return None
print('Wrong FLAG!')

下面解决这个加密,设计异或和位运算所以使用这Z3对其进行解密

给一下代码:
 

from z3 import *
from Cryptodome.Util.number import *

def enc(key):
    R = bytes_to_long(b"Welcome To PaluCTF!")
    MOD = 2**418
    R = R ^ ((R - 60) >> 24)
    R = R ^ ((R - 60) << 88)
    R ^= key
    R = (-R * R * 2024) % MOD
    R = (R * key) % MOD
    return R
answer = 0x2E441F765514CCA89173554726494D37E9FBE774B6F807BC5F6E71117530CE3D7DB5F70554C03CD9055F4E42969600904DF1F4DB8

s=Solver()
key=BitVec('key',418)
s.add(enc(key)==answer)
s.check()
res=s.model()
#print(res)
flag=long_to_bytes(res[key].as_long())
print(flag)

所以得到flag,就是

3、O2:
 

1、经典查壳:
 

32位elf程序

2、IDA启动:

可以发现ELF的头文件报错了。所以我们找一个32位ELF文件修改一下

下面是正常的:

所以我们修改一下,将第五个01修改为02,意思是这是一个64位的程序

1:32位程序

2:64位程序

成功进入主函数

但是我的main函数不能反编译,显示编译错误,这个我没查出来是什么原因,网上教程说可以通过动调解决一下,就是找到出错的地址,然后F5反编译一下,然后返回就可以F5了

试了试果然可以

得到了主函数

__int64 __fastcall main(int a1, char **a2, char **a3)
{
  __int64 v3; // rax
  std::ostream *v4; // rbx
  __int64 v5; // rax
  _BYTE *v6; // rbp
  char v7; // si
  std::ostream *v8; // rax
  __int64 v9; // rdx
  size_t v10; // rbx
  __int64 v11; // r13
  __int64 v12; // rdx
  __int64 v13; // rbx
  __int64 v14; // rdx
  __int64 (__fastcall *v16)(); // rax
  _BYTE v17[32]; // [rsp+0h] [rbp-68h] BYREF
  void *s1; // [rsp+20h] [rbp-48h] BYREF
  size_t n; // [rsp+28h] [rbp-40h]

  v3 = std::operator<<<std::char_traits<char>>(&std::cout, "Hello! ", a3);
  v4 = (std::ostream *)std::__ostream_insert<char,std::char_traits<char>>(v3, qword_607AF3F30340, qword_607AF3F30348);
  v5 = *(_QWORD *)(*(_QWORD *)v4 - 24LL);
  v6 = *(_BYTE **)((char *)v4 + v5 + 240);
  if ( !v6 )
    std::__throw_bad_cast();
  if ( v6[56] )
  {
    v7 = v6[67];
  }
  else
  {
    std::ctype<char>::_M_widen_init(*(_QWORD *)((char *)v4 + v5 + 240));
    v7 = 10;
    v16 = *(__int64 (__fastcall **)())(*(_QWORD *)v6 + 48LL);
    if ( v16 != std::ctype<char>::do_widen )
      v7 = ((__int64 (__fastcall *)(_BYTE *, __int64))v16)(v6, 10LL);
  }
  v8 = (std::ostream *)std::ostream::put(v4, v7);
  std::ostream::flush(v8);
  std::operator<<<std::char_traits<char>>(&std::cout, "Check Flag:", v9);
  std::operator>><char>(&std::cin, &unk_607AF3F30360);
  sub_607AF3F2D5C0(v17, &unk_607AF3F30360, &qword_607AF3F30340);
  sub_607AF3F2D6C0((__int64)&s1, (__int64)v17);
  v10 = n;
  v11 = qword_607AF3F30388;
  v12 = qword_607AF3F30388;
  if ( n <= qword_607AF3F30388 )
    v12 = n;
  if ( v12 && memcmp(s1, obj, v12) || (v13 = v10 - v11, v13 > 0x7FFFFFFF) || v13 < (__int64)0xFFFFFFFF80000000LL )
  {
    std::string::_M_dispose(&s1);
    goto LABEL_14;
  }
  std::string::_M_dispose(&s1);
  if ( (_DWORD)v13 )
  {
LABEL_14:
    std::operator<<<std::char_traits<char>>(&std::cout, "Wrong!", v14);
    goto LABEL_12;
  }
  std::operator<<<std::char_traits<char>>(&std::cout, "Right!", v14);
LABEL_12:
  std::string::_M_dispose(v17);
  return 0LL;
}

观察一下,我们可以发现 sub_607AF3F2D5C0(v17, &unk_607AF3F30360, &qword_607AF3F30340);这个就是加密函数

__int64 *__fastcall sub_607AF3F2D5C0(__int64 *a1, char **a2, _QWORD *a3)
{
  __int64 *v3; // rcx
  char *v4; // r12
  __int64 v6; // r15
  int v7; // ebp
  __int64 v8; // rax
  unsigned __int64 v9; // r13
  unsigned __int64 v10; // rax
  int v12; // [rsp+Ch] [rbp-4Ch]
  char *v13; // [rsp+10h] [rbp-48h]

  v3 = a1 + 2;
  *((_BYTE *)a1 + 16) = 0;
  *a1 = (__int64)(a1 + 2);
  v4 = *a2;
  a1[1] = 0LL;
  v13 = &a2[1][(_QWORD)v4];
  if ( v4 != v13 )
  {
    v6 = 0LL;
    v7 = 0;
    while ( 1 )
    {
      v9 = v6 + 1;
      v12 = (*v4 + *(char *)(*a3 + v7)) % 128;
      v10 = a1 + 2 == v3 ? 15LL : a1[2];
      if ( v10 < v9 )
      {
        std::string::_M_mutate(a1, v6, 0LL, 0LL, 1LL);
        v3 = (__int64 *)*a1;
      }
      ++v4;
      *((_BYTE *)v3 + v6) = v12;
      v8 = *a1;
      a1[1] = v9;
      *(_BYTE *)(v8 + v9) = 0;
      v7 = (unsigned __int64)(v7 + 1) % a3[1];
      if ( v13 == v4 )
        break;
      v6 = a1[1];
      v3 = (__int64 *)*a1;
    }
  }
  return a1;
}

v12 = (*v4 + *(char *)(*a3 + v7)) % 128;这一句就是主要的加密函数

可以简单分析一下各个变量的意思

v4就是我们输入的值,也就是flag(enc)

a3就是传入的key,我们可以找到他

也就是

v12=(enc[i]+key[i])%128

所以我们进行解密


enc=bytes.fromhex("364d4d5c3e387e00421c597a0a7302144d5b70087e064619567336297d151f56770a7935424f2a780643")
key=b'PaluCTF'
flag=''
for i in range(len(enc)):
    flag+=chr((enc[i]-key[i%7])%128)

print(flag)

解释一下代码,方便下次自己使用

bytes.fromhex是将连续的两个数转换为hex

hex_string = '616263'
byte_array = bytearray.fromhex(hex_string)
print(byte_array)

bytearray(b'abc')

还有一种解法就是爆破,学习一下大佬的答案


from string import printable

enc = bytearray.fromhex("364d4d5c3e387e00421c597a0a7302144d5b70087e064619567336297d151f56770a7935424f2a780643")

key = "PaluCTF"

for i in range(len(enc)):
    for c in printable:
        if (ord(c) + ord(key[i % len(key)])) % 128 == enc[i]:
            print(c, end="")
            break

这里的from string import printable

# import string library function  
import string  
    
# Storing the sets of punctuation, 
# digits, ascii_letters and whitespace 
# in variable result  
result = string.printable 
    
# Printing the set of values  
print(result) 

0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+, -./:;<=>?@[\]^_`{|}~ 

返回一系列可打印的值,所以我们使用双重循环进行爆破就ok了

答案是

flag{d80a0d76-23af-486e-a0bc-43a463eac552}

剩下两道不是很懂,再分析分析再写吧,帕鲁杯收获还是挺大的,对于加密算法也是又了解到一种

  • 24
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值