Author Topic: Fasm Module for Squirrel  (Read 57 times)

Mr_Motley

  • Newbie
  • *
  • Posts: 21
  • Karma: +5/-1
    • View Profile
Fasm Module for Squirrel
« on: Yesterday at 06:36:32 pm »
The FASM Advantage: Stripping Squirrel to the Bone


Why is the FASM source shorter than the C source? The official C SDK relies on a giant wrapper structure called SquirrelAPI. Every time C code calls sq->pushstring, the compiler generates code to look up an offset, dereference a pointer, and then execute.
In FASM, we skip the middleman. We define the offsets (like SQ_PUSHSTRING = 0x7C) as direct constants. We aren't asking a struct where the function is; we are telling the CPU exactly where to jump. We also skip the sprintf overhead used in the C RegisterSquirrelFunc for parameter checking.


The Linux Portability Factor One of the biggest strengths of writing in FASM is how close we are to the binary level. While the current source is a PE DLL for Windows, a Linux version is entirely possible with minimal effort.
Quote
The cross-platform logic: On Windows, the server uses LoadLibrary() to bring the module into memory. On Linux, it uses dlopen().
To port this to a Linux-based server, we would simply:
  • Change the format PE header to format ELF.
  • Adjust the SQLoad export to match the Linux shared object symbols (.so).
  • Swap Win32-specific setup (like AllocConsole) for standard ioctl or write syscalls.


The "Direct Include" Revolution Normally, to use an external library in Squirrel, you have to write a C++ bridge and register every single function. By writing a custom Loader Module in FASM, we can hook sq_compile.
Instead of registering functions one by one, the FASM module acts as a dynamic linker—using GetProcAddress (Win) or dlsym (Linux) to map exports directly into the Squirrel Root Table at runtime. This allows a script to include() a system library as if it were a native part of the VM.


The Full FASM Source
Code: [Select]
format PE DLL at 0x10000000
entry DllMain

include 'win32ax.inc'

; =============================================================================
; SQUIRREL API OFFSETS
; =============================================================================
SQ_GETTOP                = 0x50
SQ_PUSHSTRING            = 0x7C
SQ_PUSHINTEGER           = 0x84
SQ_PUSHBOOL              = 0x88
SQ_GETINTEGER            = 0xB0
SQ_SETNATIVECLOSURENAME  = 0xDC
SQ_PUSHROOTTABLE         = 0x108
SQ_NEWSLOT               = 0x11C
SQ_POP                   = 0x44
SQ_NEWCLOSURE            = 0x70

section '.data' data readable writeable
    sq_v        dd 0
    sq_ptr      dd 0
   
    szHelloName     db 'Hello', 0
    szHelloMsg      db 'FASM: UserData retrieved successfully!', 10, 0
    szSumName       db 'Sum', 0
    szInitMsg       db 10,'--- FASM Squirrel Module Initialized Synchronously ---', 10, 0

section '.code' code readable executable

DllMain:
    mov     eax, 1
    ret     12

; -----------------------------------------------------------------------------
; SQLoad(v, api) - Synchronous Entry Point
; -----------------------------------------------------------------------------
SQLoad:
    push    ebp
    mov     ebp, esp
    push    esi
    push    ebx

    ; 1. Store the VM and API pointers immediately
    mov     eax, [ebp+8]
    mov     [sq_v], eax
    mov     esi, eax        ; ESI = HSQUIRRELVM

    mov     eax, [ebp+12]
    mov     [sq_ptr], eax
    mov     ebx, eax        ; EBX = API Pointer

    ; 2. Initialize Console for debugging
    invoke  AllocConsole
    cinvoke printf, szInitMsg

    ; 3. Register "Hello"
    push    szHelloName
    push    Hello
    push    esi
    call    RegisterFunc
    add     esp, 12

    ; 4. Register "Sum"
    push    szSumName
    push    Sum
    push    esi
    call    RegisterFunc
    add     esp, 12

    ; 5. Return 0 (or 1 depending on LU requirements, usually 0 for success in SQ modules)
    xor     eax, eax
    pop     ebx
    pop     esi
    pop     ebp
    ret

