吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3914|回复: 20
收起左侧

[CTF] 【CTF reverse】SMC好题赏析——网鼎杯2020青龙组jocker、SCUx401CTF2021-RE2-pixpix

[复制链接]
hans7 发表于 2022-7-27 02:11
本帖最后由 hans7 于 2022-7-27 02:18 编辑

SMC,即self modifying code,自修改代码。

为什么是好题呢?因为我刚好都会写

本文52pojie:https://www.52pojie.cn/thread-1667202-1-1.html

本文juejin:https://juejin.cn/post/7124745598271488030/

作者:hans774882968以及hans774882968

【网鼎杯2020青龙组】jocker

传送门:在buuoj上找qwq。

依赖

IDA版本为7.7。

分析

32位,Section: [.text], EP: 0x000008E0故无壳。用IDA打开,一进来就是main函数。

IDA7.7相比于6.6强大了许多,终于positive sp的代码也可以反编译了,感动!

// positive sp value has been detected, the output may be wrong!
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char Str[50]; // [esp+12h] [ebp-96h] BYREF
  char Destination[80]; // [esp+44h] [ebp-64h] BYREF
  DWORD flOldProtect; // [esp+94h] [ebp-14h] BYREF
  size_t v7; // [esp+98h] [ebp-10h]
  int i; // [esp+9Ch] [ebp-Ch]

  __main();
  puts("please input you flag:");
  if ( !VirtualProtect(encrypt, 0xC8u, 4u, &flOldProtect) )
    exit(1);
  scanf("%40s", Str);
  v7 = strlen(Str);
  if ( v7 != 24 )
  {
    puts("Wrong!");
    exit(0);
  }
  strcpy(Destination, Str);
  wrong(Str);
  omg(Str);
  for ( i = 0; i <= 186; ++i )
    *((_BYTE *)encrypt + i) ^= 0x41u;
  if ( encrypt(Destination) )
    finally(Destination);
  return 0;
}

可以发现encrypt是SMC(self modifying code),写一个IDAPython脚本:

import idc
addr = 0x401500  # encrypt函数的地址
for i in range(187):
    b = get_bytes(addr + i, 1)
    idc.patch_byte(addr + i, ord(b) ^ 0x41)

运行IDAPython脚本:选择File -> Script File…

接下来,需要先右键undefine encrypt的函数定义,键盘不断按c,把所有孤立的db xx的数据全部强转为汇编代码,保证db xx不再出现。然后Edit -> functions -> Create function...重新创建函数。这样就能成功得到encrypt函数。

int __cdecl encrypt(char *a1)
{
  int v2[19]; // [esp+1Ch] [ebp-6Ch] BYREF
  int v3; // [esp+68h] [ebp-20h]
  int i; // [esp+6Ch] [ebp-1Ch]

  v3 = 1;
  qmemcpy(v2, &unk_403040, sizeof(v2));
  for ( i = 0; i <= 18; ++i )
  {
    if ( (char)(a1[i] ^ Buffer[i]) != v2[i] )
    {
      puts("wrong ~");
      v3 = 0;
      exit(0);
    }
  }
  puts("come here");
  return v3;
}

我们写脚本,执行以后,可以看到没有拿到完整flag。接下来看finally函数。finally函数也被smc影响了,所以同样需要进行encrypt函数的流程,才能拿到代码。

int __cdecl finally(char *a1)
{
  unsigned int v1; // 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;
  v3[6] = 0;
  *(_WORD *)&v3[7] = 0;
  if ( (v3[(unsigned __int8)v3[5]] != a1[(unsigned __int8)v3[5]]) == v4 )
    return puts("Really??? Did you find it?OMG!!!");
  else
    return puts("I hide the last part, you will not succeed!!!");
}

这里我认为是无解的了。查了答案发现,是社工,假设你已经知道:

  • 对常量串"%tp&:"的操作和前面一样,是xor。
  • 最后一个字符是'}'

于是可以解出最后5个字符。

代码

