决赛一共3道web两道re,web没有复现环境不讲,看看re

ransomware

搜字符串找到关键函数

int __stdcall sub_4016A0(int a1, int a2, int a3, int a4)
{
  FILE *Stream; // eax
  FILE *Stream_1; // edi
  signed int ElementCount; // ebx
  char *Buffer; // esi
  int n8; // eax
  char i; // dl
  int n9; // eax
  char n111; // dl
  size_t j_1; // eax
  unsigned int v14; // edx
  int j; // edi
  int n8_1; // ecx
  int v17; // edi
  __time32_t v18; // eax
  int v19; // ecx
  unsigned int k; // edx
  int n1000; // edi
  char *Buffer_1; // ebp
  FILE *Stream_2; // edi
  char Str[46]; // [esp+2Eh] [ebp-2Eh] BYREF

  if ( IsDebuggerPresent() || sub_401550() )
  {
    MessageBoxA(0, "Debugger or VM detected!", "Error", 0x10u);
    return -1;
  }
  sub_401630();
  Stream = fopen("flag.txt", "rb");
  Stream_1 = Stream;
  if ( !Stream )
  {
    MessageBoxA(0, "file not found!", "Error", 0x10u);
    return -1;
  }
  fseek(Stream, 0, 2);
  ElementCount = ftell(Stream_1);
  rewind(Stream_1);
  Buffer = (char *)malloc(ElementCount + 1);
  fread(Buffer, 1u, ElementCount, Stream_1);
  fclose(Stream_1);
  
  n8 = 0;
  for ( i = '\x92'; ; i = byte_4061C0[n8] )
  {
    Str[n8++] = i ^ 0xAA;
    if ( n8 == 8 )
      break;
  }
  
  n9 = 0;
  for ( n111 = 111; ; n111 = byte_4061B4[n9] )
  {
    Str[n9++ + 8] = n111 ^ 0x55;
    if ( n9 == 9 )
      break;
  }
  
  Str[17] = 0;
  j_1 = strlen(Str);
  if ( j_1 )
  {
    v14 = -1;
    for ( j = 0; j != j_1; ++j )
    {
      v14 ^= (unsigned __int8)Str[j];
      n8_1 = 8;
      do
      {
        while ( (v14 & 1) != 0 )
        {
          v14 = (v14 >> 1) ^ 0xEDB88320;
          if ( !--n8_1 )
            goto LABEL_18;
        }
        v14 >>= 1;
        --n8_1;
      }
      while ( n8_1 );
LABEL_18:
      ;
    }
    v17 = ~v14;
  }
  else
  {
    v17 = 0;
  }
  v18 = time(0);
  v19 = 1;
  dword_408020[0] = v17 ^ v18;
  for ( k = v17 ^ v18; ; k = dword_40801C[v19] )
  {
    dword_408020[v19] = v19 + 1812433253 * (k ^ (k >> 30));
    if ( ++v19 == 624 )
      break;
  }
  n624 = 624;
  n1000 = 1000;
  do
  {
    sub_401460();
    --n1000;
  }
  while ( n1000 );
  Buffer_1 = Buffer;
  if ( ElementCount > 0 )
  {
    do
      *Buffer_1++ ^= sub_401460();
    while ( &Buffer[ElementCount] != Buffer_1 );
  }
  Stream_2 = fopen("flag.encrypted", "wb");
  fwrite(Buffer, 1u, ElementCount, Stream_2);
  fclose(Stream_2);
  free(Buffer);
  MessageBoxA(
    0,
    " Your files have been encrypted!\r\n"
    "\r\n"
    "To decrypt them, send 1000 BTC to address:\r\n"
    "01E4058C731768EC00FAD41CFD30EF7A\r\n"
    "\r\n"
    "After payment, contact us at hhh@mail.com",
    "!!! FILES ENCRYPTED !!!",
    0x10010u);
  return 0;
}

这里其实就可以反推加密逻辑,我们一步步看

  n8 = 0;
  for ( i = '\x92'; ; i = byte_4061C0[n8] )
  {
    Str[n8++] = i ^ 0xAA;
    if ( n8 == 8 )
      break;
  }

给Str前8位赋值,

看看byte_4061C0具体内容

c还原

#include <stdio.h>
int n8 = 0;
char i;
char Str[46];
char byte_4061C0[8] = {0x92, 0x9F, 0x90, 0x9F, 0xE9, 0x90, 0x9C, 0x9A};
int main(){
  for ( i = -110 ; ; i = byte_4061C0[n8] )
  {
    Str[n8++] = i ^ 0xAA;
    if ( n8 == 8 )
      break;
  }
printf(Str);
}

//85:5C:60

下面那段同理

#include <stdio.h>
int n9 = 0;
char n111;
char Str[46];
char byte_4061B4[12] = {0x6F,0x11,0x61,0x6F,0x63,0x10,0x6F,0x66,0x63,0,0,0};
int main(){
  for ( n111 = 111; ; n111 = byte_4061B4[n9] )
  {
    Str[n9++] = n111 ^ 0x55;
    if ( n9 == 9 )
      break;
  }
printf(Str);
}

//:D4:6E:36

所以Str=85:5C:60:D4:6E:36

下一步分析

  j_1 = strlen(Str);//18
  if ( j_1 )
  {
    v14 = -1;
    for ( j = 0; j != j_1; ++j )//从0到17
    {
      v14 ^= (unsigned __int8)Str[j];
      n8_1 = 8;
      do
      {
        while ( (v14 & 1) != 0 )
        {
          v14 = (v14 >> 1) ^ 0xEDB88320;
          if ( !--n8_1 )
            goto LABEL_18;
        }
        v14 >>= 1;
        --n8_1;
      }
      while ( n8_1 );
LABEL_18:
      ;
    }
    v17 = ~v14;
  }
  else
  {
    v17 = 0;
  }

