因为蜜汁自信导致我在短短两天内重装了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"
由于我们并没有去找机器码的算法,所以要做一些额外的工作。
大致思路如下:
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时间戳。
麻雀虽小,五脏俱全。
HWS计划·2020安全精英夏令营来了!我们在华为松山湖欧洲小镇等你
最后于 1天前 被黑洛编辑 ,原因: