吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3429|回复: 14
收起左侧

[CTF] [网鼎杯 2020 青龙组]jocker分析与记录

  [复制链接]
Tokameine 发表于 2021-11-13 10:31
前言:

        如您所见的是,图片似乎带有水印,但这并不意味着它是转载的,因为某站上的这篇帖子也是我写的......好不容易终于注册上这个大佬如云的地方的账号了,于是整理一下也发在这里了。另外一边已经完全弃用了。
        自己的水平还不是很高,欢迎大佬们交流。如果您发现我的叙述存在任何问题,也请务必纠正!感谢!

正文:
        无壳,IDA打开可以直接进入main函数:


        第12行调用VirtualProtect函数更改了offset encrypt处的访问保护权限
        BOOL VirtualProtect(  LPVOID lpAddress,  SIZE_T dwSize,  DWORD  flNewProtect,  PDWORD lpflOldProtect);

参见:Memory Protection Constants (WinNT.h) - Win32 apps | Microsoft Docs


         该处数据为0x4:PAGE_READWRITE

Enables read-only or read/write access to the committed region of pages. If Data Execution Prevention is enabled, attempting to execute code in the committed region results in an access violation.


        简单来说就是让这块数据能够被读写了(通常text段中只能拥有读/写中的一种)
        继续往下读,发现输入值应该符合 24字节 的长度,然后遇到wrong 和 omg这两个函数
[C] 纯文本查看 复制代码
char *__cdecl wrong(char *a1)
{
  char *result; // eax
  int i; // [esp+Ch] [ebp-4h]

  for ( i = 0; i <= 23; ++i )
  {
    result = &a1[i];
    if ( (i & 1) != 0 )
      a1[i] -= i;
    else
      a1[i] ^= i;
  }
  return result;
}


[C] 纯文本查看 复制代码
int __cdecl omg(char *a1)
{
  int result; // eax
  int v2[24]; // [esp+18h] [ebp-80h] BYREF
  int i; // [esp+78h] [ebp-20h]
  int v4; // [esp+7Ch] [ebp-1Ch]

  v4 = 1;
  qmemcpy(v2, &unk_4030C0, sizeof(v2));
  for ( i = 0; i <= 23; ++i )
  {
    if ( a1[i] != v2[i] )
      v4 = 0;
  }
  if ( v4 == 1 )
    result = puts("hahahaha_do_you_find_me?");
  else
    result = puts("wrong ~~ But seems a little program");
  return result;
}


        wrong对输入值进行了一些加减或异或处理,然后将结果在omg中同unk_4030C0处数据进行对比;wrong的逆算法容易实现,照抄就行了

         (现在才知道能够通过导出窗口快捷的提取出数据,一直以来的手抄实在是太笨了)

[] 纯文本查看 复制代码
        unsigned int k[24] = { 0x66,0x6b,0x63,0x64,0x7f,0x61,0x67,0x64,0x3b,0x56,0x6b,0x61,0x7b,0x26,0x3b,0x50,0x63,0x5f,0x4d,0x5a,0x71,0xc,0x37,0x66 };
        for (int i = 0;i < 24; i++)
        {
                if ((i & 1) != 0)
                {
                        k[i] += i;
                }
                else
                {
                        k[i] ^= i;
                }
                cout << (char)k[i];
        }
        cout << endl;

        得到结果flag{fak3_alw35_sp_me!!},提交发现错误;由于往下还有关键的encrypt段没分析,所以不用太怀疑flag是否算错,可以大胆的将它当作一个假的flag
        再往下读for循环,发现它对offset encrypt进行了异或,判断其为代码段解密,可以用动调转到这个地方


         IDA没能及时更新,需要我们手动修正为函数
        选中00401500~0040152F,将其标为代码(Force)

         然后在00401502处创建函数,即可得到合适的结果


[C++] 纯文本查看 复制代码
// positive sp value has been detected, the output may be wrong!
void __usercall __noreturn sub_401502(int a1@<ebp>)
{
  unsigned __int32 v1; // eax

  v1 = __indword(0x57u);
  *(_DWORD *)(a1 - 32) = 1;
  qmemcpy((void *)(a1 - 108), &unk_403040, 0x4Cu);
  for ( *(_DWORD *)(a1 - 28) = 0; *(int *)(a1 - 28) <= 18; ++*(_DWORD *)(a1 - 28) )
  {
    if ( (char)(*(_BYTE *)(*(_DWORD *)(a1 - 28) + *(_DWORD *)(a1 + 8)) ^ Buffer[*(_DWORD *)(a1 - 28)]) != *(_DWORD *)(a1 + 4 * *(_DWORD *)(a1 - 28) - 108) )
    {
      puts("wrong ~");
      *(_DWORD *)(a1 - 32) = 0;
      exit(0);
    }
  }
  if ( *(_DWORD *)(a1 - 32) == 1 )
    puts("come here");
}



        IDA分析得到的代码并不是那么易读,显然,它将一些索引给翻译错了,但并非无法理解的程度
        首先,提取unk_403040处的数据放入(a1-108)处,以及循环中用到的Buffer
