首页
社区
课程
招聘
[原创] 看雪 2024 KCTF 大赛 第九题 第一次接触
发表于: 2024-9-4 01:31 2090

[原创] 看雪 2024 KCTF 大赛 第九题 第一次接触

2024-9-4 01:31
2090

第九题难得一见是基于方案一的(上一道还是第二题),着实缓了一口气

IDA打开,从main函数开始一点点看代码:

主函数,逻辑是读取输入、检查字符为数字和小写字母、长度是3的倍数;
然后将输入拆成等长的三份,分别由sub_456BCB、sub_4578CD、sub_456E78进行检查;
最后,还对原始输入做sub_4579F4的变换,然后与常量比较,通过后才输出成功。
 
从sub_4579F4连续追进去几层,根据数值常量(sub_45D890等),可以判定是计算md5然后输出hexencode,暂时不用去管。
 
注意一个情况:主函数没有任何对输入长度的判断。

sub_456BCB处理第一段输入,逐层查看,主要逻辑在两处:

sub_45CCA0只是将大写字母转为小写,对于本题的合法输入没有影响;
sub_45C360是简单的字符串匹配,所以,第一部分的前8个字符确定是hellocat(注意这里是strncmp而不是strcmp,所以第一段的确切长度仍然不知道)(p.s. 第一次看的时候确实没有注意这种细节)。
 
 
sub_4578CD处理第二段输入,逐层查看,主要逻辑如下:

sub_45CBC0是从sub_455357进入并最终调用到的,从代码和最后的字符串看,功能是字符串倒序。
unknown_libname_75是从j_unknown_libname_94进入并最终调用到的。从return调用的函数名看,它的功能是把字符串转换为十进制整数。
dword_531004[0]是常量13,dword_531008是常量3。
 
结合以上,sub_45C400的功能是:将提供的字符串倒序并解析为十进制数字,然后要求每位数字相加之和等于13,且整体乘以3后等于150633606。
逆推一下,150633606除以3等于50211202,再倒序是20211205,长度是8。
(那么,第二段输入的长度一定是8吗?这个问题稍后再回来看)
 
 
sub_456E78处理第三段输入,逐层查看,主要逻辑仍然在两个函数里:

先看sub_45C710,参数是一个长度为2的字符串,第一个字符是'A'、'B'、'C'、'D'或'a'、'b'、'c'、'd'之一(根据main函数的检查,这里只可能是小写字母),第二个字符是'1'、'2'、'3'、'4'之一。
这个函数的大部分路径都返回0,只有一条路径返回1,合理假设后者为正确路径(因为约束更多)。
byte_531EA0是4×4的方阵,初始值全部0;参数可以看作坐标,六个for循环分别检查在坐标位置填入1时其所在的行列和斜线上所有的位置都是0。
手动试一试,可以发现满足的填法只有两种:

两种是对称的,转换为坐标从上到下依次为:a2b4c1d3a3b1c4d2
 
再看sub_45C500,先检查参数字符串的长度不小于8(等于或者大于都是允许的);
然后,是顺序检查,索引0、2、4、8位置的字符要递增,否则dword_531E9C与任何一个值做了“|”运算后都不可能等于目标值。
 
但是,最后经过仔细计算可以发现,单纯按照这个函数的逻辑,无论后面四个sub_458232的返回值是什么,dword_531E9C都永远不可能等于0x10000039。
(看起来下面四个分支的 1 ^ 8 ^ 0x10 ^ 0x20 恰好等于 0x39,但是1是等于赋值而不是异或会消除掉0x10000000;另外如果要进入异或分支,sub_45C710需要返回0而不是1,也与前面的推断不符)

分析到这里,第一个怀疑是,会不会程序只检查最后的md5而不管前面的逻辑?.
假设三段长度都是8,可以拼出两个字符串"hellocat20211205a2b4c1d3"和"hellocat20211205a3b1c4d2",但是md5都不正确
 
