ARM(手机、嵌入式)架构上ShellCode编写入门教程

原文地址:https://azeria-labs.com/writing-arm-shellcode/

编写ARM架构shellcode的基本介绍

如果你正在阅读这部分教程,请确保对ARM汇编有一个基础的了解(关于这点,你可以查看“ARM汇编基础”系列教程)。在这个部分,你将学习到怎么根据已有的ARM汇编基础知识,从而在ARM上创建你的第一个简单的shellcode。如果你手头没有ARM设备(ARM架构的手机、嵌入式设备等),那么你可以根据这篇教程:如何通过QEMU模拟树莓派,在VM虚拟机上模拟出你自己的树莓派,这样你就有了自己的ARM设备了。

这篇教程是为那些不满足于仅仅使用shellcode自动生成器,而想要自己使用ARM汇编来写出shellcode的人而写的。毕竟,知道在表象之下,这一切是怎么工作的,并且能够完全控制最终的shellcode结果,比单纯用工具可有趣多了,你说呢?而且用汇编写出自己的shellcoe,还是一项非常有用的技巧!因为有的时候你需要绕过shellcode检测算法,或者是在某些情况下绕过一些限制,此时shellcode自动生成器生成的代码则可能会失效,这些情况下,手写shellcode就能大放异彩。让我告诉你一个好消息吧,一旦你熟悉了编写shellcode的整个过程后,这将是一项非常容易学习的技术。

在这篇教程中,我们将使用到以下工具(如果你是Linux系统,那么其中大部分工具都已经默认安装好了):

  • GDB-我们选择的调试器
  • GEF-GDB的增强版,强烈推荐(由@hugsy开发)
  • GCC-Gnu编译集
  • as-汇编器(assembler)
  • ld-链接器(linker)
  • strace-追踪系统调用(system call)的工具‘
  • objdump-检查反汇编代码中是否有空字节(null-bytes)的工具
  • objcopy -从ELF二进制文件导出shellcode的工具

请保证你是在ARM架构下编译、运行这篇教程中的所有示例。

在开始写自己的shellcode之前,请保证你知道这些基本的原则,比如:

  1. 你需要让shellcode尽可能的简洁紧凑,并且没有空字节(null-bytes)
  • 原因:我们所写的shellcode,利用的是内存污染漏洞比如缓冲区溢出。一些缓冲区溢出发生的条件是,编程工作人员错误的使用了‘strcpy’函数。这个函数的作用就是拷贝数据,直到遇到了一个空字节(null-byte)。正是利用这种溢出,我们才能够获得程序运行流程的控制权。但如果‘strcpy’函数遇到了空字节,那么我们的shellcode将无法被完整的拷贝,我们的漏洞利用也很可能会失败。
  1. 你应该尽量避免对特殊库函数的调用,并且shellcode中不能出现绝对内存地址
  • 原因:为了让shellcode能在更多的设备上运行而不出错,我们就不能使用那些需要特殊依赖的库函数,也不能使用那些特定ARM设备才有的绝对不变的内存地址。

编写shellcode的过程,可分为以下几步:

  1. 知道你要使用哪种系统调用(system call)
  2. 弄清楚系统调用号(syscall number),以及这个系统调用函数所需要的系列参数
  3. 把你shellcode中的空字节去除掉
  4. 将你的shellcode转换成16进制的字符串

理解系统函数

在开发我们第一个shellcode之前,不如先来写一个简单的ARM汇编程序,其功能为输出字符串。第一步是找到我们所需要的系统调用,在本例中,就是“write”了。而这个系统调用的原型可以在Linux man pages中找到:

ssize_t write(int fd, const void *buf, size_t count);

从更高级编程语言的角度来看,比如C语言,这个系统调用的使用可能是如下形式的代码:

const char string[12] = "Azeria Labs\n";
write(1, string, sizeof(string)); //Here sizeof(string) is 13

