首页
社区
课程
招聘
[转帖]加密狗破解经验分享
2019-11-26 15:18 42481

[转帖]加密狗破解经验分享

2019-11-26 15:18
42481

这个教程没有谈太多的技术(主要是我对[脱壳破解]还是处在初级爆破阶段),只想和大家分享一些破解过程,有些过程已经有些忘记,大家凑合看吧。
因为自己是程序员,真是非常热爱这个行业,正因为热爱,所以对各方面的技术也都感兴趣,黑客入侵、木马、外挂、破解都干过,主要是当初所谓的成就感和来自别人崇拜的目光吧,但是因为自己主要是平时没事玩一玩,对一些软件自己动手破解后用着舒服一些,也没有加入过任何的组织(主要也是水平确实不够), 所以在几个方面始终都没有步入很深入的层次,最近加入吾爱,主要也是因为破解这个加密狗的原因,同时也是想自己能够在这个领域再提高一些,希望得到大家的指教。

好了,步入正题吧,朋友问我一个软件能否破解去掉狗,因为我对狗实际的破解经验不多,所以我只能说试试看吧,软件名字这里就不说了。

1:运行
拿到一个软件肯定是先运行看看喽(未插加密狗)

报错:
 




缺少运行环境,从标题上面看可以确定加密狗的厂商,google搜索之(遇到问题我很少会先问别人,基本都是先在网络上搜索,自己在寻找学习的过程也是自己进步的过程,毕竟自己经历了才是最难忘的嘛,过程比结果更重要)。
在搜索后发现,这个狗不简单啊,自己信心受到了一些打击,毕竟看介绍功能非常强大(其实目前我还不知道这个狗是哪种类型)

最终在官网下载了软件和驱动:
http://www.aladdin.com.cn/hasp/support.asp

安装完成后,再运行,哦,检测到调试器?我没调戏它啊,想了一下肯定是因为我开着Visual Studio开发环境。

 


关掉VS再运行,果然没有提示被调戏,但已经感觉到这个软件有些不简单啊,现在显示找不到狗了,出现找不到加密狗了.
 


2:简单分析
有了提示对话框,大都都懂的,Debugger工具该上场了,先选择WinDbg吧,结果也不出所料一顿崩溃,算了用OD吧,虚拟机上的WinXP有OD(话说OD在Win8.1 64bits上面支持的非常不好),隐藏调试器,下断点在MessageBoxA(W),运行后进程自动退出,看来对付调戏做足了检测啊,那就直接运行吧,弹出无狗窗口.运行后F12暂停,切换到调用栈:

 


找到了调用来自哪里了,经过一顿分析,没什么进展,反而随着跟踪和调试,发现水越来越深,大量的花指令,大量的崩溃,深感自己有点力不从心了,还是继续在网上搜索相关的东西吧。

3:深入分析
通过一系列的分析,知道这个狗是 Aladdin HASP SRM,而且有AES-128加密算法的,本地安装有服务程序:hasplms.exe,开放TCP端口:1947(SentinelSRM (hasplm), Aladdin HASP License Manager),狗是网络版,只不过是localhost,客户端通过socket连接到localhost的hasplms去交互,同时对程序进行了查壳,壳为:

Signature: HASP HL Protection V1.X -> Aladdin
Matches: 42
Signature: HASP HL Protection V1.X -> Aladdin
Matches: 39



4:尝试各种方案
    1).不带狗脱壳:<失败>
    经过一系列尝试,确定不带狗是不可能脱掉的,遂向朋友拿到了USB加密狗(开始主要还是低估了这个狗,以为不带狗就可以脱壳,很傻很天真).

    2).带狗脱壳:<失败>
    有了加密狗,程序终于可以正确运行了,所以就尝试脱壳,使用"两次内存断点法寻找OEP",对.IDAta和.text分别下断点,还真定位到了,很兴奋(佩服大牛们分享的经验).

    Ctrl+A后代码显示为:
 

