首页
社区
课程
招聘
[原创]看雪 2023 KCTF 年度赛 第六题 至暗时刻 解题过程(数独)
发表于: 2023-9-14 23:06 9475

[原创]看雪 2023 KCTF 年度赛 第六题 至暗时刻 解题过程(数独)

2023-9-14 23:06
9475

直接扔调试器运行,运行后黑框框没有内容。换终端里运行,提示请输入。
意味着有反调试。

于是打开反反调插件,SyllaHide默认配置随便选第1个、第2个或者SharpOD全部打勾。
这时在调试器运行,能执行到提示请输入。

静态分析加载完,进去main就能看见入口sub被创建线程。
跟进去,四处查看,初步情况如下:

字符串异或加密,解密密钥带在解密函数(0x1400013B0)入参。

有一些重要函数是放了一些4字节常量然后走到syscall,且无法输出伪代码。

考虑到字符串不多,先对解密函数返回的ret指令(0x14000144E)下日志断点,输出明文字符串查看。

测试运行一次,字符串并不多:

将那个syscall指令的函数设置成__fastcall 4个参数,这时基本都正常输出伪代码。如果其他函数出现参数特别多的情况,也先给他降到4个。

进一步分析syscall涉及的函数:

考虑到涉及函数也不是很多,还是采用日志断点输出的方法。
在0x140002818:populate_syscall_index函数中,
下2个日志断点:

重来运行一次,日志中就得到了全部函数名与hash的对应。
复制日志到文本备用。

根据对应关系,重命名部分函数:
ZwAllocateVirtualMemory
ZwWriteVirtualMemory
ZwCreateThreadEx
ZwQueueApcThread_140002E4E

同时调整这些函数的参数数量,重点是ZwQueueApcThread,它有5个参数。

把syscall函数都重命名之后,流程变得清晰。

在ZwQueueApcThread pMem+500 call处(0x140001D9C)下断点,断下后对第2个参数即RDX寄存器跳转到反汇编窗口,对要执行的shellcode入口下断点,运行即断在shellcode入口。
移除入口处断点,单步步进一次,那个call很神奇,call到的自己的最后一个机器码。
把下一条指令地址压栈备用的同时,使得一般的反汇编器不能从call那个地方反汇编下去。
因而需要从call指令的最后一个字节开始反汇编。

此时用savedata保存shellcode单独分析,从pMem处开始,长度500+2347 = 0xb1f。
比如:

然后把sc.bin按dump时的pMem地址作为基址加载到分析工具。

后来发现在此处dump还不合适,加载之后发现call目标不再范围内。
单步研究后发现,紧接着代码还有一处循环自修改。

改为在循环之后dump,得到的shellcode即可全部正常加载出代码。

shellcode加载之后,发现有动态定位API的call。
shellcode需要动态定位API才能位置无关。
无需要跟进分析,步过call,根据返回值显示的函数名称信息,重命名局部函数指针即可。
感兴趣的可以参考很久以前写过的动态定位API文章:
https://bbs.kanxue.com/thread-203319.htm

重命名局部函数指针之后,逻辑就很清晰了。

checkall里面依次调用check1,check2,check3,且3个函数都调用同一个公共check函数。

分析后check原型大致如下:

后两个入参为十位、个位,返回百位为正确分支。

看了check1 2 3的检查逻辑之后,发现加起来刚好就是数独的规则。
代码还原如下:

并且通过对check的分析,得知先前被写到pMem开头的字符串结构与每段的处理:

现在就缺少60个百位,并且这60个百位与已知的21个百位,
按所在3位数的十位与个位作为二维索引,建立出一个数独。
求解出数独,再把求出的60个百位与各自对应的十位、个位组成3位数。即可得出flag。

如下python代码实现。

安装三方库后可直接运行,输出flag。

输出:

