诺莫22
知识点:
算法逆向,文件读写
开始:
使用IDA打开:
upx字段
脱壳:
1.软件脱壳:
介绍两个软件:
下载
upx官网
(1)upx
upx -d 绝对路径
(2)upxtool:
打开脱壳
2.手动脱壳:
使用OD打开,找到popa后的大段跳转,使用脱壳工具修正入口点,但这个方法我试过会报错,原因是我使用的win11系统的地址和以往的不一样。
将脱壳后的程序放入IDA:
因为没有main函数,我们通过搜索字符串定位到关键函数
int v6; // ecx
char *v7; // eax
int v8; // edx
int v9; // ecx
int v10; // ST04_4
int v11; // ST08_4
char v13; // [esp+0h] [ebp-134h]
char Buf; // [esp+D0h] [ebp-64h]
size_t i; // [esp+100h] [ebp-34h]
FILE *File; // [esp+10Ch] [ebp-28h]
char v17; // [esp+118h] [ebp-1Ch]
int v18; // [esp+130h] [ebp-4h]
int savedregs; // [esp+134h] [ebp+0h]
sub_411208((int)&unk_41C008);
sub_41137A("Input Your Flag:\n");
sub_41137F("%19s", &v17);
if ( a1 != 2 )
{
sub_41137A("Input error!\n");
exit(1);
}
sub_41137A("%s\n", *(_DWORD *)(a2 + 4));
for ( i = 0; i < j_strlen(*(const char **)(a2 + 4)); ++i )
*(_BYTE *)(*(_DWORD *)(a2 + 4) + i) += i;
if ( !j_strcmp(unk_41A068, *(const char **)(a2 + 4)) )
{
v4 = fopen(*(const char **)(a2 + 4), "r");
File = (FILE *)sub_411212(v6, v5, &v13 == &v13, (int)v4, xmm0_4_0);
if ( File )
{
v7 = fgets(&Buf, 40, File);
sub_411212(v9, v8, &v13 == &v13, (int)v7, xmm0_4_0);
if ( j_strlen(&Buf) != 32 || j_strlen(&Buf) % 2 == 1 )
exit(1);
sub_4113B1(&Buf, (int)&unk_41A4E0);
if ( sub_4113B6((int)&unk_41A4E0) )
sub_41137A("flag{%s}", &Buf);
else
sub_41137A("Input Error!\n");
}
else
{
sub_41137A("Input Error!\n");
}
}
else
{
sub_41137A("Input Error!\n");
}
sub_411235(&savedregs, dword_4154C4, 0, v3);
return sub_411212((unsigned int)&savedregs ^ v18, v11, 1, v10, xmm0_4_0);
}
修改一下参数类型好看一点:
int __usercall sub_415280@<eax>(int xmm0_4_0@<xmm0>, int a1, int **a2)
{
int v3; // edx
FILE *v4; // eax
int v5; // edx
int v6; // ecx
char *v7; // eax
int v8; // edx
int v9; // ecx
int v10; // ST04_4
int v11; // ST08_4
int v13; // [esp+0h] [ebp-134h]
char Buf; // [esp+D0h] [ebp-64h]
size_t i; // [esp+100h] [ebp-34h]
FILE *File; // [esp+10Ch] [ebp-28h]
char v17; // [esp+118h] [ebp-1Ch]
int v18; // [esp+130h] [ebp-4h]
int savedregs; // [esp+134h] [ebp+0h]
sub_411208(&unk_41C008);
printf("Input Your Flag:\n");
scanf(xmm0_4_0, "%19s", &v17);
if ( a1 != 2 )
{
printf("Input error!\n");
exit(1);
}
printf("%s\n", a2[1]);
for ( i = 0; i < j_strlen(a2[1]); ++i )
*(a2[1] + i) += i;
if ( !j_strcmp(Str1, a2[1]) )
{
v4 = fopen(a2[1], "r");
File = sub_411212(v6, v5, &v13 == &v13, v4, xmm0_4_0);// 返回第四个位置的数
if ( File )
{
v7 = fgets(&Buf, 40, File);
sub_411212(v9, v8, &v13 == &v13, v7, xmm0_4_0);
if ( j_strlen(&Buf) != 32 || j_strlen(&Buf) % 2 == 1 )
exit(1);
sub_4113B1(&Buf, &unk_41A4E0);
if ( sub_4113B6(&unk_41A4E0) )
printf("flag{%s}", &Buf);
else
printf("Input Error!\n");
}
else
{
printf("Input Error!\n");
}
}
else
{
printf("Input Error!\n");
}
sub_411235(&savedregs, dword_4154C4, 0, v3);
return sub_411212(&savedregs ^ v18, v11, 1, v10, xmm0_4_0);
}
倒着看
sub_4113B1(&Buf, &unk_41A4E0);
if ( sub_4113B6(&unk_41A4E0) )
printf("flag{%s}", &Buf);
else
printf("Input Error!\n");
要输出flag,sub_4113B6(&unk_41A4E0)返回非0
进入sub_4113B6(&unk_41A4E0):
int __usercall sub_411D90@<eax>(int xmm0_4_0@<xmm0>, int *a1)
{
int v2; // edx
int *v3; // ecx
int v4; // eax
signed int i; // [esp+D0h] [ebp-8h]
sub_411208(&unk_41C008);
for ( i = 0; i < 16; ++i )
{
v3 = a1;
v2 = a1[i] + 1;
if ( v2 != dword_41A078[i] )
{
v4 = 0;
return sub_411212(v3, v2, 1, v4, xmm0_4_0);
}
}
v4 = 1;
return sub_411212(v3, v2, 1, v4, xmm0_4_0);
}
与dword_41A078[i]作比较
查看dword_41A078数据:
发现只有8组有效数据,这里遍历了16次;会不会在前面由直接对这个地址赋值的操作;
打开sub_4113B1(&Buf, &unk_41A4E0);:
int __usercall sub_414E50@<eax>(int a1@<xmm0>, char *Str, int a3)
{
unsigned int v3; // eax
int v4; // edx
int v5; // ecx
int v7; // [esp+D0h] [ebp-14h]
signed int i; // [esp+DCh] [ebp-8h]
sub_411208(&unk_41C008);
dword_41A078[8] = 0xA7;
dword_41A078[9] = 0xDE;
dword_41A078[10] = 0xDA;
dword_41A078[11] = 0x46;
dword_41A078[12] = 0xAB;
dword_41A078[13] = 0x2E;
dword_41A078[14] = 0xFF;
dword_41A078[15] = 0xDB;
for ( i = 0; ; i += 2 )
{
v3 = j_strlen(Str);
if ( i >= v3 )
break;
if ( Str[i] >= 48 && Str[i] <= 57 )
{
*(a3 + 4 * (i / 2)) = Str[i] - 48;
}
else
{
if ( Str[i] < 97 || Str[i] > 102 )
{
printf("Input Error!\n");
exit(0);
}
*(a3 + 4 * (i / 2)) = Str[i] - 87;
}
*(a3 + 4 * (i / 2)) *= 16;
if ( Str[i + 1] >= 48 && Str[i + 1] <= 57 )
{
v7 = Str[i + 1] - 48;
}
else
{
if ( Str[i + 1] < 97 || Str[i + 1] > 102 )
{
printf("Input Error!\n");
exit(0);
}
v7 = Str[i + 1] - 87;
}
*(a3 + 4 * (i / 2)) += v7;
}
return sub_411212(v5, v4, 1, v3, a1);
}
果然有:
那我们可以推出unk_41A4E0的数据:
a1 = [0x00000050, 0x000000C6, 0x000000F1, 0x000000E4, 0x000000E3, 0x000000E2, 0x0000009A, 0x000000A1, 0xA7, 0xDE, 0xDA, 0x46, 0xAB, 0x2E, 0xFF, 0xDB]
cons = []
for n in range(16):
cons.append(a1[n] - 1)
print(cons)
再看一下上面的sub_4113B1(&Buf, &unk_41A4E0),
算法简单,将字符串转两位两位的换位为10进制:
这个能找出buf变量,就是unk_41A4E0数据的字符形式:
a1 = [0x00000050, 0x000000C6, 0x000000F1, 0x000000E4, 0x000000E3, 0x000000E2, 0x0000009A, 0x000000A1, 0xA7, 0xDE, 0xDA, 0x46, 0xAB, 0x2E, 0xFF, 0xDB]
cons = []
buf = ""
for n in range(16):
cons.append(a1[n] - 1)
print(cons)
for i in cons:
buf += str(hex(i)).replace("0x","")
print(buf)
找到了buf也就找到了字符穿:
flag{4fc5f0e3e2e199a0a6ddd945aa2dfeda}
但是密码似乎并不是
前面的代码似乎就逆向出了一个flag.txt。
这里有点愣逼,希望大佬指点