Page 1 of 2

Real-time machine-code execution

Posted: Tue Sep 04, 2012 1:32 am
by Crusiatus Black
Hey all,

This is more of a thing that was interesting me for quite a while. I know you can allocate a buffer with execution
permissions, which you in turn can fill with opcodes and data like below (calling MessageBoxA).

Code: Select all

Import "user32.lib"
  MessageBoxA(hWnd.l, szMessage.s, szTitle.s, dwFlags.l)
EndImport
Procedure assemblyCall()
  Protected requiredSize = 28
  ; 4x 1byte push with 4 dword values = 20
  ; 1x MOV EAX, DWORD = 5 bytes
  ; 1x CALL EAX = 2 bytes
  ; 1x return = 1 byte.
  
  vm = VirtualAlloc_(#Null, requiredSize, #MEM_COMMIT, #PAGE_EXECUTE_READWRITE)
  If(vm)
    Protected szTitle.s   = "Hello World!!"
    Protected szMessage.s = "Testing executing the contents of this buffer."
    Protected Flags       = #MB_ICONINFORMATION | #MB_YESNO
    Protected dwOffset    = 0
    Protected *function   = @MessageBoxA()
    
    ; MsgBoxStyle - PUSH Flags
    PokeA(vm + dwOffset, $68): dwOffset + 1
    PokeL(vm + dwOffset, Flags): dwOffset + 4
    
    ; szTitle - PUSH lpszTitle
    PokeA(vm + dwOffset, $68): dwOffset + 1
    PokeL(vm + dwOffset, @szTitle): dwOffset + 4
    
    ; szMessage - PUSH lpszMessage
    PokeA(vm + dwOffset, $68): dwOffset + 1
    PokeL(vm + dwOffset, @szMessage): dwOffset + 4
  
    ; hWindowHandle - PUSH 0
    PokeA(vm + dwOffset, $68): dwOffset + 1
    PokeL(vm + dwOffset, 0): dwOffset + 4
    
    ; push function = MOV EAX, function
    PokeA(vm + dwOffset, $B8): dwOffset + 1
    PokeL(vm + dwOffset, *function): dwOffset + 4
    
    ; Call
    PokeA(vm + dwOffset, $FF): dwOffset + 1 ; CALL EAX
    PokeA(vm + dwOffset, $D0): dwOffset + 1
    PokeA(vm + dwOffset, $C3): dwOffset + 1 ;RETN
    
    eax_result.l = CallFunctionFast(vm)
    If(eax_result = #IDYES)
      Debug "You pressed yes :)"
    Else
      Debug "You pressed no :("
    EndIf 
  
    VirtualFree_(vm, 0, #MEM_RELEASE)
  EndIf 
EndProcedure

assemblyCall()
Now, I was wondering because this is quite interesting to me, is there something available or doable
that can assemble / compile assembly code and execute it dynamically? I mean, I know it must be possible,
because this code works for me. But this isn't really an easy read.

I would like a real-time assembler like this, because then I'd be able to use assembly dynamically. Extending
scripting languages with it etc.

Anyhow, just my curiosity here. I'm curious of what is currently possible.
I remember that something like this has been done in AutoIt a while ago, dynamically assembling and executing FASM code.

Thanks.

Re: Real-time machine-code execution

Posted: Tue Sep 04, 2012 2:07 am
by jack
interesting, you could use the fasmDLL to do the assembling http://board.flatassembler.net/topic.php?t=6239

Re: Real-time machine-code execution

Posted: Tue Sep 04, 2012 2:31 am
by Crusiatus Black
Hey Jack,

That's pretty cool! I never knew about a FASM dll, it would be even more awesome if there was
a static library instead of a DLL, but I'm not going to get into compiling static libraries with FASM haha.

Anyhow, this works too, thanks for the link man, this is really interesting.

Code: Select all

; The following structure resides at the beginning of memory block provided
; to the fasm_Assemble function. The condition field contains the same value
; as the one returned by function.
;   When function returns FASM_OK condition, the output_length and
; output_data fields are filled - with pointer to generated output
; (somewhere within the provided memory block) and the count of bytes stored
; there.
;   When function returns FASM_ERROR, the error_code is filled with the
; code of specific error that happened and error_line is a pointer to the
; LINE_HEADER structure, providing information about the line that caused
; the error.

Structure FASM_STATE
  condition.l
  StructureUnion
    output_length.l
    error_code.l
  EndStructureUnion
  StructureUnion
    output_data.l
    error_line.l
  EndStructureUnion
EndStructure

; The following structure has two variants - it either defines the line
; that was loaded directly from source, or the line that was generated by
; macroinstruction. First case has the highest bit of line_number set to 0,
; while the second case has this bit set.
;   In the first case, the file_path field contains pointer to the path of
; source file (empty string if it's the source that was provided directly to
; fasm_Assemble function), the line_number is the number of line within
; that file (starting from 1) and the file_offset field contains the offset
; within the file where the line starts.
;   In the second case the macro_calling_line field contains the pointer to
; LINE_HEADER structure for the line which called the macroinstruction, and
; the macro_line field contains the pointer to LINE_HEADER structure for the
; line within the definition of macroinstruction, which generated this one.

Structure LINE_HEADER
  file_path.l
  line_number.l
  StructureUnion
    file_offset.l
    macro_calling_line.l
  EndStructureUnion
  Macro_line.l
EndStructure

; General errors and conditions

#FASM_OK                                      = 0	; FASM_STATE points to output
#FASM_WORKING                                 = 1
#FASM_ERROR                                   = 2	; FASM_STATE contains error code
#FASM_INVALID_PARAMETER                       = -1
#FASM_OUT_OF_MEMORY                           = -2
#FASM_STACK_OVERFLOW                          = -3
#FASM_SOURCE_NOT_FOUND                        = -4
#FASM_UNEXPECTED_END_OF_SOURCE                = -5
#FASM_CANNOT_GENERATE_CODE                    = -6
#FASM_FORMAT_LIMITATIONS_EXCEDDED             = -7
#FASM_WRITE_FAILED                            = -8

; Error codes for FASM_ERROR condition

#FASMERR_FILE_NOT_FOUND			                  = -101
#FASMERR_ERROR_READING_FILE		                = -102
#FASMERR_INVALID_FILE_FORMAT		              = -103
#FASMERR_INVALID_MACRO_ARGUMENTS 	            = -104
#FASMERR_INCOMPLETE_MACRO		                  = -105
#FASMERR_UNEXPECTED_CHARACTERS		            = -106
#FASMERR_INVALID_ARGUMENT		                  = -107
#FASMERR_ILLEGAL_INSTRUCTION		              = -108
#FASMERR_INVALID_OPERAND 		                  = -109
#FASMERR_INVALID_OPERAND_SIZE		              = -110
#FASMERR_OPERAND_SIZE_NOT_SPECIFIED	          = -111
#FASMERR_OPERAND_SIZES_DO_NOT_MATCH	          = -112
#FASMERR_INVALID_ADDRESS_SIZE		              = -113
#FASMERR_ADDRESS_SIZES_DO_NOT_AGREE	          = -114
#FASMERR_DISALLOWED_COMBINATION_OF_REGISTERS  = -115
#FASMERR_LONG_IMMEDIATE_NOT_ENCODABLE	        = -116
#FASMERR_RELATIVE_JUMP_OUT_OF_RANGE	          = -117
#FASMERR_INVALID_EXPRESSION		                = -118
#FASMERR_INVALID_ADDRESS 		                  = -119
#FASMERR_INVALID_VALUE			                  = -120
#FASMERR_VALUE_OUT_OF_RANGE		                = -121
#FASMERR_UNDEFINED_SYMBOL		                  = -122
#FASMERR_INVALID_USE_OF_SYMBOL		            = -123
#FASMERR_NAME_TOO_LONG			    	            = -124
#FASMERR_INVALID_NAME			  	                = -125
#FASMERR_RESERVED_WORD_USED_AS_SYMBOL	        = -126
#FASMERR_SYMBOL_ALREADY_DEFINED		            = -127
#FASMERR_MISSING_END_QUOTE		                = -128
#FASMERR_MISSING_END_DIRECTIVE		            = -129
#FASMERR_UNEXPECTED_INSTRUCTION		            = -130
#FASMERR_EXTRA_CHARACTERS_ON_LINE	            = -131
#FASMERR_SECTION_NOT_ALIGNED_ENOUGH           = -132
#FASMERR_SETTING_ALREADY_SPECIFIED            = -133
#FASMERR_DATA_ALREADY_DEFINED		              = -134
#FASMERR_TOO_MANY_REPEATS		                  = -135
#FASMERR_SYMBOL_OUT_OF_SCOPE		              = -136
#FASMERR_USER_ERROR			    		              = -140
#FASMERR_ASSERTION_FAILED		                  = -141

DataSection
  fasm_dll:   IncludeBinary "FASM.dll"
EndDataSection 

Prototype.l fasm_GetVersion()
Prototype.l fasm_Assemble(*lpszSource, *lpChunk, dwMemorySize.l, dwPassesLimit = 100, hDisplayPipe = #Null)
Prototype.l fasm_AssembleFile(*lpszSourceFile, *lpChunk, dwMemorySize.l, dwPassesLimit = 100, hDisplayPipe = #Null)

Global fasm_GetVersion  .fasm_GetVersion    = 0
Global fasm_Assemble    .fasm_Assemble      = 0
Global fasm_AssembleFile.fasm_AssembleFile  = 0
Global fasm_hLibrary = 0

Procedure fasm_init()
  fasm_hLibrary = LoadLibraryM(?fasm_dll)
  If(fasm_hLibrary)
    fasm_GetVersion   = GetProcAddressM(fasm_hLibrary, "fasm_GetVersion")
    fasm_Assemble     = GetProcAddressM(fasm_hLibrary, "fasm_Assemble")
    fasm_AssembleFile = GetProcAddressM(fasm_hLibrary, "fasm_AssembleFile")
    
    ProcedureReturn 1
  EndIf 
EndProcedure
Procedure fasm_close()
  If(fasm_hLibrary)
    FreeLibraryM(fasm_hLibrary)
    fasm_GetVersion   = 0
    fasm_Assemble     = 0
    fasm_AssembleFile = 0
  EndIf
EndProcedure

If(fasm_init())
  *lpOut.FASM_STATE = AllocateMemory(1024)
  szSource.s = "use32" + #CRLF$
  szSource.s + "push 0" + #CRLF$
  szSource.s + "xor eax,eax" + #CRLF$
  
  Debug fasm_Assemble(@szSource, *lpOut, 1024)
  Debug *lpOut\output_length
EndIf 

Re: Real-time machine-code execution

Posted: Tue Sep 04, 2012 2:53 am
by Crusiatus Black
That being said, I managed assembling the code, execution isn't quite working yet ;p

Re: Real-time machine-code execution

Posted: Tue Sep 04, 2012 3:46 am
by Crusiatus Black
There we go! Man, this is fun. :D

Code: Select all

; The following structure resides at the beginning of memory block provided
; to the fasm_Assemble function. The condition field contains the same value
; as the one returned by function.
;   When function returns FASM_OK condition, the output_length and
; output_data fields are filled - with pointer to generated output
; (somewhere within the provided memory block) and the count of bytes stored
; there.
;   When function returns FASM_ERROR, the error_code is filled with the
; code of specific error that happened and error_line is a pointer to the
; LINE_HEADER structure, providing information about the line that caused
; the error.

Structure FASM_STATE
  condition.l
  StructureUnion
    output_length.l
    error_code.l
  EndStructureUnion
  StructureUnion
    output_data.l
    error_line.l
  EndStructureUnion
EndStructure

; The following structure has two variants - it either defines the line
; that was loaded directly from source, or the line that was generated by
; macroinstruction. First case has the highest bit of line_number set to 0,
; while the second case has this bit set.
;   In the first case, the file_path field contains pointer to the path of
; source file (empty string if it's the source that was provided directly to
; fasm_Assemble function), the line_number is the number of line within
; that file (starting from 1) and the file_offset field contains the offset
; within the file where the line starts.
;   In the second case the macro_calling_line field contains the pointer to
; LINE_HEADER structure for the line which called the macroinstruction, and
; the macro_line field contains the pointer to LINE_HEADER structure for the
; line within the definition of macroinstruction, which generated this one.

Structure LINE_HEADER
  file_path.l
  line_number.l
  StructureUnion
    file_offset.l
    macro_calling_line.l
  EndStructureUnion
  Macro_line.l
EndStructure

; General errors and conditions

#FASM_OK                                      = 0	; FASM_STATE points to output
#FASM_WORKING                                 = 1
#FASM_ERROR                                   = 2	; FASM_STATE contains error code
#FASM_INVALID_PARAMETER                       = -1
#FASM_OUT_OF_MEMORY                           = -2
#FASM_STACK_OVERFLOW                          = -3
#FASM_SOURCE_NOT_FOUND                        = -4
#FASM_UNEXPECTED_END_OF_SOURCE                = -5
#FASM_CANNOT_GENERATE_CODE                    = -6
#FASM_FORMAT_LIMITATIONS_EXCEDDED             = -7
#FASM_WRITE_FAILED                            = -8

; Error codes for FASM_ERROR condition

#FASMERR_FILE_NOT_FOUND                       = -101
#FASMERR_ERROR_READING_FILE                   = -102
#FASMERR_INVALID_FILE_FORMAT                  = -103
#FASMERR_INVALID_MACRO_ARGUMENTS              = -104
#FASMERR_INCOMPLETE_MACRO                     = -105
#FASMERR_UNEXPECTED_CHARACTERS                = -106
#FASMERR_INVALID_ARGUMENT                     = -107
#FASMERR_ILLEGAL_INSTRUCTION                  = -108
#FASMERR_INVALID_OPERAND                      = -109
#FASMERR_INVALID_OPERAND_SIZE                 = -110
#FASMERR_OPERAND_SIZE_NOT_SPECIFIED           = -111
#FASMERR_OPERAND_SIZES_DO_NOT_MATCH           = -112
#FASMERR_INVALID_ADDRESS_SIZE                 = -113
#FASMERR_ADDRESS_SIZES_DO_NOT_AGREE           = -114
#FASMERR_DISALLOWED_COMBINATION_OF_REGISTERS  = -115
#FASMERR_LONG_IMMEDIATE_NOT_ENCODABLE         = -116
#FASMERR_RELATIVE_JUMP_OUT_OF_RANGE           = -117
#FASMERR_INVALID_EXPRESSION                   = -118
#FASMERR_INVALID_ADDRESS                      = -119
#FASMERR_INVALID_VALUE                        = -120
#FASMERR_VALUE_OUT_OF_RANGE                   = -121
#FASMERR_UNDEFINED_SYMBOL                     = -122
#FASMERR_INVALID_USE_OF_SYMBOL                = -123
#FASMERR_NAME_TOO_LONG                        = -124
#FASMERR_INVALID_NAME                         = -125
#FASMERR_RESERVED_WORD_USED_AS_SYMBOL         = -126
#FASMERR_SYMBOL_ALREADY_DEFINED               = -127
#FASMERR_MISSING_END_QUOTE                    = -128
#FASMERR_MISSING_END_DIRECTIVE                = -129
#FASMERR_UNEXPECTED_INSTRUCTION               = -130
#FASMERR_EXTRA_CHARACTERS_ON_LINE             = -131
#FASMERR_SECTION_NOT_ALIGNED_ENOUGH           = -132
#FASMERR_SETTING_ALREADY_SPECIFIED            = -133
#FASMERR_DATA_ALREADY_DEFINED                 = -134
#FASMERR_TOO_MANY_REPEATS                     = -135
#FASMERR_SYMBOL_OUT_OF_SCOPE                  = -136
#FASMERR_USER_ERROR                           = -140
#FASMERR_ASSERTION_FAILED                     = -141

DataSection
  fasm_dll:   IncludeBinary "FASM.dll"
EndDataSection 

Prototype.l fasm_GetVersion()
Prototype.l fasm_Assemble(*lpszSource, *lpChunk, dwMemorySize.l, dwPassesLimit = 100, hDisplayPipe = #Null)
Prototype.l fasm_AssembleFile(*lpszSourceFile, *lpChunk, dwMemorySize.l, dwPassesLimit = 100, hDisplayPipe = #Null)
Prototype.l fasm_Procedure()

Global fasm_GetVersion  .fasm_GetVersion    = 0
Global fasm_Assemble    .fasm_Assemble      = 0
Global fasm_AssembleFile.fasm_AssembleFile  = 0
Global fasm_hLibrary = 0

Procedure fasm_init()
  fasm_hLibrary = LoadLibraryM(?fasm_dll)
  If(fasm_hLibrary)
    fasm_GetVersion   = GetProcAddressM(fasm_hLibrary, "fasm_GetVersion")
    fasm_Assemble     = GetProcAddressM(fasm_hLibrary, "fasm_Assemble")
    fasm_AssembleFile = GetProcAddressM(fasm_hLibrary, "fasm_AssembleFile")
    
    ProcedureReturn 1
  EndIf 
EndProcedure
Procedure fasm_close()
  If(fasm_hLibrary)
    FreeLibraryM(fasm_hLibrary)
    fasm_GetVersion   = 0
    fasm_Assemble     = 0
    fasm_AssembleFile = 0
  EndIf
EndProcedure
Procedure fasm_Compile(*lpszSource, *lpdwBufferSize = 0)
  Protected result            = 0
  Protected chunkSize.l       = 4096
  Protected *line.LINE_HEADER = 0
  Protected *lpOut.FASM_STATE = AllocateMemory(chunkSize)
  If(*lpOut)
    Protected assemblerResult.l = fasm_Assemble(*lpszSource, *lpOut, SizeOf(FASM_STATE) + chunkSize)
    If(assemblerResult <> #FASM_OK And assemblerResult <> #FASM_OUT_OF_MEMORY)
      Debug "Error"
      Debug *lpOut\error_code
      *line = *lpOut\error_line
      Debug *line\line_number
    Else
    
      While(assemblerResult = #FASM_OUT_OF_MEMORY)
        chunkSize + 1024
        *lpOut.FASM_STATE = ReAllocateMemory(*lpOut, chunkSize)
        assemblerResult.l = fasm_Assemble(*lpszSource, *lpOut, SizeOf(FASM_STATE) + chunkSize)
        If(assemblerResult <> #FASM_OK And assemblerResult <> #FASM_OUT_OF_MEMORY)
          Debug "Error"
          Debug *lpOut\error_code
          *line = *lpOut\error_line
          Debug *line\line_number
          Break 
        EndIf 
      Wend 
      
      If(assemblerResult = #FASM_OK)
        *vm = VirtualAlloc_(#Null, *lpOut\output_length, #MEM_COMMIT, #PAGE_EXECUTE_READWRITE)
        If(*vm)
          CopyMemory(*lpOut\output_data, *vm, *lpOut\output_length)
          If(*lpdwBufferSize)
            PokeL(*lpdwBufferSize, *lpOut\output_length)
          EndIf 
          result = *vm
        EndIf 
      EndIf 
    EndIf 
    
    FreeMemory(*lpOut)
  EndIf 
  ProcedureReturn result
EndProcedure

Procedure fasm_Free(*lpvm)
  VirtualFree_(*lpvm, 0, #MEM_RELEASE)
EndProcedure

If(fasm_init())
  test.l = 10
  asm.s + "use32" + #LF$ 
  asm.s + "org 100h" + #LF$ 
  asm.s + "xor eax, eax" + #LF$ 
  asm.s + "mov eax, 100" + #LF$ 
  asm.s + "add eax, 20" + #LF$ 
  asm.s + "ret" + #LF$ 
  dwSize.l
  *chunk.fasm_Procedure = fasm_Compile(@asm, @dwSize)
  If(*chunk)
    Debug *chunk()
    fasm_Free(*chunk)
  EndIf 

EndIf 

Re: Real-time machine-code execution

Posted: Tue Sep 04, 2012 4:06 am
by jack
excellent! :)

Re: Real-time machine-code execution

Posted: Tue Sep 04, 2012 1:31 pm
by Crusiatus Black
So, I've got this to work, I'm assuming assembling and executing is possible, but
the second assembly example in this code causes a read error exception at address 6 or 7,
and 6 or 7 are the results MessageBoxA can return in this case. I'm not sure why this is
happening, because executing assembly like this is different from the PB inline assembly.

Any assembler experts here?

Code: Select all

; The following structure resides at the beginning of memory block provided
; to the fasm_Assemble function. The condition field contains the same value
; as the one returned by function.
;   When function returns FASM_OK condition, the output_length and
; output_data fields are filled - with pointer to generated output
; (somewhere within the provided memory block) and the count of bytes stored
; there.
;   When function returns FASM_ERROR, the error_code is filled with the
; code of specific error that happened and error_line is a pointer to the
; LINE_HEADER structure, providing information about the line that caused
; the error.

Structure FASM_STATE
  condition.l
  StructureUnion
    output_length.l
    error_code.l
  EndStructureUnion
  StructureUnion
    output_data.l
    error_line.l
  EndStructureUnion
EndStructure

; The following structure has two variants - it either defines the line
; that was loaded directly from source, or the line that was generated by
; macroinstruction. First case has the highest bit of line_number set to 0,
; while the second case has this bit set.
;   In the first case, the file_path field contains pointer to the path of
; source file (empty string if it's the source that was provided directly to
; fasm_Assemble function), the line_number is the number of line within
; that file (starting from 1) and the file_offset field contains the offset
; within the file where the line starts.
;   In the second case the macro_calling_line field contains the pointer to
; LINE_HEADER structure for the line which called the macroinstruction, and
; the macro_line field contains the pointer to LINE_HEADER structure for the
; line within the definition of macroinstruction, which generated this one.

Structure LINE_HEADER
  file_path.l
  line_number.l
  StructureUnion
    file_offset.l
    macro_calling_line.l
  EndStructureUnion
  Macro_line.l
EndStructure

; General errors and conditions

#FASM_OK                                      = 0	; FASM_STATE points to output
#FASM_WORKING                                 = 1
#FASM_ERROR                                   = 2	; FASM_STATE contains error code
#FASM_INVALID_PARAMETER                       = -1
#FASM_OUT_OF_MEMORY                           = -2
#FASM_STACK_OVERFLOW                          = -3
#FASM_SOURCE_NOT_FOUND                        = -4
#FASM_UNEXPECTED_END_OF_SOURCE                = -5
#FASM_CANNOT_GENERATE_CODE                    = -6
#FASM_FORMAT_LIMITATIONS_EXCEDDED             = -7
#FASM_WRITE_FAILED                            = -8

; Error codes for FASM_ERROR condition

#FASMERR_FILE_NOT_FOUND                       = -101
#FASMERR_ERROR_READING_FILE                   = -102
#FASMERR_INVALID_FILE_FORMAT                  = -103
#FASMERR_INVALID_MACRO_ARGUMENTS              = -104
#FASMERR_INCOMPLETE_MACRO                     = -105
#FASMERR_UNEXPECTED_CHARACTERS                = -106
#FASMERR_INVALID_ARGUMENT                     = -107
#FASMERR_ILLEGAL_INSTRUCTION                  = -108
#FASMERR_INVALID_OPERAND                      = -109
#FASMERR_INVALID_OPERAND_SIZE                 = -110
#FASMERR_OPERAND_SIZE_NOT_SPECIFIED           = -111
#FASMERR_OPERAND_SIZES_DO_NOT_MATCH           = -112
#FASMERR_INVALID_ADDRESS_SIZE                 = -113
#FASMERR_ADDRESS_SIZES_DO_NOT_AGREE           = -114
#FASMERR_DISALLOWED_COMBINATION_OF_REGISTERS  = -115
#FASMERR_LONG_IMMEDIATE_NOT_ENCODABLE         = -116
#FASMERR_RELATIVE_JUMP_OUT_OF_RANGE           = -117
#FASMERR_INVALID_EXPRESSION                   = -118
#FASMERR_INVALID_ADDRESS                      = -119
#FASMERR_INVALID_VALUE                        = -120
#FASMERR_VALUE_OUT_OF_RANGE                   = -121
#FASMERR_UNDEFINED_SYMBOL                     = -122
#FASMERR_INVALID_USE_OF_SYMBOL                = -123
#FASMERR_NAME_TOO_LONG                        = -124
#FASMERR_INVALID_NAME                         = -125
#FASMERR_RESERVED_WORD_USED_AS_SYMBOL         = -126
#FASMERR_SYMBOL_ALREADY_DEFINED               = -127
#FASMERR_MISSING_END_QUOTE                    = -128
#FASMERR_MISSING_END_DIRECTIVE                = -129
#FASMERR_UNEXPECTED_INSTRUCTION               = -130
#FASMERR_EXTRA_CHARACTERS_ON_LINE             = -131
#FASMERR_SECTION_NOT_ALIGNED_ENOUGH           = -132
#FASMERR_SETTING_ALREADY_SPECIFIED            = -133
#FASMERR_DATA_ALREADY_DEFINED                 = -134
#FASMERR_TOO_MANY_REPEATS                     = -135
#FASMERR_SYMBOL_OUT_OF_SCOPE                  = -136
#FASMERR_USER_ERROR                           = -140
#FASMERR_ASSERTION_FAILED                     = -141

DataSection
  fasm_dll:   IncludeBinary "FASM.dll"
EndDataSection 

Prototype.l fasm_GetVersion()
Prototype.l fasm_Assemble(*lpszSource, *lpChunk, dwMemorySize.l, dwPassesLimit = 100, hDisplayPipe = #Null)
Prototype.l fasm_AssembleFile(*lpszSourceFile, *lpChunk, dwMemorySize.l, dwPassesLimit = 100, hDisplayPipe = #Null)
Prototype.l fasm_Procedure()

Global fasm_GetVersion  .fasm_GetVersion    = 0
Global fasm_Assemble    .fasm_Assemble      = 0
Global fasm_AssembleFile.fasm_AssembleFile  = 0
Global fasm_hLibrary = 0

Procedure fasm_init()
  fasm_hLibrary = LoadLibraryM(?fasm_dll)
  If(fasm_hLibrary)
    fasm_GetVersion   = GetProcAddressM(fasm_hLibrary, "fasm_GetVersion")
    fasm_Assemble     = GetProcAddressM(fasm_hLibrary, "fasm_Assemble")
    fasm_AssembleFile = GetProcAddressM(fasm_hLibrary, "fasm_AssembleFile")
    
    ProcedureReturn 1
  EndIf 
EndProcedure
Procedure fasm_close()
  If(fasm_hLibrary)
    FreeLibraryM(fasm_hLibrary)
    fasm_GetVersion   = 0
    fasm_Assemble     = 0
    fasm_AssembleFile = 0
  EndIf
EndProcedure
Procedure fasm_Compile(*lpszSource, *lpdwBufferSize = 0)
  Protected result            = 0
  Protected chunkSize.l       = 4096
  Protected *line.LINE_HEADER = 0
  Protected *lpOut.FASM_STATE = AllocateMemory(chunkSize)
  If(*lpOut)
    Protected assemblerResult.l = fasm_Assemble(*lpszSource, *lpOut, SizeOf(FASM_STATE) + chunkSize)
    If(assemblerResult <> #FASM_OK And assemblerResult <> #FASM_OUT_OF_MEMORY)
      Debug "Error"
      Debug *lpOut\error_code
      *line = *lpOut\error_line
      Debug *line\line_number
    Else
    
      While(assemblerResult = #FASM_OUT_OF_MEMORY)
        chunkSize + 1024
        *lpOut.FASM_STATE = ReAllocateMemory(*lpOut, chunkSize)
        assemblerResult.l = fasm_Assemble(*lpszSource, *lpOut, SizeOf(FASM_STATE) + chunkSize)
        If(assemblerResult <> #FASM_OK And assemblerResult <> #FASM_OUT_OF_MEMORY)
          Debug "Error"
          Debug *lpOut\error_code
          *line = *lpOut\error_line
          Debug *line\line_number
          Break 
        EndIf 
      Wend 
      
      If(assemblerResult = #FASM_OK)
        *vm = VirtualAlloc_(#Null, *lpOut\output_length, #MEM_COMMIT, #PAGE_EXECUTE_READWRITE)
        If(*vm)
          CopyMemory(*lpOut\output_data, *vm, *lpOut\output_length)
          If(*lpdwBufferSize)
            PokeL(*lpdwBufferSize, *lpOut\output_length)
          EndIf 
          result = *vm
        EndIf 
      EndIf 
    EndIf 
    
    FreeMemory(*lpOut)
  EndIf 
  ProcedureReturn result
EndProcedure

Procedure fasm_Free(*lpvm)
  VirtualFree_(*lpvm, 0, #MEM_RELEASE)
EndProcedure


Import "user32.lib"
  MessageBoxA(hWnd.l, szMessage.s, szTitle.s, dwFlags.l)
EndImport



If(fasm_init())

  ; works
  asm.s + "use32" + #LF$ 
  asm.s + "org 100h" + #LF$ 
  asm.s + "xor eax, eax" + #LF$ 
  asm.s + "mov eax, 100" + #LF$ 
  asm.s + "add eax, 20" + #LF$ 
  asm.s + "ret" + #LF$ 
  
  flags       = #MB_YESNO | #MB_ICONINFORMATION
  hWnd        = 0 
  szTitle.s   = "Hello World!!"
  szMessage.s = "This function was executed through an FASM assembler via PureBasic! 0_o"
  hwnd        = 0
  pfunc       = @MessageBoxA()

  asm2.s + "use32" + #LF$ 
  asm2.s + "org 100h" + #LF$ 

  asm2.s + "mov [esp+12], dword " + Str(flags) + "" + #LF$ 
  asm2.s + "mov [esp+08], dword " + Str(@szTitle) + #LF$ 
  asm2.s + "mov [esp+04], dword " + Str(@szMessage) + #LF$ 
  asm2.s + "mov [esp+00], dword " + Str(hwnd) + #LF$ 
 
  asm2.s + "xor eax, eax" + #LF$ 
  asm2.s + "mov ecx, dword " + Str(@MessageBoxA()) + "" + #LF$ 
  asm2.s + "call ecx" + #LF$ 
  
  asm2.s + "ret" + #LF$ 
  
  dwSize.l = 0
  
  ; works, returns 120
  *myMessageBox.fasm_Procedure = fasm_Compile(@asm, @dwSize)
  If(*myMessageBox)
    Debug *myMessageBox()
    fasm_Free(*myMessageBox)
  EndIf 
  
  ; Doesn't work, tries interpreting the MessageBoxA result as memory address?
  *myMessageBox.fasm_Procedure = fasm_Compile(@asm2, @dwSize)
  If(*myMessageBox)
    Debug *myMessageBox()
    fasm_Free(*myMessageBox)
  EndIf 

EndIf

Re: Real-time machine-code execution

Posted: Tue Sep 04, 2012 2:21 pm
by wilbert
What is LoadLibraryM ?
I get an error message on that.

Re: Real-time machine-code execution

Posted: Tue Sep 04, 2012 2:25 pm
by Crusiatus Black
LoadLibraryM is a procedure from the LoadDllMemory library in PBOSL
http://pbosl.purearea.net/index.php?site=Libs

It allows you to load a library from a memory buffer (e.g. a IncludeBinary label) and
call its functions.

Re: Real-time machine-code execution

Posted: Tue Sep 04, 2012 2:33 pm
by wilbert
Got it :wink:

Not about your problem, simply push your variables like this

Code: Select all

  asm2.s + "push dword " + Str(flags) + "" + #LF$ 
  asm2.s + "push dword " + Str(@szTitle) + #LF$ 
  asm2.s + "push dword " + Str(@szMessage) + #LF$ 
  asm2.s + "push dword " + Str(hwnd) + #LF$ 
instead of using the mov instruction.
Windows internal calls are usually stdcall meaning that the called procedure cleans up the stack.
So after the procedure is done, it adjusts the stack pointer for the four dwords you placed on the stack but since your code didn't change the stack pointer, it crashes.

Re: Real-time machine-code execution

Posted: Tue Sep 04, 2012 2:36 pm
by jack
yes, I just found out the same thing.

Re: Real-time machine-code execution

Posted: Tue Sep 04, 2012 2:37 pm
by Crusiatus Black
Ahh great, that works, thanks mate! The additional info is appreciated too!

Awesome, I like this haha!

Re: Real-time machine-code execution

Posted: Tue Sep 04, 2012 2:53 pm
by jack
Trond posted some code to compile expressions to asm that you may find interesting http://www.purebasic.fr/english/viewtop ... 7&start=30
he also started compiler tutorial somewhere.

Re: Real-time machine-code execution

Posted: Tue Sep 04, 2012 3:25 pm
by Crusiatus Black
Cool, that's a fun read! Thanks. With this FASM dll one could execute the generated code! :D

Re: Real-time machine-code execution

Posted: Wed Sep 05, 2012 9:58 am
by DoctorLove
Well, im doing it all with http://www.oxygenbasic.org/

Its a simple DLL which compiles and execute basic code on the fly.
The language is Mature and basic to learn.

MAC osx and Linux are in the making.

Im sold to it.