通过观察这个函数原型,我们可以知道这个系统调用函数需要如下参数:

  • fd-1表示STDOUT
  • buf-指向字符串string的指针
  • count-要写入的字节数->13
  • write函数的系统调用号->0x4
    对于前3个参数,我们可以使用R0,R1和R2寄存器。对于系统调用号,我们可以使用R7寄存器,即把0x4传给R7。
mov ro, #1		@fd 1 = STDOUT
ldr r1, string	@loading the string from memory to R1
mov r2, #13		@write 13 bytes to STDOUT
mov r7, #4		@write()函数的系统调用号 = 0x4
svc #0

根据上面的这一小段代码,一个完整的ARM汇编程序看起来可能如下:

.data
string: .asciz "Azeria Labs\n"	@.asciz adds a null-byte to the end of the string
after_string: .set size_of_string, after_string - string

.text
.global _start

_start:
	mov r0, #1				@ STDOUT
	ldr r1, addr_of_string	@ memory address of string
	mov r2, #size_of_string	@ size of string
	mov r7, $4				@ write syscall
	swi #0					@ invoke syscall
	
_exit:
	mov r7, #1				@ eixt syscall
	swi #0					@ invoke syscall
	
addr_of_string: .word string

在上面程序的.data数据段中,我们通过string的结束地址(after_string)减去开始地址(string),得到了字符串的大小(size_of_string)。如果我们自己手动计算字符串的大小,并直接将计算结果传送至R2寄存器,那么.data中的计算过程就不是必需的了。另外,为了退出我们的程序,我们使用了系统调用号1号来调用exit()函数。

将上面这段程序保存为write.s文件,并编译和执行:

azeria@labs:~$ as write.s -o write.o && ld write.o -o wirte
azeria@labs:~$ ./write
Azeria Labs

很酷,从输出结果来看,我们的程序正确执行了。现在我们已经了解了编写ARM汇编程序的过程,接下来就更进一步关注其他细节,使用ARM汇编来开发出我们的第一个简单shellcode吧!

1. 追踪系统调用

第一个简单shellcode的例子,我们将会把下面的简单函数转变为ARM汇编:

#include <stdio.h>

void main()
{
	system("/bin/sh");
}

第一步是找出这个简单函数使用了哪些系统调用,并且弄清楚这些系统调用函数需要哪些参数。通过’strace’这个工具,我们可以在系统内核中监测到程序的系统调用。

把上面的代码保存为system.c文件,并完成编译,之后再使用’strace’的命令。

