自修改代码(Self Modifying Code)的详解_Oracle数据库_黑客防线网安服务器维护基地--Powered by WWW.RONGSEN.COM.CN

自修改代码(Self Modifying Code)的详解

作者:黑客防线网安Oracle维护基地 来源:黑客防线网安Oracle维护基地 浏览次数:0

黑客防线网安网讯:  自修改代码有很广泛的用途: 1.在10到20年前使用SMC(自保护代码)保护应用程序是很难的,即使是用它来把编译的代码放到内存里. 2.在90年代中期95/NT出现了,那时的程序员对在新的操作系统下如...

  自修改代码有很广泛的用途:

1.在10到20年前使用SMC(自保护代码)保护应用程序是很难的,即使是用它来把编译的代码放到内存里.

2.在90年代中期95/NT出现了,那时的程序员对在新的操作系统下如何保护应用程序感到迷惑.不知道该如何将保护措施移植到这个新的版本下.已经不可能再自由的访问内存,硬件,和一般的操作系统,所有以前学会的技巧不得不放弃,开始人们认为除了使用VxD外没法再写SMC,这都是因为文档没跟上而遭到各方的质疑.

然后发现要想在我们的程序中继续使用SMC,我们可以采用下面两种方式:

使用从Kernell32导出的WriteMemoryProcess
将代码放到堆栈中修改


Windows 内存的组织

在Wnidws下创建SMS并没有我想的那么直接,首先你必须面对一些特别的方式,其次要用到Windows提供给你的指南.

这也许你知道,Wondows为进程分配了4GB的虚拟内存.这个内存的地址,Windows有两个用途,其一是CS段寄存器,其二是给了DS,SS,和ES寄存器.他们使用同样的内存基地址,(等于0),而且同样限制在4GB

只有一个段即包含代码也包含数据,那就是进程的堆栈,可以使用NEAR 调用或者jump控制堆栈上的本地代码,即不需要要使用SS寄存器去访问堆栈,而且CS寄存器的值也不等于DS,ES,和SS寄存器,

MOV dest

CS:[src]

MOV dest

DS:[src]

MOV dest

SS:[src]

指令指向的是相同的本地地址

内存页包含的数据,代码和堆栈有不同的属性,事实上,代码页允许读和执行,数据页允许读和写,堆栈同时允许读,写,和执行.

尽管他们都绑定了一些安全属性,后面我们会继续讲到.

 

使用WriteProcessMemory

改变进程内字节的最简单的方式是使用WriteMemoryProcess(当然是一些安全标志没有被设置的时候)

对于我们要修改的内存中的进程第一件事情是用OpenProcess打开它,同时需要设上PROCESS_VM_OPERATION 和PROCESS_VM_WRITE属性.

这儿有一些SMC的简单例子,需要在C++中用到内联的汇编语言,

例程1:使用WritePreocessMemory创建SMC

int WriteMe(void *addr, int wb)
{
HANDLE h=OpenProcess(PROCESS_VM_OPERATION|PROCESS_VM_WRITE,
true, GetCurrentProcessId());
return WriteProcessMemory(h, addr, &wb, 1, NULL);
}

int main(int argc, char* argv[])
{
_asm {
push 0x74 ; JMP --> > JZ
push offset Here
call WriteMe
add esp, 8
Here: JMP short here
}
printf("Holy Sh^& OsIX, it worked! #JMP SHORT $-2 was changed to JZ $-2n");
return 0;
}

 

正如你所看到的,程序用JZ指令实现了跳转.这样程序就可以继续,程序告诉我们,使用jump成功了.

不过WriteMemoryProcess还有一些缺点,首先它会被有经验的解密者在入口表处发现,它很可能会自这个调用处设置一个断点,然后单步找到它想要的代码,WriteProcessMemory一般的用途是编译器的编译内存中,或者可执行文件的解包中,但一定要确保不被解密者利用到.

WriteMemoryProcess 的另一个缺点是无法在内存中创建新页.它只能在已存在的页上工作.

 

将代码放到堆栈上,然后执行

在堆栈上执行代码不是不可能,但是这样会不会产生线程安全性问题呢?如果安装了不允许代码在堆栈上执行的补丁,那么程序员就无技可施了.但反过来可能的结果是你的大部分程序将无法运行,Linux就有这样一个补丁.

记得上面提到的WriteMemoryProcess的几个缺点了么?好的,这是使用堆栈执行代码的两个原因,其一是,一个攻击者在不知道的内存块上是不可能通过发送指令来修改代码,他不得不去分析保护代码,这很难成功.第二个原因是在任何时候堆栈上的可执行代码是真实存在的,应用程序可以给堆栈分配足够的内存,不需要的时候可以释放,通常情况下,系统给堆栈分配1MB的内存,如果感觉分配的内存不够时,可以在程序的配置文件中修改.

 

为什么代码重定位不好呢?

你必须明白在Windows 9X, Windows NT, and Windows 2000.中堆栈的位置是不同的.为了使你的程序在移植到其他机器中还能使用,必须进行重定位,实现起来到不难,只需要满足一些简单的规则.

 

幸运的是在8086里所有的short jumps和near调用是相联系的.也就是说它不使用线性地址,只是下一条指令和目标地址之间不同,这使我们的代码重定位更加的简单,但也有一些约束

 

例如,下面函数会发生什么呢?

void OSIXDemo()

{

printf("Hi from OSIXn");

}

函数被拷贝到了堆栈,控制传递给它了么?因为printf的地址已经改变了,这很可能会产生错误.

 

在汇编里面,我们可以使用寄存器很容易的锁定它,重定位调用printf函数就很简单,例如:

LEA EAX,

printfNCALL EAX.

 

现在是绝对线性地址,而不是关联的.被放置到了EAX寄存器里,现在无论从哪调用它,控制都可以被传递给printf.

 

做这些事情需要你的编译器支持线性的汇编,

例程2:代码如何拷贝到堆栈并且执行

void Demo(int (*_printf) (const char *,...))
{
_printf("Hello, OSIX!n");
return;
}

int main(int argc, char* argv[])
{
char buff[1000];
int (*_printf) (const char *,...);
int (*_main) (int, char **);
void (*_Demo) (int (*) (const char *,...));
_printf=printf;


//加上两个赋值语句
_main = main;
_Demo = Demo;
//

int func_len = (unsigned int) _main - (unsigned int) _Demo;
for (int a=0; a<func_len; a++)
buff[a] = ((char *) _Demo)[a];
_Demo = (void (*) (int (*) (const char *,...))) &buff[0];

    黑客防线网安服务器维护方案本篇连接:http://www.rongsen.com.cn/show-13344-1.html
网站维护教程更新时间:2012-03-23 00:53:47  【打印此页】  【关闭
我要申请本站N点 | 黑客防线官网 |  
专业服务器维护及网站维护手工安全搭建环境,网站安全加固服务。黑客防线网安服务器维护基地招商进行中!QQ:29769479

footer  footer  footer  footer