载入PE.
32 bit,无壳.
载入IDA(32bit).
寻找main函数.
int __cdecl main(int argc, const char **argv, const char **envp)
{
char Buffer[128]; // [esp+0h] [ebp-94h] BYREF
char *Str1; // [esp+80h] [ebp-14h]
char *Str2; // [esp+84h] [ebp-10h]
HANDLE StdHandle; // [esp+88h] [ebp-Ch]
HANDLE hFile; // [esp+8Ch] [ebp-8h]
DWORD NumberOfBytesWritten; // [esp+90h] [ebp-4h] BYREF
hFile = GetStdHandle(0xFFFFFFF5);
StdHandle = GetStdHandle(0xFFFFFFF6);
Str2 = "x2dtJEOmyjacxDemx2eczT5cVS9fVUGvWTuZWjuexjRqy24rV29q";
WriteFile(hFile, "Enter password:\r\n", 0x12u, &NumberOfBytesWritten, 0);
ReadFile(StdHandle, Buffer, 0x80u, &NumberOfBytesWritten, 0);
Str1 = (char *)sub_401260(Buffer, NumberOfBytesWritten - 2);
if ( !strcmp(Str1, Str2) )
WriteFile(hFile, "Correct!\r\n", 0xBu, &NumberOfBytesWritten, 0);
else
WriteFile(hFile, "Wrong password\r\n", 0x11u, &NumberOfBytesWritten, 0);
return 0;
}
意思是我们输入的value的变量赋值给 NumberOfBytesWritten,同时NumberOfBytesWritten又经过sub_401260()函数的一系列操作,的到Str1的value,并且和Str2,进行比较如何相同则返回Correct.
让我们来看一下sub_401260().
_BYTE *__cdecl sub_401260(int a1, unsigned int a2)
{
// 定义局部变量
int v3; // 临时变量,用于存储读取的第三个字节
int v4; // 临时变量,用于存储读取的第二个字节(在循环中可能未完全使用)
int v5; // 临时变量,用于存储读取的第一个字节
int i; // 循环变量
unsigned int v7; // 用于组合三个字节为一个24位的整数
_BYTE *v8; // 动态分配的内存,用于存储Base64编码的结果
int v9; // 索引变量,用于追踪v8中的当前位置
int v10; // 临时索引变量
unsigned int v11; // 循环计数器,用于遍历输入数据
// 分配内存,大小为 (a2 + 2) / 3 * 4 + 1,即每三个输入字节产生四个Base64字符,外加一个空字符
v8 = malloc(4 * ((a2 + 2) / 3) + 1);
if ( !v8 ) // 如果内存分配失败,返回0
return 0;
v11 = 0; // 初始化循环计数器
v9 = 0; // 初始化索引变量
// 循环遍历输入数据,每次处理三个字节
while ( v11 < a2 )
{
// 读取第一个字节
v5 = *(unsigned __int8 *)(v11 + a1);
if ( ++v11 >= a2 ) // 如果已经到达输入数据末尾,则v4设为0
{
v4 = 0;
}
else
{
v4 = *(unsigned __int8 *)(v11 + a1); // 否则,读取第二个字节,并递增计数器
++v11;
}
// 如果已经到达或超过输入数据末尾,则v3设为0
if ( v11 >= a2 )
{
v3 = 0;
}
else
{
v3 = *(unsigned __int8 *)(v11 + a1); // 否则,读取第三个字节,并递增计数器
++v11;
}
// 将三个字节组合成一个24位的整数
v7 = v3 + (v5 << 16) + (v4 << 8);
// 使用自定义的Base64字符集(存储在byte_413000中)进行编码
// 每次从24位整数中取出6位进行编码
v8[v9] = byte_413000[(v7 >> 18) & 0x3F]; // 高6位
v10 = v9 + 1;
v8[v10] = byte_413000[(v7 >> 12) & 0x3F]; // 中6位
v8[++v10] = byte_413000[(v7 >> 6) & 0x3F]; // 低6位(但包含v3的高2位)
v8[++v10] = byte_413000[v3 & 0x3F]; // v3的低4位,作为单独的6位(前两位补0)
v9 = v10 + 1; // 更新索引变量
}
// 处理输入数据长度不是3的倍数的情况,用'='填充
for ( i = 0; i < dword_413040[a2 % 3]; ++i )
v8[4 * ((a2 + 2) / 3) - i - 1] = 61; // '='的ASCII码是61
// 在字符串末尾添加空字符
v8[4 * ((a2 + 2) / 3)] = 0;
// 返回编码后的字符串
return v8;
}
// 注释说明:
// 1. 该函数将输入数据(a1指向,长度为a2)进行Base64编码。
// 2. 它使用自定义的Base64字符集(存储在byte_413000中),而不是标准的Base64字符集。
// 3. 输入数据的长度可能不是3的倍数,因此编码后可能需要用'='字符填充。
// 4. 填充的'='字符数量由输入数据长度对3取余的结果决定,存储在dword_413040数组中。
// 5. 编码后的字符串以空字符结尾,方便作为C字符串处理。
这是一个不一样base64编码的代码,大概意思是将我们传入的字符串装化成二进制的形式,每个字符8bit
每次取三个字符,并将其按照每6个bit为一位的方法,进行编码转化,转换的内容是通过byte_413000的数据进行转换的,而最后一个for循环则是判断是否4个为一组的,如果不是则在后面加上等于号.
至于为什么说不一样呢,当然是byte_413000的原因,通常情况下base64的映射表一般为下面内容.
Base64编码使用一个包含64个字符的字符集,这些字符分别是:
- 大写字母A-Z
- 小写字母a-z
- 数字0-9
- 特殊字符+和/
这些字符分别对应64个不同的6位二进制数。在补零后形成的8位字节中,实际使用的只有低6位。
内容为:ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
而实际这个的byte_413000的内容为
ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/
所以需要进行一下映射转换.
开始构造exp
import base64
str1 = "x2dtJEOmyjacxDemx2eczT5cVS9fVUGvWTuZWjuexjRqy24rV29q"
string1 = "ZYXABCDEFGHIJKLMNOPQRSTUVWzyxabcdefghijklmnopqrstuvw0123456789+/"
string2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
print(base64.b64decode(str1.translate(str.maketrans(string1, string2))))
得到flag,游戏结束~