flat assembler
Message board for the users of flat assembler.

Index > Linux > Print argc + argv to stdout sample program

Author
Thread Post new topic Reply to topic
alkap



Joined: 18 Feb 2015
Posts: 44
Location: Dnipro, Ukraine
alkap
Hi,
I am fairly new to assembly programming.
I was wondering if I could get some feedback on the code shown below, in terms of constructive criticism, suggestions for improvements, etc. Thanks.
Code:
; cmdargs -- display argc, and argv
; fasm cmdargs.fasm cmdargs

format ELF executable 3
entry start

segment readable executable

start:
        push    argcstr
        call    print
        add     esp, 4

        pop     ecx                     ; convert argc to string representation
        add     ecx, '0'
        push    ecx
        mov     ecx, esp

        push    ecx
        call    print
        add     esp, 4

        push    nl
        call    print
        add     esp, 4

        pop ecx                 ; get rid of argc

argv:
        pop ecx                 ; pop off stack argvn
        test ecx, ecx
        jz exit

        push ecx
        call print
        add esp, 4

        push nl
        call print
        add esp, 4

        jmp argv

exit:
        mov     eax, 1
        xor     ebx, ebx
        int     80h

strlen:
        push    ebp
        mov     ebp, esp

        xor     eax, eax
        mov     ecx, -1
        cld

        mov     edi, [ebp+8]
        repnz   scasb
        mov     eax, edi
        sub     eax, [ebp+8]
        sub     eax, 1

        mov     esp, ebp
        pop     ebp
        ret

print:
        push    ebp
        mov     ebp, esp

        mov     eax, [ebp+8]
        push    eax
        call    strlen
        add     esp, 4

        mov     edx, eax
        mov     eax, WRITE
        mov     ebx, STDOUT
        mov     ecx, [ebp+8]
        int     80h

        mov     esp, ebp
        pop     ebp
        ret

segment readable        writeable

        argcstr db 'argc: ', 0
        nl      db 0Ah, 0

        WRITE   = 4
        STDOUT  = 1
    
Post 27 Feb 2015, 09:53
View user's profile Send private message Send e-mail Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 17279
Location: In your JS exploiting you and your system
revolution
Looks good.

But what happens when you have more than 9 arguments? Wink
Post 27 Feb 2015, 10:20
View user's profile Send private message Visit poster's website Reply with quote
alkap



Joined: 18 Feb 2015
Posts: 44
Location: Dnipro, Ukraine
alkap
Thanks for replying.
Outputting a string representation of argc when its value is greater than 9 calls for a better int2str routine, doesn't it?
I'll go away and work on this one. Thanks for the pointer.
Post 27 Feb 2015, 11:16
View user's profile Send private message Send e-mail Reply with quote
HaHaAnonymous



Joined: 02 Dec 2012
Posts: 1180
Location: Unknown
HaHaAnonymous
[ Post removed by author. ]


Last edited by HaHaAnonymous on 28 Feb 2015, 17:38; edited 2 times in total
Post 27 Feb 2015, 16:13
View user's profile Send private message Reply with quote
alkap



Joined: 18 Feb 2015
Posts: 44
Location: Dnipro, Ukraine
alkap
Wow! Thanks for your input.
It's all fairly new to me. So, I'll need some time to digest what's been said.
Everyone's input is much appreciated though.
Post 27 Feb 2015, 17:03
View user's profile Send private message Send e-mail Reply with quote
alkap



Joined: 18 Feb 2015
Posts: 44
Location: Dnipro, Ukraine
alkap
I've come up with a program to test itoa, which seems to work as intended:
Code:
; itoa -- convert integers to strings.
; fasm itoa.fasm itoa

format ELF executable 3
entry start

segment readable executable

start:
        push    824
        push buf
        call    itoa

        push    buf
        call    print

        push    nl
        call    print

exit:
        xor     ebx, ebx
        mov     eax, 1
        int     80h


itoa:
        push    ebp
        mov     ebp, esp

        xor     ecx, ecx                ; string buffer index
        mov     eax, [ebp+0Ch]  ; dividend
        mov     ebx, 0Ah                ; divisor
        mov     esi, [ebp+8]    ; string buffer

