解题过程
首先运行程序尝试输入,根据运行结果可以猜测存在一个值与输入的(经过运算后)flag进行比对。
程序的主函数并不复杂,在IDA里面查看一下反编译后的C代码。
可以看出比较关键的内容是sub_402970、sub_402900和sub_4028A0函数,以及v3、v9、v10和v7参数,再直接查看反汇编代码可以看出v7为用户输入的flag。
查看一下sub_4028A0函数,我们知道dword_xxxxxx表示地址为xxxxxx的32位数据,这里被当作函数来使用。
使用交叉引用查看一下,其在sub_401010函数中被赋值,该值由sub_4055A0函数通过红框中的两个参数计算而得。
再对dword_433C58使用交叉引用,对经验的应该可以看出这段代码是获取kernel32.dll的基址。
那么,知道API HASH技术的应该可以猜测到sub_4055A0函数主要用于根据模块基址和HASH寻找对应的API函数。
sub_402900
sub_402970
可以看出类似的情况分别出现在了sub_402900和sub_402970函数中,所有使用到的API函数都被隐藏了,这种情况下,我们可以采用动态调试。
在动态调试前,我们先明确这里存在一个值用于验证其输入的flag是否正确,通过上述内容可以看出这个值应该是输入的flag经过计算后的结果,我们的首要目标应该是寻得该值,并根据该值逆推flag。
sub_4028A0动态分析
可以看出在sub_4028A0函数中主要是用到的是MultiByteToWideChar函数,调试并根据参数还原该段代码,应该为:
void gb2312ToUnicode(conststring& src, wstring& result)
{
int n = kMultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, NULL, 0);
result.resize(n);
kMultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, (LPWSTR)result.c_str(), result.length());
}
sub_402900动态分析
可以看出在sub_402900函数中主要用到的是WideCharToMultiByte函数,调试并根据参数还原该段代码,应该为:
void unicodeToUTF8(const wstring& src, string& result)
{
int n = kWideCharToMultiByte(CP_UTF8, 0, src.c_str(), -1, 0, 0, 0, 0);
result.resize(n);
kWideCharToMultiByte(CP_UTF8, 0, src.c_str(), -1, (char*)result.c_str(), result.length(), 0, 0);
}
根据上述内容,我们可以知道程序会把输入的flag进行utf-8编码,并传入sub_402970函数验证
sub_402970函数中主要使用到的API为GetModuleHandleA、lstrcpyA和lstrcmpA,该函数会从.rsrc节中获取用于验证flag正确性的值,即“E4 B8 8D E5 81 9A E4 BC 9F E5 A4 A7 E6 97 B6 E4 BB A3 E7 9A 84 E6 97 81 E8 A7 82 E8 80 85 0”
到这一步,我们其实比较明确,该程序只是将输入进行utf-8编码,并与隐藏在.rsrc节中的key进行对比验证,根据该key我们写出writeup。
void unicodeToGB2312(const wstring& wstr, string& result)
{
int n = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, 0, 0, 0, 0);
result.resize(n);
::WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, (char*)result.c_str(), n, 0, 0);
}
void utf8ToUnicode(conststring& src, wstring& result)
{
int n = MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, NULL, 0);
result.resize(n);
::MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, (LPWSTR)result.c_str(), result.length());
}
int main(int argc, char** agrv)
{
string strGB2312;
wstring wstrUnicode;
char key[] = "\xE4\xB8\x8D\xE5\x81\x9A\xE4\xBC\x9F\xE5\xA4\xA7\xE6\x97\xB6\xE4\xBB\xA3\xE7\x9A\x84\xE6\x97\x81\xE8\xA7\x82\xE8\x80\x85\x00";
utf8ToUnicode(key, wstrUnicode);
unicodeToGB2312(wstrUnicode, strGB2312);
return0;
}
得到flag。
验证。