GACTF2020 WannaUp

  • 要点:任何求余的操作都指明了一个点:限制了一个范围,从而限制了用到它的地方的范围

这道题的计算由 某一处的异或爆破+循环移位操作+异或运算 组成。输入flag后生成的flag文件里的就是flag

 

定位关键代码的几种方法

1.ida查找字符串,发现flag.bin和flag.txt。再使用x查找到了函数sub_402280

2.ida查看import表 发现如CryptDecrypt GetWindowTextA 这类API,同样能定位到函数sub_402280

3.运行程序,一个gui界面,动态调试软件下断获取信息的api(r如GetDlgItemTextA GetWindowTextA GetWindowTextW)
总而言之,找关键api 函数

程序分析如下 函数sub_402280

          len = strlen((const char *)&Paint);
          if ( len >= 6 )
          {
            v37 = SLOBYTE(Paint.fErase) % 7;    // 余7 所以要长度>6 这取到的总是第5位的值 再之后拿这个值求余(动态调试的结果)
            v38 = 1;
            for ( i = 2; i < v37; ++i )
              v38 *= (_BYTE)i;                  // v38乘i i开始为2 即为阶乘
            v40 = 0;
            if ( (unsigned int)len >= 0x40 )
            {
              v41 = _mm_cvtsi32_si128(v38);     // v38的低128位赋值给一个32bits的整数
              v42 = _mm_unpacklo_epi8(v41, v41);// v41的高8位相互交错,低8位舍去
              v43 = _mm_shuffle_epi32(_mm_unpacklo_epi16(v42, v42), 0);// 128位分为4份 4*32位
              do
              {
                *(__int128 *)((char *)&input + v40) = (__int128)_mm_xor_si128(
                                                                  *(__m128i *)((char *)&Paint.hdc + v40),
                                                                  v43);// 异或运算
                *(__int128 *)((char *)&v59 + v40) = (__int128)_mm_xor_si128(
                                                                *(__m128i *)((char *)&Paint.rcPaint.right + v40),
                                                                v43);
                *(__int128 *)((char *)&v60 + v40) = (__int128)_mm_xor_si128(*(__m128i *)&Paint.rgbReserved[v40], v43);
                *(__m128i *)((char *)&v61 + v40) = _mm_xor_si128(*(__m128i *)&Paint.rgbReserved[v40 + 16], v43);
                v40 += 64;
              }
              while ( v40 < len - len % 64 );
            }
            for ( ; v40 < len; ++v40 )
              *((_BYTE *)&input + v40) = v38 ^ *((_BYTE *)&Paint.hdc + v40);// 算出来的v38的值 异或 。得出结果
            *((_BYTE *)&input + v40) = 0;
            v44 = 0;
            do
            {
              *((_BYTE *)&input + v44) = __ROL1__(*((_BYTE *)&input + v44) ^ byte_420AE0[v44], v44);// ANNAWGALFYBKVIAHMXTFCAACLAAAAYK
              ++v44;                            // 异或计算->循环左移
            }
            while ( v44 < len );
            v45 = &input;
            v46 = (char *)&unk_4278D8;          // 0x4E, 0xAE, 0x61, 0xBA, 0xE4, 0x2B, 0x55, 0xAA, 0x59, 0xFC, 0x4D, 0x02, 0x17, 0x6B, 0x13, 0xA1, 0x41, 0xFE, 0x35, 0x0B, 0xB4, 0x0B, 0x52, 0x2F, 0x46, 0xCC, 0x35, 0x82, 0xE5, 0x88, 0x50
            v47 = 27;
            while ( *(_DWORD *)v45 == *(_DWORD *)v46 )
            {
              v45 = (__int128 *)((char *)v45 + 4);
              v46 += 4;
              v48 = v47 < 4;
              v47 -= 4;
              if ( v48 )
              {
                if ( *(_WORD *)v45 == *(_WORD *)v46 && *((_BYTE *)v45 + 2) == v46[2] )// 计算结果与v46比较
                {
                  MessageBoxA(hWndParent, "orz", "orz", 0);
                  sub_401F20((BYTE *)&Paint);   // 成功后会与flag.bin结合生成flag.txt文件
                }
                return 0;

解密脚本

def ROR(i,index):
    tmp = bin(i)[2:].rjust(8,"0")
    """
    >>>bin(10)
    '0b1010' 
    所以从[2:]开始
    
    rjust(width[,fillchar=None])
    width -- 指定字符串长度,要在Unicode字符串长度范围内。
    fillchar -- 可选参数,指定的填充字符,默认为空格
    .rjust(8,"0")长度填充至8位,填充字符为0 
    """
    for _ in range(index):   # 模拟循环右移
        tmp = tmp[-1] + tmp[:-1]   #取最后一位 取从第1位到最后一位前一位,拼接起来。相当于右移了一位。
    return int(tmp, 2)
    """
    1.模拟循环右移(ROR)  左移则是ROL
    取最后一位 取从第1位到最后一位前一位,拼接起来。相当于右移了一位。
    2.右移几位,就是把1改成相应的值。在函数中index作为循环次数,决定了右移的位数
    3.同理,循环左移(ROL)
    取从第2位到最后一位  取第一位,拼接起来。 相当于左移了一位
    class int(x, base=10)
    x -- 字符串或数字。使用后边的参数时,x要以字符串形式“”
    base -- 进制数,默认十进制。说明前边的内容是什么进制数。最终返回的都是十进制
    >>>int("12",16)
    18
    1   6进制形式的12的int为18(十进制)
    
    """


def ROL(i,index):
    tmp = bin(i)[2:].rjust(8, "0")
    for _ in range(index):
        tmp = tmp[1:] + tmp[0]
    return int(tmp, 2)


str_list1 = [ord(c) for c in "ANNAWGALFYBKVIAHMXTFCAACLAAAAYK"]
# print(str_list1)
str_list2 = [0x4E, 0xAE, 0x61, 0xBA, 0xE4, 0x2B, 0x55, 0xAA,
            0x59, 0xFC, 0x4D, 0x02, 0x17, 0x6B, 0x13, 0xA1,
            0x41, 0xFE, 0x35, 0x0B, 0xB4, 0x0B, 0x52, 0x2F,
            0x46, 0xCC, 0x35, 0x82, 0xE5, 0x88, 0x50]
print(len(str_list2))
tmp1 = []
flag1 = []
"""
正向思路:
    1.取input[4]的值 
    2.求余,求余的值作为循环的次数
    3.一个循环求值,用于与input异或
    3.异或数组
    4.循环左移
逆向思路:
    1.循环右移
    2.异或数组
    3.爆破出正确的那个用于与input异或的值
    4.异或求出flag
"""
for i in range(len(str_list2)):
    #tmp1.append(ROR(str_list2[i],i))
    flag1.append((ROR(str_list2[i],i) ^ str_list1[i]))

print(flag1)
t = 1
for i in range(2,5):  # 其实就是n! 阶层?

    t = t*i
print(t) # 1 2 6 24 120
num_list = [1,2,6,24,120]
for p in range(0x100):  # 可打印字符串与
    for n in num_list:
        if n ^ p == flag1[4]:
            print(n)
flag = []
for f in flag1:
    flag.append(chr(f ^ 0x78))
print("".join(flag))

做题时的几个解决问题:

  1. 循环右移的实现
  2. 如何爆破出0x78(这是要点,围绕余的要点)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值