然后怀疑题目有反调试或自修改或者init藏逻辑之类的导致逆向的函数不是真实逻辑,但是调试器试了一圈并没有,但是确认了前两部分能通过检查。
 
此时仍然存在输入长度无法确定的疑点。可能上一题是PWN的影响还未散去,半无意半有意的点开了sub_45C500引用全局变量:

最终用于比较的dword_531E9C变量恰好位于&Str+8的位置,所以只要第三部分的长度大于等于9,就会覆盖dword_531E9C变量!

最终的比较 dword_531E9C == 0x10000039 ,0x39是可见字符'9',所以第三部分的长度应该是9(如果大于9,会覆盖第二个字节导致结果不正确),且最后一个字符为'9'。同时,四处sub_458232都要走严格路径返回1,避免进入异或分支破坏dword_531E9C。
 
由于输入分成等长的三份,所以每份的长度都需要是9。第一部分前8个字符固定,第9个字符待定;第二部分,末尾可以补0,不影响倒序后十进制解析的结果。
 
对未知的部分做一个简单的md5爆破:

可以找到唯一满足的结果:

最终正确的serial:(总长度:27 (9*3))

int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  char *Str1; // [esp+D0h] [ebp-188h]
  char *Source; // [esp+DCh] [ebp-17Ch]
  char *v6; // [esp+E8h] [ebp-170h]
  char *Destination; // [esp+F4h] [ebp-164h]
  int Count; // [esp+100h] [ebp-158h]
  signed int i; // [esp+10Ch] [ebp-14Ch]
  signed int v10; // [esp+118h] [ebp-140h]
  char Str[304]; // [esp+124h] [ebp-134h] BYREF
 
  __CheckForDebuggerJustMyCode(&unk_535028);
  sub_45548D("请输入序列号:");
  j__memset(Str, 0, 0x12Cu);
  j_scanf("%[^\n]", (char)Str);
  v10 = j__strlen(Str);
  if ( !(v10 % 3) && v10 )
  {
    for ( i = 0; i < v10; ++i )
    {
      if ( (Str[i] < 'a' || Str[i] > 'z') && (Str[i] < '0' || Str[i] > '9') )
        goto LABEL_3;
    }
    Count = v10 / 3;
    Destination = (char *)j__malloc(__CFADD__(v10 / 3, 1) ? -1 : v10 / 3 + 1);
    v6 = (char *)j__malloc(__CFADD__(Count, 1) ? -1 : Count + 1);
    Source = (char *)j__malloc(__CFADD__(Count, 1) ? -1 : Count + 1);
    j__memset(Destination, 0, v10 / 3 + 1);
    j__memset(v6, 0, v10 / 3 + 1);
    j__memset(Source, 0, v10 / 3 + 1);
    j__strncpy(Destination, Str, v10 / 3);
    j__strncpy(v6, &Str[Count], Count);
    j__strncpy(Source, &Str[2 * Count], Count);
    if ( sub_456BCB(Destination) && sub_4578CD(v6) && sub_456E78(Source) )
    {
      Str1 = (char *)sub_4579F4(Str);
      if ( !j__strcmp(Str1, "40d511825ecbc207eb6ef9a7b1c6e34b") )
        sub_45548D("Success~\n");
      else
        sub_45548D("WR0NG!\n");
      j__free(Str1);
    }
    else
    {
      sub_45548D("WR0NG!\n");
    }
    j__free(Destination);
    j__free(v6);
    j__free(Source);
    return 0;
  }
  else
  {
LABEL_3:
    sub_45548D("WRONG!\n");
    return 0;
  }
}
int __cdecl main_0(int argc, const char **argv, const char **envp)
{
  char *Str1; // [esp+D0h] [ebp-188h]
  char *Source; // [esp+DCh] [ebp-17Ch]
  char *v6; // [esp+E8h] [ebp-170h]
  char *Destination; // [esp+F4h] [ebp-164h]
  int Count; // [esp+100h] [ebp-158h]
  signed int i; // [esp+10Ch] [ebp-14Ch]
  signed int v10; // [esp+118h] [ebp-140h]
  char Str[304]; // [esp+124h] [ebp-134h] BYREF
 
  __CheckForDebuggerJustMyCode(&unk_535028);
  sub_45548D("请输入序列号:");
  j__memset(Str, 0, 0x12Cu);
  j_scanf("%[^\n]", (char)Str);
  v10 = j__strlen(Str);
  if ( !(v10 % 3) && v10 )
  {
    for ( i = 0; i < v10; ++i )
    {
      if ( (Str[i] < 'a' || Str[i] > 'z') && (Str[i] < '0' || Str[i] > '9') )
        goto LABEL_3;
    }
    Count = v10 / 3;
    Destination = (char *)j__malloc(__CFADD__(v10 / 3, 1) ? -1 : v10 / 3 + 1);
    v6 = (char *)j__malloc(__CFADD__(Count, 1) ? -1 : Count + 1);
    Source = (char *)j__malloc(__CFADD__(Count, 1) ? -1 : Count + 1);
    j__memset(Destination, 0, v10 / 3 + 1);
    j__memset(v6, 0, v10 / 3 + 1);
    j__memset(Source, 0, v10 / 3 + 1);
    j__strncpy(Destination, Str, v10 / 3);
    j__strncpy(v6, &Str[Count], Count);
    j__strncpy(Source, &Str[2 * Count], Count);
    if ( sub_456BCB(Destination) && sub_4578CD(v6) && sub_456E78(Source) )
    {
      Str1 = (char *)sub_4579F4(Str);
      if ( !j__strcmp(Str1, "40d511825ecbc207eb6ef9a7b1c6e34b") )
        sub_45548D("Success~\n");
      else
        sub_45548D("WR0NG!\n");
      j__free(Str1);
    }
    else
    {
      sub_45548D("WR0NG!\n");
    }
    j__free(Destination);
    j__free(v6);
    j__free(Source);
    return 0;
  }
  else
  {
LABEL_3:
    sub_45548D("WRONG!\n");
    return 0;
  }
}
size_t __cdecl sub_45CCA0(char *Str)
{
  size_t result; // eax
  size_t i; // [esp+D0h] [ebp-8h]
 
  result = __CheckForDebuggerJustMyCode(&unk_535028);
  if ( Str )
  {
    dword_531E90 = sub_45D4E0(0);
    for ( i = 0; ; ++i )
    {
      result = j__strlen(Str);
      if ( i >= result )
        break;
      if ( Str[i] >= 'A' && Str[i] <= 'Z' )
        Str[i] += 32;
    }
  }
  return result;
}
 
