吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 8416|回复: 51
收起左侧

[CTF] 学破解第129天,《攻防世界reverse练习区key》学习

  [复制链接]
小菜鸟一枚 发表于 2020-10-3 12:18

学破解第129天,《攻防世界reverse练习区key》学习

前言:
  从小学到大专(计算机网络技术专业),玩过去的,所以学习成绩惨不忍睹,什么证书也没考,直到找不到工作才后悔,不知道怎么办才好。

  2017年12月16日,通过19元注册码注册论坛账号,开始做伸手党,潜水一年多,上来就是找软件。(拿论坛高大上的软件出去装X)

  2018年8月某一天,报名了华中科技大学网络教育本科(计算机科学与技术专业)2018级秋季。(开始提升学历)

  2019年6月17日,不愿再做小菜鸟一枚,开始零基础学习破解。(感谢小糊涂虫大哥在我刚开始学习脱壳时,录制视频解答我的问题)

  2020年7月7日,感谢H大对我的鼓励,拥有了第一篇获得优秀的文章。(接下来希望学习逆向,逆天改命)

  坛友们,年轻就是资本,和我一起逆天改命吧,我的学习过程全部记录及学习资源:https://www.52pojie.cn/thread-1278021-1-1.html
立帖为证!--------记录学习的点点滴滴

0x1下载软件观察行为

  1.去攻防世界找到这个key题目,下载文件,PEID查壳,Microsoft Visual C++程序

  2.运行程序看一看,直接黑窗口一闪而过,会自动退出,好像看不出什么?

0x2动静结合调试分析

  1.既然会退出,那就找原因,丢到OD里面下一个exitprocess断点,停在了:

0031F7FC   7079F4ED  /CALL 到 ExitProcess 来自 ucrtbase.7079F4E7
0031F800   FFFFFFFF  \ExitCode = 0xFFFFFFFF
0031F804   00000000
0031F808   7079F474  返回到 ucrtbase.7079F474 来自 ucrtbase.7079F4B4

不知道怎么回溯到用户代码,可能经过多层调用,换成exit断点试试

