用汇编语言访问 errno.h
Accessing errno.h in assembly language
我想用汇编语言访问 errno.h 中的错误号,以处理写入函数调用的错误。为此,我发现某个地方可以用汇编语言调用 _error,但它会抛出错误:
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
我的汇编代码: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
如何在汇编语言中访问errno?
- 您正在从手写的汇编代码中进行 x86 Linux 系统调用
- 如果系统调用失败,条件
(unsigned long)eax > 0xfffff000
将为真,-(signed long)eax
为错误代码。
- 在伪 C 代码中:
if (-4095 <= eax && eax <= -1) errno = -eax;
- 因此,您 不需要 访问
errno
来处理汇编程序中的系统调用错误。您应该改为从 eax
派生 errno 值。
(暂停并思考几秒钟。这是你问题中隐含的概念误解)
进一步说明
问题:您正在进行系统调用,但没有通过系统调用 ABI 了解 Linux return 错误的详细信息。
- 常规 Linux C 程序通过 glibc 包装函数进行系统调用
- glibc 包装函数检查存储在 x86 上
eax
中的系统调用 return 代码,如果需要,将 errno
设置为正确的错误代码
- glibc 提供的常规
errno
是一个线程局部变量,要在汇编程序中访问它,您需要学习 Linux TLS ABI。查看 glibc 中的 mmap64()
系统调用包装器:
$ 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 [=29=]xfff,%edx
0x4ef952b2 : jne 0x4ef952eb
0x4ef952b4 : shrd [=29=]xc,%ecx,%edx
0x4ef952b8 : shr [=29=]xc,%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 [=29=]xc0,%eax
0x4ef952d8 : call *%gs:0x10
0x4ef952df : pop %edi
0x4ef952e0 : pop %esi
0x4ef952e1 : pop %ebx
0x4ef952e2 : pop %ebp
0x4ef952e3 : cmp [=29=]xfffff000,%eax
0x4ef952e8 : ja 0x4ef952f6
0x4ef952ea : ret
0x4ef952eb : pop %edi
0x4ef952ec : pop %esi
0x4ef952ed : pop %ebx
0x4ef952ee : pop %ebp
0x4ef952ef : mov [=29=]xffffffea,%eax
0x4ef952f4 : jmp 0x4ef952f6
0x4ef952f6 : call 0x4efd8b33
0x4ef952fb : add [=29=]xd3d05,%ecx
0x4ef95301 : mov -0x10c(%ecx),%ecx
0x4ef95307 : neg %eax
0x4ef95309 : mov %eax,%gs:(%ecx)
0x4ef9530c : or [=29=]xffffffff,%eax
0x4ef9530f : ret
参见:
0x4ef952e3 <+67>: cmp [=10=]xfffff000,%eax
0x4ef952e8 <+72>: ja 0x4ef952f6 <mmap64+86>
它检查 eax
中的系统调用 return 值的位置。
和:
0x4ef952f6 <+86>: call 0x4efd8b33 <__x86.get_pc_thunk.cx>
0x4ef952fb <+91>: add [=11=]xd3d05,%ecx
0x4ef95301 <+97>: mov -0x10c(%ecx),%ecx
0x4ef95307 <+103>: neg %eax
0x4ef95309 <+105>: mov %eax,%gs:(%ecx)
0x4ef9530c <+108>: or [=11=]xffffffff,%eax
0x4ef9530f <+111>: ret
它存储 -eax
到 errno
和 return 的位置 -1
- 您的 shellcode
ExitNewShell.asm
在构建为独立可执行文件时不会与 glibc 链接,因此除非您将该 shellcode 注入另一个进程,否则访问 errno
将不起作用,因为 glibc不会在那里为 errno
分配一个线程本地存储槽并在系统调用失败后将值 -eax
存储到它,即使你做了所有正确的事情来访问线程本地存储。
备注
- gdbdis 是我编写的脚本,用于使用 GDB 作为反汇编程序。 (它作为 "multi-call binary" 实现,根据用于调用它的程序名称更改行为。)
我想用汇编语言访问 errno.h 中的错误号,以处理写入函数调用的错误。为此,我发现某个地方可以用汇编语言调用 _error,但它会抛出错误:
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
我的汇编代码: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
如何在汇编语言中访问errno?
- 您正在从手写的汇编代码中进行 x86 Linux 系统调用
- 如果系统调用失败,条件
(unsigned long)eax > 0xfffff000
将为真,-(signed long)eax
为错误代码。- 在伪 C 代码中:
if (-4095 <= eax && eax <= -1) errno = -eax;
- 在伪 C 代码中:
- 因此,您 不需要 访问
errno
来处理汇编程序中的系统调用错误。您应该改为从eax
派生 errno 值。
(暂停并思考几秒钟。这是你问题中隐含的概念误解)
进一步说明
问题:您正在进行系统调用,但没有通过系统调用 ABI 了解 Linux return 错误的详细信息。
- 常规 Linux C 程序通过 glibc 包装函数进行系统调用
- glibc 包装函数检查存储在 x86 上
eax
中的系统调用 return 代码,如果需要,将errno
设置为正确的错误代码 - glibc 提供的常规
errno
是一个线程局部变量,要在汇编程序中访问它,您需要学习 Linux TLS ABI。查看 glibc 中的mmap64()
系统调用包装器:
$ 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 [=29=]xfff,%edx 0x4ef952b2 : jne 0x4ef952eb 0x4ef952b4 : shrd [=29=]xc,%ecx,%edx 0x4ef952b8 : shr [=29=]xc,%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 [=29=]xc0,%eax 0x4ef952d8 : call *%gs:0x10 0x4ef952df : pop %edi 0x4ef952e0 : pop %esi 0x4ef952e1 : pop %ebx 0x4ef952e2 : pop %ebp 0x4ef952e3 : cmp [=29=]xfffff000,%eax 0x4ef952e8 : ja 0x4ef952f6 0x4ef952ea : ret 0x4ef952eb : pop %edi 0x4ef952ec : pop %esi 0x4ef952ed : pop %ebx 0x4ef952ee : pop %ebp 0x4ef952ef : mov [=29=]xffffffea,%eax 0x4ef952f4 : jmp 0x4ef952f6 0x4ef952f6 : call 0x4efd8b33 0x4ef952fb : add [=29=]xd3d05,%ecx 0x4ef95301 : mov -0x10c(%ecx),%ecx 0x4ef95307 : neg %eax 0x4ef95309 : mov %eax,%gs:(%ecx) 0x4ef9530c : or [=29=]xffffffff,%eax 0x4ef9530f : ret
参见:
0x4ef952e3 <+67>: cmp [=10=]xfffff000,%eax
0x4ef952e8 <+72>: ja 0x4ef952f6 <mmap64+86>
它检查 eax
中的系统调用 return 值的位置。
和:
0x4ef952f6 <+86>: call 0x4efd8b33 <__x86.get_pc_thunk.cx>
0x4ef952fb <+91>: add [=11=]xd3d05,%ecx
0x4ef95301 <+97>: mov -0x10c(%ecx),%ecx
0x4ef95307 <+103>: neg %eax
0x4ef95309 <+105>: mov %eax,%gs:(%ecx)
0x4ef9530c <+108>: or [=11=]xffffffff,%eax
0x4ef9530f <+111>: ret
它存储 -eax
到 errno
和 return 的位置 -1
- 您的 shellcode
ExitNewShell.asm
在构建为独立可执行文件时不会与 glibc 链接,因此除非您将该 shellcode 注入另一个进程,否则访问errno
将不起作用,因为 glibc不会在那里为errno
分配一个线程本地存储槽并在系统调用失败后将值-eax
存储到它,即使你做了所有正确的事情来访问线程本地存储。
备注
- gdbdis 是我编写的脚本,用于使用 GDB 作为反汇编程序。 (它作为 "multi-call binary" 实现,根据用于调用它的程序名称更改行为。)