home *** CD-ROM | disk | FTP | other *** search
/ Collection of Hack-Phreak Scene Programs / cleanhpvac.zip / cleanhpvac / WINVCOLL.ZIP / WINTUTOR.ZIP / WININF01.TXT next >
Text File  |  1995-04-26  |  25KB  |  560 lines

  1.  
  2.         
  3.         Windows Executable Infection 
  4.                by
  5.           Qark and Quantum [VLAD]
  6.  
  7.  
  8.  This document attempts to explain technically NewExe infection for the
  9.  virus writer.  This isn't for the novice coder and you'll need to
  10.  understand DOS EXE infection before being able to use any of it.
  11.  
  12.  The infection described in detail here is the same as used by the
  13.  accompanying WinSurfer virus, there are other methods that can be done 
  14.  but they aren't our concern (it's up to you to develop new code!)
  15.  
  16.  You will want a copy of the New Exe header information which is available
  17.  in Ralf Browns interrupt listings under Int21h AH=4Bh, a copy of which is
  18.  included at the end of this article.
  19.  
  20.  This is a map of what we are trying to do:
  21.  
  22.        Before infection                  After Infection
  23.  
  24.      0h---------------------           0h---------------------   
  25.        |                   |             |                   |
  26.        |  DOS EXE header   |             |  DOS EXE header   |
  27.        |                   |             |                   |
  28.        ---------------------             ---------------------
  29.        |                   |             |                   |
  30.        |  DOS Code         |             |  DOS Code         |
  31.        |                   |             |                   |
  32.        |                   |         3F8h---------------------
  33.    400h---------------------             |                   |
  34.        |                   | <- Move     |  New EXE Header   |
  35.        |  New EXE Header   |     this    |  and some tables  |
  36.        |  and some tables  |      up     |                   |
  37.        |                   |             | ----------------- |
  38.        | ----------------- |             |   Segment         |
  39.        |   Segment         |             |   Table           |
  40.        |   Table           |             |                   |
  41.        |                   |             |                   |
  42.        |                   |   Insert -> | ----------------- | Virus segment
  43.        | ----------------- |    this     | ----------------- | entry
  44.        |   Misc Other      |             |   Misc Other      |
  45.        |   Tables          |             |   Tables          |
  46.        |                   |             |                   |
  47.        x                   x             x                   x
  48.        x                   x             x                   x
  49.        x                   x             x                   x
  50.  CS:IP ---------------------             ---------------------
  51.        |                   |             |                   |
  52.        |   Windows Code    |             |   Windows Code    |
  53.        |   and Misc        |             |   and Misc        |
  54.        |   Segments        |             |   Segments        |
  55.        |                   |             |                   |
  56.    End ---------------------       CS:IP ---------------------
  57.                      | Virus segment     |
  58.                  Append all  | with code etc     |
  59.                 this ->  |                   |
  60.                      --------------------- Virus
  61.                      |                   | Relocation
  62.                      End --------------------- Entry
  63.  
  64.  
  65.  Infection Theory:
  66.  -----------------
  67.  Test the EXE to make sure its windows and that the NE is at offset 1024.
  68.  Reduce the NE pointer at 3ch by eight.
  69.  Reduce DOS SP by eight.
  70.  Any NE offsets which are larger than the segment table offset must be
  71.   increased by eight.
  72.  Increment the segment counter.
  73.  Save the CS:IP.
  74.  Point the CS:IP to the new segment entry
  75.  Calculate the position of the end of the segment table, move all data up to
  76.   and including the segment table up by eight bytes.
  77.  Add another segment entry
  78.  Write the virus to the end of the file
  79.  Write relocation entry to the end of the file.
  80.  ---
  81.  
  82.  That is a very basic overview of what is wanted.
  83.  
  84.  The main idea of it all is moving the NE header forward to add a new
  85.  segment table entry, and changing the CS:IP to point to it.
  86.  
  87.  We'll go through all the stages step by step.
  88.  
  89.  Testing for a Windows executable.
  90.  ---------------------------------
  91.  
  92.  A NewEXE file always begins with a DOS header and some DOS executable
  93.  code.  With a windows executable:
  94.     The word at offset 0 is 'MZ'.
  95.     The word at offset 18h is 40h or greater.
  96.  assuming that these conditions are met then we also want the pointer to the
  97.  NE header (3ch) to be equal to 400h.  The reason for this is that room is 
  98.  needed to move the NE header forward by 8.
  99.  
  100.  Rewriting the DOS header.
  101.  -------------------------
  102.  
  103.  Before rewriting the DOS header, reduce the DOS SP field (10h) by 8, because
  104.  if it was pointing to the end of the DOS code section it would point to
  105.  the start of the NE header. (No real reason for doing it really)
  106.  The NE header pointer at 3ch must be reduced by 8, because we intend on
  107.  moving most of the NE header forward to that position.
  108.  
  109.  Modifications to the NE header.
  110.  -------------------------------
  111.  
  112.  From this point onwards we are referring to the offsets in relation to the
  113.  NE header.
  114.  
  115.  The pointer to the segment table is a word at 22h.  If any of the pointers
  116.  at 4,24h,26h,28h,2ah are larger than [22h], then increase that
  117.  pointer by eight.  The reason for this is that since we are inserting a
  118.  new segment table entry, all tables behind the segment table will be
  119.  eight bytes further from the start of the header.
  120.  
  121.  The segment counter is a word at offset 1ch.  Increment it by one because
  122.  since we are adding another segment, we need to indicate this in the
  123.  header.
  124.  
  125.  At offset 14h is the dword pointer to the program entry point, CS:IP
  126.  where the CS is actually the segment table entry number.  Save this pointer
  127.  for use in your relocation entry (that part will be explained in detail
  128.  later on).  Now point the CS:IP instead to the virus segment.  Do this
  129.  by writing the necessary starting IP value to 14h (offset of the entry into
  130.  your segment) and the segment counter value into 16h (since the virus
  131.  segment is the last in the segment table it will be equal to the segment
  132.  counter value which we have incremented).
  133.  
  134.  You can work this out for yourself, but what you need to do now is move
  135.  the entire NE header, upto and including the segment table, but not
  136.  the data behind it, forward by eight bytes.  Heres an equation on
  137.  calculating how much to move:
  138.     ((segmentcounter-1)*8)+word ptr [22h]
  139.  
  140.  Directly after this position is where to insert the new virus segment entry.
  141.  
  142.  The Virus Segment Entry.
  143.  ------------------------
  144.  
  145.  Format of new executable segment table record:
  146.   00h    WORD    offset in file (shift left by alignment shift for byte offs)
  147.   02h    WORD    length of image in file (0000h = 64K)
  148.   04h    WORD    segment attributes (see below)
  149.   06h    WORD    number of bytes to allocate for segment (0000h = 64K)
  150.  
  151.  Offsets 2 and 6 in the segment record are just the virus size. Offset
  152.  4 is the segment attribute, we use 180h, which makes the segment executable
  153.  with relocations.
  154.  
  155.  Calculating offset 0 is more difficult.  It is necessary to get the file
  156.  length and shift it right by the alignment shift value which was saved
  157.  earlier.  I wouldn't know how to do a dword shift so I convert it to a
  158.  division.
  159.  
  160.  File length into DX:AX :
  161.     mov     ax,4202h
  162.     xor     cx,cx
  163.     cwd
  164.     int     21h
  165.  Shift right DX:AX by alignment shift:
  166.     mov     cl,byte ptr alignment_shift
  167.     push    bx
  168.     mov     bx,1
  169.     shl     bx,cl
  170.     mov     cx,bx
  171.     pop     bx
  172.     div     cx
  173.      AX=required offset 0 value
  174.  
  175.  Write this eight byte table behind all the moved header.
  176.  
  177.  Writing the Virus.
  178.  ------------------
  179.  
  180.  Lseek to the end of the executable and write the virus.
  181.  
  182.  Writing the Relocation Entry.
  183.  -----------------------------
  184.  
  185.  You need a relocation entry so that you can return control to the host
  186.  after the virus has done its dirty work.  The relocation entries follow
  187.  the segment itself.
  188.  
  189.  Format of relocation entry:
  190.  Offset  Size    Description
  191.   00     WORD    Number of relocation entries
  192.      Offset  Size    Description
  193.       00     BYTE    relocation type
  194.       01     BYTE    relocation flags
  195.       02     WORD    offset within segment
  196.       04     WORD    target address segment
  197.       06     WORD    target address offset
  198.  
  199.  
  200.  The first word will be 1 because only 1 relocation entry.
  201.  First byte is 3 to signal a 32bit pointer (a far jump).
  202.  Second byte is 4 to signal additive relocation. (doesn't work without this)
  203.  Word at offset 2 is the offset of the dword after the 'far jump' opcode.
  204.  Word at offset 4 is the CS of the original host CS:IP
  205.  Word at offset 6 is the IP of the original host CS:IP
  206.  
  207.  To put all this into code:
  208.  
  209.         db      0eah    ;Opcode of JMP FAR PTR
  210.     ret_ip      dw      0
  211.         dw      0ffffh
  212.  
  213.     relocation  dw      1       ;One relocation entry
  214.         db      3       ;32bit pointer relocation
  215.         db      4       ;Additive relocation
  216.         dw      offset ret_ip   ;Offset of the relocation in segment
  217.     hostcs      dw      0       ;CS:IP of place we want our relocation to
  218.     hostip      dw      0       ; point.
  219.  
  220.  Note: the relocation address data (ret_ip) _must_ be FFFF:0000 as above
  221.        because windows treats it as a linked list.  If you don't understand
  222.        don't worry, just do it!  It won't work otherwise.  It is important
  223.        that before writing the virus body to the executable that you set
  224.        this address to FFFF:0000 so that executable will be setup correctly.
  225.  
  226.  Write the relocation entry to the end of the file.
  227.  
  228.  Windows allows a standard Int 21h call just fine so all file manipulation
  229.  is as simple as ever, with a few minor changes.
  230.  
  231.  
  232.  Writting TSR/ISR's under Windows.
  233.  ---------------------------------
  234.  
  235.  Windows is a protected mode environment and thus to set a vector under it we
  236.  must manipulate the various tables that protected mode requires to be
  237.  updated.  Of these tables one is of interest to us.  The "Interrupt
  238.  Descriptor Table" (IDT). The IDT holds all the Protected Mode interrupt
  239.  vectors that are in the "Interrupt Vector Table" (IVT) - if they are
  240.  supported - and some more.  This is all well and good - all we have to do is
  241.  set a vector in the IDT to point to our code like we do with dos and the 
  242.  IVT but - there are complications.
  243.  
  244.  V86 mode.
  245.  ---------
  246.  
  247.  When Windows is running in 386 enhanced mode the processor is locked into
  248.  v86 mode.  In v86 mode each application has its own 1mb memory block.  All
  249.  EMS/XMS is locked away from the application and (usually) each process has
  250.  its own IDT/IVT.  The idea being to allow the application to think it is
  251.  alone on the system.  Any attempt to access any other tasks that may be
  252.  running is a "violation of system integrity" and will cause an exception.
  253.  This is no good for a virus and by now you're thinking we're screwed but
  254.  windows solves the problem for us.  Windows has but one IDT and of course
  255.  only one IVT to shadow it and thus if we are to a hook a vector in the IDT
  256.  it will be reflected in every task.  (Ever seen that OS/2 warp advert where
  257.  they say "... and true multi-tasking" - well this simply means that OS/2
  258.  warp holds a seperate IDT for every task under v86 mode.)
  259.  
  260.  Setting the Vector.
  261.  -------------------
  262.  
  263.  So let's get this straight.. the IVT is the one at 0:0 that we all know and
  264.  love and the IDT is a protected mode/v86 shadow of it.  So you're most
  265.  probably asking "why can't we just hook the IVT and let windows reflect it
  266.  into the IDT?" - well there's 2 reasons: firstly windows doesn't "reflect"
  267.  the IVT as we would like to think, actually it copies the IVT into the IDT
  268.  on startup then replaces certain routines with "protected mode call
  269.  structures" which call the real mode routines and some routines it doesn't
  270.  even do this, secondly trying to access the IVT directly is a big nono and
  271.  will cause an exception error.
  272.  
  273.  Can we use DOS ?  well yes, you can.. most dos functions have been reflected
  274.  up into windows although windows is supposed to be trying to get rid of
  275.  them. So you can call int 21,25/35 to set the vector but remember that this
  276.  will be setting a vector in the GVT and only at the discretion of windows.
  277.  
  278.  A better way: use DPMI.  Windows is NOT your host under protected mode or
  279.  even v86 mode as microsoft would have you believe.  Windows has an
  280.  overlooker that provides windows time-slicing and exception abilities.  In
  281.  fact all windows does is act as a mediator to DPMI and basically slow things
  282.  down.  So how do we use DPMI to set the vector ?  well.. by using functions
  283.  0204h/0205h  - with the vector in BL  - of the DPMI provider int 31.  This
  284.  way windows has no say over what goes in its IDT and thus we have a LOT of
  285.  control.
  286.  
  287.  Writing the Interrupt.
  288.  ----------------------
  289.  
  290.  This is perhaps one of the easier things to do - once you understand the
  291.  principles.  In protected mode/v86 mode your code segment is supposed to be
  292.  read only and thus you're supposed to have a code, stack and data segment.
  293.  Everyone knows this is bad so what we need is a way to write to the code
  294.  segment. You won't find one - writing to the code segment just isn't
  295.  possible.  BUT - if we were to have a data segment that pointed to the same
  296.  code segment then we could do it. (actually we don't have any segments in
  297.  protect mode.. we have a thing called a "selector" which gives access
  298.  parameters to areas of memory.)
  299.  
  300.  To get an "alias selector" to the code segment we can use another DPMI
  301.  function and bypass windows altogether: function ax=000ah with the code
  302.  selector in bx of int 31h, it returns a read/writeable alias selector to
  303.  the code segment in ax.
  304.  
  305.  So now Protected mode isn't protected and we're free to write to the code
  306.  segment. One problem arises from this idea.  Qark wanted to put this code
  307.  to generate the alias in the loader part of the virus and store it in the
  308.  code segment for the interrupt routine.  This seemed fine until I found out
  309.  that windows likes to move segments around.  One minute your code might be
  310.  at one address the next it may be at another.  Thus you should always
  311.  regenerate the alias on every execution of the ISR or lock down the segment
  312.  - we chose against this last approach as it can cause exception errors from
  313.  windows (DPMI has no problems with this strategy.)
  314.  
  315.  For a detailed look at DPMI consult Ralf Brown's interrupt listing under
  316.  int 31h.  These functions haven't been included in this text.
  317.  
  318.  Staying Resident.
  319.  -----------------
  320.  
  321.  Finally: don't go resident off something that can be closed.  If you go
  322.  resident off something like mine sweeper or something and the user closes
  323.  the application, your code segment will be deleted and removed from the IDT
  324.  and will point to an empty segment.  This will cause windows to hang.
  325.  Solution: look for a process that will never be closed, direct action 
  326.  infect this as soon as you find it and only ever go resident off it.
  327.  
  328.  In the 'system.ini' file in your windows directory, is a variable 'shell='
  329.  that points to a file that is never closed.  Mostly it will be
  330.  'progman.exe' but sometimes other programs are used, so read it in and
  331.  infect it.
  332.  
  333.  To find the path of the windows directory, search the environment for a
  334.  variable called 'windir=' (yes, lowercase!).  On entry to any windows
  335.  executable ES will point to the PSP (or something functionally similar).
  336.  The word at 2ch is the segment (selector) of the environment segment.
  337.  Scan through this as you would within a DOS application.  We didn't discover
  338.  this through any documentation, but when Quantum accidentally typed 'set'
  339.  from within a DOS window.
  340.  
  341.  Facts and Possible Techniques.
  342.  ------------------------------
  343.  
  344.  At offset 0eh in the New Executable header is the 'auto data segment index'.
  345.  This is another segment table index, which will be automatically in DS
  346.  on execution entry.  Although this is pure guesswork, if you set that
  347.  'auto data segment' to the same segment as CS, (ie in every way the same
  348.  except the segment attributes) then you could write to your CS without
  349.  using DPMI and would also open the gateway to polymorphic windows viruses.
  350.  (Polymorphism would be pretty useless if you had to put a DPMI call before
  351.  you could write to your code segment.)
  352.  
  353.  Mark Ludwig's 'Windows Virus #2' uses a different form of infection in that
  354.  it extends the length of the code segment as indicated in the segment
  355.  table, and moves the entire host program starting from the end of the file
  356.  back by virus length until the end of the code segment is reached, at which
  357.  point the virus is written.  The IP is then repointed to the virus.
  358.  There are some advantages to this, such as no need to add relocation entries
  359.  because the jmp is intra-segment but this is greatly outweighed by the
  360.  fact that moving an entire file is very slow and a much larger number of
  361.  pointers in the header need to be modified to account for the change.
  362.  
  363.  There are DPMI calls for allocating memory (Int 31h ax=501h) but it is
  364.  unknown at this point whether the memory remains allocated after the program
  365.  is closed.  Mark Ludwig makes extensive use of these calls for temporary
  366.  buffers in his direct action 'Windows Virus #2'.
  367.  
  368.  Mark Ludwig also demonstrates use of the WinAPI calls, which involve adding
  369.  a new relocation entry for each call and pushing some stuff on the stack.
  370.  This may become important when win95 comes out but for the moment it is
  371.  more convenient to simply use int 21h.
  372.  
  373.  Some food for thought.
  374.  ----------------------
  375.  
  376.  Microsoft has a dream [here we go], the dream is to control the world.  They
  377.  intend to do this with applications like Windows [scary isn't it ?], and by
  378.  the looks of it they might succeed.  A lot of people think computers should
  379.  be easy, that there should be no skill involved in it and that everyone
  380.  should HAVE to use one.  Microsoft are using this idea to flood the market
  381.  with shit like Windows, in the hope that it will be accepted as a "standard".
  382.  Windows is already this but Microsoft aren't happy with one success, they
  383.  want to be on top of the hill.
  384.  
  385.  This is where we come in.  If Microsoft is going to succeed in taking over
  386.  the world then we should be there making their lives difficult.  The
  387.  WinSurfer virus created by us is so stable that an average windows
  388.  user wouldn't even notice it.  [Then again the average windows user
  389.  wouldn't notice if his hair caught on fire.]  And thus will stay with us
  390.  for quite some time.  At least until AVers recognise the windows market
  391.  and start releasing scanners.
  392.  
  393.  
  394.  
  395.  
  396.  The New Executable format.     (From Ralf Browns Interrupt listing 21 AH=4B)
  397.  --------------------------
  398.  
  399.  new executables begin running with the following register values
  400.     AX = environment segment        (Wrong! AX=0)
  401.     BX = offset of command tail in environment segment
  402.     CX = size of automatic data segment (0000h = 64K)
  403.     ES,BP = 0000h                   (Wrong! ES=PSP)
  404.     DS = automatic data segment
  405.     SS:SP = initial stack
  406.   the command tail corresponds to an old executable's PSP:0081h and
  407.   following, except that the 0Dh is turned into a NUL (00h); new
  408.   format executables have no PSP  (All wrong)
  409.  
  410. Format of .EXE file header:
  411. Offset  Size    Description
  412.  00h  2 BYTEs   .EXE signature, either "MZ" or "ZM" (5A4Dh or 4D5Ah)
  413.  02h    WORD    number of bytes in last 512-byte page of executable
  414.  04h    WORD    total number of 512-byte pages in executable (includes any
  415.         partial last page)
  416.  06h    WORD    number of relocation entries
  417.  08h    WORD    header size in paragraphs
  418.  0Ah    WORD    minimum paragraphs of memory to allocation in addition to
  419.         executable's size
  420.  0Ch    WORD    maxi paragraphs to allocate in addition to executable's size
  421.  0Eh    WORD    initial SS relative to start of executable
  422.  10h    WORD    initial SP
  423.  12h    WORD    checksum (one's complement of sum of all words in executable)
  424.  14h    DWORD   initial CS:IP relative to start of executable
  425.  18h    WORD    offset within header of relocation table
  426.         40h or greater for new-format (NE,LE,LX,PE,etc.) executable
  427.  1Ah    WORD    overlay number (normally 0000h = main program)
  428. ---new executable---
  429.  1Ch  4 BYTEs   ???
  430.  20h    WORD    behavior bits
  431.  22h 26 BYTEs   reserved for additional behavior info
  432.  3Ch    DWORD   offset of new executable (NE,LE,etc) header within disk file,
  433.         or 00000000h if plain MZ executable
  434.  
  435. Format of new executable header:
  436. Offset  Size    Description
  437.  00h  2 BYTEs   "NE" (4Eh 45h) signature
  438.  02h  2 BYTEs   linker version (major, then minor)
  439.  04h    WORD    offset from start of this header to entry table (see below)
  440.  06h    WORD    length of entry table in bytes
  441.  08h    DWORD   file load CRC (0 in Borland's TPW)
  442.  0Ch    BYTE    program flags (see below)
  443.  0Dh    BYTE    application flags (see below)
  444.  0Eh    WORD    auto data segment index
  445.  10h    WORD    initial local heap size
  446.  12h    WORD    initial stack size (added to data seg, 0000h if SS <> DS)
  447.  14h    DWORD   program entry point (CS:IP), "CS" is index into segment table
  448.  18h    DWORD   initial stack pointer (SS:SP), "SS" is segment index
  449.         if SS=automatic data segment and SP=0000h, the stack pointer
  450.         is set to the top of the automatic data segment, just below
  451.         the local heap
  452.  1Ch    WORD    segment count
  453.  1Eh    WORD    module reference count
  454.  20h    WORD    length of nonresident names table in bytes
  455.  22h    WORD    offset from start of this header to segment table (see below)
  456.  24h    WORD    offset from start of this header to resource table
  457.  26h    WORD    offset from start of this header to resident names table
  458.  28h    WORD    offset from start of this header to module reference table
  459.  2Ah    WORD    offset from start of this header to imported names table
  460.         (array of counted strings, terminated with a string of length
  461.           00h)
  462.  2Ch    DWORD   offset from start of file to nonresident names table
  463.  30h    WORD    count of moveable entry point listed in entry table
  464.  32h    WORD    file alignment size shift count
  465.         0 is equivalent to 9 (default 512-byte pages)
  466.  34h    WORD    number of resource table entries
  467.  36h    BYTE    target operating system
  468.         00h unknown
  469.         01h OS/2
  470.         02h Windows
  471.         03h European MS-DOS 4.x
  472.         04h Windows 386
  473.         05h BOSS (Borland Operating System Services)
  474.  37h    BYTE    other EXE flags
  475.         bit 0: supports long filenames
  476.         bit 1: 2.X protected mode
  477.         bit 2: 2.X proportional font
  478.         bit 3: gangload area
  479.  38h    WORD    offset to return thunks or start of gangload area
  480.  3Ah    WORD    offset to segment reference thunks or length of gangload area
  481.  3Ch    WORD    minimum code swap area size
  482.  3Eh  2 BYTEs   expected Windows version (minor version first)
  483. Note:   this header is documented in detail in the Windows 3.1 SDK
  484.     Programmer's Reference, Vol 4.
  485.  
  486. Bitfields for new executable program flags:
  487. Bit(s)  Description
  488.  0-1    DGROUP type
  489.       0 = none
  490.       1 = single shared
  491.       2 = multiple (unshared)
  492.       3 = (null)
  493.  2      global initialization
  494.  3      protected mode only
  495.  4      8086 instructions
  496.  5      80286 instructions
  497.  6      80386 instructions
  498.  7      80x87 instructions
  499.  
  500. Bitfields for new executable application flags:
  501. Bit(s)  Description
  502.  0-2    application type
  503.     001 full screen (not aware of Windows/P.M. API)
  504.     010 compatible with Windows/P.M. API
  505.     011 uses Windows/P.M. API
  506.  3      is a Family Application (OS/2)
  507.  5      0=executable, 1=errors in image
  508.  6      non-conforming program (valid stack is not maintained)
  509.  7      DLL or driver rather than application
  510.     (SS:SP info invalid, CS:IP points at FAR init routine called with
  511.       AX=module handle which returns AX=0000h on failure, AX nonzero on
  512.       successful initialization)
  513.  
  514. Format of new executable segment table record:
  515.  00h    WORD    offset in file (shift left by align shift to get byte offs)
  516.  02h    WORD    length of image in file (0000h = 64K)
  517.  04h    WORD    segment attributes (see below)
  518.  06h    WORD    number of bytes to allocate for segment (0000h = 64K)
  519. Note:   the first segment table entry is entry number 1
  520.  
  521. Bitfields for segment attributes:
  522. Bit(s)  Description
  523.  0      data segment rather than code segment
  524.  1      unused???
  525.  2      real mode
  526.  3      iterated
  527.  4      movable
  528.  5      sharable
  529.  6      preloaded rather than demand-loaded
  530.  7      execute-only (code) or read-only (data)
  531.  8      relocations (directly following code for this segment)
  532.  9      debug info present
  533.  10,11  80286 DPL bits
  534.  12     discardable
  535.  13-15  discard priority
  536.  
  537. Format of new executable relocation data (immediately follows segment image):
  538. Offset  Size    Description
  539.  00h    WORD    number of relocation items
  540.  02h 8N BYTEs   relocation items
  541.         Offset  Size    Description
  542.          00h    BYTE    relocation type
  543.                 00h LOBYTE
  544.                 02h BASE
  545.                 03h PTR
  546.                 05h OFFS
  547.                 0Bh PTR48
  548.                 0Dh OFFS32
  549.          01h    BYTE    flags
  550.                 bit 2: additive
  551.          02h    WORD    offset within segment
  552.          04h    WORD    target address segment
  553.          06h    WORD    target address offset
  554.  
  555.  
  556.  
  557.         [end]
  558.  
  559.  
  560.