BOOL __cdecl sub_45C360(char *Str1)
{
  __CheckForDebuggerJustMyCode(&unk_535028);
  sub_455816(Str1);
  return j__strncmp(Str1, "hellocat", 8u) == 0;
}
size_t __cdecl sub_45CCA0(char *Str)
{
  size_t result; // eax
  size_t i; // [esp+D0h] [ebp-8h]
 
  result = __CheckForDebuggerJustMyCode(&unk_535028);
  if ( Str )
  {
    dword_531E90 = sub_45D4E0(0);
    for ( i = 0; ; ++i )
    {
      result = j__strlen(Str);
      if ( i >= result )
        break;
      if ( Str[i] >= 'A' && Str[i] <= 'Z' )
        Str[i] += 32;
    }
  }
  return result;
}
 
BOOL __cdecl sub_45C360(char *Str1)
{
  __CheckForDebuggerJustMyCode(&unk_535028);
  sub_455816(Str1);
  return j__strncmp(Str1, "hellocat", 8u) == 0;
}
BOOL __cdecl sub_45C400(char *Str)
{
  size_t i; // [esp+D4h] [ebp-14h]
  int v3; // [esp+E0h] [ebp-8h]
 
  __CheckForDebuggerJustMyCode(&unk_535028);
  sub_455357(Str);
  v3 = 0;
  for ( i = 0; i < j__strlen(Str); ++i )
    v3 = v3 + Str[i] - '0';
  return v3 == dword_531004[0] && dword_531008 * j_unknown_libname_94((int)Str) == 150633606;
}
 
