News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Relocatable Code

Started by Biterider, January 02, 2024, 06:56:07 AM

Previous topic - Next topic

Biterider

Hi
I updated a project originally developed by Bryant Keller and later developed further by Homer and me. It's about code that can be placed anywhere in memory (relocated).
The basic trick used here is that all addresses are relative and everything is in a single writable and executable segment. The addresses of the APIs used are read from the loaded DLL images. The code used for this cannot initially access any DLL, so it must be included.

The purpose of this technique is to send code from one computer to another to realize distributed computing. There are many alternatives, but this is one of the fastest.

To demonstrate this technique, I first wrote and then executed the relocatable code on a standard Windows Shared Memory Object (SMO). The code doesn't do much. After it has loaded and parsed all the required DLLs it displays a simple dialog box. The title bar contains the ProcessID and, if applicable, an asterisk after it. The latter only appears if the SMO was required to be written. However, if the asterisk does not appear, the code could be executed directly from memory by another existing instance.

To see this, you simply need to launch the .EXE file several times. Super cool!  :cool:

I have now added support for 64 bit and WIDE strings.

With the current version of DebugCenter you can watch what the remote code is doing over the network, but that's another story.  :wink2:

; ==================================================================================================
; Title:      Relocatable.asm
; Author:     G. Friedrich
; Version:    C.1.0
; Purpose:    Demo code to create a Shared Memory Object, inject some relocatable code into it,
;             execute the relocatable code within the SMO, throw a MessageBox to prove its
;             working / hang the app instance, and finally return to the Caller.
;             K32B.inc first written by Bryant Keller, 2005 and
;             modified by EvilHomer, 2005 (100% Relative, no hardcoded addresses generated)
; Notes:      Version C.1.0, November 2023
;               - First release.
; ==================================================================================================


% include @Environ(OBJASM_PATH)\Code\Macros\Model.inc
SysSetup OOP, WIN64, WIDE_STRING;, DEBUG(WND)

% include &IncPath&Windows\winternl.inc                 ;Required for RelocCode.inc

MakeObjects Primer, SharedMemoryObject


;===================================================================================================

;Important notes:
;  Our Remote CodeBlock has three important labels:
;    - RelocCodeSTART defines the beginning of the codeblock
;    - RelocCodeENTER defines the entrypoint in the codeblock
;    - RelocCodeSTOP  defines the end of the codeblock
;
;  RelocCode.inc MUST be included in the remote code block. It contains a relocatable version of
;  GetProcAddress, as well as a bunch of handy macros for writing relocatable code.
;  It doesn't have to be the first thing in the code block, but it MUST precede any references to
;  its macros.
;
;  The $Delta macro returns the ADDRESS of label, not data stored there

SHARED_SIZE equ PAGESIZE
TypeRelocEnter typedef proto :DWORD

.code
RelocCodeSTART:
include RelocCode.inc

pBaseNTDLL POINTER  NULL    ;Base address of NTDLL.dll
pBaseK32   POINTER  NULL    ;Base address of Kernel32.dll
pBaseU32   POINTER  NULL    ;Base address of User32.dll
hLibU32    HANDLE   0       ;Library handle of User32.dll

USE_API GetModuleHandle
USE_API LoadLibrary
USE_API FreeLibrary
USE_API MessageBox
USE_API GetCurrentProcessId
USE_API wsprintf

String szUser32,  "User32"
String szCaption, "How cool is this? Caller's Process ID = %lu  %c"
String szMessage, "I'm executing from within a Shared Memory Object.", CRLF, \
                  "You can fire up simultaneous instances if you like."
szBuffer  CHR  256 dup (0)

