Memcpy()在越界内存上运行?

时间:2014-04-10 23:21:43

标签: c memcpy sigsegv

我一直在玩memcpy()可能用于恶意目的的想法。我做了几个测试应用程序,看看我是否可以“偷”"来自不同地区的内存数据。到目前为止,我已经测试了三个区域,堆,堆栈和常量(只读)内存。常量内存是我测试中唯一崩溃的内存,引起了MinGW的错误。

这是一个说明我最新测试的例子:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void removeTerminatingCharacters( char ** string, const int length )
{
    int i = 0;

    for ( ; i < length; ++i )
        if ( !( *string )[i] )
            ( *string )[i] = '0';

    return;
}

int main()
{
    int * naive = malloc( sizeof( int ) );
    *naive = 0;

    char * stolenData = malloc( 2000 );

    memset( stolenData, 0, 2000 );
    memcpy( stolenData, naive, 1999 );
    removeTerminatingCharacters( &stolenData, 2000 );

    printf( "%s\n", stolenData );

    free( stolenData );
    return 0;
}

输出:

0000-0:0Væ1lDk¦#:00000[æ0`Dk00p,:0-0:0MAIN=Computer0USERNAME=JohnDoe0USERPRO
FILE=C:\Users\JohnDoe0WATCOM=C:\watcom0windir=C:\Windows00?æ1+Ik?000S?000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00??????????????????????????000000                          00000000 000000?0?0?
00000000000 0 0 ?0000000000 0000000000 0000 00000???????????????????????0???????
                        0        00000000000000000000000000000000000000000000000