经查这是Borland C++的特征码。
    那就dump和IAT修复吧,分别是使用PETools和ImportREC,输入OEP地址并且调整IAT的Size到合适的大小,结果居然有近200个不能定位,我的妈呀,这可如何是好,进行了最后一翻挣扎,深感自己功力不够,无法确信自己可以手动修复IAT,即能找到每一个API的真正调用,但要把200个修复完也得把人累个半死啊,如果修复后还有其他检验就不好办了,最终不得不放弃。

 


    3).模拟网络数据包/DeviceIoControl:<失败>
    好吧,放弃了脱壳方案,就看看能不能模拟狗吧,因为是网络狗,所以可以先看看网络数据包。使用CommView截localhost:1947的数据包,经过带狗连续多次的分析,发现每次都是不同的,初步判断即使把网络数据包模拟返回估计也无法认证通过,最后简单的写程序测试了一下,确实是这样的,如果没有返回对应的数据包客户端就会出错,估计用HOOK DeviceIoControl也是一样的,再次放弃。

    4).MultiKey等驱动级别模拟:<失败>
    后来到网上又进行了一翻搜索,发现老外已经开发出很多驱动级别的HASP狗模拟,也找到了很多的配套工具,比如:h5dmp/h4dmp,hasploger,Toro Dongles Monitor,HaspHLDumper等等,经常一翻测试,最终使用h5dmp dump数据了,此时感觉自己终于看到了曙光,就用MultiKey相关的工具安装驱动,进行注册表文件的转换,导入注册表,结果拔掉狗经常无法找到驱动,或者还是失败,无法模拟,以下是注册表文件.


 


    在模拟加密狗部分花费了大量的时间,最终在网上发现这种办法是针对HASP HL,而且是不支持AES-128的,即使后来新的模拟狗驱动支持AES-128,也需要把加密狗每次发送和返回的数据进行记录,建立一个Q-A数据表才可以,但官方说AES-128算法可是上万种啊。
    "QTable"=hex:\
    42,84,... 84,AD,A4,\

    "ATable"=hex:\
    82,22,C2 ... 84,AD,A4,\
    如果程序总是随机使用某种加密算法,岂不哭死,直到现在才觉得模拟狗应该也走不下去了,号称全球第一的加密狗果然不简单,连国外的大牛们也不能破解其算法,还得制作Q-A数据库,对简单的或许可以,复杂的肯定就不行了吧,此时心灰意冷,感到很无助,耗费了很大的精力,到头来还是一场空,决定暂停破解,毕竟已经熬了几个夜晚和周末了。



    5).寻找高人破解:
    通过加一些QQ群找到了一些相关的有经验的大牛们,打算向他们讨教,结果没人理,说给钱才可以,我就问了一下多少钱?说是至少1w,因为这个太复杂,破解需要一周的时间,问如何破解,可否复制狗,回答只能爆破,无法复制狗,问了一下朋友可否接受这个价格,朋友说有点贵,如果没办法也可以接受。后继续询问对方,说要先付一半的钱,破解不了可以退一部分。我觉得不太可靠,和对方说我再考虑一下吧,就没有再联系对方。后来自己也考虑了一下,自己之前花费了那么大的心血,如果去找别人收费破解,真的不甘心。