; -----------------------------------------------------------------------------
; RegisterFunc(v, func_ptr, name)
; -----------------------------------------------------------------------------
RegisterFunc:
    push    ebp
    mov     ebp, esp
    push    ebx
    push    esi

    mov     esi, [ebp+8]            ; v
    mov     ebx, [sq_ptr]           ; api

    ; sq_pushroottable(v)
    push    esi
    call    dword [ebx + SQ_PUSHROOTTABLE]
    add     esp, 4

    ; sq_pushstring(v, name, -1)
    push    -1
    push    dword [ebp+16]
    push    esi
    call    dword [ebx + SQ_PUSHSTRING]
    add     esp, 12

    ; sq_newclosure(v, func_ptr, 0)
    push    0
    push    dword [ebp+12]
    push    esi
    call    dword [ebx + SQ_NEWCLOSURE]
    add     esp, 12

    ; sq_setnativeclosurename(v, -1, name)
    push    dword [ebp+16]
    push    -1
    push    esi
    call    dword [ebx + SQ_SETNATIVECLOSURENAME]
    add     esp, 12

    ; sq_newslot(v, -3, SQFalse)
    push    0
    push    -3
    push    esi
    call    dword [ebx + SQ_NEWSLOT]
    add     esp, 12

    ; sq_pop(v, 1)
    push    1
    push    esi
    call    dword [ebx + SQ_POP]
    add     esp, 8

    pop     esi
    pop     ebx
    pop     ebp
    ret

; -----------------------------------------------------------------------------
; Sum(v) - Squirrel Function
; -----------------------------------------------------------------------------
Sum:
    push    ebp
    mov     ebp, esp
    sub     esp, 8                  ; Local vars for integers
    push    ebx
    push    esi

    mov     esi, [ebp+8]            ; v
    mov     ebx, [sq_ptr]           ; api

    ; Check arg count (Stack top)
    push    esi
    call    dword [ebx + SQ_GETTOP]
    add     esp, 4

    cmp     eax, 3                  ; Root + 2 args
    jne     .wrong_args

    ; Get Arg 1 (Slot 2)
    lea     eax, [ebp-4]
    push    eax
    push    2
    push    esi
    call    dword [ebx + SQ_GETINTEGER]
    add     esp, 12

    ; Get Arg 2 (Slot 3)
    lea     eax, [ebp-8]
    push    eax
    push    3
    push    esi
    call    dword [ebx + SQ_GETINTEGER]
    add     esp, 12

    ; Add
    mov     eax, [ebp-4]
    add     eax, [ebp-8]

    ; Push Result
    push    eax
    push    esi
    call    dword [ebx + SQ_PUSHINTEGER]
    add     esp, 8
    jmp     .finish

.wrong_args:
    push    0
    push    esi
    call    dword [ebx + SQ_PUSHBOOL]
    add     esp, 8

.finish:
    mov     eax, 1
    pop     esi
    pop     ebx
    mov     esp, ebp
    pop     ebp
    ret

; -----------------------------------------------------------------------------
; Hello(v) - Squirrel Function
; -----------------------------------------------------------------------------
Hello:
    push    ebp
    mov     ebp, esp
    push    ebx
    mov     ebx, [sq_ptr]
   
    push    -1
    push    szHelloMsg
    push    dword [ebp+8]
    call    dword [ebx + SQ_PUSHSTRING]
    add     esp, 12
   
    mov     eax, 1
    pop     ebx
    pop     ebp
    ret

SQUnload:   xor eax, eax
            ret
SQCallback: xor eax, eax
            ret
SQPulse:    xor eax, eax
            ret

section '.idata' import data readable
    library kernel32,'KERNEL32.DLL', msvcrt,'MSVCRT.DLL'
    import kernel32, AllocConsole,'AllocConsole'
    import msvcrt, printf,'printf'

section '.edata' export data readable
    export 'LU_Mod.dll', \
           SQLoad,     'SQLoad', \
           SQUnload,   'SQUnload', \
           SQCallback, 'SQCallback', \
           SQPulse,    'SQPulse'

section '.reloc' fixups data discardable


High Performance. Zero Bloat. Bare Metal.

 If anyone is interested in being able to directly load a dll inside of Squirrel and skip the standard way of importing exported functions, I can work on that very soon
« Last Edit: Today at 12:38:12 am by Mr_Motley »

 

© Liberty Unleashed Team.