Tornado2之Licence暴力破解

日期:2003年6月8日 作者:1212 人气: 280

Tornado2是VxWorks开发调试工具,试用期为一个月.
超过试用期需要Licence支持,一个Licence需要$6000~$2000.买了软件,还要花钱,真是!@#$%^&*.

破解如下,希望对大家有用. 😃

1.跟踪

Tornado2的Licence程序主要运行在 ...\host\x86-win32\bin\tgtsvr.exe(...为Tornado2安装目录)

经过痛苦的跟踪过程,发现008B5236处为判断鉴权是否成功(该地址是在NT4.0+SP5.0中,可能系统不一样,地址会不一样).跟踪过程就不写了吧.大家都是高手,我的跟踪过程写出来,大家会笑话的.

在跟踪过程中,我犯了很多愚蠢的错误,甚至死了很多次.后来发现都是可以避免的.

008B5236前有一系列CALL,是进行计算的.我曾跟进去,但发现是可能一个DLL.该公司用一个DLL进行运算,我是不能通过算法破解了,只好暴力破解.

写破解代码可能对大家有一些作用,我将详细介绍.

2.破解思路

该程序是通过加壳的,壳比较奇怪,我孤陋寡闻,没有见过.
我的想法是,我也可以在程序中加外壳,先获得控制修改代码,在原来壳结束时,作跳转到我的修改代码.
原来程序入口是00426DF5,原来壳的结束位置是00426F33.

1)将程序的入口地址改为00400300

mov        ebx,426F33h
mov        dword ptr [ebx],0FD93C8E9h
add        ebx,4
mov        byte ptr [ebx],0FFh
jmp        00426DF5
//以上为将00426F33改为jmp 1111.
1111.为自己代码地址
push        ebx
mov        ebx,426F33h
mov        dword ptr [ebx],535BD0FFh
add        ebx,4
mov        byte ptr [ebx],81h
//以上为将00426F33改为
{
    00426F33 FF D0               call        eax
    00426F35 5B                  pop         ebx
    00426F36 53                  push        ebx
    00426F37 81 EB 2E 0D 00 00   sub         ebx,0D2Eh
}
mov        ebx,eax
add        ebx,0E10Ah
mov        dword ptr [ebx],7DEBC033h
//以上将程序改成鉴权成功
{
    008B5236 33 C0                xor        eax, eax
    008B5238 EB 7D                jmp        008B52B5
}
sub        ebx, 0B7h//ebx=008B517F
mov        word ptr [ebx],xxxx
//以上将程序改成98下跳到NT代码中执行
{
    008B517F jmp 008B51C5
}
pop        ebx
jmp        00426F33

外壳程序详细说明

1)改变程序的入口地址到自己代码,在自己代码地方写跳转代码.该跳转代码将00426F33处程序改成jmp xxxx.

在该项操作中,需要取硬盘序列号,与jmp xxxx的机器码做xor运算.
取硬盘序列号使用LoadLibrary,GetProcAddress和GetVolumeInformationA.
其中LoadLibrary和GetProcAddress的函数地址需要查原来程序地址.

LoadLibrary = 00426FD0
GetProcAddress = 00426FCA
CALL xxxx = E8 + 偏移(4位)

2)在上面的jmp xxxx的xxxx地方处写代码,改变程序的运行路径.

需要有如下几个具体操作:

(1)将00426F33处还原
(2)改程序为98下也到NT代码中执行
(3)该程序为鉴权成功

3)需要的固定数据:

'KERNEL32.DLL', 0 //13 BYTES,LoadLibraryA用'GetVolumeInformationA', 0 //22 BYTES,GetProcAddress'C:\', 0 //4 BYTES, GetVolumeInformationA用 总共0x27 = 39个BYTE因此数据从400300开始,代码从00400330开始.

外壳程序实现

1)将几个固定数据写进文件中:

Raw Address = 0x300
00400300    'KERNEL32.DLL', 0
0040030D    'GetVolumeInformationA', 0
00400323    'C:\', 0
00400327    4 BYTE 数据
0040032B    0

2)写跳转代码,将00426F33处程序改成

jmp xxxx, Raw Address = 0x330
00400330
00400330 55                  push        ebp
00400331 8B EC                mov        ebp,esp
00400333 83 EC 30            sub        esp,70h  ;实际使用0x22 BYTE
00400336 68 00 03 40 00      push        400300h  ;'KERNEL32.DLL'
0040033B E8 90 6C 02 00      call        426FD0h;LoadLibraryA
00400341 68 0D 03 40 00      push        40030Dh  ;'GetVolumeInformationA'
00400346 50                  push        eax      ;hModule
00400347 E8 7F 6C 02 00      call        426FCAh;GetProcAddress
0040034B 6A 0A                push        0Ah      ;sizeof(lpFileSystemNameBuffer)
0040034D 8D 5D D8            lea        ebx,[ebp-28h]
00400350 53                  push        ebx
00400351 8D 5D D0            lea        ebx,[ebp-30h]
00400354 53                  push        ebx
00400355 8D 5D E4            lea        ebx,[ebp-1Ch]
00400358 53                  push        ebx
00400359 8D 5D E8            lea        ebx,[ebp-18h]
0040035C 53                  push        ebx
0040035D 6A 0C                push        0Ch
0040035F 8D 5D F0            lea        ebx,[ebp-10h]
00400362 53                  push        ebx
00400363 68 23 03 40 00      push        400323
00400368 FF D0                call        eax
0040036A 8B 45 E8            mov        eax,dword ptr [ebp-18h];VolumeSerialNumber
0040036D BB 27 03 40 00      mov        ebx,400327h
00400372 33 03                xor        eax,dword ptr [ebx]
00400374 BB 33 6F 42 00      mov        ebx,426F33h
00400379 89 03                mov        dword ptr [ebx],eax
0040037B 83 C3 04            add        ebx,4
0040037E C6 03 FF            mov        byte ptr [ebx],0FFh;原来是E9 54 94 FD FF jmp 0040038D
00400381 83 C4 70            add        esp,70h
00400384 8B E5                mov        esp,ebp
00400386 5D                  pop        ebp
00400387 E9 69 6A 02 00      jmp        00426DF5;跳转代码完成
0040038C
0040038C 53                  push        ebx
0040038D BB 33 6F 42 00      mov        ebx,426F33h
00400392 C7 03 FF D0 5B 53    mov        dword ptr [ebx],535BD0FFh
00400398 83 C3 04            add        ebx,4
0040039B C6 03 81            mov        byte ptr [ebx],81h
0040039E 8B D8                mov        ebx,eax
004003A0 81 C3 0A E1 00 00    add        ebx,0E10Ah
004003A6 C7 03 33 C0 EB 7D    mov        dword ptr [ebx],7DEBC033h
004003AC 81 EB B7 00 00 00    sub        ebx,0B7h
004003B2 66 C7 03 EB 44      mov        word ptr [ebx],44EBh
004003B7 5B                  pop        ebx
004003B8 E9 76 6B 02 00      jmp        00426F33
004003BD

实验数据

VolumeSerialNumber = 0x283a1709
E9 54 94 FD ^ VolumeSerialNumber = 0xd5ae43e0

其中的GetVolumeInformationA和变化数据,不是必须的.在写这一段代码时,不能将他放到原来的代码块中,我曾经放过,但程序运行死了.可能有其他检查,但程序死的时候,堆栈已经被破坏了,我无法定位,不知哪位能指点?

3.C代码

#include <windows.h>
#include <shlobj.h>
#include "resource.h"

#define OPEN_ERROR          -1
#define READ_ERROR          -2
#define WRITE_ERROR        -3
#define FILE_ERROR          -4
#define NO_KEY              -5

int WriteCrack(HANDLE hFile)
{
    DWORD dwWrited;
//1.写入口
    SetFilePointer(hFile, 0xf0, 0, FILE_BEGIN);
    DWORD dwEntry = 0x330;
    WriteFile(hFile, &dwEntry, 4, &dwWrited, NULL);

//2.写固定数据
    LPTSTR tt = MAKEINTRESOURCE(IDR_CRACKDATA1);
    HRSRC hRsrc = FindResource(GetModuleHandle(NULL),
        MAKEINTRESOURCE(IDR_CRACKDATA1), "CRACKDATA");
    HGLOBAL hGlobal = LoadResource(GetModuleHandle(NULL), hRsrc);
    LPVOID lpData = LockResource(hGlobal);
    SetFilePointer(hFile, 0x300, 0, FILE_BEGIN);
    WriteFile(hFile, lpData, 0xc0, &dwWrited, NULL);

//3.写变化数据
    DWORD dwChangeData = 0xfd9454e9;//E9 54 94 FD
    BYTE lpRootPathName[4] = "c:\\"; //取C盘的序列号
    BYTE lpVolumeNameBuffer[12];//磁盘卷标
    DWORD nVolumeNameSize = 12;
    DWORD VolumeSerialNumber;//磁盘序列号
    DWORD MaximumComponentLength;
    BYTE lpFileSystemNameBuffer[10];
    DWORD nFileSystemNameSize=10;
    DWORD FileSystemFlags;
    GetVolumeInformation((char*)&lpRootPathName[0],
        (char*)&lpVolumeNameBuffer[0], nVolumeNameSize,
        &VolumeSerialNumber, &MaximumComponentLength,
        &FileSystemFlags,
        (char*)&lpFileSystemNameBuffer[0], nFileSystemNameSize);
    dwChangeData ^= VolumeSerialNumber;
    SetFilePointer(hFile, 0x327, 0, FILE_BEGIN);
    WriteFile(hFile, &dwChangeData, 4, &dwWrited, NULL);
    return 1;
}

int WriteCrackData(char* szFileName)
{
    HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL,
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if(hFile == INVALID_HANDLE_VALUE)
    {
        MessageBox(NULL, "Please quit TornadoII program!\r\n"
            "Ensure not any program lock TornadoII resource!", "Warnning", MB_OK | MB_ICONHAND);
        return OPEN_ERROR;
    }
//读数据,判断该文件是否可写
    SetFilePointer(hFile, 0xf0, 0, FILE_BEGIN);
    DWORD dwEntry;
    DWORD dwReaded;
    if(ReadFile(hFile, &dwEntry, 4, &dwReaded, NULL) == FALSE)
    {
        MessageBox(NULL, "Please quit TornadoII program!\r\n"
            "Ensure not any program lock TornadoII resource!", "Warnning", MB_OK | MB_ICONHAND);
        CloseHandle(hFile);
        return READ_ERROR;
    }
    if(dwEntry != 0x330)//自己写的入口
    {
        if(dwEntry != 0x26df5)//原本的程序入口
        {
            if(MessageBox(NULL, "TornadoII maybe infected by virus!\r\nRun the patch maybe destroy TornadoII!\r\n"
                "If you want continue,press OK,CANCEL to quit!\r\nDo you want continue?", "Warnning", MB_OKCANCEL | MB_ICONHAND)
                == IDCANCEL)
                CloseHandle(hFile);
                return FILE_ERROR;
        }
    }
    WriteCrack(hFile);
    CloseHandle(hFile);
    return TRUE;
}

int WriteBack()
{
    HKEY hKey;
    if(RegOpenKey(HKEY_CURRENT_USER, "TNT TornadoII Patch1", &hKey)
        != ERROR_SUCCESS)
        return NO_KEY;

    char szFileName[255];
    memset(szFileName, 0, sizeof(char) * 255); 
    long length = 255; 
    DWORD FileAttribute = 0; 
    BYTE FileTime[255]; 
    memset(FileTime, 0, sizeof(BYTE) * 255); 
    DWORD dwLength; 

    if(RegQueryValue(hKey, "Path", szFileName, &length) 
        != ERROR_SUCCESS) 
    { 
        RegCloseKey(hKey); 
        return NO_KEY; 
    } 
    dwLength = 4; 
    DWORD type; 
    if(RegQueryValueEx(hKey, "Attribute", 0, &type, (BYTE*)&FileAttribute, &dwLength) 
        != ERROR_SUCCESS) 
    { 
        RegCloseKey(hKey); 
        return NO_KEY; 
    } 
    dwLength = 255; 
    if(RegQueryValueEx(hKey, "Time", 0, &type, FileTime, &dwLength) 
        != ERROR_SUCCESS) 
    { 
        RegCloseKey(hKey); 
        return NO_KEY; 
    } 
    FILETIME* temp = (FILETIME*)FileTime; 
    FILETIME CreateTime; 
    CreateTime = *temp; 
    temp ++; 
    FILETIME LastAccessTime; 
    LastAccessTime = *temp; 
    temp ++; 
    FILETIME LastWriteTime; 
    LastWriteTime = *temp; 

    HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, 
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 
    if(hFile == INVALID_HANDLE_VALUE) 
    { 
        MessageBox(NULL, "Please quit TornadoII program!\r\n" 
            "Ensure not any program lock TornadoII resource!", "Warnning", MB_OK | MB_ICONHAND); 
        return OPEN_ERROR; 
    } 
//读数据,判断该文件是否可写
    SetFilePointer(hFile, 0xf0, 0, FILE_BEGIN);
    DWORD dwEntry;
    DWORD dwReaded;
    if(ReadFile(hFile, &dwEntry, 4, &dwReaded, NULL) == FALSE)
    {
        MessageBox(NULL, "Please quit TornadoII program!\r\n"
            "Ensure not any program lock TornadoII resource!", "Warnning", MB_OK | MB_ICONHAND);
        return READ_ERROR;
    }
    if(dwEntry != 0x330)//自己写的入口
    {
        CloseHandle(hFile);
        return FILE_ERROR;
    }
    DWORD dwWrited;
//1.写入口
    SetFilePointer(hFile, 0xf0, 0, FILE_BEGIN);
    dwEntry = 0x26df5;
    WriteFile(hFile, &dwEntry, 4, &dwWrited, NULL);

//2.数据
    BYTE buffer[0xc0];
    memset(buffer, 0, sizeof(BYTE) * 0xc0);
    SetFilePointer(hFile, 0x300, 0, FILE_BEGIN);
    WriteFile(hFile, buffer, 0xc0, &dwWrited, NULL);

    SetFileTime(hFile, &CreateTime, &LastAccessTime, &LastWriteTime); 
    CloseHandle(hFile); 
    RegDeleteKey(hKey, "Path"); 
    RegCloseKey(hKey); 
    RegDeleteKey(HKEY_CURRENT_USER, "TNT TornadoII Patch1"); 
    SetFileAttributes(szFileName, FileAttribute); 
    
    return TRUE; 
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmdLine, int nShowCmd)
{
    if(WriteBack() == TRUE)
        return TRUE;
    BROWSEINFO  bi;
    ITEMIDLIST* pidl;
    char        path[255];
    memset(path, 0, sizeof(char) * 255);
    bi.hwndOwner = NULL;
    bi.pidlRoot = NULL;
    bi.pszDisplayName = NULL;
    bi.lpszTitle = "Please choose the path of TornadoII installed";
    bi.ulFlags = BIF_BROWSEINCLUDEFILES;
    bi.lpfn = NULL;
    bi.lParam = 0;
    bi.iImage = 0;
    pidl = SHBrowseForFolder(&bi);
    SHGetPathFromIDList(pidl, path);

    char        szFileName[255]; 
    int        length = strlen(path); 

    if(length == 0) 
        return FALSE; 

    int        temp; 
    char*      szName = "\\host\\x86-win32\\bin\\tgtsvr.exe"; 
    memset(szFileName, 0, sizeof(szFileName)); 
    memcpy(szFileName, path, length); 
    temp = length; 
    length = strlen(szName); 
    memcpy(&szFileName[temp], szName, length); 

    HANDLE hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, NULL, 
        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); 
    if(hFile == INVALID_HANDLE_VALUE) 
    { 
        MessageBox(NULL, "Please quit TornadoII program!\r\n" 
            "Ensure not any program lock TornadoII resource!", "Warnning", MB_OK | MB_ICONHAND); 
        return FALSE; 
    } 

    BY_HANDLE_FILE_INFORMATION FileInfo; 
    GetFileInformationByHandle(hFile, &FileInfo); 
    CloseHandle(hFile); 

    DWORD FileAttribute = FileInfo.dwFileAttributes; 
    BYTE FileTime[255]; 
    memset(FileTime, 0, sizeof(BYTE) * 255); 
    length = 0; 
    memcpy(&FileTime[length], &FileInfo.ftCreationTime, sizeof(FILETIME)); 
    length += sizeof(FILETIME); 
    memcpy(&FileTime[length], &FileInfo.ftLastAccessTime, sizeof(FILETIME)); 
    length += sizeof(FILETIME); 
    memcpy(&FileTime[length], &FileInfo.ftLastWriteTime, sizeof(FILETIME)); 
    length += sizeof(FILETIME); 
    DWORD newAttribute = FileAttribute & 0xfffffffe; 
    SetFileAttributes(szFileName, newAttribute); 


    if(WriteCrackData(szFileName) == TRUE) 
    { 
        HKEY hKey; 
        RegCreateKey(HKEY_CURRENT_USER, "TNT TornadoII Patch1", &hKey); 
        RegSetValue(hKey, "Path", 
            REG_SZ, szFileName, strlen(szFileName)); 
        RegSetValueEx(hKey, "Attribute", 0, REG_DWORD, (BYTE*)&FileAttribute, 4); 
        RegSetValueEx(hKey, "Time", 0, REG_BINARY, FileTime, length); 
        if(MessageBox(NULL, "TornadoII Patch1 is OK!\r\nYou can run me again when registered,\r\n" 
            "or press yes when you registed.\r\nHave you run TornadoII to register?", "OK!", MB_YESNO) == IDYES) 
        { 
            WriteBack(); 
        } 
    } 

    return TRUE; 
}

