[原创]加密狗破解之硬件复制和软件模拟

声明:本文主要是做技术研究分享,以便让大家清楚如何更好的使用加密狗,从而防止软件不会被轻易破解。
下面主要通过对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来举例说明加密狗的完美破解方法,同样适用于其他加密狗,只要程序仅对狗的数据进行访问,狗内没有算法函数的情况就可以用差不多的方法实现。

标签: crack, dongle, api hook

已有 2 条评论

  1. 小萌 小萌

    你好

    谢谢你的专业工作

    您可以将完整的c ++源代码发送到我的电子邮件吗?

    naskari50@mail.com

  2. Sonysg Sonysg

    楼主可以帮我写个软件模拟rockey4nd么 我有usb的数据 我email已经留了 怎么联络你?

添加新评论