pwn-基础练习

pwn-基础练习

汇编基础

•寄存器简介: 

•80x86中,32位寄存器有16个: 

•4个数据寄存器、2个变址寄存器、2个指针寄存器、6个段寄存器、1个指令寄存器和1个标志寄存器

•--数据寄存器 

•eax,ebx,ecx,edx 

•作用:存放运行中间数据,ecx常用作计数器 

•特点:可以分割,比如eax可分成ax16寄存器,16位的ax又可以分成al和ah 

•--变址寄存器 

•esi源地址 edi目的地址 

•作用:存放存储单元段内偏移量,用于寻址

•--指针寄存器 

•EBP(栈底)/ESP (栈顶) 

•作用: EBP是基地址指针寄存器,可以直接访问栈中数据, ESP指向堆栈栈顶。 

• •--段寄存器 

•CS/DS/ES/SS/FS/GS 

•说明:在32位CPU中,段寄存器的作用根据工作方式不用而不同,在保护模式下, 

•段寄存器中存放的不在是段值,而是称为"选择子( Selector )的某个值。 

• •--指令寄存器和标志寄存器 

•指令寄存器: EIP ,存放下一次要执行的指令的地址。

•标志寄存器: PSW ,存放程序运行状态。

汇编指令

image.png

image.png

 

enter等价于:

push ebp

mov ebp,  esp

在函数的入口时常用。

 

leave等价于:

mov esp,  ebp

pop ebp

在函数的出        口时常用。

GDB调试器

•GDB是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。 对于一名Linux下工作的c/c++程序员,gdb是必不可少的工具

•调试可执行文件

gdb file

image.png

注:这里有异常出现但是不影响下面的操作,所以就没改

GDB常用命令

•运行 

•run:简记为 r ,其作用是运行程序,当遇到断点后,程序会在断点处停止运行,等待用户输入下一步的命令。 

•continue (简写c ):继续执行,到下一个断点处(或运行结束) 

•next:(简写 n),单步跟踪程序,当遇到函数调用时,也不进入此函数体;此命令同 step 的主要区别是,step 遇到用户自定义的函数,将步进到函数中去运行,而 next 则直接调用函数,不会进入到函数体内。 

•step (简写s):单步调试如果有函数调用,则进入函数;与命令n不同,n是不进入调用的函数的

image.png

 

disas main   查看地址

image.png

GDB常用命令

•断点 

•break n (简写b n):在第n行处设置断点 •break func(break缩写为b):在函数func()的入口处设置断点,如:break cb_button 

•delete 断点号n:删除第n个断点 

•disable 断点号n:暂停第n个断点 

•enable 断点号n:开启第n个断点 

•clear 行号n:清除第n行的断点 

•info b (info breakpoints) :显示当前程序的断点设置情况 •delete breakpoints:清除所有断点:

 

将断点下在 接收字符串的位置

image.png

查看断点信息

image.png

•查看 

•x /nfu <addr>查看内存 

•n表示要显示的内存单元的个数 

•f表示显示方式, 可取如下值
    x 按十六进制格式显示变量。
    d 按十进制格式显示变量。
    u 按十进制格式显示无符号整型。
    o 按八进制格式显示变量。
    t 按二进制格式显示变量。
    a 按十六进制格式显示变量。
    i 指令地址格式
    c 按字符格式显示变量。
    f 按浮点数格式显示变量。 

•u表示一个地址单元的长度
    b表示单字节,
    h表示双字节,
    w表示四字节,
    g表示八字节 

•Info functions  查看函数

简单的溢出

C
#include <stdio.h>
int main(int argc, char** argv)
{
    int modified;
    char buffer[64];
    modified = 0;
    gets(buffer);        // 引发缓冲区溢出
    if (modified != 0)
    {
        printf("Congratulations, you pwned it.\n");
    }
    else
    {
        printf("Please try again.\n");
    }
    return 0;
}
BASIC
Dump of assembler code for function main:
                    //压栈
   0x0000555555555145 <+0>:	    push    rbp
   0x0000555555555146 <+1>:	    mov    rbp,rsp
                    //rsp=rsp-0x60,即在栈上分配0x60个字节空间
   0x0000555555555149 <+4>:	    sub    rsp,0x60
                    //将edi的值存入[rbp-0x54]
   0x000055555555514d <+8>:	    mov    DWORD PTR [rbp-0x54],edi
                    //将rsi的值存入[rbp-0x60]
   0x0000555555555150 <+11>:	mov    QWORD PTR [rbp-0x60],rsi
                    //0x0的值存入[rbp-0x4] 这里是modified的值0
   0x0000555555555154 <+15>:	mov    DWORD PTR [rbp-0x4],0x0
                    //将rbp-0x50的值存入rax
   0x000055555555515b <+22>:	lea    rax,[rbp-0x50]
                    //将rax的值存入rdi
   0x000055555555515f <+26>:	mov    rdi,rax
                    //将0x0存入eax
   0x0000555555555162 <+29>:	mov    eax,0x0
                    /调用gets(buffer)读取输入数据
   0x0000555555555167 <+34>:	call   0x555555555040 <gets@plt>
                    //判断modified变量的值是否为0
   0x000055555555516c <+39>:	cmp    DWORD PTR [rbp-0x4],0x0
                    //如果modified不为0,打印成功提示Congratulations, you pwned it
   0x0000555555555170 <+43>:	je     0x555555555180 <main+59>
                    //将rip+0e8b的值存入rdi         成功提示的信息
   0x0000555555555172 <+45>:	lea    rdi,[rip+0xe8b]        # 0x555555556004
                    //调用puts输出数据
   0x0000555555555179 <+52>:	call   0x555555555030 <puts@plt>
                    //无条件跳转到0x55555555518c 也就是 0
   0x000055555555517e <+57>:	jmp    0x55555555518c <main+71>
                    //将rip+0xe99的值存入rdi         错误的提示信息
   0x0000555555555180 <+59>:	lea    rdi,[rip+0xe99]        # 0x555555556020
                    //调用puts输出数据
   0x0000555555555187 <+66>:	call   0x555555555030 <puts@plt>
                    //将0x0的值存入eax
   0x000055555555518c <+71>:	mov    eax,0x0
                    //弹栈操作,恢复ebp、esp
   0x0000555555555191 <+76>:	leave  
                    //栈平衡
   0x0000555555555192 <+77>:	ret    
End of assembler dump.

通过对上面的汇编代码进行分析,我们知道buffer位于[rbp-0x50]处,而modified位于[rbp-0x4]处,两个地址的距离为0x50 - 0x4 = 0x4C,即76,刚好为buffer数组的缓冲区大小。因此当我们输入的数据超过76字节时,modified变量就可以被覆盖。

下面在gdb中进行验证,在gdb中执行b *0x000000000000116c命令对gets的下一条指令下一个断点:

gdb-peda$ b *0x000000000000116c

image.png

所以接下就是证明我们的推测了,令其一个窗口使用python语句生成77个个字符,python -c "print 'a'*77"

image.png

运行程序输入构造的字符串,返回结果

image.png

连续输入两次ni,如果je没有跳转,说明modified的值不为0

image.png

输入c后程序继续执行,看到提示信息

image.png

使用quit退出,这次我们直接执行程序

image.png

可以看到,在76个字节以内的字符串,会被提示Please try again。下面输入77个字符试试

image.png

可以看到已经成功发起了溢出攻击,程序被PWN掉了!

注:

 

简单的GCC语法:

如果你只有一个文件(或者只有几个文件),那么就可以不写Makefile文件(当然有Makefile更加方便),用gcc直接编译就行了。在这里我们只介绍几个我经常用的几个参数,第一是 “-o”,它后面的参数表示要输出的目标文件,再一个是 “-c”,表示仅编译(Compile),不连接(Make),如果没有”-c”参数,那么就表示连接,如下面的几个命令:

gcc –c test.c,表示只编译test.c文件,成功时输出目标文件test.o

gcc –c test.c –o test.o ,与上一条命令完全相同

gcc –o test test.o,将test.o连接成可执行的二进制文件test

gcc –o test test.c,将test.c编译并连接成可执行的二进制文件test

gcc test.c –o test,与上一条命令相同

gcc –c test1.c,只编译test1.c,成功时输出目标文件test1.o

gcc –c test2.c,只编译test2.c,成功时输出目标文件test2.o

gcc –o test test1.o test2.o,将test1.o和test2.o连接为可执行的二进制文件test

gcc –c test test1.c test2.c,将test1.o和test2.o编译并连接为可执行的二进制文件test

gcc –m64 test1.c -o test.out 编译64位程序

gcc –m32 test1.c -o test.out 编译32程序    

如果命令出错:

 

  1. apt-get install build-essential module-assistant  
     
     
  2. apt-get install gcc-multilib g++-multilib
posted @ 2020-06-08 11:24  MTcx  阅读(383)  评论(0编辑  收藏  举报