char *__cdecl sub_45CBC0(char *Str)
{
  char *result; // eax
  char v2; // [esp+D3h] [ebp-1Dh]
  char *i; // [esp+DCh] [ebp-14h]
 
  result = (char *)__CheckForDebuggerJustMyCode(&unk_535028);
  if ( Str )
  {
    for ( i = &Str[j__strlen(Str) - 1]; ; --i )
    {
      result = i;
      if ( i <= Str )
        break;
      v2 = *Str;
      *Str = *i;
      *i = v2;
      ++Str;
    }
  }
  if ( dword_531000 )
    return (char *)sub_456B21("std::reverse()", 1);
  return result;
}
 
// Microsoft VisualC universal runtime
int __cdecl unknown_libname_75(int a1, int a2, int a3, struct __crt_locale_pointers *a4)
{
  char v5; // [esp-10h] [ebp-14h] BYREF
  int v6; // [esp-Ch] [ebp-10h]
  int v7; // [esp-8h] [ebp-Ch]
  int v8; // [esp-4h] [ebp-8h]
  char *v9; // [esp+0h] [ebp-4h]
 
  v8 = 1;
  v7 = a3;
  v9 = &v5;
  j_unknown_libname_73(&v5, a1, a2);
  return __crt_strtox::parse_integer<unsigned long,__crt_strtox::c_string_character_source<char>>(a4, v5, v6, v7, v8);
}
BOOL __cdecl sub_45C400(char *Str)
{
  size_t i; // [esp+D4h] [ebp-14h]
  int v3; // [esp+E0h] [ebp-8h]
 
  __CheckForDebuggerJustMyCode(&unk_535028);
  sub_455357(Str);
  v3 = 0;
  for ( i = 0; i < j__strlen(Str); ++i )
    v3 = v3 + Str[i] - '0';
  return v3 == dword_531004[0] && dword_531008 * j_unknown_libname_94((int)Str) == 150633606;
}
 
char *__cdecl sub_45CBC0(char *Str)
{
  char *result; // eax
  char v2; // [esp+D3h] [ebp-1Dh]
  char *i; // [esp+DCh] [ebp-14h]
 
  result = (char *)__CheckForDebuggerJustMyCode(&unk_535028);
  if ( Str )
  {
    for ( i = &Str[j__strlen(Str) - 1]; ; --i )
    {
      result = i;
      if ( i <= Str )
        break;
      v2 = *Str;
      *Str = *i;
      *i = v2;
      ++Str;
    }
  }
  if ( dword_531000 )
    return (char *)sub_456B21("std::reverse()", 1);
  return result;
}
 
// Microsoft VisualC universal runtime
int __cdecl unknown_libname_75(int a1, int a2, int a3, struct __crt_locale_pointers *a4)
{
  char v5; // [esp-10h] [ebp-14h] BYREF
  int v6; // [esp-Ch] [ebp-10h]
  int v7; // [esp-8h] [ebp-Ch]
  int v8; // [esp-4h] [ebp-8h]
  char *v9; // [esp+0h] [ebp-4h]
 
  v8 = 1;
  v7 = a3;
  v9 = &v5;
  j_unknown_libname_73(&v5, a1, a2);
  return __crt_strtox::parse_integer<unsigned long,__crt_strtox::c_string_character_source<char>>(a4, v5, v6, v7, v8);
}
// attributes: thunk
int __cdecl sub_456E78(char *Source)
{
  return sub_45C500(Source);
}
 