L1:
        cdq
        div     ebx                     ; eax = edx:eax/ebx
        add     edx, '0'                ; convert int to ascii
        mov     byte [esi+ecx*1], dl
        inc     ecx
        test    eax, eax
        jz      L2
        jmp     L1
L2:
        dec     ecx
        xor     edx, edx
L3:
        cmp     ecx, edx
        jb      L4
        mov     al, byte [esi+edx*1]
        xchg    al, byte [esi+ecx*1]
        mov     byte [esi+edx*1], al
        inc     edx
        dec     ecx
        jmp     L3
L4:
        inc     edx
        mov     byte [esi+edx*1], 0
        
        mov     esp, ebp
        pop     ebp
        ret     4*2


strlen:
        push    ebp
        mov     ebp, esp

        push    edi
        sub     ecx, ecx
        mov     edi, [ebp+8]
        not     ecx
        sub     al, al
        cld
        repne scasb
        not     ecx
        pop     edi
        lea     eax, [ecx-1]    

        mov     esp, ebp
        pop     ebp
        ret     4

print:
        push    ebp
        mov     ebp, esp

        mov     eax, [ebp+8]
        push    eax
        call    strlen

        mov     edx, eax
        mov     eax, WRITE
        mov     ebx, STDOUT
        mov     ecx, [ebp+8]
        int     80h

        mov     esp, ebp
        pop     ebp
        ret     4

segment readable writable

        buf     rb 0Bh
        nl      db 0Ah, 0

        WRITE   = 4
        STDOUT  = 1
    


But when I try to use itoa in my cmdargs program, I get a segmentation fault, when accessing the itoa parameters. My guess is, my passing the value of argc to itoa is wrong. I've spent some time trying to figure out what's wrong, but so far to no avail. I would appreciate some hints please. Thanks.

Code:
; cmdargs -- display argc, and argv
; fasm cmdargs.fasm cmdargs

format ELF executable 3
entry start

segment readable executable

start:
        push    argcstr
        call    print

        pop     eax                     ; convert argc to string representation
        push    eax
        push    buf
        call    itoa

        push    buf
        call    print

        push    nl
        call    print

        pop ecx                 ; get rid of argc

argv:
        pop ecx                 ; pop off stack argvn
        test ecx, ecx
        jz exit

        push ecx
        call print

        push nl
        call print

        jmp argv

exit:
        mov     eax, 1
        xor     ebx, ebx
        int     80h

itoa:
        push    ebp
        mov     ebp, esp

        xor     ecx, ecx                ; string buffer index
        mov     eax, [ebp+0Ch]  ; dividend
        mov     ebx, 0Ah                ; divisor
        mov     esi, [ebp+8]    ; string buffer

L1:
        cdq
        div     ebx                     ; eax = edx:eax/ebx
        add     edx, '0'                ; convert int to ascii
        mov     byte [esi+ecx*1], dl    ; [base+index*scale]
        inc     ecx
        test    eax, eax
        jz      L2
        jmp     L1
L2:
        dec     ecx
        xor     edx, edx
L3:
        cmp     ecx, edx
        jb      L4
        mov     al, byte [esi+edx*1]
        xchg    al, byte [esi+ecx*1]
        mov     byte [esi+edx*1], al
        inc     edx
        dec     ecx
        jmp     L3
L4:
        inc     edx
        mov     byte [esi+edx*1], 0
        
        mov     esp, ebp
        pop     ebp
        ret     4*2

strlen:
        push    ebp
        mov     ebp, esp

        xor     eax, eax
        mov     ecx, -1
        cld

        mov     edi, [ebp+8]
        repnz   scasb
        mov     eax, edi
        sub     eax, [ebp+8]
        sub     eax, 1

        mov     esp, ebp
        pop     ebp
        ret 4

print:
        push    ebp
        mov     ebp, esp

        mov     eax, [ebp+8]
        push    eax
        call    strlen

        mov     edx, eax
        mov     eax, WRITE
        mov     ebx, STDOUT
        mov     ecx, [ebp+8]
        int     80h

        mov     esp, ebp
        pop     ebp
        ret 4


segment readable        writeable

        argcstr db 'argc: ', 0
        nl              db 0Ah, 0

        buf             rb 0Bh

        WRITE   = 4
        STDOUT  = 1
    