5:最后的战役:
    1).希望重现:
    经历上述失败后,几天没有再继续,深感自己功力还是不够,就继续搜索相关技术和破解这个狗的相关信息,寻找更多的这个狗的相关工具,最后使用RTVIDTool2成功的将PW1和PW2从内存中dump出来,而且也保存出来.hvc文件,.hvc就是颁发给每个开发商的一个vendor code,里面肯定是有密码,AES Key和很多乱七八糟的信息,有了这个东西,就可以自己写个程序访问加密狗了。
 




    2).程序测试:
    使用Sentinel_HASP_SDK里面带的开发工具VendorSuite\toolbox,指定vendor code,果然可以成功的login,encrypt/decrypt数据,但自己还是高兴有点早了,虽然可以访问加密狗,还是不知道如何利用这个去复制和破解,因为据说这个加密狗有两种,一个是Master加密狗,另一个就是客户端的狗,使用Master加密狗才可以复制客户端加密狗的,也就是master才有写狗的权限。

 



    3).确定使用API HOOK方案:
    虽然知道了vendor code不能复制狗,但也不是完全没用,应该是说帮上了大忙,也是最后成功的转折点。有了这个就可以做很多的调试分析,跟踪HASP API的调用入口,经过一翻思考后,打算用API HOOK的方式截获相关加密狗的API,查SDK文档有如下几个:
    hasp_status_t HASP_CALLCONV hasp_login(hasp_feature_t feature_id, hasp_vendor_code_t vendor_code, hasp_handle_t *handle);
    hasp_status_t HASP_CALLCONV hasp_login_scope(hasp_feature_t feature_id, const char *scope, hasp_vendor_code_t vendor_code, hasp_handle_t *handle);
    hasp_status_t HASP_CALLCONV hasp_encrypt(hasp_handle_t handle, void *buffer, hasp_size_t length);
    hasp_status_t HASP_CALLCONV hasp_decrypt(hasp_handle_t handle, void *buffer, hasp_size_t length);
    还有hasp_logout, hasp_read,hasp_write,hasp_get_size,hasp_get_rtc,hasp_get_info等等


    4).DUMP解密的代码:
    在程序未运行时是无法找到相关的api调用的,毕竟是加密的,寻找代码解压可以先在socket api位置下断点,比如:connect,此时对当前进行了dump,用IDA Pro静态分析,通过API特征码和相关的字符串分析历尽千辛终于找到了hasp_login,hasp_login_scope,hasp_encrypt,hasp_decrypt的位置.



    5).API HOOK程序:
        主要HOOK了几个API,使用CFF修改IAT静态导入自己的DLL,真是万幸,程序没有做修改校验。这部分可以参考我发的另一个文章,比较详细:
        http://www.52pojie.cn/thread-257140-1-1.html



            gHooks.Add(_T("KERNEL32.DLL"),  "IsDebuggerPresent",    my_IsDebuggerPresent);
            gHooks.Add(_T("KERNEL32.DLL"),  "GetModuleHandleA",     my_GetModuleHandleA);
            gHooks.Add(_T("KERNEL32.DLL"),  "GetModuleHandleW",     my_GetModuleHandleW);
            gHooks.Add(_T("KERNEL32.DLL"),  "Process32FirstW",      my_Process32FirstW);
            gHooks.Add(_T("KERNEL32.DLL"),  "Process32NextW",       my_Process32NextW);
            gHooks.Add(_T("KERNEL32.DLL"),  "CloseHandle",          my_CloseHandle);
            gHooks.Add(_T("KERNEL32.DLL"),  "EnterCriticalSection", my_EnterCriticalSection);
            gHooks.Add(_T("KERNEL32.DLL"),  "GetProcAddress",       my_GetProcAddress);
            gHooks.BeginAll();

            对Process32NextW的hook是为了跳过对一些进程的检查,之前提到的,如果我开着VS就提示有调试器(没办法,工作要用VC),这样灰常的不爽,所以HOOK之后有如下代码:            

 

[C++] 纯文本查看 复制代码

?


01


02


03


04


05


06


07


08


09


10


11


12


13


14


15


BOOL
WINAPI my_Process32NextW(HANDLE
hSnapshot, LPPROCESSENTRY32W lppe)


{


CAdAutoHookApi autoHook(&gHooks, my_Process32NextW);


BOOL
ret = Process32Next(hSnapshot, lppe);


if(ret)


{


//        logOutput(formatString("Process32Next(0x%08X, Exe:%S)\r\n", (int)hSnapshot, lppe->szExeFile));


if(_wcsicmp(lppe->szExeFile, L"devenv.exe") == 0)


{


wcscpy(lppe->szExeFile, L"crack.exe");


logOutput("Process32Next, Rename \"devenv.exe\" to \"crack.exe\"\r\n");


}


}


return
ret;


}




    同时写了HASP API函数:

 

[C++] 纯文本查看 复制代码

?


01


02


03


04


05


06


07


08


09


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32


33


34


35


36


37


hasp_status_t HASP_CALLCONV my_hasp_login(hasp_feature_t feature_id,


hasp_vendor_code_t vendor_code,


hasp_handle_t *handle)


