曾经,程序的自动改写是每个黑客必备的知识,因为这样可以有效的节约内存,下面是在现代的GNU/Linux 操作系统上实现的程序自我改写(本文的演示基于32 位系统和编译器)。
列一下环境:
speller@SHELL-LAB:~/code/c$ egrep "model name" /proc/cpuinfo | uniq -c
2 model name : Pentium(R) Dual-Core CPU E6300 @ 2.80GHz
speller@SHELL-LAB:~/code/c$ uname -sr
Linux 2.6.35.10-smp
speller@SHELL-LAB:~/code/c$ gcc --version | grep GCC
gcc (GCC) 4.4.4
speller@SHELL-LAB:~/code/c$
上代码:
-
/*
-
* 文件名: self-modification.c
-
* 备注: 一个可以修改自身代码的小程序 ;p
-
*/
-
#include <stdio.h>
-
#include <unistd.h>
-
#include <sys/mman.h>
-
-
void func (void);
-
-
int main
-
(int argc, char *argv[]) {
-
-
int count = 0;
-
-
for (count = 0; count < 3; ++count) {
-
printf ("第<%d>次执行函数func:\n", count+1);
-
func ();
-
}
-
-
return 0;
-
}
-
-
void func
-
(void) {
-
-
__asm__ __volatile__ (
-
"slot:\n"
-
"nop\nnop\n" /* 这两个nop 指令用来为下面的自动改写留出位置 */
-
"after_slot:\n"
-
);
-
-
puts ("\t你只能在第<1>次执行函数func 的时候看到这条消息\n"
-
"\t\t——因为程序对自身的代码进行了修改。\n");
-
-
/* 将函数func 的堆栈属性设置为可读写、可执行 */
-
int pagesize = (int)sysconf (_SC_PAGESIZE);
-
char *p = (char *)((int)func & ~(pagesize - 1));
-
mprotect (p, pagesize * 10, PROT_READ | PROT_WRITE | PROT_EXEC);
-
-
/* 进行自我改写 */
-
__asm__ __volatile__ (
-
".byte 0xe8, 0, 0, 0, 0\n" /* 0xe8 是call 指令,把popl 指令当作函数调用 */
-
"popl %%eax\n" /* popl 出栈的是popl 指令自身的地址 */
-
"addl $20, %%eax\n" /* 定位到第二个puts 函数 */
-
"subl $after_slot, %%eax\n"/* 得到第二个puts 函数相对于after_slot 标签的偏移 */
-
"shl $8, %%eax\n" /* 这里是little endian 系统 */
-
"movb $0xeb, %%al\n" /* 0xeb 是jmp 指令,%ax 里现在是 */
-
"movw %%ax, slot" /* 填充先前两个nop 指令预留出的空间 */
-
:
-
:
-
: "eax"
-
);
-
-
puts ("\t这条消息应该在每次调用函数func 的时候都被输出到stdout。\n");
-
-
return;
-
}
效果如下:
speller@SHELL-LAB:~/code/c$ gcc -o self-modification self-modification.c
speller@SHELL-LAB:~/code/c$ ./self-modification
第<1>次执行函数func:
你只能在第<1>次执行函数func 的时候看到这条消息
——因为程序对自身的代码进行了修改。
这条消息应该在每次调用函数func 的时候都被输出到stdout。
第<2>次执行函数func:
这条消息应该在每次调用函数func 的时候都被输出到stdout。
第<3>次执行函数func:
这条消息应该在每次调用函数func 的时候都被输出到stdout。
speller@SHELL-LAB:~/code/c$
程序自身对自身的指令进行修改,有点黑客帝国的味道,不是么 ;p
阅读(1134) | 评论(0) | 转发(0) |