Post 28 Feb 2015, 16:46
View user's profile Send private message Send e-mail Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 17279
Location: In your JS exploiting you and your system
revolution
It appears as though you forgot to remove this line when you converted to stdcall format:
Code:
       pop ecx                 ; get rid of argc    
Post 28 Feb 2015, 16:52
View user's profile Send private message Visit poster's website Reply with quote
alkap



Joined: 18 Feb 2015
Posts: 44
Location: Dnipro, Ukraine
alkap
Thanks. But that doesn't seem to be it.
I've removed the line you referred to, recompiled the program, and still getting a segfault:
Code:
./cmdargs argv1 argv2 argv3 argv4 argv5 argv6 argv7 argv8
argc: Segmentation fault
    
Post 28 Feb 2015, 17:07
View user's profile Send private message Send e-mail Reply with quote
HaHaAnonymous



Joined: 02 Dec 2012
Posts: 1180
Location: Unknown
HaHaAnonymous
alkap
Your code is crashing in itoa (and probably more ahead), it does not return. I would check that.
Post 11 Mar 2015, 01:17
View user's profile Send private message Reply with quote
alkap



Joined: 18 Feb 2015
Posts: 44
Location: Dnipro, Ukraine
alkap
Thanks. I've been trying to figure out where I went wrong for a little while now.
I too gathered that the problem was either in my manipulating the stack prior to calling itoa, or in itoa itself. Although the itoa test program seems to work alright.
As far as I understand how itoa is supposed to work is, it doesn't return any value, but it manipulates the buffer via a pointer, esi.
I've also been looking into the way display_digit was implemented in the fasm source code for some insightful ideas.
I'll keep at it, and report back when succeeded.
Post 11 Mar 2015, 08:14
View user's profile Send private message Send e-mail Reply with quote
revolution
When all else fails, read the source


Joined: 24 Aug 2004
Posts: 17279
Location: In your JS exploiting you and your system
revolution
There are a couple of problems with your string reversing code.

1) For a length of one character you have ecx=0 which causes the loop to execute over the entire memory.
2) for longer strings you truncate it in the middle of the number.

I suggest you test your itoa with numbers of varying lengths from 1 to 10 digits to root out all the bugs.
Post 11 Mar 2015, 08:39
View user's profile Send private message Visit poster's website Reply with quote
alkap



Joined: 18 Feb 2015
Posts: 44
Location: Dnipro, Ukraine
alkap
Thanks for the pointers.
I'll report back on the progress made.
Post 11 Mar 2015, 09:10
View user's profile Send private message Send e-mail Reply with quote
alkap



Joined: 18 Feb 2015
Posts: 44
Location: Dnipro, Ukraine
alkap
Version of itoa inspired by code found in Programming Ground Up, chapter 10, section Converting Numbers for Display.

Code:
; cmdargs -- display argc, and argv
; fasm cmdargs.fasm cmdargs

format ELF executable 3
entry start

segment readable executable

start:
        push     argcstr
        call    print

        pop     ecx             ; convert argc to string representation
        push    ecx
        push    buf
        call    itoa

        push    buf
        call    print

        push    nl
        call    print

argv:
        pop ecx                 ; pop off stack argvn
        test ecx, ecx
        jz exit

        push ecx
        call print

        push nl
        call print

        jmp argv

exit:
        mov     eax, 1
        xor     ebx, ebx
        int     80h

itoa:
        push ebp
        mov     ebp, esp

        xor     ecx, ecx                ; string buffer index
        mov     eax, [ebp+0Ch]  ; dividend
        mov     edi, 0Ah                ; divisor

conversion_loop:
        xor     edx, edx
        div     edi                     ; eax = edx:eax/ebx
        add     edx, '0'                ; convert int to ascii
        push    edx
        inc     ecx
        test    eax, eax
        jz      end_conversion_loop
        jmp     conversion_loop

end_conversion_loop:
        mov     edx, [ebp+8]    ; string buffer

copy_reversing_loop:
        pop     eax
        mov     byte [edx], al
        dec     ecx
        inc     edx
        test    ecx, ecx
        jz      end_copy_reversing_loop
        jmp     copy_reversing_loop
        
end_copy_reversing_loop:
        mov     byte [edx], 0
        
        mov     esp, ebp
        pop     ebp
        ret     4*2

