看雪CTF逆向部分WP

第一题:神秘来信

签到题,没什么好说的

if ( v4 < 7 && v14 == 51 && v13 == 53 && v12 == 51 && v11 + v10 + v9 == 149 )

判断成立即可

401353

第六题:消失的岛屿

main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  uint8_t bindata; // [esp+11h] [ebp-3Fh]
  const char *v6; // [esp+48h] [ebp-8h]
  char *v7; // [esp+4Ch] [ebp-4h]

  __main();
  printf("please enter Serial:");
  scanf(" %s", &bindata);
  if ( strlen((const char *)&bindata) > 0x31 )
    puts("error");
  v7 = (char *)calloc(1u, 0x400u);
  v3 = strlen((const char *)&bindata);
  base64_encode(&bindata, v7, v3);
  v6 = "!NGV%,$h1f4S3%2P(hkQ94==";
  if ( !strcmp("!NGV%,$h1f4S3%2P(hkQ94==", v7) )
    puts("Success");
  else
    puts("Please Try Again");
  free(v7);
  system("pause");
  return 0;
}

逻辑非常简单,将输入base64_encode之后与常量比较,看一眼base64_encode

没什么问题,很base64,进入charEncrypt

char __cdecl charEncrypt(int data)
{
  int dataa; // [esp+18h] [ebp+8h]

  dataa = aTuvwxtulmnopqr[data];
  if ( dataa > 0x40 && dataa <= 0x5A )
    return 0x9B - dataa;
  if ( dataa > 0x60 && dataa <= 0x7A )
    return dataa - 0x40;
  if ( dataa > 0x2F && dataa <= 0x39 )
    return dataa + 0x32;
  if ( dataa == 0x2B )
    return 0x77;
  if ( dataa == 0x2F )
    dataa = 0x79;
  return dataa;
}

拿到table 'tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/'

看到table还经过了简单的变换,直接复制出来然后解base64就搞定了

import string
import base64

def fun(dataa):
    if dataa > 0x40 and dataa <= 0x5A:
        dataa = 0x9B - dataa              
    elif dataa > 0x60 and dataa <= 0x7A:
        dataa = dataa - 0x40
    elif dataa > 0x2F and dataa <= 0x39:
        dataa = dataa + 0x32                        
    elif dataa == 0x2B:
        dataa = 0x77
    elif dataa == 0x2F:
        dataa = 0x79
    return dataa

base64_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
data = 'tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/'
table = ''
cipher_text = '!NGV%,$h1f4S3%2P(hkQ94=='
for i in data:
    table += chr(fun(ord(i)))
print table

key = base64.b64decode(cipher_text.translate(string.maketrans(table,base64_table)))
print key

KanXue2019ctf_st

第四题:达芬奇密码

ida32载入,找到入口函数

int __thiscall sub_401EA0(CWnd *this)
{
  CWnd *v1; // esi
  int v2; // eax
  WCHAR String; // [esp+Ch] [ebp-310h]
  char v5; // [esp+Eh] [ebp-30Eh]
  char v6; // [esp+20Ch] [ebp-110h]
  char v7; // [esp+20Dh] [ebp-10Fh]
  DWORD v8; // [esp+30Ch] [ebp-10h]
  CWnd *v9; // [esp+310h] [ebp-Ch]
  int v10; // [esp+314h] [ebp-8h]
  DWORD flOldProtect; // [esp+318h] [ebp-4h]

  v1 = this;
  v9 = this;
  String = 0;
  memset(&v5, 0, 0x1FEu);
  v6 = 0;
  memset(&v7, 0, 0xFFu);
  CWnd::GetDlgItemTextW(v1, 1000, &String, 20);
  if ( wcslen(&String) == 16 )
  {
    v2 = 0;
    while ( !(*(&String + v2) & 0xFF00) )
    {
      *(&v6 + v2) = *((_BYTE *)&String + 2 * v2);
      if ( ++v2 >= 16 )
      {
        v8 = 64;
        flOldProtect = 0;
        VirtualProtect(sub_4010E0, 0xD17u, 0x40u, &flOldProtect);
        if ( GetLastError() )
          return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
        qmemcpy(sub_4010E0, &byte_5647B8, 0x330u);
        VirtualProtect(sub_4010E0, 0xD17u, flOldProtect, &v8);
        if ( !GetLastError() )
        {
          v10 = 0;
          v10 = sub_4010E0();
          if ( v10 == 1 )
            return CWnd::MessageBoxW(v9, L"Congratulations! You are right!", 0, 0);
        }
        v1 = v9;
        return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
      }
    }
  }
  return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
}

程序逻辑十分简单,首先判断输入字符串长度是否为十六,之后将宽字符转换为字符然后调 VirtualProtect这个API,之后qmemcpy(sub_4010E0, &byte_5647B8, 0x330u);将一段字符复制到sub_4010E0上然后继续用调 VirtualProtect这个API,进入if语句,调用sub_4010E0函数,返回值v10 = 1时正确

看似十分复杂,确实非常复杂,静态分析是不可能的,这辈子都不可能了,果断OD动态开调,发现真正对字符串加密就只有if语句中的sub_4010E0函数,即od中的TheDaVin.002A10E0

002A1FAA  |.  8D85 F0FEFFFF lea eax,[local.68]
002A1FB0      50            push eax
002A1FB1      E8 2AF1FFFF   call TheDaVin.002A10E0
002A1FB6  |.  8945 F8       mov [local.2],eax

菜鸡只能逐行看汇编代码,再转换为人类能看懂的语言

test = '???? ???? ???? ????' 
assest (test.len == 16)
table = [0x16,0x96,0x8c,0xe3,0x81,0x98,0x6e,0x64,
         0x84,0x08,0xdc,0x81,0xbe,0x4d,0x48,0x4f]

for i in range(8):
    test[i] ^ table[i] -> [008FEFF4+i]
    test[8+i] ^ table[8+i] -> [008EF00C+i]
    
&arr1 = 008FEFF4
&arr2 = 008EF00C
assest(arr1.len == 8 and arr2.len == 8)
008FF03C: 00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00
ecx = 0
edi = 008FEFFC
edx = 008FF03C
bx = 0
for i in arr1:
    for j in arr1:
        ax = i*j
        ax += bx
        (ax & 0x00ff) -> [edi + ecx]
        (ax >> 8) -> [edi + 0x08]
        bx = [edi + 0x08]
        ecx++
    ecx = 0
    eax = 0
    for i in range(9):
        eax += [edi + i] + [edx + i]
        (eax & 0x00ff) -> [edx+i]
        eax >>= 8
    ecx = 0
    edi++
    edx++


ecx = 0
edi = 008FEFFC
edx = 008FF050
bx = 0
for i in arr2:
    for j in arr2:
        ax = i*j
        ax += bx
        (ax & 0x00ff) -> [edi + ecx]
        (ax >> 8) -> [edi + 0x08]
        bx = [edi + 0x08]
        ecx++
    ecx = 0
    eax = 0
    for i in range(9):
        eax += [edi + i] + [edx + i]
        (eax & 0x00ff) -> [edx+i]
        eax >>= 8
    ecx = 0
    edi++
    edx++

addr = 008FF050
ss = 008FF014
eax = 0
for i in range(0x11):
    dx = [addr + i]
    dx *= 0x07
    eax += dx
    (eax & 0x00ff) -> [ss + i]
    eax >>= 8
eax -> [addr+0x11]

addl = 008FF03C

sa = 008FF028
dl = 0
for i in range(0x11):
    cx = [ss + i]
    si = [addl + i]
    if i > 0:
        assest(cx == si)
    if i = 0:
        assest(si == cx + 8)
#   si -= cx
#   #cx = dl
#   #si -= cx
#   ecx = si
#   #cl -> [sa + i]
#   (si & 0x0f) -> [sa + i]
#   assest (ecx >= 0)
#   if ecx < 0:
#       dl = 1
#   else:
#       dl = 0

# assest ([sa] == 0x08)
# for i in range(1,11):
#   assest ([sa+i] == 0x00)


仔细一看wtf,就是解一条方程

x^2 = 7 * y^2 + 8

嗯,双曲线方程,求64位整数解

将解和table异或一下就可以得到flag了(flag = x<<64 + y,小端序

作为一个数学蒻鶸,这怎么解得出来呢!闲着没事干搜了一下椭圆曲线,发现可以根据小整数解组推出大整数解组?果断爆破

for i in range(0xffff):
    for j in range(0xffff):
        ax = i
        bx = j 
        ax *= ax
        bx *= bx
        bx *= 7
        if ax-bx == 8:
            print hex(i),hex(j),hex(ax),hex(bx)

爆破得到这么几组整数解

0x6 0x2 0x24 0x1c
0x5a 0x22 0x1fa4 0x1f9c
0x59a 0x21e 0x1f60a4 0x1f609c
0x5946 0x21be 0x1f21bf24 0x1f21bf1c

蒻鶸怎么可能看得懂数学原理呢,自己动手找规律完事,依据两组小整数解组就可以理论上求出无穷大的整数解组

求解代码

x1 = 0x6
y1 = 0x2
x2 = 0x5a
y2 = 0x22

print hex(x1),hex(y1)
print hex(x2),hex(y2)

for i in range(3,17):
    x3 = x2*0x10 - x1
    y3 = y2*0x10 - y1
    print hex(x3) , hex(y3)
    x1,x2 = x2,x3
    y1,y2 = y2,y3

可以得到63位和64位的解,和table异或一下

0x557f3b3b9e1a55a 0x2050988b2bd38de
0x552965d47892d506 0x20302760c38eb6fe

得到

0x61396b325a6d334c 0x4d4d443633613053
0x3147fd559b1e4310 0x4f484dbe81dc088d

排除第二组解,求出flag

'0x61396b325a6d334c'[2:].decode('hex')[::-1]+'0x4d4d443633613053'[2:].decode('hex')[::-1]

L3mZ2k9aS0a36DMM

ps:逆向方面不难,耐心点追踪汇编代码仿佛看源码,主要是最后的解方程

你可能感兴趣的:(看雪CTF逆向部分WP)