Everedit的试用限制绕过
2020-08-12 17:11:37 Author: bbs.pediy.com(查看原文) 阅读量:383 收藏

 因为蜜汁自信导致我在短短两天内重装了7次系统,导致Everedit当年的注册码生成次数耗尽,所以想着先把试用限制过了凑合着用到明年。

从4.0版本开始授权文件就没有保存在本地的选项了,都是保存在注册表里,试用限制也是如此,先跟一下。

其实都不用查壳都知道是vmp,先拉到x64dbg里面对(RegQueryValueExW)跨模块调用里面的关键函数下断:

跑起来以后断下了:

   RDX应该是第二个参数: lpValueName(在x64下对函数的调用都是优先使用寄存器的,第一个参数是RCX),license不是我们想要的,继续跑。F9以后直接跑飞了,说明试用限制的call被加密了。直接对kernelbase.RegQueryValueExW这个函数下断,并记录他的RBX。跑了一遍以后发现了一个可疑的字符串:

跟踪他的来源,发现调用果然被加密了:

但是我们的目的已经达到了,打开注册表编辑器,看看这个项到底有什么玄机:

我觉得这个默认项很像时间戳啊,于是转换之:

5ECCEFBD -> 1590489021 -> 2020-05-26 18:30:21

那我们把试用条件改成今天0点试试:

2020-08-10  00:00:00 -> 1596988800 -> 5F301D80

发现还是提示过期,尝试将Value这个值删除,能正常打开了。

分析部分就到此结束了,下面开始敲代码

采用劫持导入表hook的方案。

由前面的分析已经可以大致确定几个关键点了:

RegQueryValueExW

L"ZQBKAF8AMQA0ADIANGA4"

由于我们并没有去找机器码的算法,所以要做一些额外的工作。

大致思路如下:

  1. hook advapi32.RegOpenKeyW获取项的名称和HKey用于比对。

  2. hook kernelbase.RegQueryValueExW 欺骗程序,告诉它:值“Value”并不存在;生成一个合法的“时间点”,然后改写函数的参数。

dllmain.cpp:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "framework.h"
#include "DllModule.h"

extern "C" __declspec(dllexport) void Out();
void Out()
{
	OutputDebugStringW(L"Out");
}
namespace
{
	bool is_init = false;
}

BOOL APIENTRY DllMain(HMODULE hModule,
	DWORD  ul_reason_for_call,
	LPVOID lpReserved
)
{
	if (DLL_PROCESS_ATTACH == ul_reason_for_call)
	{
		UNREFERENCED_PARAMETER(lpReserved);
		DisableThreadLibraryCalls(hModule);
		if (is_init)
		{
			return TRUE;
		}
		if (nullptr == g_DMod)
		{
			g_DMod = std::make_unique<DllModule>();
		}
		g_DMod->Attach();
		is_init = true;
	}
	else if (DLL_PROCESS_DETACH == ul_reason_for_call)
	{
		if (nullptr == g_DMod)
		{
			return TRUE;
		}
		g_DMod->Detach();
		if (is_init)
		{
			is_init = false;
		}
	}
	return TRUE;
}

创建一个名为“Out”的导出函数用于导入表劫持。

DllModule.h:

#pragma once
#include <cstdint>
#include <memory>

class DllModule
{
public:
	DllModule() = default;
	virtual ~DllModule() = default;
	static void Attach();
	static void Detach();
private:
	
};
using DllModulePtr = std::unique_ptr<DllModule>;
extern DllModulePtr g_DMod;

DllModule.cpp:

#include "DllModule.h"
#include "hooks.h"

DllModulePtr g_DMod;

void DllModule::Attach()
{
	hooks::Install();
}

void DllModule::Detach()
{
	hooks::UnInstall();
}

hooks.cpp:

#include "hooks.h"
#include "date_time.h"
#include <base/hook/fp_call.h>
#include <base/hook/inline.h>
#include <base/util/xorstr.hpp>
#include <base/util/Debug.hpp>
#include <windows.h>
#include <string>
#include <string_view>

#pragma comment(lib,"advapi32.lib")

namespace
{
	int32_t g_reg_open_key_w_call_count = 0;
	DWORD g_value_length = 0;
	PHKEY g_phkey = nullptr;
	bool g_is_reg_open_key_w_uninstall = false;
	bool g_is_reg_query_value_w_uninstall = false;
	std::wstring g_sub_key;
}

namespace hooks
{
	namespace hook_t
	{
		base::hook::hook_t RegOpenKeyW;
		base::hook::hook_t RegQueryValueExW;
	}
	namespace real
	{
		uintptr_t RegOpenKeyW = 0;
		uintptr_t RegQueryValueExW = 0;
	}

