SMC逆向

SMC逆向介绍

参考自CTF Wiki SMC

自修改代码(Self-Modified Code)是一类特殊的代码技术,即在运行时修改自身代码,从而使得程序实际行为与反汇编结果不符,同时修改前的代码段数据也可能非合法指令,从而无法被反汇编器识别。

自修改代码通常有两种破解方式,第一种是根据静态分析结果直接修改程序二进制文件,第二种则是在动态调试时将解密后的程序从内存中 dump 下来。

以下例题采用第二种方法。

[磐石行动2024]ezRE

下载文件查看是否有壳

32位文件,无壳,放入ida

flag长度30

简单异或以及减法

比较字符串
直接复制ida中unk_4040C0相关的所有内容到txt,代码提取十六进制数值

with open('data_tianqi.txt', 'r') as file:
    lines = file.readlines()

hex_values = []
for line in lines:
    match = line.split('db')[1].strip().split()[0].rstrip('h')
    if match:
        hex_values.append(int(match, 16))

hex_values = [value for value in hex_values if value != 0]  # 去除0,不需要这个功能的时候注释掉
print(hex_values)
hexx_values = [hex(value) for value in hex_values]
hex_values_trimmed = [value[2:] for value in hexx_values]
result = ' '.join(hex_values_trimmed)
print(result)


将十六进制数据进行反变换,减变成加,异或不变,代码如下:

hex_string = "66 6b 63 64 7f 63 69 70 57 60 79 54 78 5b 6b 50 67 54 73 61 7c 50 64 48 6c 56 7e 46 65 60"
hex_values = [int(char, 16) for char in hex_string.split()]
print(hex_values)

def crazy(s):
    for i in range(len(s)):
        if i % 2 == 1:  # If the index is odd
            s[i] = chr(s[i] + i)
        else:
            s[i] = chr(s[i] ^ i)

    return ''.join(s)

output_string = crazy(hex_values)
print(output_string)

运行得到flag,提交发现不对,尝试运行exe输入flag验证
flag{how_is_the_weather_today}

刚才得到的flag是假的,后续还有代码未执行,下断点接着动态调试,发现Ok,please go on.后面执行的代码找不到位置了,而且my_function函数非常奇怪,无法正常反汇编

代码中有VirtualProtect函数,猜测my_function函数进行了smc加密,所以没法直接看到,需要在动态调试过程中查看代码

找到my_function函数的位置,然后在主函数中重新下断点调试

这里的断点应该下在((void (*)(void))lpAddress)()位置上,断点在上图中的位置单步调试费时费力,没有必要

程序运行到断点之后单步执行,直到运行call命令(调用函数),下一步就是进入my_function函数,找到my_function函数所在地址,如上图

选中该函数名称,使用按键U将数据设置为无定义(或右键找到对应功能)

然后选中该函数涉及到的所有数据内容,使用按键C,选择Force选项转换成代码

最后点击my_function使用按键P定义为函数,再用F5反编译,得到my_function函数


my_function涉及到的代码如下,共三个函数my_function、xxx_init、xxx_crypt

