[ctf.show.reverse] 太牛杯 easy

貌似很复杂的一个题一点点弄明白。

程序上来就给了加密方式,但由于里边用了and运算,所以本身是不可逆的,需要正向爆破。

int __thiscall sub_401080(char *this)
{
  char v2; // al
  char *v3; // edi
  char *v4; // ebx
  int v5; // edx
  int v6; // ecx
  int v7; // eax
  int v8; // esi
  int v9; // eax
  int result; // eax
  __int128 v11[4]; // [esp+4h] [ebp-44h] BYREF

  memset(v11, 0, sizeof(v11));
  v2 = *this;
  if ( *this )
  {
    v3 = this + 2;                              // 第3个
    v4 = (char *)v11 + 4;
    do
    {
      v5 = *(v3 - 1);
      v3 += 5;
      v4 += 16;
      v6 = (v5 >> 6) | (4 * v2);
      v7 = *(v3 - 5);
      *((_DWORD *)v4 - 5) = v6;
      LOBYTE(v6) = *(v3 - 4);
      *((_DWORD *)v4 - 4) = (v7 >> 4) | (16 * (v5 & 0x3F));
      v8 = ((char)v6 >> 2) | ((v7 & 0xF) << 6);
      v9 = ((v6 & 3) << 8) | *(v3 - 3);
      *((_DWORD *)v4 - 3) = v8;
      *((_DWORD *)v4 - 2) = v9;
      v2 = *(v3 - 2);
    }
    while ( v2 );
  }
  result = 0;
  v11[0] = (__int128)_mm_and_si128(
                       _mm_andnot_si128(
                         _mm_and_si128((__m128i)xmmword_4021D0, (__m128i)v11[0]),
                         (__m128i)xmmword_4021E0),
                       _mm_xor_si128((__m128i)xmmword_4021D0, (__m128i)v11[0]));
  v11[1] = (__int128)_mm_and_si128(
                       _mm_andnot_si128(
                         _mm_and_si128((__m128i)xmmword_4021D0, (__m128i)v11[1]),
                         (__m128i)xmmword_4021E0),
                       _mm_xor_si128((__m128i)xmmword_4021D0, (__m128i)v11[1]));
  v11[2] = (__int128)_mm_and_si128(
                       _mm_andnot_si128(
                         _mm_and_si128((__m128i)xmmword_4021D0, (__m128i)v11[2]),
                         (__m128i)xmmword_4021E0),
                       _mm_xor_si128((__m128i)xmmword_4021D0, (__m128i)v11[2]));
  v11[3] = (__int128)_mm_and_si128(
                       _mm_andnot_si128(
                         _mm_and_si128((__m128i)xmmword_4021D0, (__m128i)v11[3]),
                         (__m128i)xmmword_4021E0),
                       _mm_xor_si128((__m128i)xmmword_4021D0, (__m128i)v11[3]));
  do
  {
    if ( *(_DWORD *)((char *)v11 + result * 4) != dword_402188[result] )
    {
      sub_401020("wrong\n", v11[0]);
      exit(0);
    }
    ++result;
  }
  while ( result < 16 );
  return result * 4;
}

前一半是把20年字符分成4组,每组5个放到4个32位整形里,后边一半是对其作and,andnot,xor操作,sse的128位运算,由于5个字符爆破基本是不可能的,所以需要找其实的关系。

从前边5变4可以看出,变后的整形使用了10位,一共40位正好存了5个字符,而后边的and等操作都是位操作,没改变位的位置。所以可以从结果里找出字符对应的小段来比较判断。这样每一个字符爆破1次就完成了。

u32 = lambda a: a[0] |(a[1]<<8) | (a[2]<<16) | (a[3]<<24)
p32 = lambda a: bytes([a>>i*8 & 0xff  for i in range(4)])
p128 = lambda a: bytes([a>>i*8 & 0xff  for i in range(16)])  
mm_and = lambda a,b: bytes([a[i] & b[i] for i in range(16)])
mm_andnot = lambda a,b: bytes([(0xff - a[i]) & b[i] for i in range(16)])
mm_xor = lambda a,b: bytes([a[i]^b[i] for i in range(16)])   

x_4021D0 = p128(0x20000000200000002000000020)
x_4021E0 = p128(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)
dword_402188 = [0x1a9, 0x233, 0x179, 0x17f, 0x1a5, 0x2c6, 0x137, 0x358, 
                 0xe1, 0x305, 0x3ec, 0x153, 0x15d, 0x247, 0x17b, 0x201]
   
#仅说明存储位置,不使用
def enc(a):
    v4[0] = (a[0]<<2) | (a[1]>>6) 
    v4[1] = ((a[1]*0x3f)<<4) | (a[2]>>4) 
    v4[2] = ((a[2]&0xf)<<6) | (a[3]>>2) 
    v4[3] = ((a[3]&0x3)<<8) | a[4]  
    return p32(v4[0])+p32(v4[1])+p32(v4[2])+p32(v4[3])

letter = b'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_!'
for k in range(4):
    for i in letter:
        d = p128(i<<2)
        v = mm_and(mm_andnot( mm_and(x_4021D0, d), x_4021E0), mm_xor(x_4021D0, d))
        if (u32(v[0:4])>>2)&0xff == (dword_402188[k*4+0]>>2)&0xff :
            print(chr(i), end='')   #0
            break

    for i in letter:
        d = p128(((i>>6)&0x3) | ((i&0x3f)<<(32+4)))
        v = mm_and(mm_andnot( mm_and(x_4021D0, d), x_4021E0), mm_xor(x_4021D0, d))
        if ( ( u32(v[0:4])&3 ) << 6 )|( ( u32(v[4:8] ) >> 4 ) )  == ( ( dword_402188[k*4+0]&3 ) << 6 )| (( dword_402188[k*4+1]&0x3f0 ) >> 4) :
            print(chr(i), end='')   #1
            break

    for i in letter:
        d = p128( (i>>4<<32) | (i&0xf)<<(64+6) )
        v = mm_and(mm_andnot( mm_and(x_4021D0, d), x_4021E0), mm_xor(x_4021D0, d))
        if ( ( u32(v[4:8])&0xf ) << 4 )|( ( u32(v[8:12] ) >> 6 ) )  == ( ( dword_402188[k*4+1]&0xf ) << 4 )| (( dword_402188[k*4+2] ) >> 6) :
            print(chr(i), end='')  #2
            break

    for i in letter:
        d = p128( (i>>2<<64) | (i&0x3)<<(96+8) )
        v = mm_and(mm_andnot( mm_and(x_4021D0, d), x_4021E0), mm_xor(x_4021D0, d))
        if ( ( u32(v[8:12])&0x3f ) << 2 )|( ( u32(v[12:16] ) >> 8 ) )  == ( ( dword_402188[k*4+2]&0x3f ) << 2 )| (( dword_402188[k*4+3] ) >> 8) :
            print(chr(i), end='')  #3
            break

    for i in letter:
        d = p128(i<<96)
        v = mm_and(mm_andnot( mm_and(x_4021D0, d), x_4021E0), mm_xor(x_4021D0, d))
        if (u32(v[12:16]))&0xff == (dword_402188[k*4+3])&0xff :
            print(chr(i), end='')  #4
            break

#ba5e_and_x0r_1s_fun!
#ctfshow{ba5e_and_x0r_1s_fun!}    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值