	namespace fake
	{
		static LSTATUS
			APIENTRY
			RegQueryValueExW(
				_In_ HKEY hKey,
				_In_opt_ LPCWSTR lpValueName,
				_Reserved_ LPDWORD lpReserved,
				_Out_opt_ LPDWORD lpType,
				_Out_writes_bytes_to_opt_(*lpcbData, *lpcbData) __out_data_source(REGISTRY) LPBYTE lpData,
				_When_(lpData == NULL, _Out_opt_) _When_(lpData != NULL, _Inout_opt_) LPDWORD lpcbData
			)
		{
			static bool only_call_once = false;

			auto ret_val = base::std_call<LSTATUS>(\
				real::RegQueryValueExW, \
				hKey, lpValueName, lpReserved, \
				lpType, lpData, lpcbData);

			if (only_call_once)
			{
				return ret_val;
			}
			//ERROR_FILE_NOT_FOUND
			if (nullptr != lpValueName)
			{
				std::wstring_view ValueName{ lpValueName };
				if (ValueName == xorstr_(L"Value"))
				{
					const auto ret = ERROR_FILE_NOT_FOUND;
					return ret;
				}
			}
			if (nullptr == g_phkey || nullptr == hKey)
			{
				return ret_val;
			}
			if (INVALID_HANDLE_VALUE == hKey)
			{
				return ret_val;
			}

			if (TRUE == IsBadReadPtr(g_phkey, sizeof(HKEY)))
			{
				return ret_val;
			}
			if (*g_phkey != hKey)
			{
				return ret_val;
			}

			if (0 == g_value_length)
			{
				if (nullptr != lpcbData)
				{
					if (FALSE == IsBadReadPtr(lpcbData, sizeof(DWORD)))
					{
						g_value_length = *lpcbData;
						OutputDebugStringEx(L"DEBUG_INFO | g_value_length: %d", g_value_length);
					}
				}
			}

			if (nullptr != lpData)
			{
				if (FALSE == IsBadReadPtr(lpData, g_value_length))
				{
					const auto arr_size = static_cast<size_t>(g_value_length);
					auto& arr = lpData;

					ev_date_time::DateTime d;
					auto time = d.Gen();
					auto str = d.GenString();

					OutputDebugStringEx("DEBUG_INFO | str: %s", str.c_str());
					memcpy(arr, &time, 4);
					//for (size_t i = 0; i < arr_size; ++i)
					//{

					//	OutputDebugStringEx(L"DEBUG_INFO | arr[i]: %X", arr[i]);
					//}

					only_call_once = true;
					UnInstall();
					return ret_val;
				}
			}

			return ret_val;
		}
		static LSTATUS
			APIENTRY
			RegOpenKeyW(
				_In_ HKEY hKey,
				_In_opt_ LPCWSTR lpSubKey,
				_Out_ PHKEY phkResult
			)
		{
			g_reg_open_key_w_call_count += 1;
			auto ret_val = base::std_call<LSTATUS>(real::RegOpenKeyW, hKey, lpSubKey, phkResult);
			if (g_reg_open_key_w_call_count < 3)
			{
				return ret_val;
			}
			if (!g_is_reg_open_key_w_uninstall)
			{
				if (nullptr != hook_t::RegOpenKeyW)
				{
					base::hook::uninstall(&hook_t::RegOpenKeyW);
					g_is_reg_open_key_w_uninstall = true;
				}
			}
			if (nullptr == lpSubKey)
			{
				return ret_val;
			}

			//如果能取到
			const std::wstring sub_key{ lpSubKey };
			const std::wstring_view sub_key_front{ xorstr_(L"Software\\Classes\\") };
			auto start_pos = sub_key.find(sub_key_front);

			//找不到就返回
			if (std::wstring::npos == start_pos)
			{
				return ret_val;
			}
			const auto sub_key_front_length = sub_key_front.length();
			start_pos += sub_key_front_length;
			OutputDebugStringEx(L"DEBUG_INFO | start_pos: %d", start_pos);
			const auto key_length = sub_key.length() - sub_key_front.length();
			//机器码长度(?)应为20个
			//e.g.: L"Software\\Classes\\ZQBKAF8AMQA0ADIANGA4"
			if (20 != key_length)
			{
				return ret_val;
			}
			OutputDebugStringEx(L"DEBUG_INFO | sub_key: %s", sub_key.c_str());
			auto key{ sub_key.substr(start_pos,20) };
			OutputDebugStringEx(L"DEBUG_INFO | key: %s", key.c_str());
			if (g_sub_key.empty())
			{
				g_sub_key = sub_key;
			}
			if (nullptr == g_phkey)
			{
				g_phkey = phkResult;
			}
			return ret_val;
		}
	}
	void Install()
	{
		if (0 != real::RegOpenKeyW)
		{
			return;
		}
		auto* const adv_api_32 = GetModuleHandleW(xorstr_(L"advapi32.dll"));
		if (nullptr == adv_api_32)
		{
			return;
		}
		real::RegOpenKeyW = reinterpret_cast<uintptr_t>(GetProcAddress(adv_api_32, xorstr_("RegOpenKeyW")));
		if (0 == real::RegOpenKeyW)
		{
			return;
		}
		base::hook::install(static_cast<uintptr_t*>(&real::RegOpenKeyW), \
			reinterpret_cast<uintptr_t>(fake::RegOpenKeyW), &hook_t::RegOpenKeyW);

		auto* const kernel_base = GetModuleHandleW(xorstr_(L"kernelbase.dll"));
		if (nullptr == kernel_base)
		{
			return;
		}
		real::RegQueryValueExW = reinterpret_cast<uintptr_t>(GetProcAddress(kernel_base, xorstr_("RegQueryValueExW")));
		if (0 == real::RegQueryValueExW)
		{
			return;
		}
		base::hook::install(static_cast<uintptr_t*>(&real::RegQueryValueExW), \
			reinterpret_cast<uintptr_t>(fake::RegQueryValueExW), &hook_t::RegQueryValueExW);
	}