strlen:
        push    ebp
        mov     ebp, esp

        push    edi
        sub     ecx, ecx
        mov     edi, [ebp+8]
        not     ecx
        sub     al, al
        cld
        repne scasb
        not     ecx
        pop     edi
        lea     eax, [ecx-1]    

        mov     esp, ebp
        pop     ebp
        ret     4

print:
        push    ebp
        mov     ebp, esp

        mov     eax, [ebp+8]
        push    eax
        call    strlen

        mov     edx, eax
        mov     eax, WRITE
        mov     ebx, STDOUT
        mov     ecx, [ebp+8]
        int     80h

        mov     esp, ebp
        pop     ebp
        ret     4


segment readable        writeable

        argcstr db 'argc: ', 0
        nl              db 0Ah, 0

        buf             rb 0Bh

        WRITE   = 4
        STDOUT  = 1
    

Sample run:
Code:
./cmdargs argv1 argv2 argv3 argv4 argv5 argv6 argv7 argv8 argv9 argv10 argv11 argv12
argc: 13
./cmdargs
argv1
argv2
argv3
argv4
argv5
argv6
argv7
argv8
argv9
argv10
argv11
argv12
    
Post 12 Mar 2015, 21:11
View user's profile Send private message Send e-mail Reply with quote
HaHaAnonymous



Joined: 02 Dec 2012
Posts: 1180
Location: Unknown
HaHaAnonymous
alkap
I'm glad you fixed it!

Quote:

if I could get some feedback on the code shown below, in terms of constructive criticism, suggestions for improvements, etc. Thanks.

Aligning the start of every "function" and "data" (preferably at 16, or at least a multiple of 2) seems to be beneficial. You can use the "align" directive for that.

Not that it would bring something catastrophically good, it is just a hint. I am not the right person for that but seeing unaligned code and data drives me crazy. D:

I apologize for any inconveniences (if any).
Post 13 Mar 2015, 05:04
View user's profile Send private message Reply with quote
alkap



Joined: 18 Feb 2015
Posts: 44
Location: Dnipro, Ukraine
alkap
HaHaAnonymous
Thanks for your feedback.
So for the 'data' segment the align directive would go straight after the segment definition, as shown below, wouldn't it?
Code:
fasm/source/Linux/fasm.asm:257,259
segment readable writeable

align 4
    


What about functions? Does the align directive go straight after the function label, or would it suffice to declare it globally like so?
Code:
segment readable executable

align 10h

start:
    

Thanks.
Post 13 Mar 2015, 06:26
View user's profile Send private message Send e-mail Reply with quote
HaHaAnonymous



Joined: 02 Dec 2012
Posts: 1180
Location: Unknown
HaHaAnonymous
You just need to put it above the label or data definition (or anything else):
Code:
align 16
function:
      mov eax,eax
      ret

       align 4
data:
       db '0231231'
       align 4
       db '05564'
       align 4
       dd ?
    


Last edited by HaHaAnonymous on 05 Apr 2015, 01:48; edited 1 time in total
Post 13 Mar 2015, 06:33
View user's profile Send private message Reply with quote
alkap



Joined: 18 Feb 2015
Posts: 44
Location: Dnipro, Ukraine
alkap
Understood. Thanks.
Post 13 Mar 2015, 06:58
View user's profile Send private message Send e-mail Reply with quote
alkap



Joined: 18 Feb 2015
Posts: 44
Location: Dnipro, Ukraine
alkap
I thought I'd try implementing cmdargs with function parameters passed via registers. Once again, I'd appreciate this forum's members' feedback on this piece of code. I looked to fasm's source code for ideas.
Code:
; cmdargs_regs -- test passing function parameter passing via registers.
; fasm cmdargs_regs.fasm cmdargs_regs

format ELF executable 3
entry start

segment readable executable

start:
        mov     esi, argcstr
        call    print

        pop     edx                             ; edx = argc
        mov     esi, buf
        call    itoa                            ; void itoa(int, char*)

        mov     esi, buf
        call    print                           ; void print(char*)

        mov     esi, nl
        call    print

argv:
        pop     edx                             ; pop   off stack argv
        test    edx, edx
        jz      exit

        mov     esi, edx
        call    print

        mov     esi, nl
        call    print

        jmp     argv

exit:
        movzx   ebx,al
        mov             eax, EXIT
        int             80h

