首页
社区
课程
招聘
[原创]RVS Lite2011 注册算法 IDEA 分析
发表于: 2011-3-11 22:53 19293

[原创]RVS Lite2011 注册算法 IDEA 分析

uuk 活跃值
3
2011-3-11 22:53
19293

【文章作者】: uuk
【软件名称】: RVS-Lite-2011(2.1.5.5147)
【加壳方式】: 无
【保护方式】: 机器码+序列号
【编写语言】: Borland Delphi 6.0 - 7.0 [Overlay]
【使用工具】: PEID,IDA Pro with Hex-Rays plugin,DeDe,OllyICE,Syser,VMware
【软件介绍】: 瑞泰尼尔安全防御系统软件 (英文名称: Returnil Virtual System 2010,以下简称RVS)是瑞泰尼尔公司采用最新虚拟技术实现的新一代桌面安全系统。和传统的杀毒软件完全不同,瑞泰尼尔防毒系统软件可以瞬间“克隆”用户当前的操作系统,“制造”出实际存在于用户内存中的虚拟操作系统,并利用虚拟的操作系统来替代真实的操作系统,从而以达到保护真实操作系统的目的。
【作者声明】: 大道向前,一步一个脚印!

【破解过程】
1、前期处理
    RVS Lite 2011安装后会在:1)Windows\System32\Drivers目录下复制驱动文件;2)在系统盘根目录下创建RETURNIL文件夹,里面存放配置文件和数据;3)在Program Files目录下创建Returnil Virtual System Lite 2011文件夹,里面只有一个主程序RvsCore.exe。
    就先从RvsCore.exe下手,用PEiD 查壳显示Borland Delphi 6.0 - 7.0 [Overlay],用KANAL插件分析有ADLER32、CRC32、MD5、ZLIB算法。直接拖到IDA里分析,并将几处算法标注上。IDA分析Delphi程序时要手动加载sig(Barland Visual Component Library & Packages)。默认加载了SHE for vc7/8,没用,但也不知道怎么去掉。Delphi程序也有异常处理,不过找不到sig就算了,影响不大。有些库函数识别不出来,可以有DeDe配合使用。
    Delphi逆向分析中较困难的就是一些VCL、RTL库函数被IDA识别成unknowlib,还有许多虚函数的调用,例如:call [edx+0xC],此外Pascal的参数传递方式也比较让人不习惯。虽然有DeDe这样强大的逆向工具,但个人在处理以上问题是仍需要较强的手动分析逆向技巧。Delphi的参数传递: EAX:第一个参数;  EDX:第二个参数;  ECX:第三个参数;  [ESP+8]第四,[ESP+C]第五。。。(如果有的话)(引自myskydog的《Delphi逆向分析---quick batch逆向》http://bbs.pediy.com/showthread.php?t=80205)

2、定位关键代码
    RvsCore.exe程序有个注册界面,输入注册码确定后软件会重启计算机进行验证,在DeDe是找到确定按键的处理过程,大致看了下,没找到突破口。又看了软件的几个界面,把一些按键试了下,发现在软件主界面的下方有一个提示试用版的状态栏,在DeDe是定位此状态栏的创建过程PROC_5EBCD0。在PROC_5EBCD0里有条件判断,分别指向1)Registered; 2)Expired; 3)Trial Version。(软件支持多种语言,字符串有多层引用,有点烦人)

