加密与解密 读书笔记

加密与解密,可作为经常查阅的工具书,比如查各种jmp的机器码,除法的编译,虚表详述。书中用源代码-无优化-有优化的对比来叙述。有优化这块算是盲区了,比如连续switch会编译成跳表,不连续的也会做衰减判断(自动调整为从小号到大号的顺序),lea可以拿来做算数加法,等等。

文章目录

  • 趋势
  • 虚拟机壳
  • 压缩引擎
  • CC保护
  • 加壳的一般过程
  • 脱壳的一般过程
  • 脱壳后无法运行的一种可能原因:附加数据
  • “完美”脱壳的评价标准
  • 一种强行下断的方法
  • upx魔改相关
  • aspack的手脱
  • asprotect的原理
  • 寻找oep的经验
  • od中patch
  • beingdebugged的连串反应
  • 加密之道
  • 检测调试器的存在
  • dll劫持
  • 特别的反调试思路
  • 附加
  • 软件保护相关
  • 取证相关
    • PC取证
    • 手机取证

趋势

私人壳往往被当作病毒处理。商用壳则有兼容性问题。所以现在正经软件的保护一般不加壳,而是靠序列号设计。

虚拟机壳

虚拟机壳的设计思路是提高分析成本。将x86指令替换成自己的字节码,然后执行虚拟机解释字节码。
效率换安全,一条指令会膨胀几十上百倍。一般只对关键代码做保护。
VMProtect的使用有两种方式,一种是直接指定要保护的起止地址。另一种是SDK方法,开发的时候加个关键字VMProtectbegin和VMProtectend标记,然后用VMProtect打开编译后的exe实施保护。
这是目前最流行的趋势。

压缩引擎

压缩可以慢,解压必须快。常见的压缩引擎包括aPLib,JCALG1,LZMA。

CC保护

跟卖服务器的CC保护不是一个意思,他们那个说的是DDoS。
将子进程中所有跳转指令换成INT 3指令(CC)。父进程负责截获异常,并计算正确的跳转地址。

加壳的一般过程

  • 保存入口参数
  • 加载加壳需要的API。如果DLL不在就loadLibrary,如果在了就直接getModuleHandle。然后是getProcAddress(有的会自己实现)获取输入函数地址。这三个很重要。
  • 按区块解密原代码,放在内存中合适的位置
  • 填写IAT,因为壳的IAT顶替了原来的IAT被PE装载器加载,所以需要做恢复工作
  • 重定位修复,exe可以不做修复,dll要做修复
  • hook api,按书中描述,这一步我个人理解和IAT修复好像是一样的?
  • 跳转到OEP,简单情况下壳与OEP之间有明显的分界线,但有的壳会将OEP代码段搬到外壳的地址空间,然后擦除这段代码(stolen bytes),从而消去这条分界线。

在调试器中,看见的都是call 立即数,call [ebp+x],很容易茫然。书中给了一个例子,流程如下:

  • 调用getProcAddress,获取virtualAlloc的地址
  • 调用virtualAlloc,为壳的第二部分申请空间
  • 调用解压函数
  • 跳转到壳的第二部分
  • 解压各区块,写回
  • 重建IAT
  • 反DUMP
  • 返回OEP

脱壳的一般过程

最终目标是文件能够正常运行。

  • 找OEP
  • dump内存
  • 重建可执行文件

一种方法是,从壳的开始处跟踪,直至到达代码段。

脱壳后无法运行的一种可能原因:附加数据

感觉这是一种不错的反调试方法。在可执行文件的末尾附加上一段数据,这段数据不会被映射进内存,块表里也看不见。所以脱壳时dump内存,结果无法正常运行(信息损失了)。
除了将附加数据粘贴到脱壳结果,代码上可能也需要修改文件指针的偏移量,总之最终要确保能读取附加数据。

“完美”脱壳的评价标准

基本标准是可以运行。完美则不止于此。

  • 输入表存储位置最好与原来相同。重建时不讲究的话可以存在新增区块。原位置的推测主要是要熟悉各种编译器会把输入表存在哪。
  • 图标等不应当随壳一起被脱掉。需要去外壳中找回来,然后放回rsrc段。资源修复工具如DT_ResFix。
  • 区块调整。删除一些不用的区块(既在块表中删,也删实际内容)。
  • PE文件头修复。

一种强行下断的方法

将汇编指令改为EB FE,就能简单实现一个死循环。FE是-2。

upx魔改相关

魔改的目的是,用最少的劳动阻止upx -d官方脱壳。
upxpr,只修改了两个地方(块名和upx!),手工改回来即可。判断时或许可以看段总数和段名。
upxfix_by_diken,这个在看雪上能找到,据说能把魔改壳改回普通壳。

