一、工具和调试环境
二、分析用户名/注册码的算法
2.1运行程序
输入Name和Key后,直接点击ok按钮会报错
009-01.png (18.12 KB, 下载次数: 0)
下载附件
2023-2-9 23:40 上传
2.2查壳
用Die查壳,无壳,vb程序
2.3详细分析
既然有提示字符串,直接上x64dbg搜索字符串。和008一样,基本都是关键字符串
009-02.png (33.55 KB, 下载次数: 0)
下载附件
2023-2-9 23:40 上传
直接选中失败提示字符串双击跟过去看看,和008基本类型,向上很快就能找到关键跳转
009-03.png (245.25 KB, 下载次数: 0)
下载附件
2023-2-9 23:51 上传
向上继续找,可以发现vbaVarTstEq函数的返回值来确定是否跳转。该函数是比较两个Variant变量值是否相等。我们在该函数下个断点,然后随便输入name和key,点击ok。成功断在该断点
009-04.png (256.83 KB, 下载次数: 0)
下载附件
2023-2-9 23:40 上传
分析该函数参数,可以看到一个是我们输入的key,另一个就是计算后的真码(也就是我们需要输入的正确的key)。
vbaVarTstEq函数的两个参数是两个指向Variant类型的指针,Variant类型的结构如下
struct Variant
{
short varType; // 说明data数据的真实类型
short res1;
short res2;
short res3;
long long data; // 8个字节,具体数据使用几个字节根据varType的值决定,为字符串时使用4个字节存储保存字符串的缓冲区的首地址
}
类型如下,来自VBA) (VarType 常量 | Microsoft Learn
009-05.png (58.09 KB, 下载次数: 0)
下载附件
2023-2-9 23:40 上传
其中注意为vbEmpty类型时表示尚未被初始化的 Variant 变量(已分配初始值)。 包含 vbEmpty 的 Variant 用于数值上下文时为 0;用于字符串上下文时为零长度字符串 ("")。
长整型是占4个字节,如下
009-06.png (27.23 KB, 下载次数: 0)
下载附件
2023-2-9 23:40 上传
搞清楚了Variant类型结构下面的分析就好分析了。
通过不断的回溯找到算法位置如下,由于大部分使用Variant变量进行运算,所以单步调试的时候无法直接看到变量数据,每次还需要自己在内存中查看
009-07.png (223.38 KB, 下载次数: 0)
下载附件
2023-2-9 23:41 上传
009-08.png (120.81 KB, 下载次数: 0)
下载附件
2023-2-9 23:41 上传
key的算法:name的每个字符的ascii码值相加之和的结果乘以1234567890(0x499602D2)获得的新结果转换为字符串,然后将其中的第4个字符和第9个字符替换为'-'。
三、算法核心代码模拟
char* GetKey8(char* szName)
{
static char szKey[60] = {};
int nSum = 0;
for (int i = 0; i
四、API总结
vbaVarMove
// 移动(可以理解成剪切),pVariant2 移动到 pVariant1
// 移动之后 pVariant2 就不可用了, 其类型会变为vbEmpty,数据没变
// 参数一通过 ecx 传递,参数二通过 edx 传递
// 返回移动后的地址, 即 pVariant1
Variant* vbaVarMove(Variant* pVariant1, Variant* pVariant2);
vbaLenVar
// 获取变体字符串(变体类型为字符串)长度
// 获取 pVariant2 中字符串的长度,结构保存到 pVariant1 中
// 通过栈传递参数
// 返回获取的长度,也是变体指针,即 pVariant1
Variant* vbaLenVar(Variant* pVariant1, Variant* pVariant2);
vbaVarForInit
// for循环的初始化(循环计数初值(i = start),循环结束边界(i
vbaVarForNext
// 循环下一个(会检查是否还能继续循环,如果不能就返回0,能就返回1)
// 如果还能循环就会修改循环计数, 即 pForI 指向的变体的值
// 通过栈传递参数
// 成功返回1,失败返回0
int vbaVarForNext(Variant* pForI, Variant* pForEnd, Variant* pForStep);
vbaI4Var
// 将变体(需要是长整型变体或Integer变体)转换为4个字节的整型数值
// 通过栈传递参数
int vbaI4Var(Variant* pVariant1);
rtcMidCharVar
// 从变体(字符串类型)字符串中指定位置开始获取指定个字符组成的新字符串
// Variant* pSubStr 接收获取的字符串
// Variant* pSrcStr 源字符串
// int nIdx 开始的位置(从1开始计算)
// Variant* pCnt 取多少个字符
// 通过栈传递参数
// 返回新的字符串(变体), 即 pSubStr
Variant* rtcMidCharVar(Variant* pSubStr, Variant* pSrcStr, int nIdx, Variant* pCnt);
vbaStrVarVal
// 将变体转换为字符串
// wchar** pStr 如果变体类型为整型,浮点型等数值型就先将其转换为字符串,该参数就是接收转换后的字符串的首地址
// 如果变体类型为字符串类型,则该参数忽略
// Variant* pVariant1 待转换的变体指针
// 通过栈传递参数
// 返回转换后的字符串首地址
wchar* vbaStrVarVal(wchar** pStr, Variant* pVariant1);
rtcAnsiValueBstr
// 获取字符串第一个字符的ascii码值
// wchar* pStr 字符串首地址
// 通过栈传递参数
// 返回转换后的字符串首地址
short rtcAnsiValueBstr(wchar* pStr);
vbaVarAdd
// 两个变体相加
// pVariant1 = pVariant2 + pVariant3
// 通过栈传递参数
// 返回存储结果的变体的地址, 即 pVariant1
Variant* vbaVarAdd(Variant* pVariant1, Variant* pVariant2, Variant* pVariant3);
vbaVarMul
// 两个变体相乘
// pVariant1 = pVariant2 * pVariant3
// 通过栈传递参数
// 返回存储结果的变体的地址, 即 pVariant1
Variant* vbaVarMul(Variant* pVariant1, Variant* pVariant2, Variant* pVariant3);
vbaMidStmtVar
// 修改字符串指定位置指定长度的字符
// Variant* pReplaceStr 用于替换的字符串
// int pCnt 替换的个数
// int nIdx 开始的位置(从1开始计算)
// Variant* pSrcStr 源字符串
// 通过栈传递参数
// 返回值未知
void vbaMidStmtVar(Variant* pReplaceStr, int nCnt, int nIdx, Variant* pSrcStr);
vbaVarTstEq
// 比较两个变体是否相等(类型和数据都要一致)
// 通过栈传递参数
// 相等返回-1,不相等返回0
int vbaVarTstEq(Variant* pVariant1, Variant* pVariant2);