CODE:005EC52D    mov     eax, ds:ppBaseAddress
CODE:005EC532    mov     eax, [eax]
CODE:005EC534    mov     al, [eax+0E25h] ; al = 0(registered), 2(expired),
CODE:005EC534               ;      4(expired), 1(trial)
CODE:005EC53A    sub     al, 1
CODE:005EC53C    jb      short Registered
CODE:005EC53E    dec     al
CODE:005EC540    jz      Expired
CODE:005EC546    sub     al, 2
CODE:005EC548    jz      Expired
CODE:005EC54E    jmp     Trial
CODE:005EC553 ; ---------------------------------------------------------------------------
CODE:005EC553
CODE:005EC553 Registered:            ; CODE XREF: PROC_5EBCD0+86C j
CODE:005EC553    mov     edx, ds:strRegistered
CODE:005EC559    mov     edx, [edx]
CODE:005EC55B    mov     eax, [ebp+var_4]
CODE:005EC55E    mov     eax, [eax+470h]
CODE:005EC564    call    @Controls@TControl@SetText$qqrx17System@AnsiString ; Controls::TControl::SetText(System::AnsiString)

    [edx+0E25h]是注册标志,只要能找到对它赋值的地方,一般就找到的算法核心的地方。对于面向对象程序而言,想要用静态分析的方法找到某个变量的赋值操作是很困难的,得用动态调试。在OllyICE中加载程序,对内存中注册标志所在的位置下断点,运行程序,发现程序只有读注册标志,没有写操作。没有头绪,一顿乱翻,突然发现任务管理器里有两个RvsCore.exe,想到这种软件一般会往系统里添加服务,一看,果然。将Rvssrv服务停止,在OllyICE添加程序启动命令 –service,下断点,运行,程序直接退出,也没写注册标志的操作,服务没有启动。一步一步运行,缩小范围,发现是在某个与服务有关的Windows API调用后程序退出,猜测是系统服务不能以常规方法调试。上网搜,只看到使用注册表用Windbg调试系统服务,而我用OllyICE试不成功。看来以后要转而用Windbg了。现在只能在虚拟机里用Syser试试。添加启动命令 –service,添加断点,直接运行程序可是退出。偶然情况下,点了重启服务,发现Syser断下来了。原来注册标志是放在系统地址里(跟系统的内存管理有关),系统地址的断点在程序结束后是不会被Syser清除的,所以当RvsCore.exe以服务的方式再次启动并且访问断点所在的地址时被Syser断下。找到写注册标志的代码段:
CODE:005FF787    mov     eax, ds:off_60BB0C
CODE:005FF78C    mov     eax, [eax]
CODE:005FF78E    mov     al, [eax+14h]   ; [00E485E8] 取值
CODE:005FF791    mov     edx, ds:ppBaseAddress
CODE:005FF797    mov     edx, [edx]
CODE:005FF799    mov     [edx+0E25h], al
继续跟踪对[eax+14h]的操作,找到:
CODE:005FB085    mov     byte ptr [esi+14h], 1 ; [00E485E8] 赋值为1
前面的分析可知,值为1时是试用版,不是我们想要的,我们要找赋0的时候。在CODE:005FB085上下翻了翻,也没找到赋值为0的代码,估计这是注册标志的初始化,之后肯定会有别的操作,但是我们的注册码的错误的,Syser断不到赋值为0的指令。虽然静态分析的方法找到某个变量的赋值操作是很困难的,但也不是没可能。比如:
CODE:005EC534    mov     al, [eax+0E25h]
CODE:005FF799    mov     [edx+0E25h], al
都是[exx+0E25h],this指针加偏移的形式,举一反三,会不会有:
mov     byte ptr [esi+14h], 0
这种形式的汇编代码呢?在OllyICE中查找命令,果然找到:(IDA里不知道怎么查找命令……)
CODE:005FB6B9                 mov     byte ptr [esi+14h], 0
在这个函数里上下浏览代码,还看到:
CODE:005FB779                 mov     byte ptr [esi+14h], 2
看来找到目标了,在Syser里进行验证,确实是这里。将这个函数sub_5FB5D0命名为KeyCall。

3、  算法分析

