flat assembler
Message board for the users of flat assembler.
Index
> Main > Self assembling asm file. ASM+shell script in one file |
Author |
|
revolution 03 Feb 2023, 08:52
Here is a c+shell script version. Naturally the c code is just a wrapper for the more important asm code.
Code: //bin/true ' /*' NAME="${0%.*}" for BITS in 32 64 ; do gcc -m$BITS -DBITS=$BITS -x c -masm=intel -o "$NAME" "${0}" && "$NAME" && rm "$NAME" || exit $? done exit 0 */ #include <unistd.h> // STDOUT_FILENO #include <sys/syscall.h> // SYS_write, SYS_exit #define S_(v) #v #define S(v) S_(v) // convert macro into literal string const char text[] = "Hello from "S(BITS)"-bit world!\n"; int main() { asm ( #if BITS == 64 " mov eax, "S(SYS_write)" \n" " mov edi, "S(STDOUT_FILENO)" \n" " lea rsi, %[str] \n" " mov edx, %[len] \n" " syscall \n" " mov eax, "S(SYS_exit)" \n" " xor edi, edi \n" " syscall \n" #else " mov eax, "S(SYS_write)" \n" " mov ebx, "S(STDOUT_FILENO)" \n" " lea ecx, %[str] \n" " mov edx, %[len] \n" " int 0x80 \n" " mov eax, "S(SYS_exit)" \n" " xor ebx, ebx \n" " int 0x80 \n" #endif : : [str] "m" (text), [len] "i" (sizeof (text)-1) ); } |
|||
03 Feb 2023, 08:52 |
|
revolution 05 Feb 2023, 14:49
There is a way to eliminate the extraneous false label and leave no constraints on the assembly source.
Bash + fasm: Code: false equ ; if false ; then restore false rept 0 { fi #; save this as a .asm file and chmod +x #; within the shell: #; Linux automatically runs the default shell when text files don't have the leading #! signature #; the shell then runs the "false" executable and the argument "equ" is ignored #; then the if/then/fi block encloses two fasm lines #; use "exit" at the end to prevent the shell from trying to interpret the assembly code #; within fasm: #; make "false" an equ and then restore it #; use "rept 0" to create a dummy block around this text #; you can include closing "}"s inside this text if they are enclosed within quotes for BITS in 32 64 ; do EXE_NAME="${0%.*}.$BITS" # strip the trailing extension ".asm" # the closing "}" there is ignored by fasm because it is inside quotes fasm -DBITS=$BITS "$0" "$EXE_NAME" && "$EXE_NAME" && rm "$EXE_NAME" || exit $? done exit $? } ; close the dummy block and begin the assembly code match BITS, BITS { ; BITS is defined on the command line elf32 equ elf format elf#BITS executable text db 'Hello from ',`BITS,'-bit world!',10 len = $ - text } STDOUT_FILENO = 1 entry $ if BITS = 64 SYS_write = 1 SYS_exit = 60 mov eax, SYS_write mov edi, STDOUT_FILENO lea rsi, [text] mov edx, len syscall mov eax, SYS_exit xor edi, edi syscall else SYS_write = 4 SYS_exit = 1 mov eax, SYS_write mov ebx, STDOUT_FILENO mov ecx, text mov edx, len int 0x80 mov eax, SYS_exit xor ebx, ebx int 0x80 end if |
|||
05 Feb 2023, 14:49 |
|
revolution 07 Feb 2023, 05:48
Bash + Batch + Windows + Linux + fasm self assembling hybrid, just run it on whatever system you have.
Code: echo equ >/dev/null ; if false ; then # 2>NUL >NUL & goto BATCH restore echo rept 0 { ; open dummy block so fasm ignores the build scripts fi # # #; save this with CR/LF line endings as a .bat file and chmod +x #; it needs a .bat extension or Windows won't run it #; it needs chmod +x or *nix won't run it #; each *nix script line needs to have a # to comment out the CR # #; bash script portion #; this assembles and runs the ELF parts FORMAT=elf # for BITS in 32 64 ; do # EXE_NAME="${0%.*}.$BITS" # strip the trailing extension ".bat" fasm -DFORMAT=$FORMAT$BITS "$0" "$EXE_NAME" && "$EXE_NAME" && rm "$EXE_NAME" || exit $? # done # exit $? # :BATCH rem cmd script portion rem assembles and runs the PE parts @echo off set FORMAT=pe set EXE_NAME=%~dpn0 for %%E in (32, 64) do ( if errorlevel 1 goto :DONE fasm -DFORMAT=%FORMAT%%%E "%0" "%EXE_NAME%.%%E.exe" if errorlevel 1 goto :DONE "%EXE_NAME%.%%E.exe" if errorlevel 1 goto :DONE del "%EXE_NAME%.%%E.exe" if errorlevel 1 goto :DONE ) :DONE exit /b %errorlevel% } ; close the dummy block and begin the assembly code STDOUT_FILENO = 1 STD_OUTPUT_HANDLE = -11 if FORMAT eq elf32 format elf executable text db 'Hello from 32-bit ELF world!',10 len = $ - text SYS_write = 4 SYS_exit = 1 entry $ mov eax,SYS_write mov ebx,STDOUT_FILENO mov ecx,text mov edx,len int 0x80 mov eax,SYS_exit xor ebx,ebx int 0x80 else if FORMAT eq elf64 format elf64 executable text db 'Hello from 64-bit ELF world!',10 len = $ - text SYS_write = 1 SYS_exit = 60 entry $ mov eax,SYS_write mov edi,STDOUT_FILENO lea rsi,[text] mov edx,len syscall mov eax,SYS_exit xor edi,edi syscall else if FORMAT eq pe32 format pe console data import dd 0,0,0,rva kernel_name,rva kernel_table dd 0,0,0,0,0 kernel_table: ExitProcess dd rva _ExitProcess GetStdHandle dd rva _GetStdHandle WriteFile dd rva _WriteFile dd 0 kernel_name db 'KERNEL32.DLL',0 _ExitProcess db 0,0,'ExitProcess',0 _GetStdHandle db 0,0,'GetStdHandle',0 _WriteFile db 0,0,'WriteFile',0 end data text db 'Hello from 32-bit PE world!',13,10 len = $ - text entry $ push STD_OUTPUT_HANDLE call [GetStdHandle] push 0 mov ecx,esp push 0 push ecx push len push text push eax call [WriteFile] push 0 call [ExitProcess] else if FORMAT eq pe64 format pe64 console data import dd 0,0,0,rva kernel_name,rva kernel_table dd 0,0,0,0,0 kernel_table: ExitProcess dq rva _ExitProcess GetStdHandle dq rva _GetStdHandle WriteFile dq rva _WriteFile dq 0 kernel_name db 'KERNEL32.DLL',0 _ExitProcess db 0,0,'ExitProcess',0 _GetStdHandle db 0,0,'GetStdHandle',0 _WriteFile db 0,0,'WriteFile',0 end data text db 'Hello from 64-bit PE world!',13,10 len = $ - text entry $ sub rsp, 8 * 7 mov ecx,STD_OUTPUT_HANDLE call [GetStdHandle] mov rcx,rax lea rdx,[text] mov r8d,len lea r9,[rsp + 4 * 8] mov qword[rsp + 4 * 8],0 call [WriteFile] xor ecx,ecx call [ExitProcess] end if Last edited by revolution on 07 Feb 2023, 10:25; edited 1 time in total |
|||
07 Feb 2023, 05:48 |
|
ProMiNick 07 Feb 2023, 07:25
features that could be added(if possible):
check presence of fasm (presence of it setup(value of INCLUDE environment variable, is in linux exist equivalent of environment var, is it reachable from script?)) download fasm if notexisted, setup its INCLUDE var by the way only console apps could be created & executed this way ? will script wait until GUI executable will close to delete executable (by GUI I mean GUI for both windows & linux, may be executing one of them not reachable from script in principle)? could script hide console window for cases of GUI executables? could script determine console or GUI. all question are related to potential functionality of scripts, not to current script. if add these features fasm would become full featured interpreter environment, while it source files would become equaled to native executables. at current stage with absence of INCLUDE and limitation to console only - fasm is only console limited interpreter environment. realy impressive, even on current state. |
|||
07 Feb 2023, 07:25 |
|
revolution 07 Feb 2023, 07:30
You have the entire power of the scripting engine of your system to use. Go for it. Add all of those things.
I only posted the simplest examples so as not to get bogged down with tricky details hiding the meat. |
|||
07 Feb 2023, 07:30 |
|
ProMiNick 07 Feb 2023, 08:13
Go for it - how about 50/50? In batch I could try to realise its part. But in case of bash I`m zero.
|
|||
07 Feb 2023, 08:13 |
|
revolution 07 Feb 2023, 10:22
Adding " & rem ^" the the end of the first line can allow for formatting with just LF line endings. Some newer versions of CMD.EXE are okay with only LF terminations.
Code: echo equ >/dev/null ; if false ; then # 2>NUL >NUL & goto BATCH & rem ^ ;... |
|||
07 Feb 2023, 10:22 |
|
revolution 07 Feb 2023, 10:24
ProMiNick wrote: Go for it - how about 50/50? In batch I could try to realise its part. But in case of bash I`m zero. Post your stuff we can see how it goes. |
|||
07 Feb 2023, 10:24 |
|
revolution 07 Feb 2023, 10:50
It is possible to make common code that works in 32-bit and 64-bit. Eliminating some need to create different versions for each bit width. Then add only minimal code to give an OS interface.
Code: echo equ >/dev/null ; if false ; then # 2>NUL >NUL & goto BATCH & rem ^ restore echo rept 0 { ; open dummy block so fasm ignores the build scripts fi # # #; save this with CR/LF line endings as a .bat file and chmod +x #; it needs a .bat extension or Windows won't run it #; it needs chmod +x or *nix won't run it #; each *nix script line needs to have a # to comment out the CR # #; bash script portion #; this assembles and runs the ELF parts FORMAT=ELF # for BITS in 32 64 ; do # EXE_NAME="${0%.*}.$BITS" # strip the trailing extension ".bat" fasm -DFORMAT=$FORMAT -DBITS=$BITS "$0" "$EXE_NAME" && # "$EXE_NAME" && rm "$EXE_NAME" || exit $? # done # exit $? # :BATCH @echo off rem cmd script portion rem assembles and runs the PE parts set FORMAT=PE set EXE_NAME=%~dpn0 for %%E in (32, 64) do ( if errorlevel 1 goto :DONE fasm -DFORMAT=%FORMAT% -DBITS=%%E "%0" "%EXE_NAME%.%%E.exe" if errorlevel 1 goto :DONE "%EXE_NAME%.%%E.exe" if errorlevel 1 goto :DONE del "%EXE_NAME%.%%E.exe" if errorlevel 1 goto :DONE ) :DONE exit /b %errorlevel% } ; close the dummy block and begin the assembly code ; internally use STDCALL for 32-bit, and a dodgy version of STDCALL for 64-bit ; OS interfacing if FORMAT eq ELF STDOUT_FILENO = 1 if BITS eq 32 format elf executable SYS_write = 4 SYS_exit = 1 write: push ebx mov eax,SYS_write mov ebx,STDOUT_FILENO mov ecx,[esp+12] ;text mov edx,[esp+8] ;len int 0x80 pop ebx ret 8 exit: mov eax,SYS_exit mov ebx,[esp+4] int 0x80 else if BITS eq 64 format elf64 executable SYS_write = 1 SYS_exit = 60 write: push rsi rdi mov eax,SYS_write mov edi,STDOUT_FILENO mov rsi,[rsp+0x20] ;text mov edx,[rsp+0x18] ;len syscall pop rdi rsi ret 0x10 exit: mov eax,SYS_exit mov rdi,[rsp+0x8] syscall end if else if FORMAT eq PE macro imports def { data import dd 0,0,0,rva kernel_name,rva kernel_table dd 0,0,0,0,0 kernel_table: ExitProcess def rva _ExitProcess GetStdHandle def rva _GetStdHandle WriteFile def rva _WriteFile def 0 kernel_name db 'KERNEL32.DLL',0 _ExitProcess db 0,0,'ExitProcess',0 _GetStdHandle db 0,0,'GetStdHandle',0 _WriteFile db 0,0,'WriteFile',0 end data } STD_OUTPUT_HANDLE = -11 if BITS eq 32 format pe console imports dd write: push STD_OUTPUT_HANDLE call [GetStdHandle] push 0 mov ecx,esp push 0 push ecx push dword[esp+0x10] ;len push dword[esp+0x18] ;text push eax call [WriteFile] add esp,4 ret 8 exit: add esp,4 call [ExitProcess] else if BITS eq 64 format pe64 console imports dq write: mov r12,rsp and rsp,-0x10 sub rsp, 8 * 6 mov ecx,STD_OUTPUT_HANDLE call [GetStdHandle] mov rcx,rax mov rdx,[r12+0x10] ;text mov r8,[r12+0x8] ;len lea r9,[rsp + 4 * 8] mov qword[rsp + 4 * 8],0 call [WriteFile] mov rsp,r12 ret 0x10 exit: mov rcx,[rsp+8] and rsp,-0x10 sub rsp, 8 * 4 call [ExitProcess] end if purge imports end if ; create an alternate set of register names tax, tbx, ... tsp match =32, BITS { irp reg,ax,bx,cx,dx,si,di,bp,sp \{ t\#reg equ e\#reg \} } match =64, BITS { irp reg,ax,bx,cx,dx,si,di,bp,sp \{ t\#reg equ r\#reg \} } ; common code for all variants text1 db 'Hello from MAX=' len1 = $ - text1 text2 db ' ' match F,FORMAT{db `F} db ' world!',13,10 len2 = $ - text2 print_decimal: .buff_size = BITS mov tax,[tsp+BITS shr 3] push tsi tdi mov tsi,tsp sub tsp,.buff_size mov ecx,10 .loop: xor edx,edx div tcx dec tsi add dl,'0' mov [tsi],dl test tax,tax jnz .loop lea tdx,[tsp+.buff_size] sub tdx,tsi push tsi tdx call write add tsp,.buff_size pop tdi tsi ret (BITS shr 3) * 1 entry $ lea tax,[text1] push tax len1 call write push -1 call print_decimal lea tax,[text2] push tax len2 call write push 0 call exit |
|||
07 Feb 2023, 10:50 |
|
revolution 08 Feb 2023, 04:52
The extraneous first line echo in CMD can be eliminated by changing the first two lines:
Code: @echo equ 2>/dev/null ; if false ; then # 2>NUL >NUL & goto BATCH & rem ^ restore @echo Last edited by revolution on 10 Feb 2023, 15:00; edited 1 time in total |
|||
08 Feb 2023, 04:52 |
|
revolution 09 Feb 2023, 17:55
With the extreme permissiveness in Linux with filenames it turns out the trying to replace the extention portion is riddled with problems. The method I used first EXE_NAME="${0%.*} fails in many ways.
I came up with this small function just_name and a few test cases. It "works" as far as I can tell. I'm probably missing an obvious, more robust, and more elegant, way to do this. Code: just_name () { PATH_=$(dirname -- "${1}") NAME=$(basename -- "${1}") EXT="${NAME##*.}" NAME=$(basename -- "${NAME}" ".${EXT}") EXE_NAME="${PATH_}/${NAME}" echo "${EXE_NAME}" ; } # some test vectors declare -a a a[0]="/path.to/me.ext" a[1]="/path.to/me." a[2]="/path.to/me" a[3]="/path.to/.me" a[4]="me.ext" a[5]="me." a[6]="me" a[7]=".me" a[8]="--me" a[9]=".--me" a[10]="m e.ext" a[11]="m e." a[12]="m e" a[13]="m e.ext" a[14]="m e." a[15]="m e" a[16]="m "$'\n'" e" for path in "${a[@]}" ; do just_name "${path}" done |
|||
09 Feb 2023, 17:55 |
|
Furs 10 Feb 2023, 14:19
Why replace any generic extension? Just append to it, or replace a specific extension (e.g. .sh) if you want to. I would certainly be wary of a tool that tries to be too smart if I rename its filename to something else.
Anyway this is pretty awesome. |
|||
10 Feb 2023, 14:19 |
|
revolution 10 Feb 2023, 15:13
Furs wrote: Why replace any generic extension? Just append to it, or replace a specific extension (e.g. .sh) if you want to. It's just that GenericAppName.bat.exe doesn't feel right to me. That is like something phishing emails do ImportantDocument.txt.exe. So if one doesn't care about the final name then a more simple SOURCE_NAME=$(dirname -- "$0")/$(basename -- "$0") creates a "sanitised" name always starting with either dot or slash so no accidental dash to invoke trouble. |
|||
10 Feb 2023, 15:13 |
|
revolution 11 Feb 2023, 05:15
Less than 1000 bytes source, 4 lines of preamble. A bit of code golf to show the bare minimum.
Code: @echo equ 2>/dev/null ; if false ; then # 2>NUL >NUL & goto BATCH & rem ^ rept 0 { ; fi ; fasm -DFORMAT=elf "$0" "$0.32" && "$0.32" && rm "$0.32" ; exit $? # :BATCH @echo off & fasm -DFORMAT=pe "%0" & "%~dpn0" & del "%~dpn0.exe" & exit /b %errorlevel% & } if FORMAT eq elf format elf executable mov eax,4 ;SYS_write mov ebx,1 ;STDOUT_FILENO mov ecx,text mov edx,len int 0x80 mov eax,1 ;SYS_exit xor ebx,ebx int 0x80 else if FORMAT eq pe format pe console push -11 ;STD_OUTPUT_HANDLE call [GetStdHandle] lea ecx,[esp-8] push 0 0 ecx len text eax call [WriteFile] call [ExitProcess] kernel_name db 'KERNEL32.DLL',0 _ExitProcess db 0,0,'ExitProcess',0 _GetStdHandle db 0,0,'GetStdHandle',0 _WriteFile db 0,0,'WriteFile',0 ExitProcess dd rva _ExitProcess GetStdHandle dd rva _GetStdHandle WriteFile dd rva _WriteFile,0 data import dd 0,0,0,rva kernel_name,rva ExitProcess,5 dup 0 end data end if text db 'Hello world!',13,10 len = $-text |
|||
11 Feb 2023, 05:15 |
|
Furs 11 Feb 2023, 19:09
revolution wrote:
I guess you need to make sure it's not named only .bat (i.e. starting with dot) cause that's a special case on Linux (hidden files). |
|||
11 Feb 2023, 19:09 |
|
revolution 14 Feb 2023, 20:48
An auto-patching x32 executable.
fasm doesn't natively support x32 outputs. But it is easy to make an x32 executable. Code: false equ ; if false ; then rept 0 { ; fi EXE_NAME="$0.x32" fasm "$0" "$EXE_NAME" || exit $? # patch byte at 0x12 (e_machine) from 0x03 (x86) to 0x3e (AMD x86-64) printf '\x3e' | dd of="$EXE_NAME" count=1 bs=1 seek=18 conv=notrunc 2>/dev/null || exit $? "$EXE_NAME" || exit $? rm "$EXE_NAME" || exit $? exit $? } restore false format elf executable at 1 shl 16 ; normal 32 bit elf header that needs byte 0x12 patched use64 ; x32 is 64-bit mode ; the syscall entry numbers have their own range ; see /usr/include/x86_64-linux-gnu/asm/unistd_x32.h __X32_SYSCALL_BIT = 0x40000000 SYSx32_write = (__X32_SYSCALL_BIT + 1) SYSx32_exit = (__X32_SYSCALL_BIT + 60) entry $ mov eax,SYSx32_write mov edi,1 ; syscall registers are the same as 64-bit lea esi,[message] ; can use 32-bit addressing mov edx,len ; third arg is rdx ;mov r10,4 ; fourth arg is r10 ;mov r8,5 ; fifth arg is r8 ;mov r9,6 ; sixth arg is r9 syscall mov eax,SYSx32_exit xor edi,edi syscall ; yes add r10,r15 ; can use 64-bit registers pxor xmm15,xmm15 ; can use 16 xmm registers ; no ;push eax ; can't push 32-bit values ;daa ; can't use daa, das, ... etc. message db 'Hello from x32 ELF World!',10 len = $ - message |
|||
14 Feb 2023, 20:48 |
|
< Last Thread | Next Thread > |
Forum Rules:
|
Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.
Website powered by rwasa.