I want to access errno present in errno.h in assembly language in order to handle errors of write function call. I found somewhere that make call to _error in assembly language for this purpose but it is throwing errors as :
ExitNewShell.asm:71: error: symbol `_error' undefined ExitNewShell.asm:85: error: symbol `_error' undefined ExitNewShell.asm:98: error: symbol `_error' undefined ExitNewShell.asm:111: error: symbol `_error' undefined ExitNewShell.asm:124: error: symbol `_error' undefined ExitNewShell.asm:137: error: symbol `_error' undefined ExitNewShell.asm:150: error: symbol `_error' undefined ExitNewShell.asm:163: error: symbol `_error' undefined ExitNewShell.asm:176: error: symbol `_error' undefined
My assembly code : ExitNewShell.asm
[SECTION .text] global _start _start: jmp ender starter: xor eax, eax ;clean up the registers xor ebx, ebx xor edx, edx xor ecx, ecx mov al, 4 ;syscall write mov bl, 1 ;stdout is 1 pop ecx ;get the address of the string from the stack mov dl, 11 ;length of the string int 0x80 cmp eax,0xffffffff jne exit call _error mov eax,[eax] cmp eax,0xb jne callOff2 mov dl,14 lea ecx,[msg1] mov bl,1 mov al,4 int 0x80 jmp exit callOff2: call _error mov eax,[eax] cmp eax,0xb jne callOff3 mov dl,14 lea ecx,[msg2] mov bl,1 mov al,4 int 0x80 jmp exit callOff3: call _error mov eax,[eax] cmp eax,0xb jne callOff4 mov dl,14 lea ecx,[msg3] mov bl,1 mov al,4 int 0x80 jmp exit callOff4: call _error mov eax,[eax] cmp eax,0xb jne callOff5 mov dl,14 lea ecx,[msg4] mov bl,1 mov al,4 int 0x80 jmp exit callOff5: call _error mov eax,[eax] cmp eax,0xb jne callOff6 mov dl,14 lea ecx,[msg5] mov bl,1 mov al,4 int 0x80 jmp exit callOff6: call _error mov eax,[eax] cmp eax,0xb jne callOff7 mov dl,14 lea ecx,[msg6] mov bl,1 mov al,4 int 0x80 jmp exit callOff7: call _error mov eax,[eax] cmp eax,0xb jne callOff8 mov dl,14 lea ecx,[msg7] mov bl,1 mov al,4 int 0x80 jmp exit callOff8: call _error mov eax,[eax] cmp eax,0xb jne callOff9 mov dl,14 lea ecx,[msg8] mov bl,1 mov al,4 int 0x80 jmp exit callOff9: call _error mov eax,[eax] cmp eax,0xb jne exit mov dl,14 lea ecx,[msg9] mov bl,1 mov al,4 int 0x80 jmp exit exit: xor eax, eax mov al, 1 ;exit the shellcode xor ebx,ebx int 0x80 ender: call starter ;put the address of the string on the stack db 'Hello World',0xa [SECTION .data] msg1 db 'ERROR - EAGAIN',0 msg2 db 'ERROR - EBADF',0 msg3 db 'ERROR - EPIPE',0 msg4 db 'ERROR - EFAULT',0 msg5 db 'ERROR - EFBIG',0 msg6 db 'ERROR - EINTR',0 msg7 db 'ERROR - EINVAL',0 msg8 db 'ERROR - EIO',0 msg9 db 'ERROR - ENOSPC',0
How to access errno in assembly language?
Advertisement
Answer
- You’re making x86 Linux syscalls from hand written assembler code
- If a syscall fails, the condition
(unsigned long)eax > 0xfffff000
will be true and-(signed long)eax
will be the error code.- In pseudo C code:
if (-4095 <= eax && eax <= -1) errno = -eax;
- In pseudo C code:
- Thus, you do NOT need to access
errno
to handle syscall errors in assembler. You should derive the errno value fromeax
instead.
(Pause and think about this for a few seconds. It’s a conceptual misunderstanding implicit in your question)
Further Explanation
The problem: you’re making system calls but didn’t learn the details of how Linux returns errors through the syscall ABI.
- Regular Linux C programs make syscalls through glibc wrapper functions
- glibc wrapper functions check the syscall return code, stored in
eax
on x86, and seterrno
to the correct error code if required - The regular
errno
, provided by glibc, is a thread local variable, to access it in assembler you’ll need learn the Linux TLS ABI. Look at themmap64()
syscall wrapper in glibc:
$ gdbdis /lib/libc.so.6 mmap64 0x4ef952a0 : push %ebp 0x4ef952a1 : push %ebx 0x4ef952a2 : push %esi 0x4ef952a3 : push %edi 0x4ef952a4 : mov 0x28(%esp),%edx 0x4ef952a8 : mov 0x2c(%esp),%ecx 0x4ef952ac : test $0xfff,%edx 0x4ef952b2 : jne 0x4ef952eb 0x4ef952b4 : shrd $0xc,%ecx,%edx 0x4ef952b8 : shr $0xc,%ecx 0x4ef952bb : jne 0x4ef952eb 0x4ef952bd : mov %edx,%ebp 0x4ef952bf : mov 0x14(%esp),%ebx 0x4ef952c3 : mov 0x18(%esp),%ecx 0x4ef952c7 : mov 0x1c(%esp),%edx 0x4ef952cb : mov 0x20(%esp),%esi 0x4ef952cf : mov 0x24(%esp),%edi 0x4ef952d3 : mov $0xc0,%eax 0x4ef952d8 : call *%gs:0x10 0x4ef952df : pop %edi 0x4ef952e0 : pop %esi 0x4ef952e1 : pop %ebx 0x4ef952e2 : pop %ebp 0x4ef952e3 : cmp $0xfffff000,%eax 0x4ef952e8 : ja 0x4ef952f6 0x4ef952ea : ret 0x4ef952eb : pop %edi 0x4ef952ec : pop %esi 0x4ef952ed : pop %ebx 0x4ef952ee : pop %ebp 0x4ef952ef : mov $0xffffffea,%eax 0x4ef952f4 : jmp 0x4ef952f6 0x4ef952f6 : call 0x4efd8b33 0x4ef952fb : add $0xd3d05,%ecx 0x4ef95301 : mov -0x10c(%ecx),%ecx 0x4ef95307 : neg %eax 0x4ef95309 : mov %eax,%gs:(%ecx) 0x4ef9530c : or $0xffffffff,%eax 0x4ef9530f : ret
See:
0x4ef952e3 <+67>: cmp $0xfffff000,%eax 0x4ef952e8 <+72>: ja 0x4ef952f6 <mmap64+86>
where it checks the syscall return value in eax
.
and:
0x4ef952f6 <+86>: call 0x4efd8b33 <__x86.get_pc_thunk.cx> 0x4ef952fb <+91>: add $0xd3d05,%ecx 0x4ef95301 <+97>: mov -0x10c(%ecx),%ecx 0x4ef95307 <+103>: neg %eax 0x4ef95309 <+105>: mov %eax,%gs:(%ecx) 0x4ef9530c <+108>: or $0xffffffff,%eax 0x4ef9530f <+111>: ret
where it stores -eax
to errno
and returns -1
- Your shellcode,
ExitNewShell.asm
, won’t be linked with glibc when built as a standalone executable, thus accessingerrno
won’t work unless you inject that shellcode into another process, since glibc wouldn’t be there to allocate a thread local storage slot forerrno
and store the value-eax
to it after a syscall failure, even if you do all the right things to access thread local storage.
Notes
- gdbdis is a script I wrote to use GDB as a disassembler. (It’s implemented as a “multi-call binary” that changes behavior according to the program name used to invoke it.)