00C3123E  |. /74 25         je short key.00C31265
00C31240  |. |8B0D C850C300 mov ecx,dword ptr ds:[<&MSVCP140.std::ce>;  msvcp140.std::cerr
00C31246  |. |BA E452C300   mov edx,key.00C352E4                     ;  ?W?h?a?t h?a?p?p?e?n?
00C3124B  |. |68 502CC300   push key.00C32C50
00C31250  |. |E8 AB170000   call key.00C32A00
00C31255  |. |8BC8          mov ecx,eax                              ;  msvcp140.std::cerr
00C31257  |. |FF15 9850C300 call dword ptr ds:[<&MSVCP140.std::basic>;  msvcp140.std::basic_ostream<wchar_t,std::char_traits<wchar_t> >::operator<<
00C3125D  |. |6A FF         push -0x1                                ; /status = FFFFFFFF (-1.)
00C3125F  |. |FF15 6851C300 call dword ptr ds:[<&api-ms-win-crt-runt>; \exit
00C31265  |> \8D55 AC       lea edx,[local.21]

往上翻,记下段首00C31100

  2.看退出函数的返回后的用户代码段首是00C31100 ,用IDA F5反编译一下401100这个函数,看,主要用户代码都出来了,省时省力。

int sub_401100()
{
  signed int v0; // esi@1
  signed int v1; // esi@3
  unsigned int v2; // edi@3
  void **v3; // ebx@3
  void **v4; // eax@4
  int v5; // ecx@7
  int v6; // ST04_4@7
  int v7; // ST08_4@7
  int v8; // ST0C_4@7
  int v9; // eax@8
  char *v10; // esi@10
  int v11; // ecx@16
  void **v12; // eax@18
  int v13; // eax@20
  int v14; // ecx@20
  int v15; // eax@21
  int v16; // eax@21
  int v17; // eax@21
  int v18; // eax@21
  int v19; // eax@21
  int v20; // eax@21
  int v21; // eax@21
  const char *v22; // edx@21
  int v23; // eax@23
  int result; // eax@27
  int (__cdecl *v25)(int); // [sp-4h] [bp-13Ch]@20
  int Dst; // [sp+14h] [bp-124h]@7
  char v27[4]; // [sp+20h] [bp-118h]@7
  char v28; // [sp+24h] [bp-114h]@10
  int v29; // [sp+5Ch] [bp-DCh]@16
  char v30; // [sp+61h] [bp-D7h]@16
  int v31; // [sp+64h] [bp-D4h]@16
  int v32; // [sp+68h] [bp-D0h]@16
  char v33; // [sp+6Ch] [bp-CCh]@16
  FILE *File; // [sp+70h] [bp-C8h]@10
  char v35; // [sp+84h] [bp-B4h]@23
  void *v36; // [sp+CCh] [bp-6Ch]@1
  int v37; // [sp+DCh] [bp-5Ch]@1
  unsigned int v38; // [sp+E0h] [bp-58h]@1
  void *v39; // [sp+E4h] [bp-54h]@1
  int v40; // [sp+F4h] [bp-44h]@1
  unsigned int v41; // [sp+F8h] [bp-40h]@1
  void *Memory[4]; // [sp+FCh] [bp-3Ch]@1
  int v43; // [sp+10Ch] [bp-2Ch]@1
  unsigned int v44; // [sp+110h] [bp-28h]@1
  __int128 v45; // [sp+114h] [bp-24h]@1
  __int16 v46; // [sp+124h] [bp-14h]@1
  char v47; // [sp+126h] [bp-12h]@1
  int v48; // [sp+134h] [bp-4h]@1

  v41 = 15;
  v40 = 0;
  LOBYTE(v39) = 0;
  v48 = 0;
  v38 = 15;
  v37 = 0;
  LOBYTE(v36) = 0;
  LOBYTE(v48) = 1;
  v0 = 0;
  v43 = 1684630885;
  LOWORD(v44) = 97;
  *(_OWORD *)Memory = xmmword_40528C;
  v46 = 11836;
  v47 = 0;
  v45 = xmmword_4052A4;
  do
  {
    sub_4021E0(1u, (*((_BYTE *)Memory + v0) ^ *((_BYTE *)&v45 + v0)) + 22);
    ++v0;
  }
  while ( v0 < 18 );
  v1 = 0;
  v44 = 15;
  v43 = 0;
  LOBYTE(Memory[0]) = 0;
  LOBYTE(v48) = 2;
  v2 = v38;
  v3 = (void **)v36;
  do
  {
    v4 = &v36;
    if ( v2 >= 0x10 )
      v4 = v3;
    sub_4021E0(1u, *((_BYTE *)v4 + v1++) + 9);
  }
  while ( v1 < 18 );
  memset(&Dst, 0, 0xB8u);
  sub_401620(v5, v6, v7, v8);
  LOBYTE(v48) = 3;
  if ( v27[*(_DWORD *)(Dst + 4)] & 6 )
  {
    v9 = sub_402A00(std::cerr, "?W?h?a?t h?a?p?p?e?n?");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v9, sub_402C50);
    exit(-1);
  }
  sub_402E90(&Dst, &v39);
  v10 = &v28;
  if ( File )
  {
    if ( !(unsigned __int8)sub_4022F0(&v28) )
      v10 = 0;
    if ( fclose(File) )
      v10 = 0;
  }
  else
  {
    v10 = 0;
  }
  v33 = 0;
  v30 = 0;
  std::basic_streambuf<char,std::char_traits<char>>::_Init(&v28);
  v31 = dword_408590;
  File = 0;
  v32 = dword_408594;
  v29 = 0;
  if ( !v10 )
    std::basic_ios<char,std::char_traits<char>>::setstate((char *)&Dst + *(_DWORD *)(Dst + 4), 2, 0);
  v12 = Memory;
  if ( v44 >= 0x10 )
    v12 = (void **)Memory[0];
  v13 = sub_4020C0(v11, v40, v12, v43);
  v14 = std::cout;
  v25 = sub_402C50;
  if ( v13 )
  {
    v22 = "=W=r=o=n=g=K=e=y=";
  }
  else
  {
    v15 = sub_402A00(std::cout, "|------------------------------|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v15, sub_402C50);
    v16 = sub_402A00(std::cout, "|==============================|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v16, sub_402C50);
    v17 = sub_402A00(std::cout, "|==============================|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v17, sub_402C50);
    v18 = sub_402A00(std::cout, "|==============================|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v18, sub_402C50);
    v19 = sub_402A00(std::cout, "\\  /\\  /\\  /\\  /\\==============|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v19, sub_402C50);
    v20 = sub_402A00(std::cout, " \\/  \\/  \\/  \\/  \\=============|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v20, sub_402C50);
    v21 = sub_402A00(std::cout, "                 |-------------|");
    std::basic_ostream<char,std::char_traits<char>>::operator<<(v21, sub_402C50);
    std::basic_ostream<char,std::char_traits<char>>::operator<<(std::cout, sub_402C50);
    v14 = std::cout;
    v22 = "Congrats You got it!";
    v25 = sub_402C50;
  }
  v23 = sub_402A00(v14, v22);
  std::basic_ostream<char,std::char_traits<char>>::operator<<(v23, v25);
  sub_401570(&v35);
  std::basic_ios<char,std::char_traits<char>>::~basic_ios<char,std::char_traits<char>>(&v35);
  if ( v44 >= 0x10 )
    sub_402630(Memory[0], v44 + 1);
  if ( v2 >= 0x10 )
    sub_402630(v3, v2 + 1);
  result = v41;
  if ( v41 >= 0x10 )
    result = sub_402630(v39, v41 + 1);
  return result;
}

  3.代码是有了,但是还是没办法看出为什么退出了,搜一下可疑字符串,看到了:
1.png

一个文件路径比较可疑,点开看看,有一个API函数在尝试打开这个文件。

01052583  |.  6A 40         push 0x40
01052585  |.  6A 01         push 0x1
01052587  |.  68 B8520501   push key.010552B8                                            ;  C:\Users\CSAW2016\haha\flag_dir\flag.txt
0105258C  |.  FF15 BC500501 call dword ptr ds:[<&MSVCP140.std::_Fiopen>]                 ;  msvcp140.std::_Fiopen

  4.那我就去对应目录创建一个对应的文件,内容随便写一个52pojie试试,打开cmd窗口,输入:
mkdir C:\Users\CSAW2016\haha\flag_dir\
echo 52pojie>C:\Users\CSAW2016\haha\flag_dir\flag.txt

然后可以看到跳过了0004126E 这个退出函数,然后再看IDA中的代码,v13为1,就提示错误的key,可以看到最近的地方是000420C0影响了v13的值。

  5.OD下断点跑过来一看:

00041317  |.  FF75 D4       push [local.11]                                              ;  0x12
0004131A  |.  0F4345 C4     cmovnb eax,[local.15]
0004131E  |.  50            push eax                                                     ;  idg_cni~bjbfi|gsxb
0004131F  |.  FF75 BC       push [local.17]                                              ;  0x7
00041322  |.  51            push ecx                                                     ;  52pojie
00041323  |.  8D4D AC       lea ecx,[local.21]
00041326  |.  E8 950D0000   call key.000420C0

咦,为什么IDA里面会有5个参数看一下thiscall函数调用约定,从右往左传递,第一个参数是this指针。

  6.又是一段很长的代码,我们记住自己的目的就好,返回值必须为0。

signed int __thiscall sub_4020C0(int this, int a2, unsigned int a3, int a4, unsigned int a5)
{
  unsigned int v5; // edi@1
  unsigned int v6; // edx@5
  int v7; // esi@8
  unsigned int v8; // edx@8
  bool v9; // cf@12
  unsigned __int8 v10; // al@14
  unsigned __int8 v11; // al@16
  unsigned __int8 v12; // al@18
  signed int result; // eax@19

  v5 = a3;
  if ( *(_DWORD *)(this + 16) < a3 )
    v5 = *(_DWORD *)(this + 16);
  if ( *(_DWORD *)(this + 20) >= 0x10u )
    this = *(_DWORD *)this;
  v6 = a5;
  if ( v5 < a5 )
    v6 = v5;
  if ( v6 )
  {
    v7 = a4;
    v9 = v6 < 4;
    v8 = v6 - 4;
    if ( v9 )
    {
LABEL_11:
      if ( v8 == -4 )
        goto LABEL_20;
    }
    else
    {
      while ( *(_DWORD *)this == *(_DWORD *)v7 )
      {
        this += 4;
        v7 += 4;
        v9 = v8 < 4;
        v8 -= 4;
        if ( v9 )
          goto LABEL_11;
      }
    }
    v9 = *(_BYTE *)this < *(_BYTE *)v7;
    if ( *(_BYTE *)this != *(_BYTE *)v7
      || v8 != -3
      && ((v10 = *(_BYTE *)(this + 1), v9 = v10 < *(_BYTE *)(v7 + 1), v10 != *(_BYTE *)(v7 + 1))
       || v8 != -2
       && ((v11 = *(_BYTE *)(this + 2), v9 = v11 < *(_BYTE *)(v7 + 2), v11 != *(_BYTE *)(v7 + 2))
        || v8 != -1 && (v12 = *(_BYTE *)(this + 3), v9 = v12 < *(_BYTE *)(v7 + 3), v12 != *(_BYTE *)(v7 + 3)))) )
    {
      result = -v9 | 1;
      goto LABEL_21;
    }
LABEL_20:
    result = 0;
LABEL_21:
    if ( result )
      return result;
  }
  if ( v5 >= a5 )
    result = v5 != a5;
  else
    result = -1;
  return result;
}

从result 0反向推导,然后定位到LABEL_11:,能看到v8不能等于-4,继续往下看result = -v9 | 1;上面计算中v9必须为0,再继续往下看result = v5 != a5;,可以知道这里v5必须等于a5,这个过程参数很多,有点头皮发麻,动态调试走一下,看看能不能看出点信息。

  7.可以看到是拿52pojie和idg_cni~bjbfi|gsxb相比较,这里比较完,都相等,然后走je跳到xor eax,eax就是正确的流程。

00A52104  |. /74 34         je short key.00A5213A
00A52106  |> |8A01          mov al,byte ptr ds:[ecx]                                     ;  ecx寄存器:52pojie
00A52108  |. |3A06          cmp al,byte ptr ds:[esi]                                     ;  eax寄存器:idg_cni~bjbfi|gsxb
00A5210A  |. |75 27         jnz short key.00A52133
00A5210C  |. |83FA FD       cmp edx,-0x3
00A5210F  |. |74 29         je short key.00A5213A
00A52111  |. |8A41 01       mov al,byte ptr ds:[ecx+0x1]
00A52114  |. |3A46 01       cmp al,byte ptr ds:[esi+0x1]
00A52117  |. |75 1A         jnz short key.00A52133
00A52119  |. |83FA FE       cmp edx,-0x2
00A5211C  |. |74 1C         je short key.00A5213A
00A5211E  |. |8A41 02       mov al,byte ptr ds:[ecx+0x2]
00A52121  |. |3A46 02       cmp al,byte ptr ds:[esi+0x2]
00A52124  |. |75 0D         jnz short key.00A52133
00A52126  |. |83FA FF       cmp edx,-0x1
00A52129  |. |74 0F         je short key.00A5213A
00A5212B  |. |8A41 03       mov al,byte ptr ds:[ecx+0x3]
00A5212E  |. |3A46 03       cmp al,byte ptr ds:[esi+0x3]
00A52131  |. |74 07         je short key.00A5213A
00A52133  |> |1BC0          sbb eax,eax
00A52135  |. |83C8 01       or eax,0x1
00A52138  |. |EB 02         jmp short key.00A5213C
00A5213A  |> \33C0          xor eax,eax

  8.那我把文件的内容替换成idg_cni~bjbfi|gsxb,然后运行程序发现提示成功了,提交flag,正确:
2.png

0x3总结

  1.这道题看上去很难,想要返回值0,好似要经过多番运算,最后OD一调试,实际上就是把两个传进来的字符串作比较。

  2.刚开始下断点想着exitprocess,没回溯过去,习惯了点OD上面的API断点,后来想到exit就断下,回溯过去了。

  3.有时候看不出反编译的那一段代码作用时,用OD跑起来看看,会有意外发现。

  4.OD和IDA都用起来,便于定位和分析问题。

0x4参考资料

加密解密第四版-函数的调用约定

  PS:善于总结,善于发现,找到分析问题的思路和解决问题的办法。虽然我现在还是零基础的小菜鸟一枚,也许学习逆向逆天改命我会失败,但也有着成功的可能,只要还有希望,就决不放弃!

免费评分

参与人数 14吾爱币 +22 热心值 +12 收起 理由
1257135463 + 1 我很赞同!
Hmily + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Carrot-Chou + 3 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
hujie110290 + 1 + 1 我很赞同!
妈咪她亲我 + 1 + 1 论坛禁止求脱求破,求助软件分析思路,务必在主题帖中描述清楚你的分析思路.
vince_z + 1 + 1 用心讨论,共获提升!
pipi125874 + 1 + 1 命在你自己手里!就看你如何改。加油!
yan13733960937 + 1 + 1 谢谢@Thanks!
fqr2009 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
linden007x + 1 用心讨论,共获提升!
wuliwuli + 1 热心回复!
sharenle + 1 + 1 我很赞同!
huwoniu + 2 + 1 用心讨论,共获提升!
hanschris + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| 小菜鸟一枚 发表于 2022-2-7 18:31
snowhee 发表于 2022-2-7 17:21
师傅我想问一下,result 0反向推导,定位到LABEL_11,然后判断v8等于-4才能跳转LABEL_20,如果v8不等于-4, ...

我们的目的是return 0,只有lable 20才会给将返回值置0,然后向上定位到lable 11,那么v8就必须等于-4,这样就就不会执行lable 21前面那个if,不然,result值必然非0,if一判断true,返回result了。我们必须让他跳转到lable 21,但是不能执行前两行,所以必须先让result为0,跳过第一个if,然后执行这个if ( v5 >= a5 ),这里v5必须等于a5,然后就return 0了.
pipi125874 发表于 2020-10-5 07:14
总有癫嘿想坑朕 发表于 2020-10-4 10:04
看不懂,但是感觉很厉害,支持你一下,活到老学到老,我快40了,刚开始学python,共勉一个。

共勉一个。同样的人,同样的在做,都是一开始!
cptw 发表于 2020-10-3 13:10
樊汶鑫 发表于 2020-10-3 13:43
好像有点看懂,找了半天找出了判断条件和判断结果,然后修改判断结果来达到想要的结果是吧
dbg2020 发表于 2020-10-3 14:53
支持原创。破解是个体力活,学习破解是脑力活
fireant 发表于 2020-10-3 15:17
贵在坚持,看来我也得好好学习
Leland 发表于 2020-10-3 15:27
巧遇学长,我是华科计院的大一新生,在准备ctf的招新
以后应该也会学这些吧。
静一静 发表于 2020-10-3 16:22
这个游戏还是不错的
行则将至 发表于 2020-10-3 16:23
前排膜拜大神
xiaogui00 发表于 2020-10-3 16:57
谢谢分享,学习了
cheng911001 发表于 2020-10-3 23:14
up加油。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-16 01:37

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表