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!}
标签:逆向,int,hex,unsigned,a1,v4,v6,SMC
From: https://www.cnblogs.com/sK07XdAy/p/18213011