2020-GKCTF-Reverse

本文详细解析了一道GKCTF比赛中的密码破解任务,涉及字符串查找、加密算法(如strcmp、base64、base58)、键盘过滤驱动、TLS回调函数、自解密算法(RC4)以及RSA加密。解题步骤包括迷宫导航、MD5哈希、RSA在线解密等,展示了信息技术领域的密码学和逆向工程实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2020-GKCTF

Check_1n

思路一:


查找字符串之后,点击"密码错误"发现了关键,上方的一个strcmp函数,发现了密码:

‘HelloWorld’

在这里插入图片描述

我们直接运行一下,然后输入密码开机,之后移动到"flag",结果给了base64编码的Why don’t you try the magic brick game

然后我们玩打砖块游戏,死了之后就出flag

在这里插入图片描述

思路二:

查找字符串的时候发现:

2i9Q8AtFJTfL3ahU2XGuemEqZJ2ensozjg1EjPJwCHy4RY1Nyvn1ZE1bZe

点过去之后交叉引用发现了base58的table,直接base58解密得到flag

Chelly’s ldentity

在这里插入图片描述

加密函数:

在这里插入图片描述

比较函数:

在这里插入图片描述

至于为什么知道哪个变量是哪个,动调来看就知道了

WP:

# _*_ coding:utf-8 _*_
# @功能描述:
# @程序作者:SYJ
# @版权信息:Reversed By SYJ
# @版本信息:0.0.0
arr = [0x00000002, 0x00000003, 0x00000005, 0x00000007, 0x0000000B, 0x0000000D, 0x00000011, 0x00000013, 0x00000017, 0x0000001D, 0x0000001F, 0x00000025, 0x00000029, 0x0000002B, 0x0000002F, 0x00000035, 0x0000003B, 0x0000003D, 0x00000043, 0x00000047, 0x00000049, 0x0000004F, 0x00000053, 0x00000059, 0x00000061, 0x00000065, 0x00000067, 0x0000006B, 0x0000006D, 0x00000071, 0x0000007F]
cmp = [0x000001B6, 0x00000498, 0x00000441, 0x00000179, 0x00000179, 0x00000640, 0x0000039C, 0x00000179, 0x0000064A, 0x0000039C, 0x0000027D, 0x0000027F, 0x00000178, 0x00000236, 0x00000344, 0x0000033E]
flag = ''
for i in range(len(cmp)):
    for x in range(32, 127):
        su_m = 0
        for j in arr:
            if j < x:
                su_m += j
        temp = x ^ su_m
        if temp == cmp[i]:
            flag += chr(x)
print('flag{'+flag+'}')

flag{Che11y_1s_EG0IST}

BabyDriver

是一道很经典的键盘过滤驱动,下面几张图是经典的键盘过滤驱动:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我们主要看的就是IRP读操作的回调函数(第二个函数),也就是CompletionRoutine的位置

分析我们的题目:
找到DriverEntry函数中的sub_1400011E0函数

__int64 __fastcall sub_1400011E0(PDRIVER_OBJECT DriverObject)
{
  DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_140001260;// 注册驱动卸载函数
  KdDisableDebugger();
  memset64(DriverObject->MajorFunction, (unsigned __int64)sub_1400012B0, 27ui64);// 注册通用的IRP分发函数
  DriverObject->MajorFunction[3] = (PDRIVER_DISPATCH)sub_1400014E0;// 注册读IRP分发函数
  DriverObject->MajorFunction[22] = (PDRIVER_DISPATCH)&sub_140001340;// 注册电源IRP分发函数
  DriverObject->MajorFunction[27] = (PDRIVER_DISPATCH)sub_1400012D0;// 注册即插即用IRP分发函数
  AttachDevice(DriverObject);                   // 绑定设备
  return 0i64;
}

点击读IRP分发函数