{


logOutput(formatString("HASP API : Call hasp_login(%d)\r\n", (int)feature_id));


*handle = 0x88888888;


return
HASP_STATUS_OK;


}


 


hasp_status_t HASP_CALLCONV my_hasp_login_scope(hasp_feature_t feature_id,


const
char*scope,


hasp_vendor_code_t vendor_code,


hasp_handle_t *handle)


{


logOutput(formatString("HASP API : Call hasp_login_scope(%d)\r\n", (int)feature_id));


*handle = 0x88888888;


return
HASP_STATUS_OK;


}


 


 


hasp_status_t HASP_CALLCONV my_hasp_encrypt(hasp_handle_t handle,


void
*buffer,


hasp_size_t length)


{


logOutput(formatString("HASP API : Call hasp_encrypt(0x%08X, 0x%08X, %d)\r\n",


(int)handle, (int)buffer, (int)length));


return
HASP_STATUS_OK;


}


 


hasp_status_t HASP_CALLCONV my_hasp_decrypt(hasp_handle_t handle,


void
*buffer,


hasp_size_t length)


{


logOutput(formatString("HASP API : Call hasp_decrypt(0x%08X, 0x%08X, %d)\r\n",


(int)handle, (int)buffer, (int)length));


这部分代码后面再说。。。


}





    这是找到的每个API地址,这部分也是大费周折(开发的SDK API和这个程序使用的API版本不同,没办法完全匹配)

 

[C++] 纯文本查看 复制代码

?


01


02


03


04


05


06


07


08


09


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32


33


34


35


36


37


38


39


40


41


42


43


44


45


46


47


48


49


50


51


52


53


54


55


56


57


58


59


60


61


62


63


64


65


66


67


int
HookHaspAPI_Begin()


{


if(!g_is_hasp_login_scope)


{


void
*pOldAddr = (void
*)0x0053FCE0;


const
BYTEverifyData[] = { 0x81, 0xEC, 0x00, 0x04, 0x00, 0x00, 0x56 };


HANDLE
hHook = gHooks.Add(pOldAddr, my_hasp_login_scope, verifyData,sizeof(verifyData));


if(hHook != NULL)


{


gHooks.Begin(hHook);


logOutput(formatString("HASP API : hasp_login_scope hooked\r\n"));


g_is_hasp_login_scope =true;


}


}


 


if(!g_is_hasp_encrypt)


{


void
*pOldAddr = (void
*)0x0053EF60;


const
BYTEverifyData[] = { 0x56, 0x8B, 0x74, 0x24, 0x10, 0x83, 0xFE, 0x08 };


HANDLE
hHook = gHooks.Add(pOldAddr, my_hasp_encrypt, verifyData,sizeof(verifyData));


if(hHook != NULL)


{


gHooks.Begin(hHook);


logOutput(formatString("HASP API : my_hasp_encrypt hooked\r\n"));


g_is_hasp_encrypt =true;


}


}


 


if(!g_is_hasp_decrypt)


{


void
*pOldAddr = (void
*)0x0053F000;


const
BYTEverifyData[] = { 0x56, 0x8B, 0x74, 0x24, 0x10, 0x83, 0xFE, 0x08 };


HANDLE
hHook = gHooks.Add(pOldAddr, my_hasp_decrypt, verifyData,sizeof(verifyData));


if(hHook != NULL)


{


gHooks.Begin(hHook);


logOutput(formatString("HASP API : my_hasp_decrypt hooked\r\n"));


g_is_hasp_decrypt =true;


}


}


 


if(!g_is_hasp_get_info)


{


void
*pOldAddr = (void
*)0x0053FDE0;


const
BYTEverifyData[] = { 0x81, 0xEC, 0xB8, 0x09, 0x00, 0x00, 0x8B, 0x84 };


HANDLE
hHook = gHooks.Add(pOldAddr, my_hasp_get_info, verifyData,sizeof(verifyData));


if(hHook != NULL)


{


gHooks.Begin(hHook);


logOutput(formatString("HASP API : my_hasp_get_info hooked\r\n"));


g_is_hasp_get_info =true;


}


}


 


if(!g_is_hasp_logout)


{


void
*pOldAddr = (void
*)0x0053EED0;


const
BYTEverifyData[] = { 0x56, 0x8B, 0x74, 0x24, 0x08, 0x6A, 0x00 };


HANDLE
hHook = gHooks.Add(pOldAddr, my_hasp_logout, verifyData,sizeof(verifyData));


if(hHook != NULL)


{


gHooks.Begin(hHook);


logOutput(formatString("HASP API : my_hasp_logout hooked\r\n"));


g_is_hasp_logout =true;


}


}


}





    6).HOOK时机:
    上述已经找到了每个API的地址,但何时进行HOOK,这里通过测试,发现程序在使用GetProcAddress调用某个API后而且沿未调用hasp_login时代码是解密的,因为我要hook某个地址时可以预先判断代码是否匹配,所以最后索性在截获的GetProcAddress中直接调用HookHaspAPI_Begin(),直接某个时刻成功为止:

 

