Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing to stderr in x86 assembly

Tags:

x86

assembly

I'm pretty new to assembly, and I was wondering how one would write output to stderr. I know you can access C Standard Library functions, like printf, to print to the console. But I can't figure out how to print to stderr. I was trying to use fprintf, but I'm just sort of guessing at arguments, and I have no idea how to specify stderr as the file pointer. Thanks.

Edit: As per sehe's suggestion, I tried this:

.586
.model small,c
.stack 100h

.data
msg db 'test', 0Ah

.code
includelib MSVCRT
extrn fprintf:near
extrn exit:near

public main
main proc
    push    offset msg
    push    2       ;specify stderr
    call    fprintf ;print to stderr
    push    0
    call    exit    ;exit status code 0

main endp

end main

But it just caused my program to crash. Any other advice?

like image 525
Rose Kunkel Avatar asked Jan 16 '23 10:01

Rose Kunkel


2 Answers

Are you using fprintf from the MSVCRT dll?

The first parameter is a pointer to a stream. Here is how you could use fprintf in assembly. Also, when calling C functions from Assembly, you need to adjust the stack after each call that you push parameters to.

Also, a BIGGIE... your string is NOT NULL terminated! You must NULL terminate your strings, that is how functions find the length of the string. Not sure what Assembler you are using, but this is how you can do it in MASM:

include masm32rt.inc

_iobuf STRUCT
    _ptr        DWORD ?
    _cnt        DWORD ?
    _base       DWORD ?
    _flag       DWORD ?
    _file       DWORD ?
    _charbuf    DWORD ?
    _bufsiz     DWORD ?
    _tmpfname   DWORD ?
_iobuf ENDS

FILE TYPEDEF _iobuf

.data
msg         db 'test', 0Ah, 0    

.data?
stdin       dd ?
stdout      dd ?
stderr      dd ?

.code
start: 

    call    crt___p__iob
    mov     stdin,eax          
    add     eax,SIZEOF(FILE)
    mov     stdout,eax          
    add     eax,SIZEOF(FILE)
    mov     stderr,eax        

    push    offset msg
    push    eax
    call    crt_fprintf
    add     esp, 4 * 2

    push    0
    call    crt_exit 

end start 
like image 100
Gunner Avatar answered Jan 21 '23 19:01

Gunner


I got here because I was having trouble writing to stderr under Debian Linux. I use yasm, because I am going through Ray Seyfarth's book. I am writing in Intel syntax and assembling for the elf64 format and the dwarf2 debugging protocol.

My first attempt to use fprintf met with a segmentation fault. I looked at the internals of stdio. I managed to write the equivalent of a one line program:

     int main()
     {
       fprintf(stderr, "%d\n", 3);
     }

But when I modified it at all, I started getting a segmentation fault again. My goal was to get the assembly equivalent of:

    int main(int argc, char* argv[])
    {
      if (argc != 2) {
        fprintf(stderr, "Usage: %s n\n", argv[0]);
      } else {
        /* program code */
      }
    }

My final solution was to use a sprintf() call and then a write() system call using a file descriptor. A call to strlen() is also needed. This strategy should work in any environment in which calls to C library functions can be made from assembly code. Here is some sample code. If no arguments or too many arguments are given, a usage message is printed and the program exits. If one argument is provided, it is converted to an integer and printed.

        segment .text
        global  main
        extern  printf, atoi, sprintf, strlen, write

    main:
    .argc   equ 0
    .argv   equ 4

        segment .data
    .f1 db  "Usage: %s n", 0x0a, 0
    .f2 db  "%d", 0x0a, 0

        segment .bss
    .buf    resb    255

        segment .text

        push    rbp
        mov rbp, rsp
        sub rsp, 16

        ; Save the arguments.
        mov [rsp+.argc], rdi
        mov [rsp+.argv], rsi

        ; if (argc != 2)
        cmp rdi, 2
        jne .usage

        ; Convert argv[1] to an integer, then print it.
        ; We could have printed argv[1] as a string,
        ; but in an actual program we might want to do
        ; n = atoi(argv[1])
        ; because we are passing a number on the command line.

        ; printf("%d\n", atoi(argv[1]))

        ; Get argv[1].
        mov rax, [rsp+.argv]
        mov rdi, [rax+8]        ; rdi gets *(argv + 1), or argv[1].
        call    atoi            ; Return value in eax.

        lea rdi, [.f2]
        mov esi, eax
        xor eax, eax
        call    printf

        jmp .done

    .usage:
        ; Get argv[0].
        mov rax, [rsp+.argv]
        mov rax, [rax]

        ; sprintf(buf, "Usage: %s n\n", argv[0])
        lea rdi, [.buf]
        lea rsi, [.f1]
        mov rdx, rax
        xor eax, eax
        call    sprintf

        ; len = strlen(buf);
        lea rdi, [.buf]
        call    strlen      ; string length is placed in eax.

        ; write(2, buf, strlen(buf))
        mov edi, 2      ; fd for stderr
        lea rsi, [.buf]
        mov edx, eax
        call    write

    .done:
        leave
        ret

Assuming the above code is contained in progname.asm, this is assembled and linked as follows:

    yasm -f elf64 -g dwarf2 -l progname.lst progname.asm
    gcc progname.o -o progname
like image 43
Unclechromedome Avatar answered Jan 21 '23 19:01

Unclechromedome