为什么在写入可写 .data 部分时出现段错误?使用 Ubuntu、x86、nasm、gdb、readelf
Why Segment fault when writing to writeable .data section? Using Ubuntu, x86, nasm, gdb, readelf
我正在学习使用汇编编写简单的 shell 代码。当执行 mov 操作码以覆盖数据库数据时,出现段错误。为什么?任何指导表示赞赏!使用 gdb 调试确认数据在 运行 时与代码连续,并且程序的 readelf 分析确认数据段是可写的。
section .text
global _start
_start:
; The following code calls execve("/bin/sh", argv, envp=0)
jmp short two
one:
pop ebx
xor eax, eax
mov [ebx+12], eax
mov [ebx+7], al
mov [ebx+8], ebx
lea ecx, [ebx+8]
lea edx, [ebx+12]
mov al, 11
int 0x80
two:
call one
section .data align=1
db '/bin/shzargvenvp'
阅读评论后的补充信息:
当 运行 在 linux 命令行 (./myshdb) 上独立运行时,以及当我使用 gdb 进入 mov 指令时(将中断设置为“一”),它会出现段错误,运行,然后重复步骤)。
是在 32 位 Ubuntu 安装上编译并 运行ning。以下是我正在使用的各种命令行(对于变体 shell 代码工作都可以正常工作):
nasm -f elf32 -g -F stabs myshdb.s -o myshdb.o
objdump -Mintel --disassemble myshdb.o
ld myshdb.o -o myshdb
readelf -a myshdb
gdb myshdb
使用不同的算法,编译和命令都可以正常工作,程序 运行 也很好。这是关于数据紧接在代码之后的接近度并试图写入给我带来麻烦的数据部分。最初它都是 .text 部分,但显然是只读的,所以我认为在 1 字节边界上对齐的数据声明会起作用。 1 字节边界有效,但不知何故,即使 readelf 说它已加载为可写,写入也不起作用。注意带有“W”标志的数据段中的 16 个字节(大小=0x10)。
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 08048080 000080 000025 00 AX 0 0 16
[ 2] .data PROGBITS 080490a5 0000a5 000010 00 WA 0 0 1
[ 3] .stab PROGBITS 00000000 0000b8 0000d8 0c 4 0 4
[ 4] .stabstr STRTAB 00000000 000190 00000a 00 0 0 1
[ 5] .shstrtab STRTAB 00000000 00029b 000036 00 0 0 1
[ 6] .symtab SYMTAB 00000000 00019c 0000d0 10 7 9 4
[ 7] .strtab STRTAB 00000000 00026c 00002f 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
数据是否紧跟代码?下面的 gdb 输出,在执行第一个 mov 操作码之前停止。数据在代码之后连续出现。 EBX 包含地址 0x80480a5,它指向紧跟在代码之后的有效字符串数据。检查内存 (x 0x80480a5) 也确认了一个连续的位置。
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x80480a5 ("/bin/shZargvenvp")
ECX: 0x0
EDX: 0x0
ESI: 0x0
EDI: 0x0
EBP: 0x0
ESP: 0xbfffeda0 --> 0x1
EIP: 0x804808d (<one+3>: mov DWORD PTR [ebx+0xc],eax)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048088 <zero>: jmp 0x80480a0 <two>
0x804808a <one>: pop ebx
0x804808b <one+1>: xor eax,eax
=> 0x804808d <one+3>: mov DWORD PTR [ebx+0xc],eax
0x8048090 <one+6>: mov DWORD PTR [ebx+0x8],ebx
0x8048093 <one+9>: mov BYTE PTR [ebx+0x7],al
0x8048096 <one+12>: lea ecx,[ebx+0x8]
0x8048099 <one+15>: lea edx,[ebx+0xc]
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0804808d in one ()
gdb-peda$ x 0x80480a5
0x80480a5: "/bin/shZargvenvp"
@Employed Russian 要求从 reaelf -Wl 打印输出。这是我从头开始重建时的信息:
---------- code snippet compiled with nasm, ld -----------------
zero: jmp short two
one: pop ebx
xor eax, eax
mov [ebx+12], eax
mov [ebx+8], ebx
mov [ebx+7], al
lea ecx, [ebx+8]
lea edx, [ebx+12]
mov al, 11
int 0x80
two: call one
section .data align=1
msg: db '/bin/sh0argvenvp'
-------- readelf output as requested --------
readelf -Wl myshdb
Elf file type is EXEC (Executable file)
Entry point 0x8048080
There are 2 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x0009d 0x0009d R E 0x1000
LOAD 0x00009d 0x0804909d 0x0804909d 0x00010 0x00010 RW 0x1000
Section to Segment mapping:
Segment Sections...
00 .text
01 .data
-------------- run with gdb and step to mov instructions ----------
---------------registers--------------
EAX: 0x0
EBX: 0x804809d ("/bin/sh0argvenvp")
----------- memory address checks ------------
gdb-peda$ p zero
= {<text variable, no debug info>} 0x8048080 <zero>
gdb-peda$ p one
= {<text variable, no debug info>} 0x8048082 <one>
gdb-peda$ p two
= {<text variable, no debug info>} 0x8048098 <two>
gdb-peda$ p $ebx
= 0x804809d
gdb-peda$ p msg
= 0x6e69622f
gdb-peda$ x 0x804809d
0x804809d: "/bin/sh0argvenvp"
gdb-peda$ x msg
0x6e69622f: <error: Cannot access memory at address 0x6e69622f>
换句话说,字符串消息可直接从代码 (0x804809d) 之后的内存位置获得。然而 msg 标签映射到 0x6e69622f,我如何使用 gdb 查看那里的数据? reaelf -Wl 输出告诉我什么?
Debugging with gdb confirms the data is contiguous with the code at run time and readelf analysis of the program confirms the data segment is writeable.
您希望 db '...'
立即关注 CALL one
。
这 实际上 不会发生,您的 .data
部分位于不同的 段 (因为它需要不同的权限) :
readelf -Wl myshdb
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x00094 0x00094 R 0x1000
LOAD 0x001000 0x08049000 0x08049000 0x0001d 0x0001d R E 0x1000
LOAD 0x002000 0x0804a000 0x0804a000 0x00010 0x00010 RW 0x1000
Section to Segment mapping:
Segment Sections...
00
01 .text
02 .data
请注意 .data
在第二个 LOAD
段中,该段从不同的页面开始。
可能会让您感到困惑的是,您的链接器 可能 在 [=17] 的代码后留下 .data
的 副本 =](我的版本没有——对我来说都是 0
)。
在任何情况下,您的代码都会尝试写入 first LOAD
段,紧接 two
结束后的位置,但是该段(显然)不可写。
我正在学习使用汇编编写简单的 shell 代码。当执行 mov 操作码以覆盖数据库数据时,出现段错误。为什么?任何指导表示赞赏!使用 gdb 调试确认数据在 运行 时与代码连续,并且程序的 readelf 分析确认数据段是可写的。
section .text
global _start
_start:
; The following code calls execve("/bin/sh", argv, envp=0)
jmp short two
one:
pop ebx
xor eax, eax
mov [ebx+12], eax
mov [ebx+7], al
mov [ebx+8], ebx
lea ecx, [ebx+8]
lea edx, [ebx+12]
mov al, 11
int 0x80
two:
call one
section .data align=1
db '/bin/shzargvenvp'
阅读评论后的补充信息:
当 运行 在 linux 命令行 (./myshdb) 上独立运行时,以及当我使用 gdb 进入 mov 指令时(将中断设置为“一”),它会出现段错误,运行,然后重复步骤)。
是在 32 位 Ubuntu 安装上编译并 运行ning。以下是我正在使用的各种命令行(对于变体 shell 代码工作都可以正常工作):
nasm -f elf32 -g -F stabs myshdb.s -o myshdb.o
objdump -Mintel --disassemble myshdb.o
ld myshdb.o -o myshdb
readelf -a myshdb
gdb myshdb
使用不同的算法,编译和命令都可以正常工作,程序 运行 也很好。这是关于数据紧接在代码之后的接近度并试图写入给我带来麻烦的数据部分。最初它都是 .text 部分,但显然是只读的,所以我认为在 1 字节边界上对齐的数据声明会起作用。 1 字节边界有效,但不知何故,即使 readelf 说它已加载为可写,写入也不起作用。注意带有“W”标志的数据段中的 16 个字节(大小=0x10)。
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 08048080 000080 000025 00 AX 0 0 16
[ 2] .data PROGBITS 080490a5 0000a5 000010 00 WA 0 0 1
[ 3] .stab PROGBITS 00000000 0000b8 0000d8 0c 4 0 4
[ 4] .stabstr STRTAB 00000000 000190 00000a 00 0 0 1
[ 5] .shstrtab STRTAB 00000000 00029b 000036 00 0 0 1
[ 6] .symtab SYMTAB 00000000 00019c 0000d0 10 7 9 4
[ 7] .strtab STRTAB 00000000 00026c 00002f 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
数据是否紧跟代码?下面的 gdb 输出,在执行第一个 mov 操作码之前停止。数据在代码之后连续出现。 EBX 包含地址 0x80480a5,它指向紧跟在代码之后的有效字符串数据。检查内存 (x 0x80480a5) 也确认了一个连续的位置。
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x80480a5 ("/bin/shZargvenvp")
ECX: 0x0
EDX: 0x0
ESI: 0x0
EDI: 0x0
EBP: 0x0
ESP: 0xbfffeda0 --> 0x1
EIP: 0x804808d (<one+3>: mov DWORD PTR [ebx+0xc],eax)
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
0x8048088 <zero>: jmp 0x80480a0 <two>
0x804808a <one>: pop ebx
0x804808b <one+1>: xor eax,eax
=> 0x804808d <one+3>: mov DWORD PTR [ebx+0xc],eax
0x8048090 <one+6>: mov DWORD PTR [ebx+0x8],ebx
0x8048093 <one+9>: mov BYTE PTR [ebx+0x7],al
0x8048096 <one+12>: lea ecx,[ebx+0x8]
0x8048099 <one+15>: lea edx,[ebx+0xc]
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x0804808d in one ()
gdb-peda$ x 0x80480a5
0x80480a5: "/bin/shZargvenvp"
@Employed Russian 要求从 reaelf -Wl 打印输出。这是我从头开始重建时的信息:
---------- code snippet compiled with nasm, ld -----------------
zero: jmp short two
one: pop ebx
xor eax, eax
mov [ebx+12], eax
mov [ebx+8], ebx
mov [ebx+7], al
lea ecx, [ebx+8]
lea edx, [ebx+12]
mov al, 11
int 0x80
two: call one
section .data align=1
msg: db '/bin/sh0argvenvp'
-------- readelf output as requested --------
readelf -Wl myshdb
Elf file type is EXEC (Executable file)
Entry point 0x8048080
There are 2 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x0009d 0x0009d R E 0x1000
LOAD 0x00009d 0x0804909d 0x0804909d 0x00010 0x00010 RW 0x1000
Section to Segment mapping:
Segment Sections...
00 .text
01 .data
-------------- run with gdb and step to mov instructions ----------
---------------registers--------------
EAX: 0x0
EBX: 0x804809d ("/bin/sh0argvenvp")
----------- memory address checks ------------
gdb-peda$ p zero
= {<text variable, no debug info>} 0x8048080 <zero>
gdb-peda$ p one
= {<text variable, no debug info>} 0x8048082 <one>
gdb-peda$ p two
= {<text variable, no debug info>} 0x8048098 <two>
gdb-peda$ p $ebx
= 0x804809d
gdb-peda$ p msg
= 0x6e69622f
gdb-peda$ x 0x804809d
0x804809d: "/bin/sh0argvenvp"
gdb-peda$ x msg
0x6e69622f: <error: Cannot access memory at address 0x6e69622f>
换句话说,字符串消息可直接从代码 (0x804809d) 之后的内存位置获得。然而 msg 标签映射到 0x6e69622f,我如何使用 gdb 查看那里的数据? reaelf -Wl 输出告诉我什么?
Debugging with gdb confirms the data is contiguous with the code at run time and readelf analysis of the program confirms the data segment is writeable.
您希望 db '...'
立即关注 CALL one
。
这 实际上 不会发生,您的 .data
部分位于不同的 段 (因为它需要不同的权限) :
readelf -Wl myshdb
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x00094 0x00094 R 0x1000
LOAD 0x001000 0x08049000 0x08049000 0x0001d 0x0001d R E 0x1000
LOAD 0x002000 0x0804a000 0x0804a000 0x00010 0x00010 RW 0x1000
Section to Segment mapping:
Segment Sections...
00
01 .text
02 .data
请注意 .data
在第二个 LOAD
段中,该段从不同的页面开始。
可能会让您感到困惑的是,您的链接器 可能 在 [=17] 的代码后留下 .data
的 副本 =](我的版本没有——对我来说都是 0
)。
在任何情况下,您的代码都会尝试写入 first LOAD
段,紧接 two
结束后的位置,但是该段(显然)不可写。