[C++] 纯文本查看 复制代码

?


01


02


03


04


05


06


07


08


09


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


FARPROC WINAPI my_GetProcAddress(HMODULE
hModule,LPCSTR
lpProcName)


{


CAdAutoHookApi autoHook(&gHooks, my_GetProcAddress);


FARPROC ret = GetProcAddress(hModule, lpProcName);


string moduleName = getFileName(getModuleNameA(hModule));


if(IsBadStringPtrA(lpProcName, 255))


{


//        logOutput(formatString("GetProcAddress(0x%08X(%s), 0x%08X) : 0x%08X\r\n",


//            (int)hModule, moduleName.c_str(), (int)lpProcName, (int)ret));


}


else


{


//        logOutput(formatString("GetProcAddress(0x%08X(%s), %s) : 0x%08X\r\n",


//            (int)hModule, moduleName.c_str(), (lpProcName != NULL) ? lpProcName : "NULL", (int)ret));


/*


if(strcmp(lpProcName, "LoadLibraryA") == 0)


{


MessageBoxW(::GetDesktopWindow(), L"GetProcAddress(LoadLibraryA)", L"ApiDebugger", MB_OK);


}


*/


}


 


HookHaspAPI_Begin();


 


return
ret;


}





    7).分析截获的数据:
    通过数据截获分析,最终发现调用hasp_login_scope成功后,就会反复的调用hasp_decrypt去解密数据,每次去解密的数据大小都是16,输入的数据是随机的,但有2次解密的数据大小是32768,而且输入的数据是固定的内含,当然解密后的数据也是固定的,这是API log.




    [  1688]12:24:20.179 - ApiDebugger Loaded.
    [  1688]12:24:20.211 - Process32Next, Rename "devenv.exe" to "crack.exe"
    [  1688]12:24:20.215 - Process32Next, Rename "devenv.exe" to "crack.exe"
    [  1688]12:24:20.246 - HASP API : hasp_login_scope hooked
    [  1688]12:24:20.246 - HASP API : my_hasp_encrypt hooked
    [  1688]12:24:20.247 - HASP API : my_hasp_decrypt hooked
    [  1688]12:24:20.247 - HASP API : my_hasp_get_info hooked
    [  1688]12:24:20.247 - HASP API : my_hasp_logout hooked
    [  1688]12:24:20.248 - HASP API : Patch Address 0x0052B45B
    [  1688]12:24:20.248 - HASP API : Patch Address 0x00591DFA
    [  1688]12:24:20.250 - HASP API : Call hasp_login_scope(5476)
    [  1688]12:24:20.252 - HASP API : Call hasp_decrypt(0x88888888, 0x0028F998, 16)
    [  1688]12:24:20.255 - D : D67BF973E80CC4AF98D23FB8285FCB4A
    [  1688]12:24:20.256 - HASP API : Call hasp_decrypt(0x88888888, 0x0028F998, 16)
    [  1688]12:24:20.259 - D : 083E3F19B6527EB976CF7D18BBB80890
    [  1688]12:24:20.263 - HASP API : Call hasp_decrypt(0x88888888, 0x003A0000, 32768)
    [  1688]12:24:20.267 - HASP API : Read data from "data.enc1.bin"
    [  1688]12:24:20.270 - HASP API : Call hasp_decrypt(0x88888888, 0x003A0000, 32768)
    [  1688]12:24:20.274 - HASP API : Read data from "data.enc2.bin"
    [  1688]12:24:20.330 - HASP API : Call hasp_decrypt(0x88888888, 0x0028F91C, 16)
    [  1688]12:24:20.333 - D : 3CE5A8C8DCC197E5C426D78A3CBFA846
    [  1688]12:24:21.393 - HASP API : Call hasp_logout()
    [  1688]12:24:21.395 - ApiDebugger Unloaded.

    根据这些重要数据进行分析,可以认为:16个字节的数据解密纯粹是为了校验加密狗的合法性,32768的数据解密应该是对主程序的内容解密,因此之前以为不带狗就能去破解是多么的天真,如果不带狗就能破解,这狗还有啥意思?



    8).确定判断地址:
    现在我可以对hasp_decrypt函数在请求32768长度数据解密时返回固定的数据,但16字节的校验请求怎么办?这又是一个难题,因为你不知道算法,如果你随便返回数据程序就报错.
    这部分不细说了,主要是设置参数内存地址的访问断点,最终确定了有2处hasp_decrypt调用后数据验证地址:

 