void __cdecl __noreturn my_function(char *a1)
{
  unsigned int v1; // eax
  char Str[50]; // [esp+16h] [ebp-2D2h] BYREF
  int v3[30]; // [esp+48h] [ebp-2A0h] BYREF
  unsigned __int8 v4[256]; // [esp+C0h] [ebp-228h] BYREF
  char v5[256]; // [esp+1C0h] [ebp-128h] BYREF
  unsigned int v6; // [esp+2C0h] [ebp-28h]
  unsigned int j; // [esp+2C4h] [ebp-24h]
  int v8; // [esp+2C8h] [ebp-20h]
  int i; // [esp+2CCh] [ebp-1Ch]

  puts("please input your True flag:");
  scanf("%40s", Str);
  v6 = strlen(Str);
  if ( v6 != 30 )
  {
    puts("Wrong!");
    exit(0);
  }
  qmemcpy(v3, &unk_404040, sizeof(v3));
  memset(v4, 0, sizeof(v4));
  memset(v5, 0, sizeof(v5));
  v1 = strlen(a1);
  xxx_init(v4, (unsigned __int8 *)a1, v1);
  for ( i = 0; i <= 255; ++i )
    v5[i] = v4[i];
  xxx_crypt(v4, (unsigned __int8 *)Str, v6);
  v8 = 1;
  for ( j = 0; ; ++j )
  {
    if ( j >= v6 )
      goto LABEL_11;
    if ( (unsigned __int8)Str[j] != v3[j] )
      break;
  }
  v8 = 0;
LABEL_11:
  if ( v8 )
    puts("Good! have a beautiful day for you!");
  else
    puts("May be try again?");
  exit(0);
}
char *__cdecl xxx_init(unsigned __int8 *a1, unsigned __int8 *a2, unsigned int a3)
{
  char *result; // eax
  _DWORD v4[64]; // [esp+7h] [ebp-111h] BYREF
  unsigned __int8 v5; // [esp+107h] [ebp-11h]
  int v6; // [esp+108h] [ebp-10h]
  int i; // [esp+10Ch] [ebp-Ch]

  v6 = 0;
  v4[0] = 0;
  v4[63] = 0;
  result = 0;
  memset((char *)v4 + 1, 0, 4 * ((((char *)v4 - ((char *)v4 + 1) + 256) & 0xFFFFFFFC) >> 2));
  v5 = 0;
  for ( i = 0; i <= 255; ++i )
  {
    a1[i] = i;
    result = (char *)v4 + i;
    *((_BYTE *)v4 + i) = a2[i % a3];
  }
  for ( i = 0; i <= 255; ++i )
  {
    v6 = (a1[i] + v6 + *((char *)v4 + i)) % 256;
    v5 = a1[i];
    a1[i] = a1[v6];
    result = (char *)v5;
    a1[v6] = v5;
  }
  return result;
}
unsigned int __cdecl xxx_crypt(unsigned __int8 *a1, unsigned __int8 *a2, unsigned int a3)
{
  unsigned int result; // eax
  unsigned __int8 v4; // [esp+Fh] [ebp-15h]
  unsigned int i; // [esp+14h] [ebp-10h]
  int v6; // [esp+18h] [ebp-Ch]
  int v7; // [esp+1Ch] [ebp-8h]

  v7 = 0;
  v6 = 0;
  for ( i = 0; ; ++i )
  {
    result = i;
    if ( i >= a3 )
      break;
    v7 = (v7 + 1) % 256;
    v6 = (v6 + a1[v7]) % 256;
    v4 = a1[v7];
    a1[v7] = a1[v6];
    a1[v6] = v4;
    a2[i] ^= a1[(unsigned __int8)(a1[v7] + a1[v6])];
  }
  return result;
}

flag长度同样为30,加密后用来对比的字符串为unk_404040

同样复制下来运行代码提取十六进制

接着下断点,动态调试

动态调试查看参数的值,发现init函数的参数v4是256个0,a1是开始输入的假flag字符串,v1是十进制的30,应该是长度。函数用于初始化v4。
crypt函数的参数v4是刚从init初始化的数据,str是my_function中输入的字符串,v6是30。
假flag:flag{how_is_the_weather_today}

分析init函数和crypt函数,发现对输入的字符串的加密只有一个异或,根据异或的性质,再异或同样的数据一次就可以抵消,直接正向运行代码,不用作任何修改,用unk_404040的数据进行异或,得到flag。

hex_string = "4d d8 76 2d c 26 c 53 da c0 17 37 8c d7 f3 d9 d0 46 2b 15 98 67 f1 ad a6 e 7c 66 90 7f"
hex_values = [int(char, 16) for char in hex_string.split()]
print(hex_values)

def xxx_init(a1, a2, a3):
    v4 = [0] * 256
    v5 = 0
    v6 = 0
    v4[0] = 0
    v4[63] = 0
    for i in range(1, 65):
        v4[i-1] = 0

    for i in range(256):
        a1[i] = i
        v4[i] = a2[i % a3]

    for i in range(256):
        v6 = (a1[i] + v6 + v4[i]) % 256
        v5 = a1[i]
        a1[i] = a1[v6]
        a1[v6] = v5

    return a1

a1 = [0] * 256
a2 = "flag{how_is_the_weather_today}"
a2_list = [ord(char) for char in a2]
a3 = len(a2)   # 30
xxx_init(a1, a2_list, a3)
# print("Initialized a1 array:", [hex(num) for num in a1])

def xxx_decrypt(a1, a2, a3):
    v4 = 0
    v6 = 0
    v7 = 0
    for i in range(a3):
        v7 = (v7 + 1) % 256
        v6 = (v6 + a1[v7]) % 256
        v4 = a1[v7]
        a1[v7] = a1[v6]
        a1[v6] = v4
        a2[i] ^= a1[(a1[v7] + a1[v6]) & 0xFF]

    return a2

enc = hex_values
result = xxx_decrypt(a1, enc, 30)
ascii_symbols = [chr(num) for num in result]
print(''.join(ascii_symbols))
# flag{This_is_a_beautiful_day!}
posted @ 2024-05-27 13:14  skdtxdy  阅读(69)  评论(0编辑  收藏  举报