0x00 前言

注意:此解法非常暴力,如果你想通过这道题学习神经网络相关的数学与算法知识,建议点击返回并查看其它大佬的WP。所以请版主大大千万别加精,要脸。。。

0x01 字符长度与范围

长度限定为10,但他计算长度的方法非常的魔性,我是没看懂,是猜+调试弄出来的。

iter = input;
do
{
    v5 = *(_DWORD *)iter;
    iter += 4;
    v6 = ~v5 & (v5 - 0x1010101) & 0x80808080;
}
while ( !v6 );    // iter = length as dword + base pointer, round to 4
if ( !(~v5 & (v5 - 0x1010101) & 0x8080) )
    v6 >>= 16;
if ( !(~v5 & (v5 - 0x1010101) & 0x8080) )
    iter += 2;
if ( &iter[-__CFADD__((_BYTE)v6, (_BYTE)v6) - 3] - input != 10 )// length == 10
{
      //error
}

然后是字符范围,要求[A-F0-9],并且会通过这个把它转换成数值

if ( (unsigned int)capitical_hex_to_num(input, &input_num, 10) != 5 )
{
      //error
}

如果范围不对,capitical_hex_to_num会返回0

然后在后面,有一个input_num & 0xF0000就会错误的检查,最后得出flag为XXXXX0XXXX的形式,其中X[0-9A-F]

PS:这个坑了我整整一天,一直看错了以为flag是XXXX00XXXX,然后一直跑不出解,还以为是反调试了,调试的时候的训练数据跟不调试的不一样,还对程序做了补丁输出了所有不加调试时候的数据,然后发现一样怀疑人生。。。也还是自己犯傻了。。。

0x02 暴力破解