[Asm] 纯文本查看 复制代码

?


01


02


03


04


05


06


07


08


09


10


11


12


13


14


007040FB    8A5429 F0          MOV
DL,BYTE
PTRDS:[ECX+EBP-10]


007040FF    3BC2               CMP
EAX,EDX     
// 这里是判断,这是壳程序的检查


00704101    68 3D457000        PUSH
AAA.0070453D


00704106    C3                 RETN


00704107    D840 E9            FADD
DWORDPTRDS:[EAX-17]


0070410A    FFF9                ???                                            ; Unknown command


0070410C    FFFF                ???                                            ; Unknown command


 


 


 


0052B45B    8A5429 F0          MOV
DL,BYTE
PTRDS:[ECX+EBP-10]


0052B45F    3BC2               CMP
EAX,EDX  
// 同样的代码,也是判断,这是主程序运行后的检查


0052B461    68 9DB85200        PUSH
AAA.0052B89D


0052B466    C3                 RETN





    9).爆破方法:
        尝试了2种办法,第一种是,我既然已经知道了返回数据与正确数据的判断位置,完美的做法是,要检查之前,把正确的数据复制到返回的数据地址:
 

[Asm] 纯文本查看 复制代码

?


01


02


03


04


05


06


07


08


09


10


11


12


13


14


15


16


17


18


19


PUSH
EAX


 


MOV
EAX,DWORD
PTRDS:[EDX+52C4E4+0]


MOV
DWORDPTRDS:[EBP-10+0],EAX


 


MOV
EAX,DWORD
PTRDS:[EDX+52C4E4+4]


MOV
DWORDPTRDS:[EBP-10+4],EAX


 


MOV
EAX,DWORD
PTRDS:[EDX+52C4E4+8]


MOV
DWORDPTRDS:[EBP-10+8],EAX


 


MOV
EAX,DWORD
PTRDS:[EDX+52C4E4+0C]


MOV
DWORDPTRDS:[EBP-10+0C],EAX


 


POP
EAX


 


MOV
DL,AL


CMP
EAX,EDX


JMP
0052B461





        代码写好了,也要选择插入位置,最终在内存找到了大片的90 90 90 90内存,然后在程序运行中动态的将代码写入到这个位置,同时在原代码处做了一个JMP语句.

        完成后再运行程序,最终程序完美运行,这时候真的有一种想哭的感觉,心情豁然开朗!

        成功破解后,又试了一种简单的方案,直接Patch Code "CMP EAX,EDX" 为  "CMP EAX,EAX/CMP EDX,EDX"也是成功的。所以也就验证了对于随机解密后的数据仅仅是为了加密狗的校验。




    10).最终的my_hasp_decrypt函数:
        随着一步步的深入,搞明白了,核心API就是hasp_decrypt,最终hook的函数代码为:
 

[C++] 纯文本查看 复制代码

?


01


02


03


04


05


06


07


08


09


10


11


12


13


14


15


