因为蜜汁自信导致我在短短两天内重装了7次系统,导致Everedit当年的注册码生成次数耗尽,所以想着先把试用限制过了凑合着用到明年。
1.分析
从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这个值删除,能正常打开了。
分析部分就到此结束了,下面开始敲代码
2.编码
采用劫持导入表hook的方案。
由前面的分析已经可以大致确定几个关键点了:
RegQueryValueExW
L"ZQBKAF8AMQA0ADIANGA4"
由于我们并没有去找机器码的算法,所以要做一些额外的工作。
大致思路如下:
hook advapi32.RegOpenKeyW获取项的名称和HKey用于比对。
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的流程大致如下:
使用一个全局静态变量g_reg_open_key_w_call_count记录其被调用的次数。
当次数为3的时候进入我们的流程。
找到关键项并记录、记录HKey用于比对。
卸载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时间戳。
3.总结
麻雀虽小,五脏俱全。
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 2020-8-12 17:04
被黑洛编辑
,原因: