解题过程

首先运行程序尝试输入,根据运行结果可以猜测存在一个值与输入的(经过运算后)flag进行比对。
image.png
程序的主函数并不复杂,在IDA里面查看一下反编译后的C代码。
image.png
可以看出比较关键的内容是sub_402970、sub_402900和sub_4028A0函数,以及v3、v9、v10和v7参数,再直接查看反汇编代码可以看出v7为用户输入的flag。

查看一下sub_4028A0函数,我们知道dword_xxxxxx表示地址为xxxxxx的32位数据,这里被当作函数来使用。
image.png
使用交叉引用查看一下,其在sub_401010函数中被赋值,该值由sub_4055A0函数通过红框中的两个参数计算而得。
image.png
再对dword_433C58使用交叉引用,对经验的应该可以看出这段代码是获取kernel32.dll的基址。
image.png
那么,知道API HASH技术的应该可以猜测到sub_4055A0函数主要用于根据模块基址和HASH寻找对应的API函数。

sub_402900
image.png

sub_402970
image.png

可以看出类似的情况分别出现在了sub_402900和sub_402970函数中,所有使用到的API函数都被隐藏了,这种情况下,我们可以采用动态调试。
在动态调试前,我们先明确这里存在一个值用于验证其输入的flag是否正确,通过上述内容可以看出这个值应该是输入的flag经过计算后的结果,我们的首要目标应该是寻得该值,并根据该值逆推flag。

sub_4028A0动态分析
image.png
可以看出在sub_4028A0函数中主要是用到的是MultiByteToWideChar函数,调试并根据参数还原该段代码,应该为:

  1. void gb2312ToUnicode(conststring& src, wstring& result)
  2. {
  3. int n = kMultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, NULL, 0);
  4. result.resize(n);
  5. kMultiByteToWideChar(CP_ACP, 0, src.c_str(), -1, (LPWSTR)result.c_str(), result.length());
  6. }

sub_402900动态分析
image.png
可以看出在sub_402900函数中主要用到的是WideCharToMultiByte函数,调试并根据参数还原该段代码,应该为:

  1. void unicodeToUTF8(const wstring& src, string& result)
  2. {
  3. int n = kWideCharToMultiByte(CP_UTF8, 0, src.c_str(), -1, 0, 0, 0, 0);
  4. result.resize(n);
  5. kWideCharToMultiByte(CP_UTF8, 0, src.c_str(), -1, (char*)result.c_str(), result.length(), 0, 0);
  6. }

根据上述内容,我们可以知道程序会把输入的flag进行utf-8编码,并传入sub_402970函数验证
image.png
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。

  1. void unicodeToGB2312(const wstring& wstr, string& result)
  2. {
  3. int n = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, 0, 0, 0, 0);
  4. result.resize(n);
  5. ::WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, (char*)result.c_str(), n, 0, 0);
  6. }
  7. void utf8ToUnicode(conststring& src, wstring& result)
  8. {
  9. int n = MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, NULL, 0);
  10. result.resize(n);
  11. ::MultiByteToWideChar(CP_UTF8, 0, src.c_str(), -1, (LPWSTR)result.c_str(), result.length());
  12. }
  13. int main(int argc, char** agrv)
  14. {
  15. string strGB2312;
  16. wstring wstrUnicode;
  17. 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";
  18. utf8ToUnicode(key, wstrUnicode);
  19. unicodeToGB2312(wstrUnicode, strGB2312);
  20. return0;
  21. }

得到flag。
image.png
验证。
image.png