	void UnInstall()
	{
		if (nullptr == hook_t::RegQueryValueExW)
		{
			return;
		}
		if (g_is_reg_query_value_w_uninstall)
		{
			return;
		}
		base::hook::uninstall(&hook_t::RegQueryValueExW);
		g_is_reg_query_value_w_uninstall = true;
	}
}

detour RegOpenKeyW的流程大致如下:

  1. 使用一个全局静态变量g_reg_open_key_w_call_count记录其被调用的次数。

  2. 当次数为3的时候进入我们的流程。

  3. 找到关键项并记录、记录HKey用于比对。

  4. 卸载hook。

detour RegQueryValueExW的流程大致如下:

     1. 定一个静态局部变量only_call_once用于确保流程只执行一次。

     2. 先判断lpValueName是不是L“Value”,若是,直接返回ERROR_FILE_NOT_FOUND告诉程序不存在这个项。

     3.调用原函数。

     4.用在detour RegOpenKeyW中获得的 HKEY和sub_key确保当前流程是读取试用起始日期。

     5.获取默认值的长度。

     6.获取由函数填充的lpData。

     7.生成一个合法的日期,然后替换它。

date_time.h:

#pragma once
#include <boost/date_time.hpp>

namespace ev_date_time
{
	using namespace boost::gregorian;
	class DateTime
	{
	public:
		DateTime();
		~DateTime() = default;
		date GetNow() const;
		static date::ymd_type GetTodayBase();
		static boost::posix_time::ptime GetToDay();
		int Gen() const;
		std::string GenString() const;

	private:
		date now_;
	};
}

date_time.cpp:

#include "date_time.h"

namespace ev_date_time
{
	DateTime::DateTime()
		:now_(day_clock::universal_day())
	{
	}

	date DateTime::GetNow() const
	{
		return now_;
	}

	date::ymd_type DateTime::GetTodayBase()
	{
		return day_clock::universal_day_ymd();
	}

	boost::posix_time::ptime DateTime::GetToDay()
	{
		const auto base = GetTodayBase();
		const boost::posix_time::ptime p(date(base.year, base.month, base.day));
		return p;
	}

	int DateTime::Gen() const
	{
		auto base = to_time_t(GetToDay());
		return static_cast<int>(base);
	}

	std::string DateTime::GenString() const
	{
		auto base = to_time_t(GetToDay());
		return std::to_string(base);
	}
}

大致阐述一下思路:

使用boost的day_clock::universal_day_ymd();获取当前的日期(精确到天)。

使用boost::posix_time::ptime创建“今天0点”这个时间点(有bug,东8区要减去8,不过无伤大雅)。

使用to_time_t()将它转换为unix时间戳。

麻雀虽小,五脏俱全。

HWS计划·2020安全精英夏令营来了!我们在华为松山湖欧洲小镇等你

最后于 1天前 被黑洛编辑 ,原因:


文章来源: https://bbs.pediy.com/thread-261368.htm
如有侵权请联系:admin#unsafe.sh