buffer = 'hahahaha_do_you_find_me?'
v2 = [
    0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x09, 0x00,
    0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00,
    0x05, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x56, 0x00,
    0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
    0x0C, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x1F, 0x00,
    0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
    0x6B, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x59, 0x00,
    0x00, 0x00, 0x0D, 0x00, 0x00, 0x00
]
ans = ''
for i in range(0, len(v2), 4):
    ans += chr(ord(buffer[i // 4]) ^ v2[i])
# 最后5位
v3 = '%tp&:'
xor_v = ord(v3[-1]) ^ ord('}')
for c in v3:
    ans += chr(xor_v ^ ord(c))
print(ans)

SCUx401CTF2021-RE2-pixpix

传送门

依赖

IDA7.7

分析

32位,Section: [.text], EP: 0x000007AD故无壳。

打开IDA,立刻到了main函数

int __cdecl main(int argc, const char **argv, const char **envp)
{
  HDC DC; // eax
  COLORREF Pixel; // eax
  unsigned int v5; // edi
  unsigned int v6; // kr00_4
  unsigned int v7; // esi
  int v9; // [esp+0h] [ebp-Ch]
  DWORD flOldProtect; // [esp+8h] [ebp-4h] BYREF

  DC = GetDC(0);
  Pixel = GetPixel(DC, 401, 401);
  word_9A3384 = Pixel;
  byte_9A3386 = BYTE2(Pixel);
  VirtualProtect(sub_9A1050, (char *)nullsub_1 - (char *)sub_9A1050, 0x40u, &flOldProtect);
  v5 = 0;
  if ( (char *)nullsub_1 != (char *)sub_9A1050 )
  {
    do
    {
      v6 = v5;
      v7 = v5++;
      *((_BYTE *)sub_9A1050 + v7) ^= *((_BYTE *)&word_9A3384 + v6 % 3);
    }
    while ( v5 < (char *)nullsub_1 - (char *)sub_9A1050 );
  }
  VirtualProtect(sub_9A1050, (char *)nullsub_1 - (char *)sub_9A1050, flOldProtect, &flOldProtect);
  sub_9A1050(v9);
  return 0;
}

nullsub_1的地址是9A10B0,所以进行运行时解密的范围是9A1050 ~ 9A10B0。而运行时解密仅仅是一个简单的异或。

word_9A3384相当于一个长为3的数组,数组内容是(401,401)处的像素值。因为动态调试可以修改任意处内存的值,所以也就相当于你输入的内容

我们知道,c语言程序生成的x86 exe,其各函数头是固定的,目的是处理栈帧。具体是什么呢?查看main函数开头的汇编:

.text:009A10C0 55                            push    ebp
.text:009A10C1 8B EC                         mov     ebp, esp

另外,0x9a1050处的值为61 BB DD。所以我们求出0x34, 0x30, 0x31为所求数组。

法一

那么我们写一段IDAPython脚本:

import idc
st_addr = 0x9a1050
ed_addr = 0x9a10b0
pix = [0x34, 0x30, 0x31]
for i in range(st_addr, ed_addr):
    b = get_bytes(i, 1)
    idc.patch_byte(i, ord(b) ^ pix[(i - st_addr) % 3])

脚本运行方法和运行后的undefine+重新创建函数的操作同jocker那题。

于是我们拿到

int sub_9A1050()
{
  char v1[28]; // [esp+0h] [ebp-20h] BYREF
  int v2; // [esp+1Ch] [ebp-4h]

  strcpy(v1, "scuctf{pixel!pixel!pixel!}");
  v1[27] = 0;
  v2 = 0;
  return ((_DWORD (__cdecl *)(char *))sub_9A1010)(v1);
}

int sub_9A1010(char *Format, ...)
{
  unsigned __int64 *v1; // eax
  FILE *v3; // [esp-10h] [ebp-18h]
  va_list va; // [esp+14h] [ebp+Ch] BYREF

  va_start(va, Format);
  v3 = _acrt_iob_func(1u);
  v1 = (unsigned __int64 *)sub_9A1000();
  return _stdio_common_vfprintf(*v1, v3, Format, 0, va);
}

答案很明显了。

法二

当然,你也可以用x64dbg动态调试的方式,在GetPixel()执行完后修改word_9A3384处的内容,然后让它正常执行直接输出flag。动态调试做法的好处是:

  • 不需要写idc脚本。
  • 也许不需要知道代码写的啥。

免费评分

参与人数 2吾爱币 +8 热心值 +2 收起 理由
Hmily + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
ddddhm + 1 + 1 热心回复!

查看全部评分

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

WuJ1n9 发表于 2022-7-27 11:09
jocker最后5个字符是真的恶心,当年耗了半天在这儿,这种与misc的结合挺没意思的
 楼主| hans7 发表于 2022-7-27 23:01
WuJ1n9 发表于 2022-7-27 11:09
jocker最后5个字符是真的恶心,当年耗了半天在这儿,这种与misc的结合挺没意思的

我也觉得这个答案挺牵强的
LuHaHa627 发表于 2022-7-27 11:19
可以,很6

免费评分

参与人数 1吾爱币 -1 收起 理由
WuJ1n9 -1 请勿灌水,提高回帖质量是每位会员应尽的义务!

查看全部评分

silencespy 发表于 2022-7-27 11:35
感谢分享 ,牛逼
XgbbtmjLnn 发表于 2022-7-27 11:38
感谢分享 ,牛逼
BMK 发表于 2022-7-27 12:50
学习了!!
lanlinux 发表于 2022-7-27 13:49
学习了 牛皮
zohuan 发表于 2022-7-27 17:53
IDA7.7哪下的...
qq420984 发表于 2022-7-27 19:06

感谢分享 ,牛逼
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

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

GMT+8, 2024-5-16 17:28

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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