其中的IDR_CRACKDATA1为:

00000000 4B 45 52 4E 45 4C 33 32 2E 44 4C 4C 00 47 65 74    //KERNEL32.DLL Ge
00000010 56 6F 6C 75 6D 65 49 6E 66 6F 72 6D 61 74 69 6F    //VolumeInformati
00000020 6E 41 00 43 3A 5C 00 00 00 00 00 00 00 00 00 00    //nA C:\
00000030 55 8B EC 83 EC 30 68 00 03 40 00 E8 90 6C 02 00
00000040 68 0D 03 40 00 50 E8 7F 6C 02 00 6A 0A 8D 5D D8
00000050 53 8D 5D D0 53 8D 5D E4 53 8D 5D E8 53 6A 0C 8D
00000060 5D F0 53 68 23 03 40 00 FF D0 8B 45 E8 BB 27 03
00000070 40 00 33 03 BB 33 6F 42 00 89 03 83 C3 04 C6 03
00000080 FF 83 C4 70 8B E5 5D E9 69 6A 02 00 53 BB 33 6F
00000090 42 00 C7 03 FF D0 5B 53 83 C3 04 C6 03 81 8B D8
000000a0 81 C3 0A E1 00 00 C7 03 33 C0 EB 7D 81 EB B7 00
000000b0 00 00 66 C7 03 EB 44 5B E9 76 6B 02 00 00 00 00

后记

今年7月底破解该软件,原来想买给xx公司.不过后来失败了.这也是加GeVolumeInformatinA的原因.

前两天看到贵论坛上一篇文章,深有感触.破解应该是一个业余爱好吧,希望能和大家交个朋友.

运行该Patch程序注意:

1.保证您已经安装好TornadoII,且TornadoII的应用程序没有运行.
2.运行程序patch.
3.选择TornadoII的安装路径.
4.将出现如图patch1OK.jpg所示,此时patch1已经成功,这时patch.exe可以按否关闭,或者不关闭,直接运行TornadoII进行注册.
注意:现在一定不能马上按"是"按钮.如果您按下,那么只好从第二步重新来.
5.注册TornadoII将出现如register.jpg所示,此时您可以输入任意的26个数字或字母,可以数字和字母混合.注册成功后,将TornadoII的应用程序退出.
6.如果刚才您没有关闭patch.exe,现在直接按patch1OK.jpg所示的"是"按钮,TornadoII将完全破解.如果您刚才将patch.exe推出了, 现在需要重新运行他.这一步成功的标志是patch2OK.jpg所示.

注意:如果您的操作系统是98/95,在第5步时将出现如98.jpg所示(98.jpg不能贴上来,见谅), 此时实际已经注册成功.您需要关闭TornadoII的注册程序,按照第六步的指示做.如果您的操作系统是98/95,而且已经过期了,该Patch无能为力.只好重装系统和TII了.😦

哈哈,这个昂贵的商业软件的保护说穿了实在是不值得一提!

它是用Crypkey加壳的(www.crypkey.com),而Crypkey Instant的通用注册机早被Duelist/CORE写出来了,在Keygen Studio上可以得到,用的RSA算法。
至于脱壳,更是简单得不得了,写个ProcDump的script自动把tgtsvr.exe脱壳即可(壳调用cryp*.dll判断时间),得到的exe的import table未做任何手脚,可以在各种版本的32-bit windows上运行!

所有用Crypkey加壳的大软件如法炮制!

blowfish

Contributors: FHL