NTSTATUS __fastcall sub_1400014E0(_QWORD *a1, IRP *a2)
{
  NTSTATUS result; // eax
  _DEVICE_OBJECT *v5; // rcx
  _IO_STACK_LOCATION *v6; // rax
  _IO_STACK_LOCATION *v7; // rax

  if ( a2->CurrentLocation == 1 )
  {
    a2->IoStatus.Information = 0i64;
    a2->IoStatus.Status = 0xC0000010;
    IofCompleteRequest(a2, 0);
    result = 0xC0000010;
  }
  else
  {
    v5 = *(_DEVICE_OBJECT **)(a1[8] + 24i64);
    v6 = a2->Tail.Overlay.CurrentStackLocation;
    *(_OWORD *)&v6[-1].MajorFunction = *(_OWORD *)&v6->MajorFunction;
    *(_OWORD *)&v6[-1].Parameters.NotifyDirectoryEx.CompletionFilter = *(_OWORD *)&v6->Parameters.NotifyDirectoryEx.CompletionFilter;
    *(_OWORD *)(&v6[-1].Parameters.SetQuota + 6) = *(_OWORD *)(&v6->Parameters.SetQuota + 6);
    v6[-1].FileObject = v6->FileObject;
    v6[-1].Control = 0;
    v7 = a2->Tail.Overlay.CurrentStackLocation;
    v7[-1].Context = a1;
    v7[-1].CompletionRoutine = (PIO_COMPLETION_ROUTINE)sub_140001380;// 键盘过滤驱动,主要看下IRP读操作的回调函数(第二个函数),也就是CompletionRoutine的位置
    v7[-1].Control = 0xE0;
    result = IofCallDriver(v5, a2);
  }
  return result;
}

在其中找到CompletionRoutine对应的函数sub_140001380

在其中发现了迷宫

__int64 __fastcall sub_140001380(__int64 a1, __int64 a2)
{
  __int64 v3; // rdi
  __int64 v4; // rax
  int direction; // ecx
  __int16 *v6; // rsi
  __int64 v7; // rbp
  __int16 v8; // dx
  char v9; // dl
  const CHAR *v10; // rcx

  if ( *(int *)(a2 + 48) >= 0 )
  {
    v3 = *(_QWORD *)(a2 + 24);
    v4 = *(_QWORD *)(a2 + 56) >> 3;
    if ( (_DWORD)v4 )
    {
      direction = dword_1400030E4;              // 初值为16
      v6 = (__int16 *)(v3 + 2);
      v7 = (unsigned int)v4;
      while ( *(_WORD *)(v3 + 4) )
      {
LABEL_28:
        v6 += 6;
        if ( !--v7 )
          goto LABEL_29;
      }
      aO[direction] = '.';
      v8 = *v6;
      if ( *v6 == 23 )
      {
        if ( (direction & 0xFFFFFFF0) != 0 )
        {
          direction -= 16;                      // 上移
          goto LABEL_21;
        }
        direction += 0xD0;
        dword_1400030E4 = direction;
      }
      if ( v8 == 37 )
      {
        if ( (direction & 0xFFFFFFF0) != 0xD0 )
        {
          direction += 16;                      // 下移,+16代表下移一行
          goto LABEL_21;
        }
        direction -= 208;
        dword_1400030E4 = direction;
      }
      if ( v8 == 36 )
      {
        if ( (direction & 0xF) != 0 )
        {
          --direction;                          // 左移
          goto LABEL_21;
        }
        direction += 15;
        dword_1400030E4 = direction;
      }
      if ( v8 != 38 )
        goto final;
      if ( (direction & 0xF) == 15 )
        direction -= 15;
      else
        ++direction;                            // 右移
LABEL_21:
      dword_1400030E4 = direction;
final:
      v9 = aO[direction];
      if ( v9 == '*' )                          // 撞墙
      {
        v10 = "failed!\n";
      }
      else
      {
        if ( v9 != '#' )                        // 没走到目标'#'
        {
LABEL_27:
          aO[direction] = 'o';                  // 就回到起点
          goto LABEL_28;
        }
        v10 = "success! flag is flag{md5(input)}\n";
      }
      dword_1400030E4 = 16;
      DbgPrint(v10);
      direction = dword_1400030E4;
      goto LABEL_27;
    }
  }
LABEL_29:
  if ( *(_BYTE *)(a2 + 'A') )
    *(_BYTE *)(*(_QWORD *)(a2 + 0xB8) + 3i64) |= 1u;
  return *(unsigned int *)(a2 + '0');
}

导出迷宫并走出路线:

# _*_ coding:utf-8 _*_
# @功能描述:
# @程序作者:SYJ
# @版权信息:Reversed By SYJ
# @版本信息:0.0.0
maze = "****************o.*..*......*..**.**...**.*.*.***.****.**.*.*.***...**....*.*.*****..***.**.*..**.**.***.**.**.**.**.******.**.**.**....***.**.**.*****.***....**...***.**********..***......#****.*****************************"
for i in range(len(maze)):
    if (i+1) % 16 == 0:
        print(maze[i])
    else:
        if i != 0:
            print(maze[i], end=' ')
        else:
            print(maze[i], end=' ')
import hashlib
line = "LKKKLLKLKKKLLLKKKLLLLLL"
flag = hashlib.md5(line.encode("utf-8")).hexdigest().lower()
print('flag{'+flag+'}')

在这里插入图片描述

flag{403950a6f64f7fc4b655dea696997851}

DbglsFun

打开之后发现了TLS回调函数,会在主线程开始的时候会调用,TLS回调函数的调用要先于EP代码的执行

在其中发现了smc自解密代码:

void __stdcall TlsCallback_0(int a1, int a2, int a3)
{
  int i; // eax
  DWORD flOldProtect; // [esp+0h] [ebp-8h] BYREF

  if ( a2 == 1 )
  {
    VirtualProtect(sub_4010F0, 0x42Eu, 0x40u, &flOldProtect);
    for ( i = 0; i < 1070; ++i )
      *((_BYTE *)sub_4010F0 + i) ^= i;
    CreateThread(0, 0, sub_4010F0, 0, 0, 0);
  }
}

我们静态的话就写个脚本跑一下这个smc自解密:

addr = 0x004010F0
for i in range(1070):
    ida_bytes.patch_byte((addr+i), idc.get_wide_byte(addr+i) ^ i)

然后将地址004010F0开始的重新定义为一个函数
在这里插入图片描述

很明显的能看出来是先将我们的flag和一个值异或,然后再进行了RC4

最后再进行比较
在这里插入图片描述

写出脚本:

/* 注意事项
* 1.明文密文还有状态数组s[i](0-255)的类型应都选择unsigned char类型,因为其取值范围为0-255,一字节
* 2.明文密文以及状态数组s[i]的类型应该全部一样,这样做异或运算时就不会发生内存溢出的问题
*/
#include <stdio.h>
#include <string.h>

void rc4_init(unsigned char*s,unsigned char*key,unsigned long len)
{
 int i=0;
 int j=0;
 unsigned char k[256]={};
 unsigned char temp = 0;
 for(i=0;i<256;i++)
 {
  s[i]=i;         //0-255赋给s
  k[i]=key[i%len];   //将k重新计算
 }
 for(i=0;i<256;i++)
 {
  j=(j+s[i]+k[i])%256;    //给j赋值
  temp=s[i];
  s[i]=s[j];
  s[j]=temp;    //s[i]和s[j]交换
 }
}

void rc4_crypt(unsigned char*s,unsigned char*data,unsigned long len)
{
 int i=0,j=0,t=0;
 unsigned long k=0;
 unsigned char temp;
 for(k=0;k<len;k++)
 {
  i=(i+1)%256;            //固定方式生成的i
  j=(j+s[i])%256;          //固定方式生成的j
  temp=s[i];
  s[i]=s[j];
  s[j]=temp;             //交换s[i]和s[j]
  t=(s[i]+s[j])%256;      //固定方式生成的t
  data[k]^=s[t];          //来作为s的下标和data进行异或运算
 }
}

int main()
{
 unsigned char s[256]={0};
 char key[256] = "GKCTF";
 unsigned char data[512]={0x2D, 0xD4, 0x0F, 0xD0, 0x54, 0xEE, 0x75, 0xD0, 0xE0, 0x30, 0x96, 0xE1, 0x79, 0x8A, 0xE0, 0xFE, 0x18, 0x3A, 0x27, 0xE7, 0x2F, 0x86, 0xC9, 0xFE, 0x66, 0x43, 0xA7, 0x75};
 unsigned long data_len = 28;
 unsigned long key_len = strlen(key);
 rc4_init(s,(unsigned char*)key,key_len);//初始化得到s
 rc4_crypt(s,(unsigned char*)data,data_len);//解密
 //printf("解密后为:%s",(unsigned char*)data);
 for(int i=0;i<28;i++)
 {
     printf("%c",(data[i]^0xC9)&0xff);
 }
 return 0;
}

flag{5tay4wayFr0m8reakp0int}
Process returned 0 (0x0) execution time : 0.010 s
Press any key to continue.

EzMachine