[C++] 纯文本查看 复制代码
        char Buffer[] = "hahahaha_do_you_find_me?";
        unsigned int unk_403040[19] = {0x0E,0x0D ,0x09 ,0x06 ,0x13 ,0x05 ,0x58 ,0x56 ,0x3E ,0x06 ,0x0C ,0x3C ,0x1F ,0x57 ,0x14 ,0x6B ,0x57 ,0x59 ,0x0D };

         *(a1-28)实际上是一个索引,指示了这个循环会执行19次;而(*(a1 - 28) + *(a1 + 8))相当于输入值指针加上一个偏移,其内容就是我们的输入值
        这个输入值和Buffer异或后的结果应该等于(a1 - 108)的内容,也就是unk_403040处的数据,同样也容易写出解密代码
[C++] 纯文本查看 复制代码
        char key1[] = "hahahaha_do_you_find_me?";
        unsigned int f[19] = {0x0E,0x0D ,0x09 ,0x06 ,0x13 ,0x05 ,0x58 ,0x56 ,0x3E ,0x06 ,0x0C ,0x3C ,0x1F ,0x57 ,0x14 ,0x6B ,0x57 ,0x59 ,0x0D };
        for (int i = 0; i < 19; i++)
        {
                f[i] ^= key1[i];
                cout << (char)f[i];
        }
        cout << endl;


得到flag{d07abccf8a410c
我们知道,flag应有24字节,但for循环只有19次,也就是缺少了5个字符;由于encrypt函数已经读完了,所以我们需要的结果应该在最后一个函数中,也就是finally函数

        将40159A~40159D处的数据全都转为代码,并将函数改为Undefine

         重新在40159A处创建函数,得到新函数finally:


[C++] 纯文本查看 复制代码
int __cdecl finally(char *a1)
{
  unsigned int v1; // eax
  int result; // eax
  char v3[9]; // [esp+13h] [ebp-15h] BYREF
  int v4; // [esp+1Ch] [ebp-Ch]

  strcpy(v3, "%tp&:");
  v1 = time(0);
  srand(v1);
  v4 = rand() % 100;
  if ( (v3[*&v3[5]] != a1[*&v3[5]]) == v4 )
    result = puts("Really??? Did you find it?OMG!!!");
  else
    result = puts("I hide the last part, you will not succeed!!!");
  return result;
}


        time(0)用以获取当前时间,第10行将其作为种子,第11行获取随机数;大概率我们是难以获取到出题人得到的种子的,因此,这个随机数若是必要的,应该只能通过预测得出
        以及下面的if判断条件过于难以理解,不妨试着用OD去动调一下吧(个人觉得OD的动调会更好用一些,也好在这个函数没有被加密,OD还是能分析出来的,否则只能用IDA动调了,虽然没什么差别......)

        即便用OD动调也仍然不是很容易能够读懂其意义
        关键的比较在401617处,如果相等的话,就说明flag输对了
        大致就是取flag的第几位同“%tp&:"几位,相等即可;并且这正好是5个字节,很可能就是剩下的flag
        但汇编代码中似乎也同样没有相应的加密过程,只能靠猜测它没有被复杂的加密
        通过前半段的flag猜测最后一个字符应该为‘}’,将其与“%tp&:”的最后一个异或后得到 71,并由此得到最后结果

[C++] 纯文本查看 复制代码
        char key2[] = "%tp&:";
        int v5 = '}' ^ key2[4];
        for (int i = 0; i < 5; i++)
        {
                cout << (char)(key2[i] ^ v5);
        }
//flag{d07abccf8a410cb37a}


        我也试着将这个提交成功的flag输入进去,但它仍然不会输出成功的标识,可能是出题人的一点“恶意”吧......最后要靠猜测来得到结果,说实在的,有点难以释然,总觉得是不是自己看漏了什么重要内容......

免费评分

参与人数 3威望 +1 吾爱币 +21 热心值 +2 收起 理由
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Luka + 1 谢谢@Thanks!
jindar + 1 热心回复!

查看全部评分

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

