[原创]加密狗破解之硬件复制和软件模拟
声明:本文主要是做技术研究分享,以便让大家清楚如何更好的使用加密狗,从而防止软件不会被轻易破解。
下面主要通过对Rockey4ND狗的分析来说明如何破解加密狗,包括硬件复制和软件模拟,不谈爆破方法。
首先说明可以硬件复制或者软件模拟的狗要具备下面几种条件:
1.有真狗的运行环境,没狗破解一般只能爆破;
2.硬件复制要知道狗的用户密码和开发密码,软件模拟密码可以不需要;
3.程序只从狗中读取或写入数据;
4.如果程序在狗中写入了算法函数,这个一般会比较难,但如果逆向出函数算法,也可以模拟;
下面就介绍一款使用了Rockey4ND狗的软件的分析过程,软件名字当然不会说了。
Rockey4ND是无驱HID类型狗,这种截获数据方法有多种,可以用USBTrace,hid.dll替换,HOOK CreateFileA等方法, 但直接有效的方法是直接替换狗的API调用.
1.首先下载一份Rockey4ND的开发SDK,通过查看文件很容易知道,这个狗只有一个API,API定义为:
WORD WINAPI Rockey(WORD function, WORD* handle, DWORD* lp1, DWORD* lp2, WORD* p1, WORD* p2, WORD* p3, WORD* p4, BYTE* buffer);
主要功能定义(Rockey4_ND_32.h文件):
#define RY_FIND 1 // Find Dongle
#define RY_FIND_NEXT 2 // Find Next Dongle
#define RY_OPEN 3 // Open Dongle
#define RY_CLOSE 4 // Close Dongle
#define RY_READ 5 // Read Dongle
#define RY_WRITE 6 // Write Dongle
#define RY_RANDOM 7 // Generate Random Number
#define RY_SEED 8 // Generate Seed Code
#define RY_WRITE_USERID 9 // Write User ID
#define RY_READ_USERID 10 // Read User ID
#define RY_SET_MOUDLE 11 // Set Module
#define RY_CHECK_MOUDLE 12 // Check Module
#define RY_WRITE_ARITHMETIC 13 // Write Arithmetic
#define RY_CALCULATE1 14 // Calculate 1
#define RY_CALCULATE2 15 // Calculate 2
#define RY_CALCULATE3 16 // Calculate 3
#define RY_DECREASE 17 // Decrease Module Unit
#define RY_CALLNET 18 // NetRockey4ND arithmetic
(1) Find Dongle
Input:
function = 1
*p1 = pass1
*p2 = pass2
*p3 = pass3
*p4 = pass4
Return:
*lp1 = Rockey4ND HID
return 0 = Success, else is error code
(2) Find Next Dongle
Input:
function = 2
*p1 = pass1
*p2 = pass2
*p3 = pass3
*p4 = pass4
Return:
*lp1 = Rockey4ND HID
return 0 = Success, else is error code
(3) Open Dongle
Input:
function = 3
*p1 = pass1
*p2 = pass2
*p3 = pass3
*p4 = pass4
*lp1 = Rockey4ND HID
Return:
*handle = Opened dongle handle
return 0 = Success, else is error code
(4) Close Dongle
Input:
function = 4
*handle = dongle handle
Return:
return 0 = Success, else is error code
(5) Read Dongle
Input:
function = 5
*handle = dongle handle
*p1 = pos
*p2 = length
buffer = pointer of buffer
Return:
Fill buffer with read contents
return 0 = Success, else is error code
(6) Write Dongle
Input:
function = 6
*handle = dongle handle
*p1 = pos
*p2 = length
buffer = pointer of buffer
Return:
return 0 = Success, else is error code
(7) Generate Random Number
Input:
function = 7
*handle = dongle handle
Return:
*p1 = random number
return 0 = Success, else is error code
(8) Generate Seed Code
Input:
function = 8
*handle = dongle handle
*lp2 = seed code
Return:
*p1 = seed return code 1
*p2 = seed return code 2
*p3 = seed return code 3
*p4 = seed return code 4
return 0 = Success, else is error code
(9) Write User ID
Input:
function = 9
*handle = dongle handle
*lp1 = User ID
Return:
return 0 = Success, else is error code
(10) Read User ID
Input:
function = 10
*handle = dongle handle
Return:
*lp1 = User ID
return 0 = Success, else is error code
(11) Set Module
Input:
function = 11
*handle = dongle handle
*p1 = module number
*p2 = module content
*p3 = set whether allow decrease (1 = allow, 0 = no allow)
Return:
return 0 = Success, else is error code
(12) Check Module
Input:
function = 12
*handle = dongle handle
*p1 = module number
Return:
*p2 = 1 means the module is valid, 0 means the module is invalid
*p3 = 1 means the module can't decrease, 0 means the module can decrease
return 0 = Success, else is error code
(13) Write Arithmetic
Input:
function = 13
*handle = dongle handle
*p1 = pos
buffer = arithmetic instruction string
Return:
return 0 = Success, else is error code
(14) Calculate 1 (Hide Unit Init Content = HID high 16bit, HID low 16bit, module content, random number)
Input:
function = 14
*handle = dongle handle
*lp1 = calculate begin pos
*lp2 = module number
*p1 = input value 1
*p2 = input value 2
*p3 = input value 3
*p4 = input value 4
Return:
*p1 = return code 1
*p2 = return code 2
*p3 = return code 3
*p4 = return code 4
return 0 = Success, else is error code
(15) Calculate 2 (Hide Unit Init Content = seed return code 1, seed return code 2, seed return code 3, seed return code 4)
Input:
function = 15
*handle = dongle handle
*lp1 = calculate begin pos
*lp2 = seed code
*p1 = input value 1
*p2 = input value 2
*p3 = input value 3
*p4 = input value 4
Return:
*p1 = return code 1
*p2 = return code 2
*p3 = return code 3
*p4 = return code 4
return 0 = Success, else is error code
(16) Calculate 3 (Hide Unit Init Content = module content, module+1 content, module+2 content, module+3 content)
Input:
function = 16
*handle = dongle handle
*lp1 = calculate begin pos
*lp2 = module begin pos
*p1 = input value 1
*p2 = input value 2
*p3 = input value 3
*p4 = input value 4
Return:
*p1 = return code 1
*p2 = return code 2
*p3 = return code 3
*p4 = return code 4
return 0 = Success, else is error code
(17) Decrease Module Unit
Input:
function = 17
*handle = dongle handle
*p1 = module number
Return:
return 0 = Success, else is error code
最好先看看SDK中自带的示例程序,对狗的API一般是如何调用的。
2.分析软件,发现软件是直接使用DLL来对狗进行访问的,Rockey4ND的DLL名字是: Rockey4ND.dll,对这种直接使用DLL来调用的程序真是无语,连分析API函数特征都不需要了(如果是静态库方式调用就需要分析API地址)。
3.建立一个DLL工程,实现和狗API一样的函数: Rockey,定义如下:
WORD WINAPI Rockey(WORD function, WORD* handle, DWORD* lp1, DWORD* lp2, WORD* p1, WORD* p2, WORD* p3, WORD* p4, BYTE* buffer)
{
WORD ret = ERR_SUCCESS;
// ......后面再说
return ret;
}
Makefile内容:
LIBRARY ApiRockey4
EXPORTS
Rockey @ 1
这样DLL就导出了和加密狗一样的API, 同时也要把自己的DLL改为Rockey4ND.dll,原Rockey4ND.dll就要换一个名字,比如改为:Rockey4ND.org.dll
4.获取原狗DLL的API地址:
1)定义API函数,然后声明一个全局变量:
typedef WORD (WINAPI * api_Rockey)(WORD function, WORD* handle, DWORD* lp1, DWORD* lp2,
WORD* p1, WORD* p2, WORD* p3, WORD* p4, BYTE* buffer);
// 这个就是为了保存原API的地址
api_Rockey g_Rockey = NULL;
2)获取原API地址:
HMODULE hModule = LoadLibraryW(L"Rockey4ND.org.dll");
if(hModule != NULL)
{
// 获取原API函数地址
g_Rockey = (api_Rockey)GetProcAddress(hModule, "Rockey");
}
在DLL加载的时候运行上面的代码
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
// 加载原DLL模块
HMODULE hModule = LoadLibraryW(L"Rockey4ND.org.dll");
if(hModule != NULL)
{
// 获取原API函数地址
g_Rockey = (api_Rockey)GetProcAddress(hModule, "Rockey");
}
}
break ;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
{
}
break;
}
return TRUE;
}
5.数据截获
WORD WINAPI Rockey(WORD function, WORD* handle, DWORD* lp1, DWORD* lp2, WORD* p1, WORD* p2, WORD* p3, WORD* p4, BYTE* buffer)
{
WORD ret = ERR_SUCCESS;
// 这里是执行原API调用
ret = g_Rockey(function, handle, lp1, lp2, p1, p2, p3, p4, buffer);
// 根据定义,我把对狗进行数据读写的数据转化为字符串,便于记录
string strBuffer;
if((function == RY_READ) || (function == RY_WRITE))
{
if((p2 != NULL) && ((*p2) > 0))
{
// 数据转换成文本
strBuffer = data2HexString((const char *)buffer, (*p2));
}
}
// 格式所有参数
string str = formatString("Rockey(function(%d), handle(0x%04X), "
"lp1(0x%08X), lp2(0x%08X), "
"0x%04X, 0x%04X, 0x%04X, 0x%04X, "
"%s), ret = %d\r\n",
(int)(function), (handle != NULL) ? (int)(*handle) : 0,
(lp1 != NULL) ? (int)(*lp1) : 0, (lp2 != NULL) ? (int)(*lp2) : 0,
(p1 != NULL) ? (int)(*p1) : 0, (p2 != NULL) ? (int)(*p2) : 0,
(p3 != NULL) ? (int)(*p3) : 0, (p4 != NULL) ? (int)(*p4) : 0,
strBuffer.c_str(), (int)(ret));
//打印输出或者记录到文件
logOutput(str);
return ret;
}
6.把自己的DLL替换后,运行程序,如果程序没问题,所有数据都会被记录:
// 查找狗,ret = 0,表示找到狗,成功.
Rockey(function(1), handle(0x0000), lp1(0x66666666), lp2(0x77777777), 0x1111, 0x2222, 0x3333, 0x4444, ), ret = 0
// 打开狗,ret = 0,打开成功
Rockey(function(3), handle(0x0000), lp1(0x66666666), lp2(0x77777777), 0x1111, 0x2222, 0x3333, 0x4444, ), ret = 0
// 查找下一个,ret = 17, 没有找到更多的狗,失败
Rockey(function(2), handle(0x0000), lp1(0x66666666), lp2(0x77777777), 0x1111, 0x2222, 0x3333, 0x4444, ), ret = 17
// 从位置0x0008,读取0x0010长度的数据,读取到的内容是:A869BA3E70096F3C0D578FE34E5F8FD4
Rockey(function(5), handle(0x0000), lp1(0x66666666), lp2(0x77777777), 0x0008, 0x0010, 0x3333, 0x4444, A869BA3E70096F3C0D578FE34E5F8FD4), ret = 0
通过参数定义可以知道,开发商ID是0x66666666, 加密狗的4个密码也获取到了,基本密码为:0x1111, 0x2222,高级密码:0x3333, 0x4444 (我截获的密码当然不是这个啦)。
然后多运行几次程序,看看每次读取的数据是否都是相同的,如果每次都一样,那真的是要恭喜你了,但如果有调用狗内函数的功能,那就复杂了,这里暂时不讨论算法这种情况。
7.硬件复制方案:
1)既然4个密码都有了,最完美的方案就是硬件复制,从TB上面买空狗;
2)用官方工具可以直接复制,Rockey4ND_Editor.exe,这个在Rockey4ND的SDK中有,就是使用上面稍有复杂;
3)如果觉得工具有点麻烦的话,可以写一个程序,先从原狗中把数据都读出来,然后再把数据写入到新狗中;
8.软件模拟方案:
就是把我们之前记录数据的部分反过来,下面是大概的代码,一些代码不具体解释了:
WORD WINAPI Rockey(WORD function, WORD* handle, DWORD* lp1, DWORD* lp2, WORD* p1, WORD* p2, WORD* p3, WORD* p4, BYTE* buffer)
{
WORD ret = ERR_SUCCESS;
*handle = NULL;
switch(function)
{
case RY_FIND:
{
// 返回开发商ID
*lp1 = 0x66666666;
}
break ;
case RY_OPEN:
{
// 返回句柄
*handle = 0x8888;
}
break ;
case RY_FIND_NEXT:
{
// 没有更多加密狗
ret = ERR_NOMORE;
}
break ;
case RY_READ:
{
// gRockey4Data是原狗的所有数据
memcpy(buffer, gRockey4Data+(*p1), (*p2));
}
break ;
case RY_WRITE:
{
memcpy(gRockey4Data+(*p1), buffer, (*p2));
}
break ;
case RY_RANDOM:
{
*p1 = (WORD)(rand()%(0x10000));
}
break ;
case RY_SEED:
{
*p1 = (WORD)(rand()%(0x10000));
*p2 = (WORD)(rand()%(0x10000));
*p3 = (WORD)(rand()%(0x10000));
*p4 = (WORD)(rand()%(0x10000));
}
break ;
}
}
硬件复制和软件模拟不需要对原程序进行破解,软件升级一般也不需要做任何改动,算是完美的方案,本文是通过Rockey4ND来举例说明加密狗的完美破解方法,同样适用于其他加密狗,只要程序仅对狗的数据进行访问,狗内没有算法函数的情况就可以用差不多的方法实现。
《女友第二季》欧美剧高清在线免费观看:https://www.jgz518.com/xingkong/149942.html
哈哈哈,写的太好了https://www.cscnn.com/
哈哈哈,写的太好了https://www.cscnn.com/
不错不错,我喜欢看 www.jiwenlaw.com
看的我热血沸腾啊https://www.ea55.com/
叼茂SEO.bfbikes.com
想想你的文章写的特别好
看的我热血沸腾啊https://www.jiwenlaw.com/
叼茂SEO.bfbikes.com
博主真是太厉害了!!!
你好
谢谢你的专业工作
您可以将完整的c ++源代码发送到我的电子邮件吗?
naskari50@mail.com
楼主可以帮我写个软件模拟rockey4nd么 我有usb的数据 我email已经留了 怎么联络你?