linux-汇编布局分析初识

出于好奇学习一下,根据实践分析结果,并未根据gcc 源码分析,可能不正确

本案例在 ubuntu 20.04 x86_64 上测试

源码

int oo(char*passwd)
{
    int auth = 8;
    char buf[8];
    strcpy(buf, passwd);
    printf("auth=%d\n", auth);
    return 0;
}

int overlow(char *passwd)
{
    char buf[6];                                                                                                                                                                                                                                                                          
    int auth = 5;
    char d[6];
    strcpy(buf, passwd);
    strcpy(d, passwd);
    printf("auth=%d\n", auth);
    return 0;
}
int main()
{
    overlow("passwordab");
    return 0;
}

这是基础测试的最初代码,很明显示这个代码会产生数组溢出,结果就是 auth 的值被修改了。

并且这样的溢出会导致 auth 的值是固定的

分析

先对 oo 函数分析,提取出汇编代码

int oo(char*passwd)
{   
    1169:   f3 0f 1e fa             endbr64 
    116d:   55                      push   %rbp
    116e:   48 89 e5                mov    %rsp,%rbp
    1171:   48 83 ec 20             sub    $0x20,%rsp
    1175:   48 89 7d e8             mov    %rdi,-0x18(%rbp)
    int auth = 8; 
    1179:   c7 45 fc 08 00 00 00    movl   $0x8,-0x4(%rbp)
    char buf[8];
    strcpy(buf, passwd);
    1180:   48 8b 55 e8             mov    -0x18(%rbp),%rdx
    1184:   48 8d 45 f4             lea    -0xc(%rbp),%rax
    1188:   48 89 d6                mov    %rdx,%rsi
    118b:   48 89 c7                mov    %rax,%rdi
    118e:   e8 cd fe ff ff          callq  1060 <strcpy@plt>
    printf("auth=%d\n", auth);      
    1193:   8b 45 fc                mov    -0x4(%rbp),%eax
    1196:   89 c6                   mov    %eax,%esi
    1198:   48 8d 3d 65 0e 00 00    lea    0xe65(%rip),%rdi        # 2004 <_IO_stdin_used+0x4>
    119f:   b8 00 00 00 00          mov    $0x0,%eax
    11a4:   e8 c7 fe ff ff          callq  1070 <printf@plt>
    return 0;
    11a9:   b8 00 00 00 00          mov    $0x0,%eax
}

1171 行 rsp-0x20 说明整个栈分配了32字节的空间

1175 将rdi 放到了rbp-0x18 的位置,rdi 是调用之前设置的地址,值是参数内容,而放到的位置是 rbp-24 个字节的位置

1179 将数值8放到 rbp-4的位置,也就是 auth 的位置

1180 1184 是从 rbp-0x18(参数的位置) 取数据放到 rbp-0xc(buf,要存储到的位置)

其中的 rsi rdi 用于传参时的参数位置指定

这里的重点就是这些位置是如何确定的。

大概布局

//
// +--------+
// |        | - rbp
// |   auth | - rbp-4
// |        |
// |   ...  | - rbp-0xc
// |        |
// |        | - rbp-0x18
// |        |
// +--------+ - rbp-0x20 (rsp)

rbp 在栈顶的位置, rsp 会被设置成当前所指向的栈帧位置,根据资料,rsp 最后要存储返回地址,

rbp-4 是auth的地址,下面 rbp-0xc 是 buf 的位置 (c-4) = 8 是 buf 的长度,这样总结下来 ,栈空间的总长度应该是 (返回地址8+auth4+buf8+参数8=28) 但是 rbp-0x20 是 32字节空间是哪来的?

经过测试,这个栈空间应该是按 16 的整数倍来分配 (这个只是实验得来,并非通过 gcc源码获得,不一定正确)

进一步实验

overlow 的汇编放出来