结合下面

dword_408020[0] = v17 ^ v18;

知道关键在于求解v17

写c还原

#include <stdio.h>
#include <string.h>
char Str[46]="85:5C:60:D4:6E:36";
int j_1;
int j;
unsigned int v14;
int n8_1;
int v17;

int main(){
j_1 = strlen(Str);//17
if ( j_1 )
{
v14 = -1;
for ( j = 0; j != j_1; ++j )//从0到16
{
    v14 ^= (unsigned __int8)Str[j];
    n8_1 = 8;
    do
    {
    while ( (v14 & 1) != 0 )
    {
        v14 = (v14 >> 1) ^ 0xEDB88320;
        if ( !--n8_1 )
        goto LABEL_18;
    }
    v14 >>= 1;
    --n8_1;
    }
    while ( n8_1 );
LABEL_18:
    ;
}
v17 = ~v14;
}
else
{
v17 = 0;
}

printf("%d",v17);
}

//812059967

就是time未知,但是根据加密文件生成出的时间也可以知道

dword_408020[0]=1525977351

for ( k = v17 ^ v18; ; k = dword_40801C[v19] )
  {
    dword_408020[v19] = v19 + 1812433253 * (k ^ (k >> 30));
    if ( ++v19 == 624 )
      break;
  }

梅森旋转

关键函数

if ( ElementCount > 0 )
  {
    do
      *Buffer_1++ ^= sub_401460();
    while ( &Buffer[ElementCount] != Buffer_1 );
  }

由于逻辑过于复杂,所以我们直接在伪代码的基础上进行改造,这是完整代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int v17 = 812059967;
int v18 = 1788058680;
int dword_408020[624];
#define dword_40801C (dword_408020 - 1)
int n624; // eax
int n624_2; // edx
unsigned int v2; // eax
unsigned int v3; // eax
int n624_1; // ecx
int v6; // ebx
unsigned int v7; // ebx
unsigned int v8; // esi
int n1000;
unsigned int k;
char *Buffer_1;
char *Buffer;
int ElementCount;
int v19=1;
FILE *Stream;
FILE *Stream_1;
FILE *Stream_2;



unsigned int sub_401460()
{


  n624_2 = n624 + 1;
  if ( n624 > 623 )
  {
    for ( n624_1 = 0; n624_1 != 624; dword_40801C[n624_1] = dword_408020[(n624_1 + 396) % 624] ^ v8 )
    {
      v6 = dword_408020[n624_1++];
      v7 = (dword_408020[n624_1 % 624] & 0x7FFFFFFF) | (v6 & 0x80000000);
      v8 = v7 >> 1;
      if ( (v7 & 1) != 0 )
        v8 ^= 0x9908B0DF;
    }
    n624_2 = 1;
    n624 = 0;
  }
  v2 = dword_408020[n624];
  n624 = n624_2;
  v3 = ((((v2 >> 11) ^ v2) << 7) & 0x9D2C5680) ^ (v2 >> 11) ^ v2;
  return ((((v3 << 15) & 0xEFC60000) ^ v3) >> 18) ^ ((v3 << 15) & 0xEFC60000) ^ v3;
}



int main(){

Stream = fopen("flag.encrypted", "rb");
  Stream_1 = Stream;
  if ( !Stream )
  {
    printf("file not found!");
    return -1;
  }
  fseek(Stream, 0, 2);
  ElementCount = ftell(Stream_1);
  rewind(Stream_1);
  Buffer = (char *)malloc(ElementCount + 1);
  fread(Buffer, 1u, ElementCount, Stream_1);
  fclose(Stream_1);



    dword_408020[0] = v17 ^ v18;
    for ( k = v17 ^ v18; ; k = dword_40801C[v19] )
    {
    dword_408020[v19] = v19 + 1812433253 * (k ^ (k >> 30));
    if ( ++v19 == 624 )
        break;
    }
    n624 = 624;
    n1000 = 1000;
    do
    {
    sub_401460();
    --n1000;
    }
    while ( n1000 );
    Buffer_1 = Buffer;
    if ( ElementCount > 0 )
    {
    do
        *Buffer_1++ ^= sub_401460();
    while ( &Buffer[ElementCount] != Buffer_1 );
    }
    Stream_2 = fopen("flag.txt", "wb");
    fwrite(Buffer, 1u, ElementCount, Stream_2);
    fclose(Stream_2);
    free(Buffer);
}

这里有几个坑点,我们一个一个来讲:

1.#define dword_40801C (dword_408020 - 1)

dword_40801C和dword_408020其实是一个数组

从这段代码就能看出来

如果dword_40801C是独立数组,那么dword_40801C[v19]就会为0,很明显这不可能,所以dword_40801C[]只是dword_408020[]偏移了一个单位

所以我们编写c代码务必要将二者联系起来,否则代码逻辑就会混乱

使用宏定义:define dword_40801C (dword_408020 - 1)

2.MessageBoxA函数未定义

这个简单,稍微修改代码改成printf即可

3.运算优先级

sub_401460()中有大量合并运算,优先级即:&>^>|,按优先级打括号即可

还原出flag


第二题

(不记得名字了)

如果是静态分析的话,flag出现位置在这里,后面会销毁

动态调试也能做,更方便

一共有三个反调试,用插件全部禁用掉即可

然后结合静态分析,在这个位置下断点:401E9E

下完断点后f9一直跑到断点处,ctrl+g输入esp+0xd8看到flag