16


17


18


19


20


21


22


23


24


25


26


27


28


29


30


31


32


33


34


35


36


37


38


39


40


41


42


43


44


45


46


47


48


49


50


51


52


53


54


55


56


57


58


59


60


61


62


63


64


65


66


67


68


69


70


71


72


73


74


75


76


77


78


79


80


81


82


83


84


hasp_status_t HASP_CALLCONV my_hasp_decrypt(hasp_handle_t handle,


void
*buffer,


hasp_size_t length)


{


logOutput(formatString("HASP API : Call hasp_decrypt(0x%08X, 0x%08X, %d)\r\n",


(int)handle, (int)buffer, (int)length));


 


if(length == 0x8000)


{


const
BYTEdata1[] = { 0x41, 0xC9, 0x2A, 0x77, 0x12, 0xA5, 0xB3, 0x6B };


const
BYTEdata2[] = { 0x08, 0xB3, 0xC4, 0x2E, 0xC8, 0xAF, 0x78, 0x6E };


if(memcmp(buffer, data1,sizeof(data1)) == 0)


{


//            writeDataToFile("data.dec1.bin", buffer, length);


if(readDataFromFile("data.enc1.bin", buffer, length) == length)


{


logOutput(formatString("HASP API : Read data from \"data.enc1.bin\"\r\n"));


}


}


if(memcmp(buffer, data2,sizeof(data2)) == 0)


{


//            writeDataToFile("data.dec2.bin", buffer, length);


if(readDataFromFile("data.enc2.bin", buffer, length) == length)


{


logOutput(formatString("HASP API : Read data from \"data.enc2.bin\"\r\n"));


}


}


}


else
if((length == 16) && (memcmp(buffer, gZeroInput,sizeof(gZeroInput)) == 0))


{


memcpy(buffer, gZeroOutput, length);


logOutput(formatString("HASP API : Decrypt Zero Buffer.\r\n"));


string hexData = string("E : ") + toHexString((const
char*)buffer, length) +"\r\n";


logOutput(hexData);


}


else


{


writeDataToFile("enc.bin", buffer, length);


string hexData = string("D : ") + toHexString((const
char*)buffer, length) +"\r\n";


logOutput(hexData);


memset(buffer, 0, length);


 


if(!g_is_patch_code1)


{


// 0052B45B


const
BYTEverifyData[] = { 0x8A, 0x54, 0x29, 0xF0, 0x3B, 0xC2, 0x68 };


const
BYTEpatchCode[]  = { 0x3B, 0xC0 };


 


void
*pVerifyAddr = (void
*)0x0052B45B;


void
*pPatchAddr  = (void
*)0x0052B45F;


 


if(CAdHookApi::VerifyAddress(pVerifyAddr, verifyData,sizeof(verifyData)))


{


if(CAdHookApi::PatchCode(pPatchAddr, patchCode,sizeof(patchCode)))


{


logOutput(formatString("HASP API : Patch Address 0x0052B45F, (CMP EAX, EAX)\r\n"));


g_is_patch_code1 =true;


}


}


}


 


if(!g_is_patch_code2)


{


// 007040FB


const
BYTEverifyData[] = { 0x8A, 0x54, 0x29, 0xF0, 0x3B, 0xC2, 0x68 };


const
BYTEpatchCode[]  = { 0x3B, 0xC0 };


 


void
*pVerifyAddr = (void
*)0x007040FB;


void
*pPatchAddr  = (void
*)0x007040FF;


 


if(CAdHookApi::VerifyAddress(pVerifyAddr, verifyData,sizeof(verifyData)))


{


if(CAdHookApi::PatchCode(pPatchAddr, patchCode,sizeof(patchCode)))


{


logOutput(formatString("HASP API : Patch Address 0x007040FF, (CMP EAX, EAX)\r\n"));


g_is_patch_code2 =true;


 


ApiDebugferShutdown();


}


}


}


}


return
HASP_STATUS_OK;


}







    最后经过多次验证,100%确信自己真的把这个狗给破解了,过程中经历的艰辛和不眠立马化为无有,都被最后的成功所淹没,这种感觉妙不可言。

    对于这个加密狗破解,是自己花费了最大力气的一个(总历时应该有100小时),虽然最终是API HOOK+爆破,代码仅Patch 2个节,但为了这个2个字节确实经历了太多,每个破解思路都失败的时候也想过放弃,庆幸自己最终还是坚持了下来,赢取了胜利.

    从这个过程中也发现自己在某些地方还非常的初级,尤其是算法分析上面,只有能深入算法分析,才能到达更高的层次,总是爆破不利于水平的提高,后面有时候会继续对这个加密狗进行研究,争取做到完美脱壳,将来再与大家分享。

    PS:谢谢大家能看完,多提建议和意见,也希望熟悉这个加密狗的大牛和大神们也能分享这方面的经验,能够找到一种更简单快速的破解方法。


