首页
论坛
课程
招聘
[原创]KCTF2020秋季赛 第二题 异常信号 WP
2020-11-20 11:33 586

[原创]KCTF2020秋季赛 第二题 异常信号 WP

2020-11-20 11:33
586

输入处理

控制台程序是通过NtReadFile获取输入的, 不出意外的化, 会有一个线程处于NtReadFile的同步等待状态, 依次查看线程当前EIP, 看是否有这样的线程存在.

查看线程列表如下:

可以看到4号线程, 就是我们要找的输入线程, 在777E1E6C下断. 输入测试注册码12345678, 回车, 不出意外, 成功断下, 观察栈:

地址0xCC3C30存放的就是读取到的我们输入的测试注册码. 对其下硬件读断点, 跟踪注册码处理.


保持对测试注册码的读取跟踪, 直到下图所示位置:

通过上下问分析, sub_561270会从注册码中依次取一个字符放到'参数2'所指地址, 暂且将其命名为get_char, 继续跟踪:

ebp-1dc内存数据如下:


可以看到, 561410行-561454行代码, 读取验证码每一个字符直到'\a'(不包括), 做对应的ASCII-数值转换(从中也可以看出, 是不支持小写字符的), 存放到缓冲区 ebp-36c, 执行完毕后, ebp-36c内存数据如下:

紧随其后, 再次转换:

上图代码将ebp-36c中数据重组, 放到ebp-dc缓冲区, 处理后结果如下图:


礼貌的开场白

第一个算法5614C0-5615BC, 注意规避561523-561553之间的反调试代码:

根据索引算出hash值, hash算法如下:

unsigned short calc_hash(short val)
{
	long _seed = val >= 0 ? val : 0x10000 + val;

	for (size_t i = 0; i < 101; i++)
	{
		_seed *= 0x343FD;
		_seed += 0x269EC3;
	}

	_seed >>= 0x10;
	_seed &= 0x7fff;
	return (unsigned short)_seed;
}

等价逻辑:

short m = 0,n = 0
for (m = 0, m < length:word:[ebp-dc], ++m)
{
    if (word:[ebp-dc][m] == calc_hash(m))
    {
        dword:[ebp-4fc] = n
    }
    else if(word:[ebp-dc][m] == calc_hash(-m))
    {
        ++n
    }
    else
    {
        print "一个礼貌的开场白"
        exit
    }
}

if(n < 12)
{
    print "行星太少了"
    exit
}

根据分析内容, 做个礼貌的回应, 构造一个最短长度的测试注册码:675EE1125B47D47B4E30C8644219BC4D3602B036296BA31F, 再次执行, '开场白'已过, 是时候进入真正的验证了.



有人说这题是无解的

分析随后的代码:

此时, ebp-4fc为上文中解析出的索引值集合,如下:


验证逻辑

fill [ebp-78], 0, 100

short i = 0, j = 0;
for (i = 0; i < length:dword:[ebp-4fc] - 1; ++i)
{
     for (j = i + 1; j < length:dword:[ebp-4fc]; ++j)
     {
         dis = dword:[ebp-4fc][j] - dword:[ebp-4fc][i]
         if(byte:[ebp-78][dis] == 0)
         {
              byte:[ebp-78][dis] = 1
         }
         else
         {
             print "此题无解"
             exit
         }      
     }
}

那么根据上面的验证逻辑, 什么样的注册码是满足条件的呢? 

考虑这样一个数列:

一个最少包含12个数的自然数数列, 其中任意两数的差都不相等.


如果这样的数列存在, 那么题中的验证逻辑是可以满足的, 那么是否存在这样的数列呢? 百度一下.

已经找到了我们想要的答案, 那就是: 0,2,6,24,29,40,43,55,68,75,76,85

上代码:

#include <iostream>
#include <sstream>
#include <set>
#include <vector>
#include <algorithm>
#include <iomanip>
using namespace std;

//hash算法
unsigned short calc_hash(short val)
{
	long _seed = val >= 0 ? val : 0x10000 + val;

	for (size_t i = 0; i < 101; i++)
	{
		_seed *= 0x343FD;
		_seed += 0x269EC3;
	}

	_seed >>= 0x10;
	_seed &= 0x7fff;
	return (unsigned short)_seed;
}

//注册码算法
string calc_seri(const vector<unsigned short>& nums)
{
	vector<unsigned short> seri_nums;
	ostringstream oss;
	
	auto m = *max_element(nums.begin(), nums.end());
	auto n = nums.begin();
	for (unsigned short i = 0; i <= m; i++)
	{
		if (i == *n)
		{
			++n;
			seri_nums.push_back(calc_hash(i));
		}
		else
		{
			seri_nums.push_back(calc_hash(-i));
		}
	}
	for (auto it = seri_nums.begin(); it != seri_nums.end(); ++it)
	{
		unsigned char low = *it & 0xff;
		unsigned char high = (*it >> 8) & 0xff;
		oss << uppercase << setw(2) << setfill('0') << right << hex << (unsigned)low << flush;
		oss << uppercase << setw(2) << setfill('0') << right << hex << (unsigned)high << flush;
	}
	return oss.str();
}

int main()
{
	unsigned short seri[] = { 0,2,6,24,29,40,43,55,68,75,76,85 };
	auto seri_str = calc_seri(vector<unsigned short>(seri, seri + 0xc));
	cout << "seri: " << endl << seri_str << endl;
	cin.get();
}



验证

完工.


反调试

https://bbs.pediy.com/thread-263720.htm



[培训]12月3日2020京麒网络安全大会《物联网安全攻防实战》训练营,正在火热报名中!地点:北京 · 新云南皇冠假日酒店

最后于 2020-11-24 10:28 被Anakin Stone编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (3)
雪    币: 5458
活跃值: 活跃值 (376)
能力值: ( LV5,RANK:72 )
在线值:
发帖
回帖
粉丝
qux 活跃值 2020-11-23 23:59
2
0
可以问一下是怎么附加进程的吗?或者说怎么绕过反调试开始调试的?
我把程序运行起来,然后x64dbg附加,因为有别的线程在反调试,就直接退出了。
雪    币: 611
活跃值: 活跃值 (161)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
Anakin Stone 活跃值 2020-11-24 10:28
3
0
qux 可以问一下是怎么附加进程的吗?或者说怎么绕过反调试开始调试的? 我把程序运行起来,然后x64dbg附加,因为有别的线程在反调试,就直接退出了。
https://bbs.pediy.com/thread-263720.htm
已补充, 请查阅.
雪    币: 5458
活跃值: 活跃值 (376)
能力值: ( LV5,RANK:72 )
在线值:
发帖
回帖
粉丝
qux 活跃值 2020-11-24 13:36
4
0
Anakin Stone https://bbs.pediy.com/thread-263720.htm 已补充, 请查阅.
感谢!
游客
登录 | 注册 方可回帖
返回