• 标 题:转一点starforce的资料 (9千字)
  • 作 者:lidou
  • 时 间:2003-2-21 14:41:30
  • 链 接:http://bbs.pediy.com

前两天玩仙剑II被这个东东给折磨得够呛,找点资料也难。前面还有一篇是俄文的呢,郁闷。终于在smth上找了一些关于sf的资料,转过来。
******************************

对 Star-Force 的研究 (Cossacks)
作者 ASMax ,邮件地址 asmax@imail.ru
译者 kxn@SMTH, 邮件地址 kxn@btamail.net.cn
原文 URL : http://www.reversing.net/articles/122001/sf-cossacks.htm

译者说明:
原文是俄文,本文系译者参考机器翻译到英文的结果和自己对脱壳的理解翻译而成,由于
译者并没有条件去试验文章中的内容,错误在所难免,请各位不吝赐教,转载时请保留原
作者和译者信息,谢谢。

××××××××

2000 年终最值得纪念的恐怕就是 Cossacks : The European wars --第一个使用
Star-Force 保护系统游戏的推出了。Star-Force的出现给程序研究者带来了很多新的问
题,现在是给这些问题一个答案的时候了。



Star-Force是怎样的保护?
    这个保护系统最强的主要是以下几点:它的核心是一个伪代码的解释器,伪代码
大大复杂了对Star-Force的研究工作;一部分导入函数(imported function)
的代码是从系统库中拷贝出来并进行修改过的;一部分程序代码只有在执行时
候才解密出来。除了这些,保护中还大量的应用了下面这些手段:检测某些内存段的CRC
校验和,经常性地将调试寄存器DRx清零,利用RDTSC指令来控制解码不同块代码的时刻(
译者注:这句不是很明白,此句是对照两种不同的英文机器译本硬译出的),最后一块
代码的解码甚至是通过截获int0在 ring0 中进行的。关于这个保护的详细情况我大致就
介绍这么多,因为我们将另辟蹊径,使这些保护方法中的大多数在我们面前根本起不了
作用。



Dump

    在这个版本中Dump还是一件很简单的事情,在protect.dll中跳转到原始入口点
之前大致是这样的代码:
        push 64h
        MOV of eax, [Kernel32!Sleep ]
        call eax
        ret
    因此我们设一个 bpx Sleep,启动游戏,在断点处停下来,跟进 ret 指令,然
后用PEditor之类就可以dump了。




恢复import

    Star-Force所使用的import保护技术是我所见过的保护程序中最强的一种。在传
统的保护中一般最后都是跳转到真正的函数入口处,或者跳转到真正函数入口几条指令之
后的地方。在Star-Force中,所有从kernel32.dll user32.dll advapi32.dll
中import的函数代码都整个被拷贝到保护代码的内存中了(除了push,pop,jxx 指令),拷
贝过来的代码按照下面的规则进行了修改:

        1:  ?????? 改成 nop 和 jmp short $2 (译者注:这里没有一个软件可以正确
翻译出原来的代码,大家猜猜是什么呢?)

        2:      push 12345678h 修改成
                MOV eax, 12345678h
                xchg eax, [esp]
        3:      push esi 修改成
                xchg eax, esi
                push eax
                xchg eax, esi
        4:      pop esi 修改成
                MOV esi, eax
                pop eax
                xchg eax, esi
        5: 两字节的 jcc 代码被修改成 6 字节的相应代码 (译者注:
          即 0F 开头的相对跳转代码)
        6: 两字节的 push 00h 修改成 5 字节的版本

    import 函数入口的长跳转 jmp X 修改成 jmp $+5 ,在首次执行到入口时,X指向
的代码进行将windows库函数代码复制和修改的一系列工作,并将 jmp 指令指向修改后
的代码。(译者注:原文说得不是很清楚,这一段是按照自己的理解翻译的,可能有误)
不过就这个游戏来说,被保护的函数只有11个,因此我决定用手动恢复它们(在我后来对
游戏Venom的研究中解决了这个问题):把代码手动转换回原始的形式,用sice在内存中
搜索入口前10个字节左右相同的库函数。找出的函数结果按照在保护代码的内存中位置排
列如下:
ReadFile, GetUserNameA,  FindNextFileA, WriteFile, VirtualFree, GetFileType,
VirtualAlloc, CreateFileA, DeleteFileA, FindClose, MessageBoxA
将所有import都手动恢复以后,我们就使用一个我常用的工具 ImportList by Boris来重
建 IT。我们把它放进 dump 里面,这部分的工作就完成了。

恢复 _DllDispatch 的代码

        还没有看晕?那我们进行下一步工作。在我们 dump 出来的代码中大约有20多处
(!)调用了 _DllDispatch  --  protect.dll中唯一的导出函数。调用它的代码大概看
起来是这样的:

        push ID
        call _DllDispatch

    其中 ID 这个参数决定了哪一段代码需要被解码并执行,ID = 0 的情况是将主
程序解码出来。值得注意的是,解码出来的代码仍然包含着对 _DllDispatch 的调用。但
是这个 _DllDispatch是用前面提到过的伪代码引擎执行的伪代码。怎么办呢?自己跟踪
每一个调用直到跳转到实际的代码?(译者注:自己的理解)你如果愿意试可以自己去
试,不过我将提供一种更有趣的方法,一次性自动获得所有被保护的代码。为了做到这
一点就需要自己写点程序了。

    首先我们知道一旦被执行过一次,需要的代码就以解密的形式存在内存中了,怎么
捕捉到这一时刻呢?很简单,每个这样的函数多少都会执行到原来的代码中,因此我们
可以截获对_DllDispatch的调用,在将控制权交给protect.dll之前,把原代码段统统
清成0CCh,这样从解码出来的代码跳转出来到任何地方都会触发中断,我们截获int 3
就可以处理之。 更进一步的,我们从ring0的堆栈中找到ring3的堆栈标志,然后就可以
很快找到解码出来的代码了。



对实现的一些说明


    为了方便处理类似的游戏,我建议将我们的deprotect.dll 加入到程序的import中
,它就可以截获protect.dll的入口了,不过这时程序的代码还没有被解码出来,只有一
个指向protect.dll中的入口调用。我们把它修改一下,使得当protect.dll将代码段解
码之后将控制权返回给我们的dll, 接下来我们搜索游戏的代码,就可以找到很多对
_DllDispatch的调用,以及它们对应的ID,把ID都存到一个文件里面以避免出现重复的。
然后我们挨个用这些ID调用_DllDispatch,在 int 3 的处理函数里面将控制权正确的交
给我们的搜索-恢复函数。我们需要在解码出的代码中继续搜寻对 _DllDispatch的调用
以得到全部代码。当然还得把调用这一次DllDispatch的代码修改成直接调用解密代码。
因此搜索需要进行两遍,一遍在原来的代码中,另一遍在解密后的代码中。


    全部工作做完以后,我们得到了两个文件,一个是text.bin,这是程序代码段中
的代码,另一个是code.bin,这是经_DllDispatch解密出的代码,需要手动加到dump中
去。整个EXE文件组装完毕后, protect.dll 就没用了,可以删掉。
最后的一点修正

    启动解密后的游戏试验一下,结果很令人失望:金钱狂涨到无限多,任务说明不
显示,TCP/IP联网对战时选择地图后不生成地图。前两个问题好像是文件名的问题,
把可执行文件改成 dmcr.exe 就正常了,最后一个错误原因是对某一个_DllDispatch的
恢复时出错,大约是这个_DllDispatch中没有跳到其他的代码,就没有被我们的程序捕
捉到(译者注:这里大概是一个没有调用任何其他函数的函数),导致最后生成的代码
里面有一个错误的调用。这样的函数是我们研究的一个盲点,不过就这个例子来说很好
解决,手动跟踪了一下,看出这个函数的地址(译者注:在解码内存区域中的偏移量)
是0A3Ch,手动修改一下调用,把5个nop改成对这里的调用就可以了。
现在所有的问题都解决了,Star-Force完全被搞定了。

源程序
        deprotect.asm (7K) 源程序,使用 TASM5.0 编译成 DLL
        deprotect.dll (8K) 编译好的DLL文件
注意:这个DLL只适用于文中提到的游戏。