aspack的手脱

upx破坏了输入表和重定位表,并且会合并区块。而aspack则都不会。
aspack直接对text段的实际内存地址(imagebase+text voffset)下写断点,就能等到解压完毕。

asprotect的原理

用得比较广泛、研究得也比较多的加密壳。

  • 外壳校验。这加大了脱壳难度。权宜之计是在想要修改的地方下断点,然后修改eip。
  • emulate standard system function。把标准api的开头代码移到壳里,然后把标准api调用代码改为壳调用代码。对抗时,用patch覆盖上述过程,在IAT表中搜索当事api,然后把当事api地址写回原来的代码。
  • stolen bytes。将代码变形,然后搬进外壳段。

寻找oep的经验

dll的oep可以在载入时找,也可以在退出时找。退出时找更容易,书中示例也都是退出时找。

od中patch

可以patch任意一条指令而做到只添加不删除。
比如将or al,al指令修改为jmp,
在jmp的目标位置先做想做的事(先pushad,最后popad),此过程中可以用hideOD这样的插件来分配空间,
然后最后or al,al,再jmp回原来or al, al的下一条指令。

beingdebugged的连串反应

beingdebugged为true,为影响ntglobalflag,进一步影响heap flag,影响堆的填充内容。如果不从源头上消灭,会留下很多痕迹。

加密之道

任何单个的反调试手段,破解起来都不困难。将常见的手段组合起来,就能拖垮耐心。

检测调试器的存在

除了检查窗口名称和进程名称,其实还有很多方法。
windows提供了进程间互访内存的函数,readprocessmemory和writeprocessmemory。用virtualprotect函数设置权限之后,就能读写进程的地址空间。

  • 特征码法。枚举当前运行的进程,检查每个进程特定位置的内容。此法版本敏感。
  • 检测进程是否加载dbghelp.dll。
  • 检查进程是否具有sedebugprivilege权限。
  • 父进程检测。正常启动时父进程一般是cmd、explorer、services。
  • 两次汇编指令RDTSC之间的时间差。

dll劫持

类似Linux的preload library,但只能劫持非核心系统库DLL,比如网络应用程序中的ws2_32.dll和游戏中的d3d8.dll,以及大部分应用程序都会用的lpk.dll。

特别的反调试思路

  • 攻击调试器。比如向调试器发送格式化字符串。
  • 双进程保护。一个进程只能有一个调试器。

附加

如果不方便用调试器启动,可以先启动目标进程,然后附加。调试游戏时一般附加。

软件保护相关

切记,试用版不能包含重要功能的代码。
序列号机制的本质是,对比用户输入序列号和内置算法生成序列号。
较为简单的注册机的本质是正向(而非逆向)抠出了内置生成算法,此算法一般的输入是用户名、机器信息等。
“期望的用户输入”在任何时候都不应该出现在内存中。生成端再复杂都没用,关键是用户输入端。CTF中也是如此,以strcmp为界,其实只有输入一侧需要研究。
如果使用对称加密的话,用户输入的注册码应作为密钥,而不是明文。否则密钥就只能硬编码,硬编码的结果就是别人容易写出注册机。对称加密的秘密是密钥。
如果使用非对称加密的话,生成算法不放在软件本地,版权方用私钥签名来生成。输入端需要输入一个能被公钥成功验签的内容。
针对各类签名算法的软件保护,都可以用patch n的方法来攻击。有点打包重签名的意思。修改算法中的大数n,自己制定私钥,写出注册机,一起重新发布即可。

取证相关

PC取证

取证标准要求不能开机。硬盘被系统挂载时会有数据写入,导致污染。
不能拆机时,使用linux可启动光盘启动目标计算机,然后用dd命令复制硬盘。
能拆机时,可以借助硬盘复制机连接src和dst,或者用另一台取证计算机(linux用dd,windows用winhex)。

手机取证

手机自带的闪存芯片焊在主板上,部分手机会用SD卡。前者可以用热风枪吹下来(chip-off,但有损坏风险),后者直接用读卡器即可。
自带存储的取证可以用JTAG法,通过跳线将手机置于调试模式。
对于已root、开启usb调试的手机上,将/system分区以读写模式重新挂载,把busybox装进/system/xbin,然后nc、dd。
移动设备中不能被用户使用的分区有很多。正常情况下,只有/data和/mnt/sdcard以读写模式挂载,因此大多数取证操作中只需要检查这两个分区就可以了。

你可能感兴趣的:(安全,安全)