000000000000000000abcdefghijklmnopqrstuvwxyz000000ABCDEFGHIJKLMNOPQRSTUVWXYZ0000
0000â000000Ü0£0P00000000000è0î0Ä 0000000000¬0000000000¦0000¦00000aßGpSsµtFTOd8fe
ä?:0ú?:0-?:0T?:0)?:0`?:0£?:0-?:0p?:0?:0??:0+?:08?:0T?:0¦?:0µ?:0²?:0¶?:03?:0D?:0R
?:0ä :0- :0+ :0a :0v :0?!:0^!:0q!:0ë!:0ñ!:0+!:0±!:0¤":0P":0g":0ó":0¦":0+":0¦":0?
#:0.#:0B#:0W#:0x#:0ë#:00000ALLUSERSPROFILE=C:\ProgramData0APPDATA=C:\Users\Chris
topher\AppData\Roaming0asl.log=Destination=file0CLASSPATH=.;C:\Program Files (x8
6)\Java\jre6\lib\ext\QTJava.zip0CommonProgramFiles=C:\Program Files (x86)\Common
 Files0CommonProgramFiles(x86)=C:\Program Files (x86)\Common Files0CommonProgram
W6432=C:\Program Files\Common Files0COMPUTERNAME=COMPUTER0ComSpec=C:\Windows\sys
tem32\cmd.exe0FPPUILang=en-US0FP_NO_HOST_CHECK=NO0HOMEDRIVE=C:0HOMEPATH=\Users\C
hristopher0HuluDesktopPath=C:\Users\JohnDoe\AppData\Local\HuluDesktop\instan
ces\0.9.13.1\HuluDesktop.exe0LOCALAPPDATA=C:\Users\JohnDoe\AppData\Local0LOG
ONSERVER=\\COMPUTER0NUMBER_OF_PROCESSORS=20OnlineServices=Online Services0OOBEUI
Lang=en-US0OS=Windows_NT0Path=.;F:\CodeBlocks\MinGW\bin;F:\CodeBlocks\MinGW;C:\M
inGW\bin;C:\MinGW;C:\Windows\System32;C:\Windows;C:\Windows\System32\wbem;C:\Pro
gram Files\Common Files\Microsoft Shared\Windows Live;C:\Program Files (x86)\Com
mon Files\Microsoft Shared\Windows Live;C:\Windows\System32\WindowsPowerShell\v1
.0;c:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static;c:\Program Files
(x86)\Common F0Uô1m+k

我的代码并不漂亮,但它证明了我的观点。正如您所看到的,数据主要是垃圾值,但是从堆中抛出了一些有趣的字符串。

我的主要问题是为什么此操作不会导致内存访问冲突错误?

7 个答案:

答案 0 :(得分:8)

当您访问未映射的页面时,虚拟内存硬件会捕获内存访问冲突错误。并非每个超出界限的地址都在未映射的页面中。页面大小通常相同。典型的页面大小,如果是4096字节。 (页面大小是特定于硬件的:一些芯片具有允许可编程页面大小的内存管理,甚至是不同页面大小的混合,用于不同的内存区域。)有时只有页面的一部分包含有效数据。只是页面的一个片段不可能被取消映射,因此也会映射包含垃圾的部分。此外,像malloc这样的内存管理器并不总是将内存返回给操作系统;他们保留免费区域以供重复使用。这些区域是有效的内存(映射页面)。另外,构建指针值,你可以通过侥幸,最终超越越界,并且可以“降落”在与有效对象相对应的内存中。

这就是它在PC上的工作原理,具有虚拟内存操作系统。虚拟内存并不是无处不在。在没有虚拟内存的计算机上(现在是小型嵌入式系统),您可以访问内存中的任何位置。但是,访问某些区域可能会产生改变硬件状态(即I / O寄存器)的副作用。某些地址范围可能会触发“总线错误”类型的CPU异常,因为该范围不存在硬件,因此访问请求超时。除此之外,就有效的程序存储器而言,它不受保护以免受越界访问。

没有记忆保护的操作系统被用于20世纪60年代的早期交互系统。当个人微型计算机出现时,历史就会重演:再次,由于存储器和CPU不够复杂,它们的操作系统没有内存保护。在这些操作系统上,应用程序经常在彼此的内存空间上踩踏,导致频繁的崩溃。 (想象一下,使用memcpy,您不仅可以复制自己的越界区域,例如先前释放的某个malloc块,还可以复制完全不同的运行程序区域。)用户有时会发现模式就像某些程序按某些顺序加载到内存中一样,问题较少,或者应用程序之间存在所谓的“冲突”。

答案 1 :(得分:2)

是的,它可以用于恶意目的,但不是你想象的方式。

您的应用程序读写的内存是您的进程的虚拟内存,它不是物理内存。您甚至无法知道您的物理内存或任何应用程序的实际内存地址,只有系统内核才知道。

如果没有适当的许可和使用系统调用,您无法与其他进程的活动内存进行交互。

你可以知道,读取曾经被其他进程使用的未映射内存区域中剩下的垃圾,你甚至可能偶然发现留在那里的敏感信息,比如密码,证书密钥或用户在某些时候输入的个人资料,但是你处于极不稳定的地位,那里的信息很可能已经破坏,没有简单的方法来寻找具体的信息。

以下是关于此的文章:https://security.stackexchange.com/questions/29019/are-passwords-stored-in-memory-safe

答案 2 :(得分:1)

您不得尝试访问越界内存。
访问内存时,您无法访问未定义行为 任何事情都可能发生。

答案 3 :(得分:0)

你可以偷窃&#34;记忆。但是,这不是你应该依赖的东西,而是根据它编写代码。

答案 4 :(得分:0)

memcpy()没有进行任何边界检查,程序员必须处理它。在您的情况下,它会导致heap overflow

这几乎总结了heap overflow的结果:
https://www.owasp.org/index.php/Heap_overflow

说明

  

堆溢出条件是缓冲区溢出,其中缓冲区   可以被覆盖的是在内存的堆部分中分配的,   通常意味着缓冲区是使用例程分配的   作为POSIX malloc()调用。

后果

  
      
  1. 可用性:缓冲区溢出通常会导致崩溃。其他攻击导致可用性不足,包括   将程序置于无限循环中。
  2.   
  3. 访问控制(存储器和指令处理):缓冲区溢出通常可用于执行任意代码,即   通常在程序的隐式安全策略范围之外。
  4.   
  5. 其他:当后果是任意代码执行时,通常可以使用它来破坏任何其他安全服务。
  6.   

避免和缓解

  
      
  1. 预设计:使用执行自动边界检查的语言或编译器。
  2.   
  3. 设计:使用抽象库来抽象出有风险的API。不完整的解决方案。
  4.   
  5. 通过构建进行预设计:Canary样式边界检查,确保块数据有效性的库更改以及其他此类修复   是可能的,但不应该依赖。
  6.   
  7. 操作:使用操作系统级预防功能。不是一个完整的解决方案。
  8.   

讨论

  

堆溢出通常和堆栈溢出一样危险。   除了重要的用户数据外,还可以使用堆溢出来覆盖   可能存在于内存中的函数指针,指向它   攻击者的代码。
  即使在没有明确使用的应用程序中   函数指针,运行时通常会在内存中留下很多。对于   例如,C ++中的对象方法通常使用   函数指针。即使在C程序中,通常也存在全局偏移   底层运行时使用的表。

答案 5 :(得分:0)

要回答“为什么此操作不会导致内存访问冲突错误?”的问题,答案是因为naive分配区域中的内存已分配给进程。可能是C运行时为进一步可能的malloc()请求做好准备的内存。

在大多数桌面系统上,最小的内存保护单元是一个页面,其大小可能不同,但通常在4KB左右的范围内。如果您的读取请求较大(但仍然可能不是 - 堆可能比您的小分配大得多),您可能会遇到内存访问冲突。然后,您的分配可能接近页面结束并立即发出访问错误(调试分配器有时会有目的地帮助捕获错误)。

答案 6 :(得分:-1)

memcpy无法真实地用于恶意&#34;目的。 memcpy可以访问的唯一内存是属于您自己的进程的内存(无论如何都是您拥有的内存)。如果搞砸了自己的内存,你的程序就会失败,但操作系统会保护所有其他进程(如果没有mmap或类似的话,你就无法触及它们的内存空间。)

大多数&#34;有趣的字符串&#34;是由OS设置的环境变量并传递给您的程序。