The essence of shellcode is actually a piece of assembly code that can run independently. It does not have any file structure, it does not rely on any compilation environment, and cannot be double-clicked to run like an exe. I won’t go into details about the specific shellcode here. You can search for relevant information on Baidu yourself.
Because I have done a lot of penetration in the past six months, the shellcode used is also generated by CS or MSF. However, the shellcode automatically generated by the tool is dead after all, and there is no way to expand the function by yourself. Another example is that you know a new Vulnerability, but the given vulnerability exploitation POC can only pop up a calculator. If you want to realize the function you want, you must write the shellcode yourself. Therefore, it is particularly important to master the shellcode writing technology, and the same is true for shellcode on buffer overflows and worms. essential and important role.
If you want to write shellcode yourself, you must know the most important knowledge points in writing shellcode. Below I will put what needs to be solved in the form of questions. Let’s list a few points:
1. Shellcode is also a program. If it wants to run normally, it also needs to use various data (such as global strings, etc.), but we all know the access of global variables. They are all fixed addresses (hard-coded, that is, hard-coded and cannot be changed), and our shellcode may be arranged to run anywhere in any program. How do we ensure that the shellcode is still accurate in this case? of access to the required data?
2. Shellcode also runs in the operating system and must call some system APIs. How can we get the addresses of these APIs?
3. If the API we need is not imported into the program run by Shellcode and we have to use it, what should we do?
Due to space reasons, you can find the answers to these questions from the three articles "Introduction to Windows Platform Shellcode Development 1, 2, and 3" translated by FB senior author Rabbit_Run. Basically, what you need to know about writing shellcode All prerequisite knowledge is involved, so you can ask questions to find answers.
After knowing these prerequisite knowledge, it will still be troublesome and difficult for you to write shellcode purely by hand. Writing native js code is far less convenient and faster than using js frameworks such as jquery. Therefore, we need to establish a shellcode programming framework that facilitates writing custom functions. There are many such shellcode programming frameworks on the Internet, such as the one that TK used to open source, the two that Teacher OneBugMan wrote before, etc. I wrote a shellcode programming framework when I was studying in school, but I can’t find it anymore, and I During this period of penetration, I forgot a lot of my previous knowledge, so I wrote a shellcode framework and practiced it based on the course taught by OneBugMan (if you are interested, you can support the teacher’s open class, which is very good).
1. Turn off SDL checking when creating the project
2. Properties->C/C->Code Generation->Runtime Library- >Multi-threading (/MT) If it is debug, set it to MTD
3. Properties->General->Platform Toolset->Set to Visual Studio 2015- Windows XP (v140_xp), if If not, you can install the corresponding XP-compatible component
4. Properties->C/C->Code Generation->Disable Security Check GS
5. Close the generated manifest Properties->Linker->Manifest File->Generate Manifest Select No
The following introduces the role of each file:
1.api.h——>Used by shellcode Function pointers to system functions, and a structure containing these function pointers.
2.header.h——>Function declaration of header files and custom function functions.
3.0.entry.cpp——> Entry of the framework, create the final generated shellcode file.
4.a.start.cpp——> Marks the starting position of the shellcode, which is used to perform pre-operations before writing the shellcode and initialize the functions used
5.b.work .cpp——>Execution of shellcode, implementation of specific functions
6.z.end.cpp——>Mark the end position of shellcode
The reason why the files are named in this way is because they are arranged first with numbers and then with letters. The files in the project are named this way so that when they are compiled and generated exe, they are compiled and generated in the order shown below, so The order of functions in the generated exe code segment is also arranged according to the order of functions in the file below, so that we can easily calculate the size of the Shellcode (ShellcodeEnd in z.end minus ShellcodeStart in a.start. is the size of the shellcode size), thereby writing the shellcode into the final generated file.
The function names must be consistent, otherwise the entry point cannot be found. Because we have modified the entry point, some C-form functions cannot be used directly and must be changed to dynamic calling forms. There is also the calculation of the size of the shellcode we write.
## Configuration preparation: dynamically obtain the matrix of kernel32.dll and use the knowledge of PE file format to obtain the GetProcAddress function address, and further obtain the LoadLibrary address. With these pre-steps, we can obtain the address of any other API, and then realize various functions of our shellcode Function
功能 功能 4 Get Kernel32.dll base address
The following is the GetProcadDress function address. []="xxxxx"; This will write the string to the rdata section in the program and turn it into an absolute address. Using absolute addresses will cause shellcode execution errors.
FARPROC getProcAddress(HMODULE hModuleBase) { FARPROC pRet = NULL; PIMAGE_DOS_HEADER lpDosHeader; PIMAGE_NT_HEADERS32 lpNtHeaders; PIMAGE_EXPORT_DIRECTORY lpExports; PWORD lpwOrd; PDWORD lpdwFunName; PDWORD lpdwFunAddr; DWORD dwLoop; lpDosHeader = (PIMAGE_DOS_HEADER)hModuleBase; lpNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)hModuleBase + lpDosHeader->e_lfanew); if (!lpNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size) { return pRet; } if (!lpNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress) { return pRet; } lpExports = (PIMAGE_EXPORT_DIRECTORY)((DWORD)hModuleBase + (DWORD)lpNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); if (!lpExports->NumberOfNames) { return pRet; } lpdwFunName = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNames); lpwOrd = (PWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNameOrdinals); lpdwFunAddr = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfFunctions); for (dwLoop = 0; dwLoop NumberOfNames - 1; dwLoop++) { char * pszFunction = (char*)(lpdwFunName[dwLoop] + (DWORD)hModuleBase); if (pszFunction[0] == 'G' &&pszFunction[1] == 'e' &&pszFunction[2] == 't' &&pszFunction[3] == 'P' &&pszFunction[4] == 'r' &&pszFunction[5] == 'o' &&pszFunction[6] == 'c' &&pszFunction[7] == 'A' &&pszFunction[8] == 'd' &&pszFunction[9] == 'd' &&pszFunction[10] == 'r' &&pszFunction[11] == 'e' &&pszFunction[12] == 's' &&pszFunction[13] == 's') { pRet = (FARPROC)(lpdwFunAddr[lpwOrd[dwLoop]] + (DWORD)hModuleBase); break; } } return pRet; }
Figure 5 initialization function
When the specific function is implemented, just remember to declare the string used in the function according to the array of the array in the figure below. The box prompts hello, and then creates a 1.txt document.
图7 生成的shellcode
例如我们想让dbgview.exe运行我们生成的shellcode
第一步:我们使用lordPE查看dbgview.exe程序的入口点。
然后使用010Editor打开dbgview.exe找到入口点位置,从入口点位置删除掉我们需要替换进去的shellcode大小的字节,然后替换成我们的shellcode,保存运行即可执行我们的shellcode。
源码A
#include <windows.h> #include <stdio.h> #pragma comment(linker, "/section:.data,RWE") unsigned char shellcode[] = "\xfc\xe8\x89\x00\x00\x00\x60\x89........在这里写入shellcode"; void main() { __asm { mov eax, offset shellcode jmp eax } }</stdio.h></windows.h>
源码B
// dllmain.cpp : 定义 DLL 应用程序的入口点。 #include "stdafx.h" #include<windows.h> #include<iostream> //data段可读写 #pragma comment(linker, "/section:.data,RWE") HANDLE My_hThread = NULL; //void(*ptrceshi)() = NULL; typedef void(__stdcall *CODE) (); unsigned char shellcode[] = "x00\x49\xbe\x77\x69\x6e\x.........在这里填入shellcode"; DWORD WINAPI ceshi(LPVOID pParameter) { PVOID p = NULL; if ((p = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)) == NULL) { } if (!(memcpy(p, shellcode, sizeof(shellcode)))) { } CODE code = (CODE)p; code(); return 0; } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: My_hThread = ::CreateThread(NULL, 0, &ceshi, 0, 0, 0);//新建线程 case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }</iostream></windows.h>
我们如果不想复制出来shellcode运行我们也可以直接运行我们生成的sc.bin,不过得需要自己写一个加载器。
代码如下
#include<stdio.h> #include<stdlib.h> #include<windows.h> int main(int argc, char* argv[]) { HANDLE hFile = CreateFileA(argv[1], GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { printf("Open File Error!%d\n", GetLastError()); return -1; } DWORD dwSize; dwSize = GetFileSize(hFile, NULL); LPVOID lpAddress = VirtualAlloc(NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); if (lpAddress == NULL) { printf("VirtualAlloc error:%d\n", GetLastError()); CloseHandle(hFile); return -1; } DWORD dwRead; ReadFile(hFile, lpAddress, dwSize, &dwRead, 0); __asm { call lpAddress; } _flushall(); system("pause"); return 0; }</windows.h></stdlib.h></stdio.h>
写好加载器编译生成exe以后我们只需要把sc.bin文件拖到生成的exe上就可以自动运行我们的shellcode,或者使用命令行 >某某.exe sc.bin
如果大家不想自己写加载器也可以使用别人写好的工具,我以前在看雪上发现一个小工具也挺好用的,这个小工具可以把shellcode转换成字符串形式也可以将字符串形式的shellcode转换成bin文件形式然后再加载运行shellcode。
原帖子链接工具链接
我们可以使用这个工具执行生成的sc.bin文件,只需将该文件拖入工具中,然后点击转换为字符串形式
这和我们在010Editor中看到的是一样的,相当于帮我们自动复制出来了。
因为我们生成的sc.bin文件是可以直接执行的,所以就不需要点击转成Bin文件了,所以我们直接点击执行shellcode,弹出了Messagebox窗口,我们点击确定后,又创建了1.txt文档。
至此我们就可以根据框架举一反三,编写我们自己功能的shellcode了。
The above is the detailed content of What does shellcode mean?. For more information, please follow other related articles on the PHP Chinese website!