Partager via


Windows Security and how it affects running generated code

Here I described how VFP generates executable code and runs it for early and late binding COM clients and implementing COM interfaces. However, there is an important issue with generating and running executable code in the same process.

A computer has a processor (or a few) that fetches instructions from memory and executes them. Some of the instructions refer to other areas of memory. The Operating System loads the memory initially. Various areas of memory are used for different purposes:

  • Code (the program)
  • Stack (for local variables and return addresses)
  • Heap
  • Initialized Data (like constants)
  • Uninitialized Data (like an uninitialized static array)

It gets a little more complicated with multiple DLLs each of which may have its own code/data sections, and threads, each of which will have its own stack, but we’ll ignore this for now.

Certain areas of memory will remain constant for the process lifetime, such as the code. Other areas will change dynamically, such as the stack and the heap. The CPU will fetch instructions only through the Instruction Pointer.

The NX feature prevents code from running from non-code marked pages of memory, so if the IP points to an NX area of memory, an exception is thrown. Certain viruses and worms cause execution of code from the stack or heap, so this prevents certain kinds of malicious attacks.

The generated machine code for VFP COM servers and implemented interfaces lives in heap memory, so the IP will be fetching instructions from the heap.

(This feature is called NX (No Execute) on AMD machines, XD (Execute Disable) by Intel, and DEP (Data Execution Prevention) on Windows Server 2003 and I believe, WinXP SP2. Does anyone see this feature on WinXPSP2?)

To test this, I installed VFP8 and VFP9 (RTM) on an AMD64 machine running Windows Server 2003 SP1. Right click on My Computer, select Properties->Advanced->Performance Settings->Data Execution Prevention->Turn on DEP for all programs and services except those I select:

Then I built my universal COM server below (which is used to run my website, by the way) and tested it. On a WinXP machine, the code runs fine, with both early and late bound calls succeeding. If I use CREATEOBJECTEX to create an object which will be accessed via early binding, I can use GETINTERFACE to get the IDispatch interface to call it via late binding. Conversely, an object that is created via CREATEOBJECT will be accessed by late binding, but I can use GETINTERFACE to get its early bound interface

On the AMD machine with DEP enabled, VFP8 caused an Access Violation (see What is a C0000005 crash?) for the early bound calls, but VFP9 worked just fine. Why? Because generated executable code in VFP9 now gets put into a memory page marked with the PAGE_EXECUTE_READWRITE flag.

If your application invites any other 3rd party code into the process space (such as ActiveX controls, COM servers, DECLARE DLL, Load/Call or a SET LIBRARY TO) and you are getting unexpected errors, it may be due to this NX feature.

These articles go into a lot more detail: https://en.wikipedia.org/wiki/NX_bit

A detailed description of the Data Execution Prevention (DEP) feature in Windows XP Service Pack 2, Windows XP Tablet PC Edition 2005, and Windows Server 2003

* A universal COM server

TEXT TO tempvar NOSHOW

DEFINE CLASS c1 as session olepublic

      proc MyDoCmd(cCmd as string,p2 as Variant,p3 as Variant,p4 as Variant,p5 as Variant) helpstring 'Execute a command'

            &cCmd

      proc MyEval(cExpr as string,p2 as Variant,p3 as Variant,p4 as Variant,p5 as Variant) helpstring 'Evaluate an expression'

            RETURN &cExpr

ENDDEFINE

ENDTEXT

STRTOFILE(tempvar,"c1.prg")

IF FILE("t1.dll") && unregister prior one

      DECLARE integer DllUnregisterServer IN t1.dll

      ?"Unregister",DllUnregisterServer()

      CLEAR DLLS

ENDIF

IF !FILE("t1.pjx") && the GUIDS are stored in the project, so to keep the same GUID, use the same PJX

      BUILD PROJECT t1 FROM c1

ENDIF

BUILD MTDLL t1 FROM t1

*Test it:

ox=CREATEOBJECT("t1.c1") && create late bound object (IDispatch interface)

?ox.MyEval("_vfp.servername")

?"Late bound object with late bound call ",ox.MyEval("'EarlyLate='+sys(2334)")

oEarly = GETINTERFACE(ox,"IC1")

?"Late bound object with early bound call ",oEarly.MyEval("'EarlyLate='+sys(2334)")

CLEAR ALL

oEarly=CREATEOBJECTEX("t1.c1","","") && create early bound object (IC1 interface)

?"Early bound object with early bound call",oEarly.MyEval("'EarlyLate='+sys(2334)")

oDisp=GETINTERFACE(oEarly,"IDispatch")

?"Early bound object with late bound call",oDisp.MyEval("'EarlyLate='+sys(2334)")

Comments

  • Anonymous
    November 09, 2005
    The comment has been removed
  • Anonymous
    November 09, 2005
    DEP in XP SP2 is found in Control Panel - System Properties - Advanced Tab - Performance. No idea why DEP is under Performance when it doesn't effect the performance.
  • Anonymous
    November 09, 2005
    OK Calvin, I'll bite: what are p2...p5 for?

    AndyD 8-)#
  • Anonymous
    November 09, 2005
    >OK Calvin, I'll bite: what are p2...p5 for?
    OK - got it <doh>

    AndyD 8-)#
  • Anonymous
    May 16, 2006
    When I posted this Sample program to create multiple threads, I knew the inevitable follow-up question...
  • Anonymous
    June 16, 2006
    A customer asked
    1) Is it better to set it to 0 (Auto) or some &quot;larger&quot; number, such as -16 (1024kb).&amp;nbsp;...