RelocCodeENTER proc uses xbx, cMarker:CHR         ;This is our relocated entry point
  GetDllBases pBaseNTDLL, pBaseK32

  ;Prepare a few common API functions
  LoadApi pBaseK32, GetModuleHandle
  LoadApi pBaseK32, LoadLibrary
  LoadApi pBaseK32, GetCurrentProcessId

  ;Gain access to User32 to be able to call "MessageBox"
  ApiRelCall GetModuleHandle, $Delta(xcx, szUser32)
  .if xax == NULL
    ;Load User32.dll
    ApiRelCall LoadLibrary, $Delta(xcx, szUser32)
    ;Remember that we had to LOAD this DLL
    SetDelta hLibU32, xax, xbx
  .endif
  SetDelta pBaseU32, xax, xbx

  ;Prepare MessageBox and wsprintf
  LoadApi pBaseU32, MessageBox
  LoadApi pBaseU32, wsprintf

  ;Grab the current Process ID
  ApiRelCall GetCurrentProcessId

  ;Format a string with PID
  ApiRelCall wsprintf, $Delta(xcx, szBuffer), $Delta(xdx, szCaption), xax, cMarker

  ;Perform MessageBox
  ApiRelCall MessageBox, NULL, $Delta(xdx, szMessage), $Delta(xax, szBuffer), MB_OK

  ;Unload User32 if required
  .if HANDLE ptr [$Delta(xbx, hLibU32)] != 0
    LoadApi pBaseK32, FreeLibrary
    ApiRelCall FreeLibrary, HANDLE ptr [xbx]
  .endif

  ;Return to Caller
  ret
RelocCodeENTER endp

RelocCodeSTOP:


;===================================================================================================

start proc uses xbx xdi
  local pSMO:$ObjPtr(SharedMemoryObject)

  SysInit

  mov ebx, ' '                                          ;Reset marker
  mov pSMO, $New(SharedMemoryObject)
  OCall pSMO::SharedMemoryObject.Init, NULL, $OfsCStr("SharedMemoryCode"), SHARED_SIZE, \
                                       FILE_MAP_EXECUTE or FILE_MAP_WRITE
  .if eax != FALSE
    mov xcx, pSMO
    mov xdi, [xcx].$Obj(SharedMemoryObject).pMem
    .if eax == SMO_OPENED_NEW
      DbgClearAll
      ;We just created a NEW SMO. copy our codeblock into it
      DbgText "Writing relocatable code to SMO"
      invoke MemClone, xdi, offset RelocCodeSTART, (RelocCodeSTOP - RelocCodeSTART)
      mov ebx, '*'                                      ;Set marker when SMO was written
    .endif

    ;We're ready to execute our codeblock
    DbgText "Executing relocated code"
    ;Calculate the address of RelocCodeENTER in remote code block and CALL to that location
    add xdi, (RelocCodeENTER - RelocCodeSTART)
    invoke TypeRelocEnter ptr xdi, ebx
    DbgText "Returned from relocated code"

  .endif

  Destroy pSMO                                          ;Unmap SMO from this app instance
  SysDone                                               ;It is only released once it has been
  invoke ExitProcess,0                                  ;released by all applications that
start endp                                              ;have opened it.

end

Attached is the 64 bit .EXE file.

Have fun!

Biterider

jj2007

It works fine, if you are courageous enough to ignore Windows Defender's warning :biggrin:

Interesting idea :thumbsup:

I wonder what is the difference to a DLL: since a DLL must contain only relocatable code, would it be possible to modify a DLL so that it gets an entry point and the exe extension? Or, alternatively, use an executable to insert code copied from a DLL?

Biterider

Quote from: jj2007 on January 02, 2024, 08:37:56 AMI wonder what is the difference to a DLL
A DLL is based on a different way to solve fixups. Check this.
The functionality described above with the so-called "delta method" calculates the relative addresses at runtime (3rd method described in the link).

Biterider

jj2007

https://en.wikipedia.org/wiki/Relocation_(computing)#32-bit_Windows
QuoteFor both DLLs and for EXEs which opt into address space layout randomization (ASLR), an exploit mitigation technique introduced with Windows Vista, relocation tables once again become mandatory because of the possibility that the binary may be dynamically moved before being executed

Hmmmm...

daydreamer

my none asm creations
https://masm32.com/board/index.php?topic=6937.msg74303#msg74303
I am an Invoker
"An Invoker is a mage who specializes in the manipulation of raw and elemental energies."
Like SIMD coding

Biterider

Quote from: jj2007 on January 02, 2024, 08:37:56 AMif you are courageous enough to ignore Windows Defender's warning
Hi
After polishing the code a bit, I uploaded the source files to the GitHub repository. 
Before building the binary, you can now check that there is no malicious code present.  :biggrin:

Biterider