APWN 发表于 2024-10-24 01:18
就差最后一部分想弄出来,看不到图片真的好难受.....我的汇编代码处就只有这部分,之后有一段很长的数据:
[Asm] 纯文本查看 复制代码
.text:0040159A
.text:0040159A
.text:0040159A                                                     ; _DWORD __cdecl finally(char *)
.text:0040159A                                                                     public __Z7finallyPc
.text:0040159A                                                     __Z7finallyPc   proc near               ; CODE XREF: _main+121↓p
.text:0040159A 000 14 C8                                                           adc     al, 0C8h        ; Add with Carry
.text:0040159C 000 A4                                                              movsb                   ; Move Byte(s) from String to String
.text:0040159D 000 C2 AD 69                                                        retn    69ADh           ; Return Near from Procedure
.text:0040159D
.text:0040159D                                                     __Z7finallyPc   endp
.text:0040159D
.text:0040159D                                                     ; ---------------------------------------------------------------------------
.text:004015A0 87 04 AA 64 87 04 AD 35 87 04 AC 31 87 04 AF 67+                    dd 64AA0487h, 35AD0487h, 31AC0487h, 67AF0487h, 7BAE0487h
.text:004015A0 87 04 AE 7B 86 45 65 41 41 41 41 E8 68 13 00 00+                    dd 41654586h, 0E8414141h, 1368h, 0E8240489h, 1368h, 136BE8h
.text:004015A0 89 04 24 E8 68 13 00 00 E8 6B 13 00 00 89 C1 BA+                    dd 0BAC18900h, 51EB851Fh, 0EAF7C889h, 8905FAC1h, 1FF8C1C8h
.text:004015A0 1F 85 EB 51 89 C8 F7 EA C1 FA 05 89 C8 C1 F8 1F+                    dd 0D089C229h, 8BF44589h, 0C06BF445h, 89C12964h, 0F44589C8h
.text:004015A0 29 C2 89 D0 89 45 F4 8B 45 F4 6B C0 64 29 C1 89+                    dd 0F045C7h, 83000000h, 7F04F07Dh, 0EB558D3Eh, 1F0458Bh
.text:004015A0 C8 89 45 F4 C7 45 F0 00 00 00 00 83 7D F0 04 7F+                    dd 10B60FD0h, 8BF04D8Bh, 0C8010845h, 3800B60Fh, 0C0950FC2h
.text:004015A0 3E 8D 55 EB 8B 45 F0 01 D0 0F B6 10 8B 4D F0 8B+                    dd 3BC0B60Fh, 0E74F445h, 2C2404C7h, 0E8004040h, 1310h
.text:004015A0 45 08 01 C8 0F B6 00 38 C2 0F 95 C0 0F B6 C0 3B+                    dd 4C70DEBh, 40405C24h, 1302E800h, 0C9900000h
.text:00401640                                                     ; ---------------------------------------------------------------------------
.text:00401640 C3                                                                  retn                    ; Return Near from Procedure

有大佬知道这是为什么吗,IDA的版本是7.7
涛之雨 发表于 2021-11-13 14:50
本帖最后由 涛之雨 于 2021-11-18 18:07 编辑

一起加油!
提几个建议
1. 推荐试用md,bbcode真的有点不fashion了。
2. 自己的文章别的地方的水印无所谓,只要不违规都行(公众号,QQ空间啊什么之类的带联系方式的肯定不行)
3. 图片尽量上传原图,就算是从别的地方转发过来的最好也下载后上传,有些地方会检测盗链。
4. 吾爱挺好,我写教程说我违规侵权,然后那个下载站里到处都是乱七八糟的东西,不少人都转cnblog或者是自己搭小站了(我是懒。。。)
GeekPwn 发表于 2021-11-13 16:39
 楼主| Tokameine 发表于 2021-11-13 17:44
涛之雨 发表于 2021-11-13 14:50
一起加油!
提几个建议
1. 推荐试用md,bbcode真的有点不fashion了。

感谢版主大大建议!
这篇文章当时在C某某N那里富文本写的,当时还并不会用markdown呢,现在再想转一遍又觉得有些麻烦,用md重写的话就更麻烦了(懒癌犯了)
图片以后会注意一下,尽量直接传原图的。
感谢大佬建议qwq
头像被屏蔽
Spoofing 发表于 2021-11-13 18:08
提示: 作者被禁止或删除 内容自动屏蔽
Fxmles 发表于 2021-11-14 11:42
学习了,不错
Ablly 发表于 2021-11-14 12:05
学习了,谢谢分享
Luka 发表于 2021-11-14 13:20
学习了,写的非常详细!
头像被屏蔽
16du 发表于 2021-11-14 21:03
提示: 作者被禁止或删除 内容自动屏蔽
Hmily 发表于 2021-11-18 17:52
我给你编辑了下,看起来csdn水印通过链接参数加上去的,你其实可以把图片上传论坛本地。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

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

GMT+8, 2024-11-22 11:54

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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