打开查壳,发现是upx壳,尝试脱壳失败,直接去网上搜索原因,是因为upx特征修改了,使用upx -d 文件名.exe无法脱壳,需要进行upx特征修复
没被修改upx特征的upx加壳文件,在winhex中打开程序后,可以看到三个区段名。“UPX0”和“UPX1”是加UPX壳后的两个区段名。其中UPX1区段包含了需要解压的数据块。“.rsrc”是程序资源信息区段名,这个区段含有原资源段的完整头部以及图标、Manifest、版本等未被压缩的资源,当然还有UPX自身需要的导入信息等(如果程序自身不含资源段,加壳后就是“UPX2”)(摘自一位大佬的博客)
这是正常的upx加壳文件,可以看到upx0,upx1的特征 ,再往后看可以看到upx的头
UPX头是供UPX通过“upx-d”命令脱壳用的,并不影响程序运行,可以把它权改为0,或者其他的数,这样就无法使用upx -d命令来快速脱壳
其实 exeinfope上显示了被修改了的upx特征,只要把它改回upx即可
正在上传…重新上传取消
可以直接在winhex或者其他十六进制编辑器中修复文件,这里使用010editor
这个时候再进行脱壳就可以脱掉
ida中打开程序
int __cdecl main(int argc, const char **argv, const char **envp)
{
size_t v3; // esi
unsigned int v4; // kr00_4
int v5; // ebx
int i; // ecx
int j; // edi
unsigned __int8 v8; // dl
int v9; // ebx
int v10; // edi
unsigned __int8 v11; // cl
int v12; // ecx
size_t v13; // esi
char v15; // [esp+0h] [ebp-90Ch]
size_t k; // [esp+4h] [ebp-908h]
char v17[512]; // [esp+8h] [ebp-904h] BYREF
__int128 v18[32]; // [esp+208h] [ebp-704h] BYREF
char Src[512]; // [esp+408h] [ebp-504h] BYREF
char v20[256]; // [esp+608h] [ebp-304h] BYREF
char v21[256]; // [esp+708h] [ebp-204h] BYREF
char v22[256]; // [esp+808h] [ebp-104h] BYREF
memset(v22, 0, sizeof(v22));
memset(v17, 0, sizeof(v17));
v18[0] = xmmword_402140;
v18[1] = xmmword_402130;
memset(&v18[2], 0, 0x1E0u);
strcpy(v21, "justfortest");
memset(&v21[12], 0, 0xF4u);
memset(Src, 0, sizeof(Src));
sub_401050("%s", (char)byte_403370);
v3 = strlen(byte_403370);
memcpy(Src, byte_403370, v3);
v5 = 0;
v4 = strlen(v21);
memset(v20, 0, sizeof(v20));
for ( i = 0; i < 256; ++i )
{
v22[i] = i;
v20[i] = v21[i % v4];
}
for ( j = 0; j < 256; ++j )
{
v8 = v22[j];
v5 = (v8 + v20[j] + v5) % 256;
v22[j] = v22[v5];
v22[v5] = v8;
}
v9 = 0;
v10 = 0;
for ( k = 0; k < v3; ++k )
{
v10 = (v10 + 1) % 256;
v11 = v22[v10];
v9 = (v11 + v9) % 256;
v22[v10] = v22[v9];
v22[v9] = v11;
Src[k] ^= v22[(unsigned __int8)(v11 + v22[v10])];
}
memcpy(v17, Src, v3);
v12 = 0;
v13 = v3 - 1;
if ( v13 )
{
while ( v17[v12] == *((_BYTE *)v18 + v12) )
{
if ( ++v12 >= v13 )
goto LABEL_10;
}
sub_401020("fail\n", v15);
}
else
{
LABEL_10:
sub_401020("success\n", v15);
}
return 0;
}
很明显的rc4加密,其中密钥是justfortest ,密文是v18中存储的数据,注意v18[0]为xmmword_402140,v18[1]为xmmword_402130,写脚本解密
import codecs
def rc4_decrypt(key, ciphertext):
S = list(range(256))
j = 0
out = []
# KSA Phase
for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]
# PRGA Phase
i = j = 0
for char in ciphertext:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
out.append(chr(char ^ S[(S[i] + S[j])%256]))
return ''.join(out)
key = b'justfortest'
ciphertext = codecs.decode('42FD5561B9276FF5B68623A9EF1C049FD41687D65468BC02156D30084B614C5E', 'hex')
plaintext = rc4_decrypt(key, ciphertext)
print(plaintext)
解得flag:flag{4950b6562657477e6685828e537f43e5}
打开文件,查壳无壳,放入到ida中
main函数f5无法解析,结合题目猜测是花指令
找到可疑的部分,这部分代码就是花指令,通过设置永真或者永假的条件来使程序执行,具体可参考一位大佬的文章https://blog.csdn.net/abel_big_xu/article/details/117927674)
把这部分的垃圾数据nop掉,就可以分析main函数
main函数出现了
int __cdecl main(int argc, const char **argv, const char **envp)
{
int i; // esi
int j; // eax
char v6; // [esp+0h] [ebp-F8h]
char *v7; // [esp+0h] [ebp-F8h]
char v8; // [esp+4h] [ebp-F4h]
char v9[100]; // [esp+Ch] [ebp-ECh]
__int128 v10; // [esp+70h] [ebp-88h]
int v11; // [esp+80h] [ebp-78h]
int v12; // [esp+84h] [ebp-74h]
int v13; // [esp+88h] [ebp-70h]
char v14; // [esp+8Ch] [ebp-6Ch]
char flag[100]; // [esp+90h] [ebp-68h] BYREF
printf(&Format, v6);
scanf("%s", flag);
v11 = -171171450;
v12 = -669748952;
v13 = 1651994351;
v14 = -6;
v10 = xmmword_402170;
if ( strlen(flag) == 29 ) // flag的长度为29
{
for ( i = 0; i < 29; ++i )
v9[i] = funcs_40117E[i % 5u](flag[i]); // 对余数为0,1,2,3,4,的值分别如下操作sub_401080 sub_401090 sub_4010A0 sub_4010B0 sub_4010C0
for ( j = 0; j < 29; ++j )
{
if ( v9[j] != *(&v10 + j) ) // 比对处理过的flag与xmmword_402170
break;
}
printf(v7, v8);
}
else
{
printf("wwwhhhaaattt???\n", v8);
}
return 0;
}
写脚本解密
#include
#include
#include
#include
#include
int main(){
unsigned char miwen[] =
{
0x7F, 0x7E, 0x51, 0xCE, 0xFB, 0x4E, 0x7A, 0x24,
0xE8, 0xDF, 0x59, 0x71, 0x26, 0xCA, 0xE1, 0x6C,0x86, 0x21, 0xCC, 0xF5,0x28, 0x71, 0x14, 0xD8,0xEF, 0x6E, 0x77, 0x62, 0xFA
};
char flag[30];
int i;
for(i=0;i<29;i++){
int n=i%5;
switch(n){
case 0:
flag[i]=miwen[i]^ 0x19;
break;
case 1:
flag[i]=miwen[i] - 18;
break;
case 2:
flag[i]=miwen[i]+ 16;
break;
case 3:
flag[i]=(miwen[i]>>1)&0x7f;//2 * (a1 & 0x7F)
break;
case 4:
flag[i]=miwen[i] & 0x7f;
break;
}
}printf("%s",flag);
return 0;
}
注意:密文不仅包含xmmword_402170,后面的v11,v12,v13.....也有部分,为什么说是部分呢,因为v11,v12...虽然数很大,但是其是int型,也就是说数再大也只是取int(4字节)的数据。
__asm{
push ebx
xor ebx,ebx
test ebx,ebx
jnz label1
jz label2
label1:
_emit junkcode
label2:
pop ebx//需要恢复ebx寄存器
}__asm{
clc
jnz label1:
_emit junkcode
label1:
}
3.写脚本时注意要提取的数据是什么类型。