dec_str    {utf8@cax}
dec_str    {utf8@cax}
dec_str    Please enter your key:
dec_str    kctf
dec_str    kernel32.dll
dec_str    RtlFillMemory
dec_str    Please enter your key:
dec_str    kctf
dec_str    kernel32.dll
dec_str    RtlFillMemory
1层:0x140002A10:call_syscall,通过syscall指令发起系统调用。
2层:0x1400029C4:get_syscall_index 该函数入参为先前赋值的4字节常量,用来获取对应的系统调用号。
3层:0x140002818:populate_syscall_index 动态从ntdll.dll建立好系统调用索引查询表。
1层:0x140002A10:call_syscall,通过syscall指令发起系统调用。
2层:0x1400029C4:get_syscall_index 该函数入参为先前赋值的4字节常量,用来获取对应的系统调用号。
3层:0x140002818:populate_syscall_index 动态从ntdll.dll建立好系统调用索引查询表。
fname = {utf8@r9}
fname = {utf8@r9}
hash = 0x{x:ebx}
hash = 0x{x:ebx}
savedata c:\temp\sc.bin,0x0000023676EB0000,0xb1f
savedata c:\temp\sc.bin,0x0000023676EB0000,0xb1f
__int64 __fastcall check(char *Sz, int _shiwei, int __gewei)
__int64 __fastcall check(char *Sz, int _shiwei, int __gewei)
char __fastcall checkall(char *Sz)
{
  unsigned int i;
  int j;
  int k;
 
  i = 0;
  while ( check1(Sz, i) && check2(Sz, i) )
  {
    if ( (int)++i >= 9 )
    {
      j = 0;
label_cont:
      k = 0;
      while ( check3(Sz, (unsigned int)j, (unsigned int)k) )
      {
        k += 3;
        if ( k >= 9 )
        {
          j += 3;
          if ( j < 9 )
            goto label_cont;
          return 1;
        }
      }
      return 0;
    }
  }
  return 0;
}
 
 
char __fastcall check1(char *Input, __int64 i)
{
  int j;
  int i_;
  unsigned int oi;
  unsigned int dws[9];
 
  memset(dws, 0, sizeof(dws));
  j = 0;
  i_ = i;
  while ( 1 )
  {
    oi = check(Input, i_, j) - 1;
    if ( oi > 8 || dws[oi] )
      break;
    ++j;
    dws[oi] = 1;
    if ( j >= 9 )
      return 1;
  }
  return 0;
}
 
char __fastcall check2(char *Sz, __int64 i)
{
  int j;
  int i_;
  unsigned int oi;
  unsigned int dws[9];
 
  memset(dws, 0, sizeof(dws));
  j = 0;
  i_ = i;
  while ( 1 )
  {
    oi = check(Sz, j, i_) - 1;
    if ( oi > 8 || dws[oi] )
      break;
    ++j;
    dws[oi] = 1;
    if ( j >= 9 )
      return 1;
  }
  return 0;
}
 
char __fastcall check3(char *Input, __int64 j, __int64 k)
{
  int addj;
  int k_;
  int j_;
  int addk;
  unsigned int oi;
  unsigned int dws[9];
 
  memset(dws, 0, sizeof(dws));
  addj = 0;
  k_ = k;
  j_ = j;
  while ( 2 )
  {
    for ( addk = 0; addk < 3; ++addk )
    {
      oi = check(Input, addj + j_, addk + k_) - 1;
      if ( oi > 8 || dws[oi] )
        return 0;
      dws[oi] = 1;
    }
    if ( ++addj < 3 )
      continue;
    break;
  }
  return 1;
}
char __fastcall checkall(char *Sz)
{
  unsigned int i;
  int j;
  int k;
 
  i = 0;
  while ( check1(Sz, i) && check2(Sz, i) )
  {
    if ( (int)++i >= 9 )
    {
      j = 0;
label_cont:
      k = 0;
      while ( check3(Sz, (unsigned int)j, (unsigned int)k) )
      {
        k += 3;
        if ( k >= 9 )
        {
          j += 3;
          if ( j < 9 )
            goto label_cont;
          return 1;
        }
      }
      return 0;
    }
  }
  return 0;
}
 
 
char __fastcall check1(char *Input, __int64 i)
{
  int j;
  int i_;
  unsigned int oi;
  unsigned int dws[9];
 
  memset(dws, 0, sizeof(dws));
  j = 0;
  i_ = i;
  while ( 1 )
  {
    oi = check(Input, i_, j) - 1;
    if ( oi > 8 || dws[oi] )
      break;
    ++j;
    dws[oi] = 1;
    if ( j >= 9 )
      return 1;
  }
  return 0;
}
 
char __fastcall check2(char *Sz, __int64 i)
{
  int j;
  int i_;
  unsigned int oi;
  unsigned int dws[9];
 
  memset(dws, 0, sizeof(dws));
  j = 0;
  i_ = i;
  while ( 1 )
  {
    oi = check(Sz, j, i_) - 1;
    if ( oi > 8 || dws[oi] )
      break;
    ++j;
    dws[oi] = 1;
    if ( j >= 9 )
      return 1;
  }
  return 0;
}
 
char __fastcall check3(char *Input, __int64 j, __int64 k)
{
  int addj;
  int k_;
  int j_;

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

收藏
免费 1
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//