2020第二届网鼎杯青龙组Reverse signal

signal

Start

无壳,直接拖进IDA,F5
在这里插入图片描述
qmemcpy()内存复制,将unk_403040的内容复制到v4的地址下
跟进unk_403040看到一堆数据,整理后得到

  [0Ah,  4,10h,  8,  3,  5,  1,  4,20h,  8,  5,  3,  1,  3,  2,  8,0Bh,  1,0Ch,  8,  4,  4,  1,  5,  3,  8,  3,21h ,  1,0Bh,  8,0Bh,  1,  4,  9,  8,  3,20h,  1,  2,51h ,  8,  4,24h ,  1,0Ch,  8,0Bh,  1,  5,  2,  8,  2,25h ,  1,  2,36h ,  8,  4,41h ,  1,  2,20h,  8,  5,  1,  1,  5,  3,  8,  2,25h ,  1,  4,  9,  8,  3,20h,  1,  2,41h ,  8,0Ch,  1,  7,22h ,  7,3Fh ,  7,34h ,  7,32h ,  7,72h ,  7,33h ,  7,18h,  7,A7h,FFh,FFh,FFh,  7,31h ,  7,F1h,FFh,FFh,FFh,  7,28h ,  7,84h,FFh,FFh,FFh,  7,C1h,FFh,FFh,FFh,  7,1Eh,  7,7Ah]

之后将该数据传入vm_operad函数中,并且发现该数组的长度为114.

跟进vm_operad函数
看到关键语句switch_case, 外面的v4传入后变成了a1,通过a1下的不同值去执行对应的case

发现case 7 是对输入的字符串进行验证,然后发现v10作为索引,初始值为0,也就是说第一次传入的值为0Ah, 对应case10

case 7:
  if ( v4[v8] != a1[v10 + 1] )
    {
      printf("what a shame...");
      exit(0);
    }
    ++v8;
    v10 += 2;
    break;

该条件下调用了read函数,参数为v3

case 10:
    read(v3);
    ++v10;
    break;

跟进去可以发现,该函数就是输入函数,并将值存储到v3中,长度为15

size_t __cdecl read(char *a1)
{
  size_t result; // eax

  printf("string:");
  scanf("%s", a1);
  result = strlen(a1);
  if ( result != 15 )
  {
    puts("WRONG!\n");
    exit(0);
  }
  return result;
}

之后对case进行分析,可将case分为3类,
第一类:验证case,

case 7:
	if ( v4[v8] != a1[v10 + 1] )
    {
      printf("what a shame...");
      exit(0);
    }
    ++v8;
    v10 += 2;
    break;

这里a1的值是写死的,说明是对v4的值的进行验证,也就是说输入的字符串经过一系列操作之后存到了v4

第二类:处理字符

case 2:
  v5 = a1[v10 + 1] + v3[v9];
  v10 += 2;
  break;
case 3:
  v5 = v3[v9] - LOBYTE(a1[v10 + 1]);
  v10 += 2;
  break;
case 4:
  v5 = a1[v10 + 1] ^ v3[v9];
  v10 += 2;
  break;
case 5:
  v5 = a1[v10 + 1] * v3[v9];
  v10 += 2;
  break;
case 11:
  v5 = v3[v9] - 1;
  ++v10;
  break;
case 12:
  v5 = v3[v9] + 1;
  ++v10;
  break;

第三类:其它, 通过之前分析可以知道那段写死的数据并不存在0x06,并且可以发现case1就是移动到下一个字符,那么就可以以1作为分隔符,对那段写死的数据进行整理。case8就是将运算后的值赋给v3,因为第一次运算后的值是以v5当中间变量

case 1:
  v4[v7] = v5;
  ++v10;
  ++v7;
  ++v9;  
  break;
case 6:
  ++v10;
  break;
case 8:
  v3[v6] = v5;
  ++v10;
  ++v6;
  break;

可以发现7出现了15次,并且是对数据进行验证,那么7后面跟着的就是加密后的字符,
再次整理后:

0Ah,  
4,10h,  8,  3,  5,  1,  
4,20h,  8,  5,  3,  1, 
3,  2,  8,0Bh,  1
,0Ch,  8,  4,  4,  1,  
5,  3,  8,  3,21h ,  1,
0Bh,  8,0Bh,  1, 
4,  9,  8,  3,20h,  1, 
2,51h ,  8,  4,24h ,  1,
0Ch,  8,0Bh,  1, 
5,  2,  8,  2,25h ,  1,
2,36h ,  8,  4,41h ,  1,
2,20h,  8,  5,  1,  1,
5,  3,  8,  2,25h ,  1,
4,  9,  8,  3,20h,  1, 
2,41h ,  8,0Ch,  1, 
7,22h ,  
7,3Fh , 
7,34h ,  
7,32h ,  
7,72h , 
7,33h , 
7,18h,  
7,A7h,FFh,FFh,FFh, 
7,31h , 
7,F1h,FFh,FFh,FFh,
7,28h , 
7,84h,FFh,FFh,FFh, 
7,C1h,FFh,FFh,FFh, 
7,1Eh,  
7,7Ah

刚好以1结尾的有15个,7开头的有15个

分析case发现,在加密的case中,v5是作为中间字符的存在,v9v3的下标,v6也是v3的下标,所以v9v6这里直接视为同一个,v7v4的下标,但是由于是顺序加密的所以v7v6v9的值是相同的

这里会存在0xFFFFFF开头的数据,这里只是自动填充,参与运算的只有低八位,而且v5的宽度也只是八位,所以不影响。
将加密后的数据提取出来得到:

0x22, 0x3F, 0x34, 0x32, 0x72, 0x33, 0x18, 0xA7, 0x31, 0xF1, 0x28, 0x84, 0xC1, 0x1E, 0x7A

将每一个字符要加密的casekey分开:

data = [[4, 0x10, 8, 3, 5, 1]
    , [4, 0x20, 8, 5, 3, 1]
    , [3, 2, 8, 0x0B, 1]
    , [0x0C, 8, 4, 4, 1]
    , [5, 3, 8, 3, 0x21, 1]
    , [0x0B, 8, 0x0B, 1]
    , [4, 9, 8, 3, 0x20, 1]
    , [2, 0x51, 8, 4, 0x24, 1]
    , [0x0C, 8, 0x0B, 1]
    , [5, 2, 8, 2, 0x25, 1]
    , [2, 0x36, 8, 4, 0x41, 1]
    , [2, 0x20, 8, 5, 1, 1]
    , [5, 3, 8, 2, 0x25, 1]
    , [4, 9, 8, 3, 0x20, 1]
    , [2, 0x41, 8, 0x0C, 1]]

尝试了一下,发现逆向写的话比较麻烦,所以这里直接遍历去试出flag

exp.py
encrypts = [0x22, 0x3F, 0x34, 0x32, 0x72, 0x33, 0x18, 0xA7, 0x31, 0xF1, 0x28, 0x84, 0xC1, 0x1E, 0x7A]

data = [[4, 0x10, 8, 3, 5, 1]
    , [4, 0x20, 8, 5, 3, 1]
    , [3, 2, 8, 0x0B, 1]
    , [0x0C, 8, 4, 4, 1]
    , [5, 3, 8, 3, 0x21, 1]
    , [0x0B, 8, 0x0B, 1]
    , [4, 9, 8, 3, 0x20, 1]
    , [2, 0x51, 8, 4, 0x24, 1]
    , [0x0C, 8, 0x0B, 1]
    , [5, 2, 8, 2, 0x25, 1]
    , [2, 0x36, 8, 4, 0x41, 1]
    , [2, 0x20, 8, 5, 1, 1]
    , [5, 3, 8, 2, 0x25, 1]
    , [4, 9, 8, 3, 0x20, 1]
    , [2, 0x41, 8, 0x0C, 1]]

for_each = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u',
            'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0']


def encrypted(c, li):
    # print(li)
    x = 0
    while x < len(li):
        if li[x] == 2:
            c = c + li[x + 1]
            x += 2
        elif li[x] == 3:
            c = c - li[x + 1]
            x += 2
        elif li[x] == 4:
            c = c ^ li[x + 1]
            x += 2
        elif li[x] == 5:
            c = c * li[x + 1]
            x += 2
        elif li[x] == 11:
            c = c - 1
            x += 1
        elif li[x] == 12:
            c = c + 1
            x += 1
        elif li[x] == 8:
            x += 1
        elif li[x] == 1:
            break
    res = c
    return res


if __name__ == '__main__':
    flag = ''
    for x in range(len(encrypts)):
        for i in for_each:
            tmp = encrypted(ord(i), data[x])
            if tmp == encrypts[x]:
                flag += i
                break
            else:
                continue
    print('flag{%s}' % flag)

# flag{757515121f3d478}
ps:因为写代码比较菜,所以其实比赛的时候是手动算的,代码是比完赛才写的
这个题不难,最开始感觉难是因为它循环了一百多次,但仔细分析后发现是有规律的也就不难了。

End

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值