align   16
itoa:                                           ; void itoa(int, char*); eax = dividend
                                                ; esi = char*
        mov     eax, edx                        ; eax = dividend
        xor     ecx, ecx                        ; string buffer index
        mov     ebx, 0Ah                        ; divisor

conversion_loop:
        xor     edx, edx
        div     ebx                             ; eax   = edx:eax/ebx
        add     edx, '0'                        ; convert int to ascii
        push    edx
        inc     ecx
        test    eax, eax
        jz      end_conversion_loop
        jmp     conversion_loop

end_conversion_loop:
        mov     edx, esi                        ; string buffer

copy_reversing_loop:
        pop     eax
        mov     byte    [edx], al
        dec     ecx
        inc     edx
        test    ecx, ecx
        jz      end_copy_reversing_loop
        jmp     copy_reversing_loop
        
end_copy_reversing_loop:
        mov     byte    [edx], 0
        ret

align   16
strlen:                         ; int strlen(char*); esi == char*; eax == strlen
        mov     edi, esi
        or      ecx, -1
        xor     al, al
        repne scasb
        neg     ecx
        sub     ecx, 2
        xchg    eax, ecx
        ret

align   16
print:                                  ; int print(char*); esi = char*
        call    strlen
        mov     edx, eax
        mov     eax, WRITE
        mov     ebx, STDOUT
        mov     ecx, esi
        int     80h                             ; TODO: check for write error
        ret

segment readable        writeable

align   4
        argcstr db      'argc: ',0
        nl              db      0Ah,0

        buf             rb      0Bh

        WRITE   =       4
        EXIT            =       1
        STDOUT  =       1
    

Sample output:
Code:
./cmdargs_regs argv^(1 2 3 4 5 6 7 8 9 10)
argc: 11
./cmdargs_regs
argv1
argv2
argv3
argv4
argv5
argv6
argv7
argv8
argv9
argv10
    
Post 15 Mar 2015, 19:01
View user's profile Send private message Send e-mail Reply with quote
HaHaAnonymous



Joined: 02 Dec 2012
Posts: 1180
Location: Unknown
HaHaAnonymous
Quote:

Once again, I'd appreciate this forum's members' feedback on this piece of code.

The align directive aligns only the data that comes after it... Then further alignment is needed (if you desire) for the others if your data size was not enough to align.
Code:
align 4
data0: db '7 bytes'
; the data below is not aligned, it starts at offset 7
data1: db 'not aligned'
    


In my opinion, it would be nice if you "separate" the initialized data (often read-only) from the data that is retrieved at run-time by another means and here are some of my ideas of code organizations most people hate:
Code:
format ELF executable 3

entry function

segment readable executable

align 16
function:
                ; as you can see, it is able to fit the longest instruction "name" without causing trouble to your code
                mov        eax,eax
               movd        eax,xmm0
   vaeskeygenassist        xmm1,xmm2,8
             movdqa        xmm0, dqword [data01]
                ret

align 16
function2:
          ; as you can see, it is able to fit the longest instruction "name" without causing trouble to your code
          mov                eax,eax
          sub                eax,eax
          pop                eax
          vaeskeygenassist   xmm1,xmm2,8
          jmp                eax

segment readable
; read only data
align 4
data0:                 db 'Woof, woof!'

align 4
data1:                 db 'Fatal Error! D:'

; for strange reasons, also align file size 16 bytes, or whatever your prefer :D
align 16
; add more 16 bytes to realize alignment
dq 2 dup($00)

segment readable writeable
; read write uninitialized data, buffers, etc...
align 4
buf1:                  rb 1024

align 4
buf2:                  rq 8

align 4
data00:                rb 256

align 16
data01:                rq 16

align 16
data02:                dq ?
    

Or you can put your data above your executable section, or whatever order you wish!


Last edited by HaHaAnonymous on 22 Mar 2015, 02:11; edited 6 times in total
Post 15 Mar 2015, 20:24
View user's profile Send private message Reply with quote
alkap



Joined: 18 Feb 2015
Posts: 44
Location: Dnipro, Ukraine
alkap
I see. Thanks.
Post 15 Mar 2015, 20:29
View user's profile Send private message Send e-mail 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-2020, Tomasz Grysztar.

Powered by rwasa.