[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

收藏
点赞1
打赏
分享
最新回复 (19)
雪    币: 1243
活跃值: (1815)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
库尔 2019-11-26 16:38
2
0
你买个加密狗开发版就知道怎么破解了。塞弗耐超级狗官方文档还教你怎么写防破解的代码文档。
雪    币: 1243
活跃值: (1815)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
库尔 2019-11-26 16:40
3
0
基本上都是api hook 监控dll的接口,监控并结合里面的传输数据进行破解。
雪    币: 633
活跃值: (254)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Jayce189 2019-11-26 16:46
4
0
库尔 你买个加密狗开发版就知道怎么破解了。塞弗耐超级狗官方文档还教你怎么写防破解的代码文档。
我买了开发版的呀,看了半天还是不会破解。你能教教我吗
雪    币: 86
活跃值: (898)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
luzhmu 2019-11-26 17:01
5
0
这狗有模拟代码都是公开了,可以完美模拟,但不支持最新版的AES
雪    币: 276
活跃值: (146)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xj无敌 2019-11-26 17:39
6
0
luzhmu 这狗有模拟代码都是公开了,可以完美模拟,但不支持最新版的AES
有链接吗?
雪    币: 633
活跃值: (254)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Jayce189 2019-11-26 17:54
7
0
luzhmu 这狗有模拟代码都是公开了,可以完美模拟,但不支持最新版的AES
有链接吗?
雪    币: 4889
活跃值: (2265)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lionnnn 2019-11-27 10:18
8
0
库尔 基本上都是api hook 监控dll的接口,监控并结合里面的传输数据进行破解。
api hook 调用之后运行一段时间会有很多16byte长或者123byte长的encrypt或decrypt,不像是编程人员自己写的,像是加密狗加壳软件设置的。这个有办法吗?没有aes加密算法和密钥,不好破呀。
雪    币: 1243
活跃值: (1815)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
库尔 2019-11-27 14:09
9
0
lionnnn api hook 调用之后运行一段时间会有很多16byte长或者123byte长的encrypt或decrypt,不像是编程人员自己写的,像是加密狗加壳软件设置的。这个有办法吗?没有aes加密算法和密 ...
你不解密也行呀。能固定这个程序发包收包数据一样也行。
雪    币: 376
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
git_92112js888889 2019-11-28 23:21
10
0
大佬辛苦
雪    币: 86
活跃值: (898)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
luzhmu 2019-12-1 14:52
11
0
论坛上有源码
雪    币: 38
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
佬头 2020-2-4 01:39
12
0
辛苦辛苦
雪    币: 14
活跃值: (263)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
ZwTrojan 2020-2-4 08:36
13
0
你这排版全乱了,没法看..
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
壹六得六 2020-3-15 00:12
15
0
流弊
雪    币: 83
活跃值: (1037)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
killpy 2 2020-3-15 02:54
16
0
看不懂
雪    币: 225
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
TAO哥 2020-4-4 13:52
17
0
高手就是这样出来的
雪    币: 3
活跃值: (65)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
finger_xyz 2020-6-4 14:13
18
0
 强啊
雪    币: 209
活跃值: (31)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
lovelin 2020-6-6 23:55
19
0
精神可嘉
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
Way_1023 2021-4-30 20:23
20
0
请问有人知道,c编译后的obj文件怎么加密吗
游客
登录 | 注册 方可回帖
返回