(译者注:上面两个程序的URL如下
http://www.reversing.net/articles/122001/deprotect.asm
http://www.reversing.net/articles/122001/deprotect.dll


总结
        Star-Force是一种很有意思的保护系统。不要怕在对它的研究上费时间,这是值
得的。我希望这篇文章对你的研究有所帮助,如果你有任何的问题请给我来信,我会很乐
于回复的。
ASMax
asmax@imail.ru





发信人: kxn (没钱的事情我是不干的), 信区: Hacker
标  题: 一些对Star-Force的个人看法以及补充
发信站: BBS 水木清华站 (Fri Nov 29 10:42:21 2002), 站内


从翻译的文章中可以看出,Star-Force 的保护确实非常优秀,几个关键创意都是
头一次提出:

1: 使用伪码解释引擎来使得对代码算法的分析基本不可能
    像 VB 和 installshield 那样的伪代码程序一直都是破解的一个难题,
    但是 VB 和 is 因为能弄到相应的伪代码生成器, 分析起来方便不少。
    对于 Star-Force 这样搞不到生成器的,希望就更渺茫了

2: 首次大范围应用对汇编代码的反向分析
    Star-Force 一定是有一个比较强的代码反向分析引擎的,首先
    在它独特的 Import 保护方式里面需要对 windows 库函数代码进行修改
    程序必须能正确的分析出所有的跳转指令进行修改,才能使得 windows
    库函数可以在其他的内存位置执行,其次 _DllDispatch 生成出来的
    代码,明显是对原 EXE 分析以后,把整个函数抠出来了,而且需要
    修改原函数里面的一些跳转指令取址指令等以保证能够正常执行

3: 非常特别的 import 保护方式
    通过将 windows 库函数代码拷贝到另外的地方执行,就完全从原理上
    废掉了 imprec 和 revirgin 这类工具

  4: 那个诡异的 DllDispatch
    只能这么说,这个设计实在是太变态了,弓虽口阿


  Star-Force 自己对这些手段看来也是比较有自信的,因此可执行文件的
其他部分,比如数据段,资源段等,统统没有加密,连程序的 OEP 都没有隐藏
加密过的程序 EP 和 OEP 数值一样,还有可执行文件各段的实际长度
在普通的壳里面, 这些数值都是要极力隐藏的。


从 Star-Force 的保护方式可以看出,将来的软件保护将越来越趋向对原有代码
进行自动修改,而不是像以前加个壳完事,而且还不要求软件厂商做任何的适应



发信人: kxn (贫僧法号一坨大师), 信区: Hacker
标  题: Re: [翻译]对 Star-Force 的研究 (Cossacks)
发信站: BBS 水木清华站 (Wed Feb 12 13:54:37 2003), 转信


还有点其他信息, 有的来源是同作者的另一篇文章, 有的来源是自己瞎看的
一起贴上来算了, 另一篇文章懒得翻译了, 辛辛苦苦翻译一堆, 转的人果然
还是把前面的话删掉 :(

ASMax 另一片文章里面的介绍了一个新版本的 SF, 主要内容就是两点,

1: 新版本 SF 代码使用了 INT1 和 INT3 , 不仅使得跟踪不便, 还使得原来的
  用 0xCC 填充代码段来恢复 __DllDispatch 的办法失效了

  ASMax 的应对方法如下:

  在 deprotect.dll 的 DLLMain 里面加上恢复 INT3 和 INT1 的代码, 方便跟踪.

  DllDispatch 的恢复思路跟以前一样, 不过这次改成用 0xFF 填充, 拦截
  INT6 (非法指令)

2: import 的快速恢复,
  把自己的 kernel32.dll user32.dll advapi32.dll 修改,使得 API 入口代码大致像

  CreateFileA :
  push  77e56724  ; 原来的 CreateFileA 入口
  retn

  这样 StarForce 复制过来的代码就变成

  mov eax, 77e56724
  xchg eax,[esp]
  ret

  一下就得到 import 的入口了


关于新的 SF 3.0

  SF3 的 import 直接用 imprec  auto search 不到了, 不过还是可以 dump
  出来, 反编译一下就看出 iat 在哪里,

  Dlldispatch 的代码 SF3 用了一个很简单也是很有效的办法来保护
  以前 SF 的 Dlldispatch 是直接把代码解出来到代码段的, 因此以前 SF
  保护的可执行其代码 section 是空的, rawsize = 0 , vsize != 0
  现在 SF 把代码段预先填充内容, 解出来的数据和原来处的数据做运算后才是
  正确的代码, 这样 0xCC 和 0xFF 大法都不能用了. so .....