暴力破解也很简单了,基本上就是把前面三个函数nop掉,因为要么会crash(可能是因为爆破只调用了main,没有调用程序的预处理,而0x401F00很可能是编译器生成的call,要么会太慢(训练的时候用GetTickCount规定了必须1秒)。然后,把预先训练好的数据dump下来,直接memcpy过去,接下来就是暴力跑了。。。

代码轻喷。。。

#include <stdio.h>
#include <stdint.h>
#include <windows.h>
#include <set>
#include <string>
#include <vector>
using namespace std;
char train_res[] =
"\xF4\xC9\x5C\x82\x72\xA7\xDB\x3F\x77\x7A\x2F\xBB\xCA\xE6\xE3\x3F"
"\x34\xD1\x6F\x19\xAF\x87\xDA\x3F\x4A\x15\xD0\x5D\xF6\x81\xE0\x3F"
"\x83\xA2\x31\xA1\x75\xBF\xE3\x3F\x04\xE7\x56\x11\xA2\x86\xE3\x3F"
"\x15\x00\xFE\x2F\x52\x6F\xE2\x3F\x96\x58\x2D\x82\x5F\xF8\xC8\x3F"
"\x24\xD6\xB9\xC5\x92\x3F\xD9\x3F\xA1\x0B\xB3\x77\xBA\x3B\xD7\x3F"
"\x6E\x63\x4B\x28\xE7\x9A\xDF\x3F\xEA\xBD\xB0\x74\xF5\xB6\xD4\x3F"
"\xA8\x56\x38\xD4\xDC\x6E\xD4\x3F\xD7\x4F\x67\x78\xF7\x12\xD9\x3F"
"\x83\x67\x73\x66\x40\xD2\xE2\x3F\x26\x31\x9B\x7B\xB6\xDA\xE0\x3F"
"\x14\x44\x18\xEE\xC1\x86\xDF\x3F\xDF\x3B\xA1\xA7\x78\xDB\xDF\x3F"
"\xB0\xEC\x02\x4D\xD9\x00\xE5\x3F\x00\x00\x00\x00\x00\x00\x00\x00"
"\x5D\x4B\x9C\x75\x74\x1C\xD6\x3E\xA4\xAF\x66\xC2\x19\x31\xF1\x3E"
"\x36\x49\x98\x5F\xD7\xAF\xEF\xBE\xDE\x98\x79\xC7\xED\xC6\x08\xBF"
"\x4C\x11\x79\x36\x80\x70\xEE\x3E\x58\x85\x25\x23\x23\xB3\x07\x3F"
"\xC4\x2E\xBF\x18\xC5\xCD\xFC\x3E\x22\x50\x8A\x2D\x82\x6F\x16\x3F"
"\x12\x26\x2B\x98\xC1\xF0\x06\xBF\x69\x75\xF2\x53\xBF\xDD\x21\xBF"
"\xBC\xEB\x0E\x22\x10\xFB\xE0\xBE\x24\xAE\x9C\x94\xE7\x8F\xFA\xBE"
"\x9C\x6B\x18\x94\x5F\xEA\x06\xBF\xB6\x98\xD6\x64\xEA\xD4\x21\xBF"
"\x2D\x5A\x76\x46\xB8\xA4\xE9\xBE\x7E\x3C\x5A\x13\x9D\x29\x04\xBF"
"\xF2\x75\x33\xC1\x9B\xA0\xF9\x3E\x3C\x94\x1A\x69\x06\xF0\x13\x3F"
"\x6E\x96\x13\xBE\x08\x92\x0D\x3F\x7A\x11\x8B\x21\xE3\x0A\x27\x3F"
"\x66\xB1\x8D\x73\x1C\x9E\xDD\xBE\xE2\xCD\xA5\x94\xA5\x12\xF7\xBE"
"\x99\x3C\xC8\x3D\x25\x7C\x07\x3F\x69\xC2\x05\xE1\x1E\x57\x22\x3F"
"\xA1\xF1\x69\x15\x8E\xC0\xE4\xBE\xBE\xDB\x9A\x16\xA3\x23\x00\xBF"
"\x1F\xCF\xF5\xBF\x2A\x0E\x06\x3F\x03\x3C\x17\x2D\x45\x2C\x21\x3F"
"\xDB\xA7\x65\x6A\x22\xAE\xFB\xBE\x67\x24\xFA\x66\x8C\x8D\x15\xBF"
"\x23\x0F\xE6\x35\x6D\xB5\xDB\xBE\xA7\x08\x93\x46\x8B\x97\xF5\xBE"
"\x25\xFE\xFD\x27\x74\x42\xF8\xBE\x33\xC1\xB6\xC9\x82\xE0\x12\xBF"
"\xBC\xB4\x2E\x5D\xD2\xE7\xEB\xBE\x82\x1C\x89\x45\x5E\xBB\x05\xBF"
"\xA6\xBE\xF7\x12\x08\x6B\x35\xBF\xA3\xE0\x65\xD5\x4F\xB4\x3E\xBF"
"\xBE\xD3\xEE\x17\x42\x24\x34\xBF\x07\x82\x9A\x18\x24\x82\x39\xBF"
"\xEC\x5F\x67\xA5\x88\x0F\x3E\xBF\x77\xF7\x1E\xCC\x62\x2E\x3E\xBF"
"\xB8\xF5\x6C\x6B\xE8\xF8\x3B\xBF\xF9\x8A\x5C\xE3\x12\xD5\x22\xBF"
"\xDF\xEC\x0B\xC7\x0F\x65\x33\xBF\xDA\x29\x3D\xC1\x29\xAD\x31\xBF"
"\xA2\x68\xE8\x18\xCB\xB9\x37\xBF\x8F\xA5\x79\xD5\x7C\x53\x2F\xBF"
"\xEB\xA5\x44\xFD\x59\x90\x2F\xBF\x98\x57\xA7\x97\x55\x1C\x33\xBF"
"\xBD\xD8\x4A\xB5\xFE\xAF\x3C\xBF\x06\x8A\xCE\xC8\x8E\xFF\x39\xBF"
"\xAC\x2D\x09\xFE\x37\xF4\x37\xBF\xB6\x4A\xFF\x23\xCB\x9D\x38\xBF"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\xC1\xDB\x56\x91\x5E\x64\xF0\xBF\x4B\x00\xE0\x6A\x9C\xB5\xB5\x3F"
"\x9A\x5F\x0F\xEF\x9D\xFD\xE0\xBF\x53\xE8\x24\x3E\xE2\x85\xE6\x3F"
"\x12\x75\x2F\xA7\xBC\xDE\xBE\xBF\x8C\xCB\xB3\xA1\x39\x0B\xD4\xBF"
"\x3C\x20\x0F\x64\x3F\x7D\xE8\xBF\x29\x95\x3E\xAD\xDC\xB9\xD5\x3F"
"\xC3\xFD\xBC\x83\xF9\x27\xDA\x3F\xAE\x9E\x58\xD9\xE5\x2B\xD6\x3F"
"\x17\xB7\x36\x9A\xB6\x81\xE5\xBF\x52\x72\x52\xBF\x1B\x80\xE6\x3F"
"\xCC\x99\xFA\xA9\x2E\xAE\xDE\x3F\x36\xCF\x49\x0A\xC0\x90\xC2\x3F"
"\x6E\xDB\xE9\xAC\xDE\x42\xE8\xBF\xE8\x64\x44\x54\x05\x0F\xF3\xBF"
"\x84\x09\x49\xDA\x1F\xEC\xE6\xBF\x07\x3E\xA9\xF5\xE2\xC4\xC7\xBF"
"\xF9\xB6\x2F\x2E\xB3\xD6\xD8\xBF\x68\x94\x7C\x85\x6F\x41\xDC\xBF"
"\xD9\xB8\xFE\x71\x0F\xF9\xE7\x3F\xF4\xFB\xF8\x4E\x5C\xCC\xD2\xBF"
"\x2B\xCD\x51\x0A\x16\xF8\xD1\xBF\x05\x6C\xB7\xE8\x2B\x20\xE5\xBF"
"\xA4\xC5\x7D\xC9\x46\x3B\xF2\xBF\xEB\xDD\x59\x93\x2B\xE4\xD7\xBF"
"\x65\x0F\x01\x0C\x0E\xC9\xD7\xBF\xDC\x1A\x66\x09\xAE\x8A\xD4\xBF"
"\x1C\x77\x3B\x01\x7C\x2D\xCD\x3F\xB2\x08\x56\xDF\x0D\x52\xD2\x3F"
"\x8A\x24\xE7\xD4\x1E\xBF\xE4\xBF\x18\x8A\xB9\x5F\xAD\xEB\xD5\x3F"
"\xC8\x0B\xD4\xE1\xDB\x13\xBA\x3F\x1F\x92\x37\x8B\x84\x23\xB1\xBF"
"\x3C\x48\xB7\x1B\xD7\x93\xE9\xBF\x8F\x8D\xD3\xE0\x92\xBB\xD1\x3F"
"\x6F\xFF\x93\x81\xBB\x91\xBE\xBF\x2E\x5B\x58\x7B\x7D\x4F\xD7\x3F"
"\x40\x0B\x20\xEE\x51\x71\xD5\xBF\xB8\x8E\x66\x55\x54\xB8\xE3\xBF"
"\x89\x96\xC2\x58\xFC\x9C\xF0\x3F\xA6\x2E\xA6\x15\xD9\xBA\xC8\x3F"
"\x67\x33\xD4\xA5\x7B\x07\xF0\x3F\x80\xA7\x9F\x61\x11\x61\xDC\x3F"
"\x64\xB0\xA1\x0A\x66\x47\xE2\xBF\x4D\xD5\xEF\x9B\x58\xE4\xF5\xBF"
"\xB1\xB5\x6D\x82\x74\x52\xC4\x3F\x73\x1D\xB8\x02\xA3\x73\xF2\xBF"
"\x63\x4D\x02\x27\xBD\x3A\xD0\x3F\x95\x6D\x38\x89\xE6\xA4\xEF\xBF"
"\xA2\x26\x9C\xE5\xB5\x90\xE3\x3F\x71\x1D\xD9\x59\xDD\x17\xC3\x3F"
"\x0D\xEB\x20\x92\x6F\x9A\xE0\x3F\x86\x2E\x01\x8E\x8E\x1B\xD3\x3F"
"\x7B\x14\xAE\x47\xE1\x7A\x84\x3F\xB8\x1E\x85\xEB\x51\xB8\x9E\x3F"
"\x52\xB8\x1E\x85\xEB\x51\x33\x40\x00\x00\x00\x00\x00\x00\x00\x00"
"\x7B\x14\xAE\x47\xE1\xFA\x23\x40\x7B\x14\xAE\x47\xE1\xFA\x23\x40"
"\xA4\x70\x3D\x0A\xD7\xA3\xD0\x3F\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x02";

set<string> outputs;
vector<uint32_t> goons;
char* module_addr = 0;
size_t main_off = 0x3880;
size_t printf_iat_off = 0xD3B4;
size_t scanf_iat_off = 0xD3C4;
bool success = false;
bool goon = false;
uint32_t number_now;
typedef int (__cdecl *main_t)();
main_t program_main;

void check(uint32_t a)
{
	number_now = a;
	memcpy(module_addr + 0xC980, train_res, sizeof(train_res) - 1);
	program_main();
	if (success)
	{
		printf("%hx", (uint16_t)(number_now & 0xffff));
		printf("00");
		printf("%hx", (uint16_t)(number_now >> 16));
		system("pause");
	}
	if (goon)
	{
		//goons.push_back(a);
		printf("%.8X\n", a);
		goon = false;
	}
}

void __fastcall hook_printf(char* content)
{
	if (strcmp(content, "Congratulation\n") == 0)
	{
		success = true;
	}
	else if (strcmp(content, "Come on, go on\n") == 0)
	{
		goon = true;
	}
	//outputs.insert(content);
	//printf("%s\n", content);
}

void __fastcall hook_scanf(char* format, char* buf)
{
	sprintf_s(buf, 5 , "%.4hX", (uint16_t)(number_now & 0xffff));
	buf[4] = 'E';
	buf[5] = '0';
	sprintf_s(buf + 6, 5, "%.4hX", (uint16_t)(number_now >> 16));
	//printf("%s\n", buf);
}

void init()
{
	module_addr = (char*)LoadLibraryA("NNCrackme.dll");
	if (module_addr == 0)
	{
		printf("fail to open dll\n");
		system("pause");
		exit(-1);
	}
		
	*(void**)(module_addr + printf_iat_off) = hook_printf;
	*(void**)(module_addr + scanf_iat_off) = hook_scanf;
	program_main = (main_t)(module_addr + main_off);
}

int main()
{
	init();
	for (size_t i = 0x00400000; i < 0x100000000; i+=0x1000000)
	{
		//printf("%zx\n", i);
		for (size_t j = 0; j < 0x100000; j++)
		{
			check((uint32_t)(i + j));
		}
	}
	for (uint32_t g : goons)
	{
		printf("0x%.8X,", g);
	}
	system("pause");
	return 0;
}

然后这个其实是有猜的成分,之前我是从0-0xffffffff然后开12个进程一直在跑的,然后发现好像只有0xXX4XXXXX会产生go on的信息,所以索性换成了以上的爆破方式,结果就在E的时候出结果了。。。

然后方法用的是把exe变成dll的老办法,为啥不用unicorn那套东西?因为还在学,没学会(逃