int overlow(char *passwd)
{
    11b0:   f3 0f 1e fa             endbr64
    11b4:   55                      push   %rbp
    11b5:   48 89 e5                mov    %rsp,%rbp
    11b8:   48 83 ec 20             sub    $0x20,%rsp
    11bc:   48 89 7d e8             mov    %rdi,-0x18(%rbp)
    char buf[6];
    int auth = 5;
    11c0:   c7 45 fc 05 00 00 00    movl   $0x5,-0x4(%rbp)
    char d[6];
    strcpy(buf, passwd);
    11c7:   48 8b 55 e8             mov    -0x18(%rbp),%rdx
    11cb:   48 8d 45 f6             lea    -0xa(%rbp),%rax
    11cf:   48 89 d6                mov    %rdx,%rsi
    11d2:   48 89 c7                mov    %rax,%rdi
    11d5:   e8 86 fe ff ff          callq  1060 <strcpy@plt>
    strcpy(d, passwd);
    11da:   48 8b 55 e8             mov    -0x18(%rbp),%rdx
    11de:   48 8d 45 f0             lea    -0x10(%rbp),%rax
    11e2:   48 89 d6                mov    %rdx,%rsi
    11e5:   48 89 c7                mov    %rax,%rdi
    11e8:   e8 73 fe ff ff          callq  1060 <strcpy@plt>
    printf("auth=%d\n", auth);
    11ed:   8b 45 fc                mov    -0x4(%rbp),%eax                                                                                                                                                                                                                                
    11f0:   89 c6                   mov    %eax,%esi
    11f2:   48 8d 3d 0b 0e 00 00    lea    0xe0b(%rip),%rdi        # 2004 <_IO_stdin_used+0x4>
    11f9:   b8 00 00 00 00          mov    $0x0,%eax
    11fe:   e8 6d fe ff ff          callq  1070 <printf@plt>
    return 0;
    120a:   b8 00 00 00 00          mov    $0x0,%eax

进一步测试,故意对参数大小做测试,如上面的代码,8+8+buf6+auth4+d6 = 32 正好是 rsp-0x20,而此时把 buf[6] ⇒ buf[7] ,此时的大小是 33 ,而汇编之后是 rsp-0x30 ,直接加大大小到 48。

同时根据这个现象看到 int 和 char 在一起的时候,int 始终在位置较高处,这个规则还不懂,有机会看看源码学习一下。

arm下的现象

void overlow(char *passwd, char *next)
{
   104e4:   e92d4800    push    {fp, lr} 
   104e8:   e28db004    add fp, sp, #4
   104ec:   e24dd020    sub sp, sp, #32 
   104f0:   e50b0020    str r0, [fp, #-32]  ; 0xffffffe0
   104f4:   e50b1024    str r1, [fp, #-36]  ; 0xffffffdc
    int t = 9;
   104f8:   e3a03009    mov r3, #9
   104fc:   e50b3008    str r3, [fp, #-8]
    char buf[6];
    int auth = 5;
   10500:   e3a03005    mov r3, #5
   10504:   e50b300c    str r3, [fp, #-12]
    char d[6];
    strcpy(buf, passwd);
   10508:   e24b3014    sub r3, fp, #20 
   1050c:   e51b1020    ldr r1, [fp, #-32]  ; 0xffffffe0
   10510:   e1a00003    mov r0, r3
   10514:   ebffff97    bl  10378 <strcpy@plt>
    strcpy(d, next);
   10518:   e24b301c    sub r3, fp, #28 
   1051c:   e51b1024    ldr r1, [fp, #-36]  ; 0xffffffdc
   10520:   e1a00003    mov r0, r3
   10524:   ebffff93    bl  10378 <strcpy@plt>
    printf("auth=%d\n", auth);
   10528:   e51b100c    ldr r1, [fp, #-12]
   1052c:   e3000674    movw    r0, #1652   ; 0x674
   10530:   e3400001    movt    r0, #1
   10534:   ebffff8c    bl  1036c <printf@plt>
}
   10538:   e320f000    nop {0} 
   1053c:   e24bd004    sub sp, fp, #4                                                                                                                                                                                                                                                    
   10540:   e8bd8800    pop {fp, pc}

在 arm 下以 fp 做为栈基址,直接将 fp+4 存储为返回地址,相当于返回地址在高地址处,而x86_64 的返回地址在低地址处

总结

  1. 没事看看汇编有助于理解代码的原因,对找寻一些困难问题的方法,思路有点帮助
  2. 栈的入参从右到左,这个很多地方都解释,不通解释规则是不一样的
  3. int 会始终放在前面
  4. 栈大小会按 16 的倍数在增加
  5. 局部变量同类型的按上到下从高地址到低地址逐步增加
  6. 通过实验进一步理解理论
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值