flat assembler
Message board for the users of flat assembler.

Index > Windows > sprintf for xp via fasmg, the codes work well.

Author
Thread Post new topic Reply to topic
uor99



Joined: 04 Dec 2014
Posts: 42
uor99 04 Oct 2016, 23:33
Code:
;sprintf for xp via fasmg, the codes work well.
;(https://board.flatassembler.net/topic.php?t=1067)
;
include '80386.inc'
include 'format/format.inc'
format PE GUI
entry start
macro stdcall proc,args&
        iterate arg, args
                indx 1+%%-%
                pushd arg
        end iterate
        call proc
end macro
macro ccall proc,args&
        local size
        size = 0
        iterate arg, args
                indx 1+%%-%
                pushd arg
                size = size + 4
        end iterate
        call proc
        if size
                add esp,size
        end if
end macro

macro invoke proc,args&
        stdcall [proc],args
end macro

macro cinvoke proc,args&
        ccall [proc],args
end macro

section '.idata' import data readable writeable
  dd 0,0,0,RVA kernel_name,RVA kernel_table
  dd 0,0,0,RVA user_name,RVA user_table
  dd 0,0,0,0,0
;
  kernel_name db 'KERNEL32.DLL',0
  user_name db 'USER32.DLL',0
;
  kernel_table:
    ExitProcess dd rva _ExitProcess
    CreateFile dd rva _CreateFileA
    ReadFile dd rva _ReadFile
    WriteFile dd rva _WriteFile
    CloseHandle dd rva _CloseHandle
    SetFilePointer dd rva _SetFilePointer
    GetCommandLine dd rva _GetCommandLineA
    GetEnvironmentVariable dd rva _GetEnvironmentVariable
    GetStdHandle dd rva _GetStdHandle
    VirtualAlloc dd rva _VirtualAlloc
    VirtualFree dd rva _VirtualFree
    GetTickCount dd rva _GetTickCount
    GetSystemTime dd rva _GetSystemTime
    GlobalMemoryStatus dd rva _GlobalMemoryStatus
    dd 0

  
  _ExitProcess dw 0
    db 'ExitProcess',0
  _CreateFileA dw 0
    db 'CreateFileA',0
  _ReadFile dw 0
    db 'ReadFile',0
  _WriteFile dw 0
    db 'WriteFile',0
  _CloseHandle dw 0
    db 'CloseHandle',0
  _SetFilePointer dw 0
    db 'SetFilePointer',0
  _GetCommandLineA dw 0
    db 'GetCommandLineA',0
  _GetEnvironmentVariable dw 0
    db 'GetEnvironmentVariableA',0
  _GetStdHandle dw 0
    db 'GetStdHandle',0
  _VirtualAlloc dw 0
    db 'VirtualAlloc',0
  _VirtualFree dw 0
    db 'VirtualFree',0
  _GetTickCount dw 0
    db 'GetTickCount',0
  _GetSystemTime dw 0
    db 'GetSystemTime',0
  _GlobalMemoryStatus dw 0
    db 'GlobalMemoryStatus',0

;
  user_table:
    MessageBoxA dd RVA _MessageBoxA
    wsprintfA dd RVA _wsprintfA
    dd 0
;

  _MessageBoxA dw 0
    db 'MessageBoxA',0
  _wsprintfA dw 0
    db 'wsprintfA',0
;
section '.reloc' fixups data readable discardable
section '.text' code readable executable
start:
        call main
        push    0
        call    [ExitProcess]
; sprintf(sOutput, sFormat, [args])
; sFormat: string containing the pattern of the output string
;%s: string
;%d: decimal number
;%h: hexadecimal number in lower case
;%H: hexadecimal number in upper case
;
;%d, %h and %H also accepts ":[number of fixed digits]"
;The number in the square brackets should then be in[0..9] and equal one less the number of fixed digits...
; sOutput: pointer to buffer which will receive the output string
; [args]: ...
;
; Put these two variables in the data section:
; alpha_addition db 0
; digit_count db 0
max_dx equ 16
sprintf:
        ;enter 0,0
        push ebp
        mov ebp,esp
        mov edx,max_dx
        mov edi,[ebp+8]
        mov esi,[ebp+12]
 .loop:
        lodsb
        cmp al,"%"
        je .format
        stosb
        test al,al
        jnz .loop
 .done:
        xor eax,eax
        jmp .finish
 .error:
        or eax,-1
 .finish:
        ;leave
        mov esp, ebp
        pop ebp
        
        pop eax
        sub edx,max_dx/2;8
        add esp,edx
        jmp eax
 .format:
        lodsb
        cmp al,"%"
        jne .not_percent
        stosb
        jmp .loop
 .not_percent:
        mov [digit_count],0
        push eax
        cmp byte [esi],":"
        jne .not_extra
        lodsb
        cmp byte [esi],":"
        je .not_extra
        lodsb
        cmp al,"0"
        jl .error
        cmp al,"9"
        jg .error
        sub al,2Fh
        mov [digit_count],al
 .not_extra:
        pop eax
        cmp al,"s"
        je .string
        cmp al,"h"
        je .number.lhex
        cmp al,"H"
        je .number.uhex
        cmp al,"d"
        jne .error
 .number.dec:
        mov ebx,10
        call .number
        jmp .loop
 .number.lhex:
        mov byte [alpha_addition],27h
        mov ebx,16
        call .number
        jmp .loop
 .number.uhex:
        mov byte [alpha_addition],7
        mov ebx,16
        call .number
        jmp .loop
 .number:
        mov eax,[ebp+edx]
        add edx,max_dx/4;4
        push edx
        xor ecx,ecx
 .number.loop:
        xor edx,edx
        div ebx
        push edx
        inc ecx
        test eax,eax
        jnz .number.loop
        cmp [digit_count],0
        je .number.putchar
        push ecx
        mov eax,ecx
        movzx ecx,byte [digit_count]
        sub ecx,eax
        jz .number.prefix.end
        js .number.prefix.end
        mov al,"0"
 .number.prefix:
        stosb
        loop .number.prefix
 .number.prefix.end:
        pop ecx
 .number.putchar:
        pop eax
        add al,30h
        cmp al,39h
        jle .number.putchar.nonalpha
        add al,[alpha_addition]
 .number.putchar.nonalpha:
        stosb
        loop .number.putchar
        pop edx
        ret
 .string:
        push esi
        mov esi,[ebp+edx]
        add edx,max_dx/4;4
 .string.loop:
        lodsb
        test al,al
        jz .string.end
        stosb
        jmp .string.loop
 .string.end:
        pop esi
        jmp .loop
main:
        stdcall sprintf,sOutput,sFormat,Hi,16,start2,start
        add esp,24
        invoke MessageBoxA,0,sOutput,0,0
        ret

section '.data' data readable writeable ;needed by wsprintf
        digit_count db 0
        alpha_addition db 0
        sFormat db "%s! %d-lol",13,10
                db "%s = offset: 0x%H:7",0
        Hi      db "Hi",0
        start2  db "start",0
        sOutput rb 100h
    

[/code]
Post 04 Oct 2016, 23:33
View user's profile Send private message ICQ Number Reply with quote
jiangfasm



Joined: 08 Mar 2015
Posts: 60
jiangfasm 08 Oct 2016, 05:59
Do you really use?
fasmg looks complicated
Post 08 Oct 2016, 05:59
View user's profile Send private message Visit poster's website Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8357
Location: Kraków, Poland
Tomasz Grysztar 08 Oct 2016, 10:08
uor99 wrote:
sprintf for xp via fasmg, the codes work well.
Your code still appears to have some problems, on my machine it displays the message correctly, but crashes afterwards.

jiangfasm wrote:
fasmg looks complicated
It is only a matter of missing macro packages. The above sample would look almost the same when converted to fasm, you'd only have to replace the macro definitions on top with fasm macro syntax:
Code:
format PE GUI 
entry start 
macro stdcall proc,[arg] {
        reverse pushd arg
        common call proc
}
macro ccall proc,[arg] {
        common local size
        size = 0
        reverse pushd arg
        size = size + 4
        common call proc
        if size 
                add esp,size 
        end if 
}

macro invoke proc,args& {
        stdcall [proc],args 
}

macro cinvoke proc,args& {
        ccall [proc],args 
}
; rest of the source unchanged    


The fasmg sources use some macros that enable the same syntax as the macros defined in fasm's official headers, you can find them in source/windows/selfhost.inc
This example for fasmg could be simplified a bit by using such macros:
Code:
; note: the FORMAT.INC from latest packages includes the instruction set
;       macros on its own; if you need to select the exact instruction set,
;       you have to use the PE.INC formatter directly
; include '80386.inc'
include 'format/format.inc'

format PE GUI
entry start

include 'macros.inc'

section '.idata' import data readable writeable

  library kernel,'KERNEL32.DLL',\
          user,'USER32.DLL'

  import kernel,\
         ExitProcess,'ExitProcess',\
         CreateFile,'CreateFileA',\
         ReadFile,'ReadFile',\
         WriteFile,'WriteFile',\
         CloseHandle,'CloseHandle',\
         SetFilePointer,'SetFilePointer',\
         GetCommandLine,'GetCommandLineA',\
         GetEnvironmentVariable,'GetEnvironmentVariable',\
         GetStdHandle,'GetStdHandle',\
         VirtualAlloc,'VirtualAlloc',\
         VirtualFree,'VirtualFree',\
         GetTickCount,'GetTickCount',\
         GetSystemTime,'GetSystemTime',\
         GlobalMemoryStatus,'GlobalMemoryStatus'

  import user,\
         MessageBoxA,'MessageBoxA',\
         wsprintfA,'wsprintfA'

; rest of the source unchanged    
with the MACROS.INC looking like this:
Code:
macro struct? name
        macro ends?!
                        end namespace
                        iterate definition, args@struct
                                match name:value, definition
                                        store value at .name
                                else match name==value, definition
                                        store value at .name
                                else match value, definition
                                        err 'unnamed values not supported'
                                end match
                        end iterate
                end struc
                virtual at 0
                        name name
                        sizeof.name = $
                end virtual
                purge ends?
        end macro
        struc name args@struct&
                label . : sizeof.name
                namespace .
end macro

macro stdcall proc*,args&
        iterate arg, args
                indx 1+%%-%
                pushd arg
        end iterate
        call proc
end macro

macro ccall proc*,args&
        local size
        size = 0
        iterate arg, args
                indx 1+%%-%
                pushd arg
                size = size + 4
        end iterate
        call proc
        if size
                add esp,size
        end if
end macro

macro invoke proc*,args&
        stdcall [proc],args
end macro

macro cinvoke proc*,args&
        ccall [proc],args
end macro

macro library? definitions&
        PE.Imports:
        iterate <name,string>, definitions
              if ~ name.redundant
                      dd RVA name.lookup,0,0,RVA name.str,RVA name.address
              end if
              name.referred = 1
        end iterate
        dd 0,0,0,0,0
        iterate <name,string>, definitions
              if ~ name.redundant
                      name.str db string,0
                               align 2
              end if
        end iterate
end macro

macro import? name,definitions&
        align 4
        if defined name.referred
                name.lookup:
                iterate <label,string>, definitions
                        if used label
                                if string eqtype ''
                                        dd RVA name.label
                                else
                                        dd 80000000h + string
                                end if
                        end if
                end iterate
                if $ > name.lookup
                        name.redundant = 0
                        dd 0
                else
                        name.redundant = 1
                end if
                name.address:
                iterate <label,string>, definitions
                        if used label
                                if string eqtype ''
                                        label dd RVA name.label
                                else
                                        label dd 80000000h + string
                                end if
                        end if
                end iterate
                if ~ name.redundant
                        dd 0
                end if
                iterate <label,string>, definitions
                        if used label & string eqtype ''
                                name.label dw 0
                                           db string,0
                                           align 2
                        end if
                end iterate
        end if
end macro    


I have not yet started working on an official set of Windows macro headers for fasmg, because my thought was that perhaps the could make a better use fasmg macro engine by adopting some new syntax. The macros like "import", and all resource macros, use the syntax that is a relic of times when fasm had only a very basic macro processor. With the macros of fasmg it could be possible to implement something much more sophisticated, or even emulate syntax of some other assemblers if needed.

Still, because fasm's old resource macros are so simple, it should be a straightforward work to convert them when anyone needs them.
Post 08 Oct 2016, 10:08
View user's profile Send private message Visit poster's website Reply with quote
uor99



Joined: 04 Dec 2014
Posts: 42
uor99 11 Oct 2016, 08:20
Eager to have more powerful macros of fasmg !

As for my codes, I have not found out the bug yet. It doesn't crash after exit.
Post 11 Oct 2016, 08:20
View user's profile Send private message ICQ Number Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.