写过这个的WP,是一道非常经典的虚拟机,拿来入门很不错的

https://bbs.pediy.com/thread-267670.htm

WannaReverse

题目解压之后得到四个文件,点击clickme.exe会更改壁纸,一个模拟病毒

然后将WannaReverse.exe拖入ida进行分析:

char v9; // [esp-4h] [ebp-40h]
  __int128 v10[2]; // [esp+8h] [ebp-34h] BYREF
  char v11; // [esp+28h] [ebp-14h]
  char string[12]; // [esp+2Ch] [ebp-10h] BYREF

  v10[0] = 0i64;
  v10[1] = 0i64;
  WinExec("clickme.exe", 1u);
  strcpy(string, "0123456789");
  v3 = _time64(0);
  srand(v3);
  for ( i = 0; i < 32; ++i )
    *((_BYTE *)v10 + i) = string[rand() % 10];  // rand() % 10
                                                // [0-10)取随机数
  v9 = v5;
  v11 = 0;
  v6 = encrypt(v5, v10);                        // 随机生成的长度为32的v9
  v7 = "encrypto success!";
  if ( !v6 )
    v7 = "encrypto false!";
  printf((int)v7, v9);
  return 0;
}

它先运行了一下clickme.exe,然后在[0-10)取随机数用来当作下标,生成一个随机的长度为32的字符串(后面可以知道这个就是我们AES的密钥)

进入encrypt函数分析:

首先打开了flag.txt

在这里插入图片描述

然后中间一截就是用AES加密目标文件flag.txt之后加了个后缀.Encry

然后将使用RSA加密后的AES密钥放在了文件flag.txt.Encry

这就是那个flag.txt.Encry的由来

解题思路:

在WannaReverse.exe中找到RSA公钥:
在这里插入图片描述

然后在clickme.exe中找到私钥:

在这里插入图片描述

在线解密RSA

https://oktools.net/rsa

得到:30776159143604297789676442413079

然后写个脚本将flag.txt.Encry的剩下部分进行AES解密即可

# _*_ coding:utf-8 _*_
# @功能描述:
# @程序作者:SYJ
# @版权信息:Reversed By SYJ
# @版本信息:0.0.0
from Crypto.Cipher import AES
mode = AES.MODE_ECB                  # 规定是ECB的加密模式
key = b"30776159143604297789676442413079"
cryptos = AES.new(key, mode)       # 传入key形成加解密函数
en_flag = [
    0x5C, 0xBC, 0xEA, 0x89, 0xBA, 0x2B, 0x18, 0x27, 0x79,
    0x3F, 0x13, 0x0A, 0x8A, 0x97, 0xB4, 0x9B, 0xCD, 0x78,
    0x9B, 0xD8, 0x35, 0x92, 0x05, 0x45, 0x4C, 0x22, 0xA5,
    0x69, 0x37, 0xEB, 0x6E, 0x2B, 0x0E, 0xBD, 0x84, 0x0F,
    0x91, 0x61, 0x38, 0xF6, 0xF1, 0xBA, 0x99, 0x19, 0x41,
    0x72, 0x07, 0x91, 0xF0, 0x26, 0x68, 0x06, 0x61, 0x26,
    0x5C, 0x20, 0x35, 0xDD, 0xCF, 0xFC, 0x77, 0x57, 0x54,
    0x81, 0xF2, 0xF2, 0xE4, 0xAF, 0xBF, 0xA2, 0x1D, 0x29,
    0xAE, 0x6C, 0x08, 0x3B, 0x76, 0x1B, 0x66, 0xB8, 0xFE,
    0x72, 0xCB, 0xD6, 0x94, 0xC3, 0xD5, 0x6A, 0xE7, 0x0C,
    0x7A, 0x28, 0xDC, 0xBC, 0xAC, 0x80]
bytes_flag = cryptos.decrypt(bytes(en_flag))        # 直接进行解密
print(bytes_flag)

# b'\xff\xfef\x00l\x00a\x00g\x00{\x003\x008\x005\x00f\x00a\x008\x006\x009\x00-\x003\x000\x004\x006\x00-\x004\x004\x00e\x00e\x00-\x009\x00d\x003\x000\x00-\x00c\x000\x003\x005\x005\x001\x002\x007\x003\x008\x006\x007\x00}\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
# flag{385fa869-3046-44ee-9d30-c03551273867}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值