CODE:005FB657    lea     ecx, [ebp+StrHex] ; StrHex
CODE:005FB65D    lea     eax, [ebp+Hex]  ; Hex
CODE:005FB663    mov     edx, 10h        ; strLen
CODE:005FB668    call    HexToHexString
CODE:005FB66D    mov     edx, [ebp+StrHex]
CODE:005FB673    lea     eax, [esi+28h]
CODE:005FB676    call    @System@LStrAsg ; Borland Visual Component Library & Packages
CODE:005FB67B    call    sub_493C04
CODE:005FB680    test    al, al
CODE:005FB682    jz      short loc_5FB6CA
CODE:005FB684    lea     ecx, [ebp+SerialCode] ; StrHex
CODE:005FB687    lea     eax, [ebp+var_136] ; Hex
CODE:005FB68D    mov     edx, 10h        ; strLen
CODE:005FB692    call    HexToHexString  ; 生成机器码 SerialCode
CODE:005FB697    lea     eax, [ebp+SerialNumber]
CODE:005FB69A    push    eax             ; a4
CODE:005FB69B    mov     ecx, 20h        ; a3
CODE:005FB6A0    mov     edx, [ebp+SerialCode] ; a2
CODE:005FB6A3    mov     eax, esi        ; a1
CODE:005FB6A5    call      GenSN       ; 由机器码生成注册码 SerialNumber
CODE:005FB6AA    mov     eax, [esi+28h]  ; 输入的注册码
CODE:005FB6AD    mov     edx, [ebp+SerialNumber]
CODE:005FB6B0    call    SysUtils_CompareText ; Borland Visual Component Library & Packages
CODE:005FB6B5    test    eax, eax
CODE:005FB6B7    jnz     short loc_5FB6CA
CODE:005FB6B9    mov     byte ptr [esi+14h], 0
CODE:005FB6BD    xor     eax, eax
CODE:005FB6BF    pop     edx
CODE:005FB6C0    pop     ecx
CODE:005FB6C1    pop     ecx
CODE:005FB6C2    mov     fs:[eax], edx
CODE:005FB6C5    jmp     loc_5FB7B7
CODE:005FB6CA ; ---------------------------------------------------------------------------

分析:CODE:005FB6A5    call    GenSN 是关键,Syser中显示 GenSN( ) 将机器码SerialCode作为输入,生成注册码SerialNumber,GenSN( ) 汇编代码如下:
CODE:005FB4EC    push    ebp
CODE:005FB4ED    mov     ebp, esp
CODE:005FB4EF    add     esp, 0FFFFFF78h
CODE:005FB4F5    push    ebx
CODE:005FB4F6    mov     [ebp+_src], edx
CODE:005FB4F9    mov     ebx, eax
CODE:005FB4FB    mov     eax, [ebp+_src]
CODE:005FB4FE    call    @System@LStrAddRef ; Borland Visual Component Library & Packages
CODE:005FB503    xor     eax, eax
CODE:005FB505    push    ebp
CODE:005FB506    push    offset loc_5FB55C
CODE:005FB50B    push    dword ptr fs:[eax]
CODE:005FB50E    mov     fs:[eax], esp
CODE:005FB511    lea     edx, [ebp+_ID_Hex]
CODE:005FB514    mov     ecx, 10h
CODE:005FB519    mov     eax, [ebp+_src]
CODE:005FB51C    call    HexStringToHex
CODE:005FB521    push    10h             ; len
CODE:005FB523    lea     ecx, [ebp+Hex2] ; Hex2
CODE:005FB529    lea     edx, [ebp+_ID_Hex] ; Hex1
CODE:005FB52C    mov     eax, ebx        ; this
CODE:005FB52E    call    sub_5FC02C
CODE:005FB533    mov     ecx, [ebp+dst]  ; StrHex
CODE:005FB536    lea     eax, [ebp+Hex2] ; Hex
CODE:005FB53C    mov     edx, 10h        ; strLen
CODE:005FB541    call    HexToHexString
CODE:005FB546    xor     eax, eax
CODE:005FB548    pop     edx
CODE:005FB549    pop     ecx
CODE:005FB54A    pop     ecx
CODE:005FB54B    mov     fs:[eax], edx
CODE:005FB54E    push    offset loc_5FB563
CODE:005FB553
CODE:005FB553 loc_5FB553:                ; CODE XREF: GenSN+75 j
CODE:005FB553    lea     eax, [ebp+_src]
CODE:005FB556    call    @System@LStrClr ; Borland Visual Component Library & Packages
CODE:005FB55B    retn