BOOL __cdecl sub_45C500(char *Source)
{
  __CheckForDebuggerJustMyCode(&unk_535028);
  j__strcpy(&Str, Source);
  if ( j__strlen(&Str) < 8 )
    return 0;
  dword_531E9C |= 0x10000000u;
  if ( Str >= *(&Str + 2) )
    dword_531E9C |= 0x88u;
  if ( *(&Str + 2) >= *(&Str + 4) )
    dword_531E9C |= 0x90u;
  if ( *(&Str + 4) >= *(&Str + 6) )
    dword_531E9C |= 0xA0u;
  if ( !sub_458232((int)&Str) )
    dword_531E9C = 1;
  if ( !sub_458232((int)&unk_531E96) )
    dword_531E9C ^= 8u;
  if ( !sub_458232((int)&unk_531E98) )
    dword_531E9C ^= 0x10u;
  if ( !sub_458232((int)&unk_531E9A) )
    dword_531E9C ^= 0x20u;
  return dword_531E9C == 0x10000039;
}
 
// attributes: thunk
int __cdecl sub_458232(int a1)
{
  return sub_45C710(a1);
}
 
int __cdecl sub_45C710(char *a1)
{
  unsigned int ii; // [esp+D0h] [ebp-8Ch]
  unsigned int v3; // [esp+DCh] [ebp-80h]
  unsigned int n; // [esp+E8h] [ebp-74h]
  unsigned int v5; // [esp+F4h] [ebp-68h]
  unsigned int m; // [esp+100h] [ebp-5Ch]
  unsigned int v7; // [esp+10Ch] [ebp-50h]
  unsigned int k; // [esp+118h] [ebp-44h]
  unsigned int v9; // [esp+124h] [ebp-38h]
  int j; // [esp+130h] [ebp-2Ch]
  int i; // [esp+13Ch] [ebp-20h]
  int v12; // [esp+148h] [ebp-14h]
  int v13; // [esp+154h] [ebp-8h]
 
  __CheckForDebuggerJustMyCode(&unk_535028);
  if ( *a1 < 'a' || *a1 > 'd' )
  {
    if ( *a1 < 'A' || *a1 > 'D' )
      return 0;
    v12 = *a1 - 65;
  }
  else
  {
    v12 = *a1 - 97;
  }
  if ( a1[1] < '1' || a1[1] > '4' )
    return 0;
  v13 = 4 - (a1[1] - 48);
  if ( byte_531EA0[4 * v13 + v12] )
    return 0;
  byte_531EA0[4 * v13 + v12] = 1;
  for ( i = 0; i < 4; ++i )
  {
    if ( i != v12 && byte_531EA0[4 * v13 + i] )
      return 0;
  }
  for ( j = 0; j < 4; ++j )
  {
    if ( j != v13 && byte_531EA0[4 * j + v12] )
      return 0;
  }
  v9 = v13 - 1;
  for ( k = v12 - 1; v9 < 4 && k < 4; --k )
  {
    if ( byte_531EA0[4 * v9 + k] )
      return 0;
    --v9;
  }
  v7 = v13 - 1;
  for ( m = v12 + 1; v7 < 4 && m < 4; ++m )
  {
    if ( byte_531EA0[4 * v7 + m] )
      return 0;
    --v7;
  }
  v5 = v13 + 1;
  for ( n = v12 - 1; v5 < 4 && n < 4; --n )
  {
    if ( byte_531EA0[4 * v5 + n] )
      return 0;
    ++v5;
  }
  v3 = v13 + 1;
  for ( ii = v12 + 1; v3 < 4 && ii < 4; ++ii )
  {
    if ( byte_531EA0[4 * v3 + ii] )
      return 0;
    ++v3;
  }
  return 1;
}
// attributes: thunk

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2024-9-4 01:35 被mb_mgodlfyn编辑 ,原因:
收藏
免费 2
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//