BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第1张图片

先看下文件信息,没有加壳,32位程序

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第2张图片

运行一下,又是一道字符串比较的题目

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第3张图片

用IDA32位打开,分析一下

// 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, 200u, 4u, &flOldProtect) )
    exit(1);
  scanf("%40s", Str);
  v7 = strlen(Str);
  if ( v7 != 24 )                               // flag长度为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) )
    ((void (__cdecl *)(char *))finally)(Destination);
  return 0;
}

这里有个函数,详情可以看这个VirtualProtect函数,大概就是设置这一段内容可以改变

if ( !VirtualProtect(encrypt, 200u, 4u, &flOldProtect) )

下面这里将这段地址都异或了一个0x41

for ( i = 0; i <= 186; ++i )
    *((_BYTE *)encrypt + i) ^= 0x41u;

而且无法访问encrypt这段地址,然后看一下汇编代码

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第4张图片

这里就是经过异或后的结果

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第5张图片

这几个函数对输入的字符串进行变换

 scanf("%40s", Str);
  v7 = strlen(Str);
  if ( v7 != 24 )                               // flag长度为24
  {
    puts("Wrong!");
    exit(0);
  }
  strcpy(Destination, Str);
  wrong(Str);
  omg(Str);

跟进查看

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;
}
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;
}

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第6张图片

这得到的肯定不是真的字符串,写个脚本输出一下看看

#include 
#include 
#include 

int main()
{
   int i,j,k;
   int a[] = {0x66,0x6B,0x63,0x64,
              0x7F,0x61,0x67,0x64,
              0x3B,0x56,0x6B,0x61,
              0x7B,0x26,0x3B,0x50,
              0x63,0x5F,0x4D,0x5A,
              0x71,0x0C,0x37,0x66};
    for(i = 0 ; i <= 23 ; i++)
    {
        if((i & 1) != 0)
            a[i] += i;
        else
            a[i] ^= i;
        printf("%c",a[i]);
    }
    return 0;
}

运行结果

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第7张图片

参考了一下大佬的博客,这个部分原来是代码加密后的结果,叫SMC加密

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第8张图片

这段代码是对加密后的代码进行自解密,除了动态调试,还可以用SMC代码字修改来解决问题,具体参考这个SMC代码自修改基础练习题以及防反编译之SMC(自修改代码)

for ( i = 0; i <= 186; ++i )
    *((_BYTE *)encrypt + i) ^= 0x41u;

然后用动态调试来解决问题,可以参考这个IDA动调exe,因为是第一次用IDA动调,所以写得详细一点

步骤:1.找到文件夹IDA目录下的dbgsrv文件夹,找到对应版本的win_32或者64remote.exe,点击运行。

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第9张图片

这个程序是32位的,所以我运行了这个win32remote,不知道win64行不行

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第10张图片
BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第11张图片

2.打开ida,将exe拖入,Debugger → Select a debugger → Remote windows debugger

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第12张图片

3.Debugger → process options → 填写三行(第一,二行为 文件的路径,第三行为空,也可设置为文件对应的根目录) 和 Hostname设为127.0.0.1(代表本机)这里前面三行都是自动填好的,所以只要填那个Hostname就行。端口是自动设置的,别改,改了就出问题

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第13张图片

先运行我们要调试的软件,然后Debugger → Attach to process… → 选中我们要调试的程序 → attach

先运行起来

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第14张图片

找不到的话可以搜索一下

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第15张图片
BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第16张图片

然后就能正常debug了,然后就是要下断点,不下断点一下输入flag后一下就结束了,要下断点就得把这个Use source-level debugging 给选上

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第17张图片

先下个断点,可以打开反汇编面板,然后按f5查看伪代码

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第18张图片

在if这里下一个断点,然后一直f8跳过

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第19张图片

然后输入之前得到的假flag

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第20张图片

继续运行,这里会调用那个encrypt,按f7跟进

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第21张图片

既然执行到这里了,那么后面的代码已经解密完成,选取401500401589,然后按c,选择force强制转换为代码。

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第22张图片BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第23张图片
BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第24张图片

(如果将解密后的数据全部选中一次性转为代码,转换成伪代码的时候后半部分就无法正常转为伪代码)

然后选中这些红色部分的代码,按p转为函数然后就能愉快的f5了

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第25张图片
BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第26张图片

转换后的代码

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第27张图片

// positive sp value has been detected, the output may be wrong!
int __usercall sub_401502@<eax>(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");
  return *(_DWORD *)(a1 - 32);
}

稍微有点绕,就是一串字符异或Buffer里的字符后要等于unk_403040

Buffer

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第28张图片

unk_403040

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第29张图片

右键转到hex窗口

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第30张图片

批量替换是个好东西

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第31张图片

搞个小脚本把原来的字符串弄出来

#include 
#include 
#include 

int main()
{
   int i,j,k;
    int b[] = { 0x0E,0x0D,0x09,0x06,
                0x13,0x05,0x58,0x56,
                0x3E,0x06,0x0C,0x3C,
                0x1F,0x57,0x14,0x6B,
                0x57,0x59,0x0D};
    char s[] = "hahahaha_do_you_find_me?";
    char flag[25];
    for(i = 0 ; i < 19; i++)
    {
        flag[i] = s[i] ^ b[i];
    }
    flag[i] = '\0';
    printf("%s\n",flag);
    return 0;
}

运行结果: flag{d07abccf8a410c

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第32张图片

明显后面缺了一块,继续按照上面的步骤将下面一段数据强转为代码

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第33张图片

然后在开始的位置右键创建一个函数,然后继续f5大法

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第34张图片

// positive sp value has been detected, the output may be wrong!
int __usercall sub_4015A0@<eax>(int a1@<ebp>)
{
  unsigned int v1; // eax
  int v2; // eax
  int result; // eax

  *(_BYTE *)(a1 - 21) = 37;
  *(_BYTE *)(a1 - 20) = 116;
  *(_BYTE *)(a1 - 19) = 112;
  *(_BYTE *)(a1 - 18) = 38;
  *(_BYTE *)(a1 - 17) = 58;
  v1 = time(0);
  srand(v1);
  v2 = rand();
  *(_DWORD *)(a1 - 12) = v2 / 100;
  result = v2 - 100 * *(_DWORD *)(a1 - 12);
  *(_DWORD *)(a1 - 12) = result;
  *(_DWORD *)(a1 - 16) = 0;
  if ( *(int *)(a1 - 16) <= 4 )
  {
    if ( (*(_BYTE *)(a1 - 21 + *(_DWORD *)(a1 - 16)) != *(_BYTE *)(*(_DWORD *)(a1 - 16) + *(_DWORD *)(a1 + 8))) == *(_DWORD *)(a1 - 12) )
      result = puts("Really??? Did you find it?OMG!!!");
    else
      result = puts("I hide the last part, you will not succeed!!!");
  }
  return result;
}

这代码挺奇怪的,估计是我方法有问题,这是别的大佬弄出来的代码

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第35张图片

这个大佬的博客推出了后面几位的方法,可以去看他的博客

大概就是说后面的5位数异或了一位数然后等于 %tp&:

(大佬的博客上面的)

25h,74h,70h,26h,3Ah

通过猜测,最后一位应该是},再猜0x3A(:)和 之间是异或关系,得到的值为71,再将之前的四个值都和71异或,得到b37a}

合起来最终的flag就是: flag{d07abccf8a410cb37a}

然后再试一下SMC解,从这个大佬那里嫖了个代码,存为idc文件

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第36张图片

然后运行,接着重复上面的强转代码操作,一样能看到伪代码

BUUCTF Reverse/[网鼎杯 2020 青龙组]jocker_第37张图片

然后再看下finally的代码,emmm更奇怪了

// positive sp value has been detected, the output may be wrong!
int __usercall sub_4015A0@<eax>(__int32 a1@<eax>, int a2@<edx>, int a3@<ebp>, int a4@<edi>)
{
  __int32 v4; // eax
  unsigned int v5; // eax
  int v6; // ebp
  unsigned int v7; // eax
  int v8; // eax
  int result; // eax
  __time32_t *v10; // [esp-4h] [ebp-4h]

  v4 = _InterlockedExchange((volatile __int32 *)(a2 + 4 * a3), a1);
  v5 = _InterlockedExchange(MK_FP(v4, 4 * a3 - 1408989387), v4);
  *(_DWORD *)(a4 - 2023248124) ^= v5;
  if ( !__SETP__(v5, 82) )
    JUMPOUT(0x40153B);
  v6 = a3 + 1;
  v7 = time(v10);
  srand(v7);
  v8 = rand();
  *(_DWORD *)(v6 - 12) = v8 / 100;
  result = v8 - 100 * *(_DWORD *)(v6 - 12);
  *(_DWORD *)(v6 - 12) = result;
  *(_DWORD *)(v6 - 16) = 0;
  if ( *(int *)(v6 - 16) <= 4 )
  {
    if ( (*(_BYTE *)(v6 - 21 + *(_DWORD *)(v6 - 16)) != *(_BYTE *)(*(_DWORD *)(v6 - 16) + *(_DWORD *)(v6 + 8))) == *(_DWORD *)(v6 - 12) )
      result = puts("Really??? Did you find it?OMG!!!");
    else
      result = puts("I hide the last part, you will not succeed!!!");
  }
  return result;
}

你可能感兴趣的:(#,BUUCTF,Reverse)