分析:CODE:005FB51C    call  HexStringToHex 是将HexString形式的机器码转换成Hex格式。然后调用 CODE:005FB52E    call  sub_5FC02C,最后是调用 CODE:005FB541    call  HexToHexString(将注册码转成HexString)。推进 sub_5FC02C:
CODE:005FC02C ; ============= S U B R O U T I N E ===============
CODE:005FC02C
CODE:005FC02C ; Attributes: bp-based frame
CODE:005FC02C
CODE:005FC02C ; void __fastcall sub_5FC02C(int this, int Hex1, int Hex2, signed int len)
CODE:005FC02C sub_5FC02C      proc near          ; CODE XREF: GenSN+42 p
CODE:005FC02C                                  ; sub_5FBDE4+77 p
CODE:005FC02C
CODE:005FC02C var_E8   = byte ptr -0E8h
CODE:005FC02C _Hex2   = dword ptr -8
CODE:005FC02C _Hex1   = dword ptr -4
CODE:005FC02C len      = dword ptr  8
CODE:005FC02C
CODE:005FC02C    push    ebp
CODE:005FC02D    mov     ebp, esp
CODE:005FC02F    add     esp, 0FFFFFF18h
CODE:005FC035    push    ebx
CODE:005FC036    push    esi
CODE:005FC037    mov     [ebp+_Hex2], ecx
CODE:005FC03A    mov     [ebp+_Hex1], edx
CODE:005FC03D    mov     ebx, eax
CODE:005FC03F    lea     eax, [ebp+var_E8]
CODE:005FC045    xor     ecx, ecx
CODE:005FC047    mov     edx, 0E0h
CODE:005FC04C    call    @System@@FillChar$qqrv ; System::__linkproc__ FillChar(void)
CODE:005FC051    lea     eax, [ebx+4]
CODE:005FC054    push    eax
CODE:005FC055    lea     edx, [ebx+4]
CODE:005FC058    lea     eax, [ebp+var_E8]
CODE:005FC05E    mov     ecx, 10h
CODE:005FC063    call    sub_5FA470
CODE:005FC068    mov     esi, [ebp+len]
CODE:005FC06B    test    esi, esi
CODE:005FC06D    jns     short loc_5FC072
CODE:005FC06F    add     esi, 7
CODE:005FC072
CODE:005FC072 loc_5FC072:                ; CODE XREF: sub_5FC02C+41 j
CODE:005FC072    sar     esi, 3
CODE:005FC075    dec     esi
CODE:005FC076    test    esi, esi
CODE:005FC078    jl      short loc_5FC09C
CODE:005FC07A    inc     esi
CODE:005FC07B    xor     ebx, ebx
CODE:005FC07D
CODE:005FC07D loc_5FC07D:                ; CODE XREF: sub_5FC02C+6E j
CODE:005FC07D    mov     eax, ebx
CODE:005FC07F    shl     eax, 3
CODE:005FC082    mov     edx, [ebp+_Hex2]
CODE:005FC085    lea     ecx, [edx+eax]
CODE:005FC088    mov     edx, [ebp+_Hex1]
CODE:005FC08B    add     edx, eax
CODE:005FC08D    lea     eax, [ebp+var_E8]
CODE:005FC093    call    sub_5FA75C
CODE:005FC098    inc     ebx
CODE:005FC099    dec     esi
CODE:005FC09A    jnz     short loc_5FC07D
CODE:005FC09C
CODE:005FC09C loc_5FC09C:                ; CODE XREF: sub_5FC02C+4C j
CODE:005FC09C    lea     eax, [ebp+var_E8]
CODE:005FC0A2    call    sub_5FA774
CODE:005FC0A7    lea     eax, [ebp+var_E8]
CODE:005FC0AD    call    ComObj_ClearExcepInfo
CODE:005FC0B2    mov     al, 1
CODE:005FC0B4    pop     esi
CODE:005FC0B5    pop     ebx
CODE:005FC0B6    mov     esp, ebp
CODE:005FC0B8    pop     ebp
CODE:005FC0B9    retn    4
CODE:005FC0B9 sub_5FC02C      endp

