- Rongsen.Com.Cn 版权所有 2008-2010 京ICP备08007000号 京公海网安备11010802026356号 朝阳网安编号:110105199号
- 北京黑客防线网安工作室-黑客防线网安服务器维护基地为您提供专业的
服务器维护
,企业网站维护
,网站维护
服务 - (建议采用1024×768分辨率,以达到最佳视觉效果) Powered by 黑客防线网安 ©2009-2010 www.rongsen.com.cn
作者:黑客防线网安Oracle维护基地 来源:黑客防线网安Oracle维护基地 浏览次数:0 |
自修改代码有很广泛的用途:
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];
我要申请本站:N点 | 黑客防线官网 | |
专业服务器维护及网站维护手工安全搭建环境,网站安全加固服务。黑客防线网安服务器维护基地招商进行中!QQ:29769479 |