azeria@labs:~$ gcc system.c -o system
azeria@labs:~$ strace -h
-f -- follow forks, -ff -- with output into separate files
-v -- verbose mode: print unabbreviated argv, stat, termio[s], etc. args
--- snip --
azeria@labs:~$ starce -f -v system
--- snip --
[pid 4575] execve("/bin/sh", ["/bin/sh"], ["MAIL=/var/mail/pi", "SSH_CLIENT=192.168.200.1 42616 2"..., "USER=pi", "SHLVL=1", "OLDPWD=/home/azeria", "HOME=/home/azeria", "XDG_SESSION_COOKIE=34069147acf8a"..., "SSH_TTY=/dev/pts/1", "LOGNAME=pi", "_=/usr/bin/strace", "TERM=xterm", "PATH=/usr/local/sbin:/usr/local/"..., "LANG=en_US.UTF-8", "LS_COLORS=rs=0:di=01;34:ln=01;36"..., "SHELL=/bin/bash", "EGG=AAAAAAAAAAAAAAAAAAAAAAAAAAAA"..., "LC_ALL=en_US.UTF-8", "PWD=/home/azeria/", "SSH_CONNECTION=192.168.200.1 426"...]) = 0
--- snip --
[pid 4575] write(2, "$ ", 2$) = 2
[pid 4575] read(0, exit
--- snip --
exit_group(0) = ?
+++ exited with 0 +++

上面的输出说明了,程序使用了execve()这个系统调用。

2.系统调用号及其参数

弄清楚使用了哪些系统调用后,下一步就是找execve()对应的系统调用号,并且弄清楚需要哪些参数

Well, this essay is still on construction…
If you find it helpful, maybe you can buy me a cup of Cola.
文章将会继续更新,如果本文对你有帮助,不如请我一杯可乐 :jack_lantern:
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Shellcode是一种在操作系统内核中执行的二进制代码。它通常用于漏洞利用和恶意软件中。编写shellcode需要了解汇编语言和操作系统内部工作原理。步骤包括确定目标系统平台、编写汇编代码、使用汇编器将代码转换为机器码并去除不必要的部分。最后,通过十六进制编辑器将机器码转换为可执行的shellcode。 ### 回答2: Shellcode是计算机安全领域中的一个术语,指的是一段精心编写的机器码,用于向远程服务器发送攻击代码。Shellcode由语言所写的基本单元构成,可以看作是一些机器指令的序列,这些指令可以被攻击者从攻击文件中提取并加载到内存中来执行。 在编写Shellcode时,攻击者需要考虑以下几个因素: 1. 选择合适的语言:通常情况下,攻击者使用较低级别的语言编写Shellcode,比如汇编、C语言等,具有较高的攻击性和灵活性。高级语言可被编译成较低级别的机器码,但由于其过于复杂,会使攻击者的目标变得更加难以实现。 2. 确定攻击目标:在编写Shellcode时,攻击者需要明确自己的目标是什么,因为Shellcode的内容和指定的操作系统有关。如果攻击者要攻击Linux操作系统,则需要编写适用于Linux的Shellcode。 3. 了解系统调用:Shellcode本质上是攻击代码,需要与操作系统进行交互才能实现攻击功能。攻击者需要深入了解目标操作系统的系统调用,以便编写能够集成到Shellcode中的函数。 4. 隐藏自己的Shellcode:攻击者的Shellcode需要能够在执行攻击时隐藏自己的存在,防止被服务器的安全防御发现并阻止其执行。因此,编写Shellcode的过程中,添加一些默默执行的优化代码通常会使Shellcode更难被检测出来。 综上所述,Shellcode编写需要仔细考虑许多细节和攻击原理,是一个非常复杂和挑战性的工作。需要具有深入的技术知识、大量的实操经验和强烈的创造力来成功实现。因此,对于那些想要保护自己计算机系统的安全和隐私的用户来说,应该注意提高自己的网络安全意识,以免成为黑客攻击的目标。 ### 回答3: Shellcode是一种机器可执行代码,通常用于利用软件或系统漏洞,实现攻击者的目的。它们是用汇编语言编写的小段程序,目的是在攻击者控制的环境中提供一个命令行接口。Shellcode是计算机安全方面的一个重要组成部分,它们用来攻击网络或操作系统,并实现攻击者的目标,比如窃取敏感信息、获得系统管理员权限等。 Shellcode编写过程需要以下步骤: 1. 选择正确的汇编代码和指令:攻击者需要使用特定的汇编代码和指令,以便在目标系统上实现其目的。这些代码和指令通常是最简单的和最小的,以便在运行时不引起注意,并且可以在目标系统上尽可能快地执行。 2. 手写代码或使用自动化工具:在编写Shellcode时,可以手动编写代码,也可以利用自动化工具(如Metasploit)来生成代码。无论哪种方法都需要足够的经验和技术。 3. 调试和测试:成功编写shellcode需要进行检查,确保其在目标环境中能够准确执行所需的操作。一些测试工具和脚本可以用于对Shellcode进行测试,以保证其正确性和稳定性。 4. 压缩和编码:为了使Shellcode足够小,可以使用压缩和编码技术来减小代码体积。这有助于减小Shellcode在内存中的占用空间,并增加攻击成功的几率。 总的来说,Shellcode编写需要具备丰富的汇编语言编程知识和安全实践经验,同时需要掌握各种测试和修复技术。攻击者使用Shellcode来实施危害,所以安全团队需要相应地采取措施,防范和识别Shellcode攻击。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值