分析:里面有三个未知 Call:sub_5FA470、sub_5FA75C、sub_5FA774。先看sub_5FA470,里面有一段很有规律的代码,按F5生成C代码,部分如下:

  j = 6;
  do
  {
    LOWORD(v7) = *(_WORD *)(v5 + 2);
    v9 = v7 << 9;
    *(_WORD *)(v5 + 16) = ((unsigned int)*(_WORD *)(v5 + 4) >> 7) | v9;
    LOWORD(v9) = *(_WORD *)(v5 + 4);
    v9 <<= 9;
    *(_WORD *)(v5 + 18) = ((unsigned int)*(_WORD *)(v5 + 6) >> 7) | v9;
    LOWORD(v9) = *(_WORD *)(v5 + 6);
    v9 <<= 9;
    *(_WORD *)(v5 + 20) = ((unsigned int)*(_WORD *)(v5 + 8) >> 7) | v9;
    LOWORD(v9) = *(_WORD *)(v5 + 8);
    v9 <<= 9;
    *(_WORD *)(v5 + 22) = ((unsigned int)*(_WORD *)(v5 + 10) >> 7) | v9;
    LOWORD(v9) = *(_WORD *)(v5 + 10);
    v9 <<= 9;
    *(_WORD *)(v5 + 24) = ((unsigned int)*(_WORD *)(v5 + 12) >> 7) | v9;
    LOWORD(v9) = *(_WORD *)(v5 + 12);
    v9 <<= 9;
    *(_WORD *)(v5 + 26) = ((unsigned int)*(_WORD *)(v5 + 14) >> 7) | v9;
    LOWORD(v9) = *(_WORD *)(v5 + 14);
    v9 <<= 9;
    *(_WORD *)(v5 + 28) = ((unsigned int)*(_WORD *)v5 >> 7) | v9;
    LOWORD(v9) = *(_WORD *)v5;
    v7 = v9 << 9;
    *(_WORD *)(v5 + 30) = ((unsigned int)*(_WORD *)(v5 + 2) >> 7) | v7;
    v5 += 16;
    --j;
  }
  while ( j );

然后看看sub_5FA75C,部分如下:

  _word_1 = *(_WORD *)Hex64_1;
  _word_2 = *(_WORD *)(Hex64_1 + 2);
  _word_3 = *(_WORD *)(Hex64_1 + 4);
  _word_4 = *(_WORD *)(Hex64_1 + 6);
  i = 8;
  do
  {
    sub_5FA22C((int)&_word_1, *(_WORD *)v3);
    v5 = (unsigned __int16 *)(v3 + 2);
    _word_2 += *v5;
    ++v5;
    _word_3 += *v5;
    ++v5;
    sub_5FA22C((int)&_word_4, *v5);
    ++v5;
    v6 = _word_3;
    _word_3 ^= _word_1;
    sub_5FA22C((int)&_word_3, *v5);
    ++v5;
    v7 = _word_2;
    _word_2 ^= _word_4;
    _word_2 += _word_3;
    sub_5FA22C((int)&_word_2, *v5);
    v3 = (int)(v5 + 1);
    _word_3 += _word_2;
    _word_1 ^= _word_2;
    _word_4 ^= _word_3;
    _word_2 ^= v6;
    _word_3 ^= v7;
    --i;
  }
  while ( i );

    如此简洁高效的代码,看着就像现成的算法,翻开《加密与解密(第三版)》,看看哪个算法和这类似。嘿嘿~ 原来是IDEA算法。网上找到的IDEA工具来算这个程序的注册码,结果都不对,猜测可能是工具中字节存取跟程序中的处理不一样。弄了个带有源代码的工具,果然,RvsCore.exe中是按char存取,网上找到的IDEA工具是按short int存取。
    RVS注册算法的验证过程是:


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 7
支持
分享
最新回复 (11)
雪    币: 1844
活跃值: (35)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
2
不错哦,来顶一下拉
2011-3-12 00:59
0
雪    币: 210
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
震撼!受教育了
2011-3-12 09:59
0
雪    币: 370
活跃值: (15)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
4
sandboxie后又一个牺牲者

确实强,工具组合拳打得不错
2011-3-12 10:46
0
雪    币: 46
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
又看了一遍 太强悍了
2011-3-12 10:50
0
雪    币: 375
活跃值: (12)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
6
楼主很强大,支持一个。
2011-3-12 12:52
0
雪    币: 442
活跃值: (312)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
经常看到楼主的文章,功力深厚哇.
2011-3-12 13:37
0
雪    币: 234
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
好文

学习了
2011-3-12 23:23
0
雪    币: 379
活跃值: (233)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
9
之前疏忽了,不好意思。RVS Lite 2011 中对于简体中文的系统和其它语言的系统的注册算法是不一样的,
2011-3-13 21:14
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
谢谢,等了很久了。。。。。。。。。
2011-5-29 13:21
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
要下来试试看了哟, 不知版本对不对呢
2011-8-1 21:12
0
雪    币: 1371
活跃值: (150)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
高人可否解析一下VSuite Ramdisk(服务器版)的本地注册算法,这个算法在论坛里没人能说明白,请教高人指点
2011-9-23 17:08
0
游客
登录 | 注册 方可回帖
返回
//