Windows 强制执行 READ-ONLY .text 部分,即使因此被 ld 链接器禁用
Windows enforces READ-ONLY .text section, even thus disabled by the ld linker
在下面的玩具程序中,我在 .text 部分声明了一个变量并写入它,这给出了分段错误,因为 .text
部分被标记为只读:
Breakpoint 1, 0x00401000 in start ()
(gdb) disassemble
Dump of assembler code for function start:
=> 0x00401000 <+0>: movl [=11=]x2,0x40100a
End of assembler dump.
(gdb) stepi
Program received signal SIGSEGV, Segmentation fault.
0x00401000 in start ()
(gdb)
这是 objdump
输出:
test.exe: file format pei-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000001f 00401000 00401000 00000200 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .idata 00000014 00402000 00402000 00000400 2**2
CONTENTS, ALLOC, LOAD, DATA
但是,link使用 --omagic
开关(禁用只读 .text
部分)会产生以下结果:
ld --omagic -o test.exe test.obj
test.exe: file format pei-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000001f 00401000 00401000 000001d0 2**4
CONTENTS, ALLOC, LOAD, CODE
1 .idata 00000014 00402000 00402000 000003d0 2**2
CONTENTS, ALLOC, LOAD, DATA
但是使用 GDB
进行调试会得到以下(奇怪的)结果:
Breakpoint 1, 0x00401000 in start ()
(gdb) disassemble
Dump of assembler code for function start:
=> 0x00401000 <+0>: dec %ebp
0x00401001 <+1>: pop %edx
0x00401002 <+2>: nop
0x00401003 <+3>: add %al,(%ebx)
0x00401005 <+5>: add %al,(%eax)
0x00401007 <+7>: add %al,(%eax,%eax,1)
End of assembler dump.
(gdb) stepi
0x00401001 in start ()
(gdb) stepi
0x00401002 in start ()
(gdb) stepi
0x00401003 in start ()
(gdb) stepi
0x00401005 in start ()
(gdb) stepi
Program received signal SIGSEGV, Segmentation fault.
0x00401005 in start ()
(gdb)
首先还是报了segmentation fault,但是汇编代码也改结构了?
如何 link .text
部分在 Windows 10 x64
上可写?
玩具程序:
BITS 32
section .text
global _start
_start:
mov [var], dword 2
var: dd 0
ret
--omagic
标志导致 GNU 链接器生成错误的 PECOFF 可执行文件。节必须在文件中对齐,最小文件对齐为 512 字节,但链接器将 .text
节放在文件偏移量 0x1d0 处。
不要使用 --omagic
标志,而是正常生成可执行文件,然后使用 objcopy
更改 header:
部分中的标志
ld -o test-tmp.exe test.obj
$(OBJCOPY) --set-section-flags .text=code,data,alloc,contents,load test-tmp.exe test.exe
出于某种原因,ld
完全更改了使用 --omagic
选项链接的 PE 可执行文件。
使用 cmp
实用程序的文件快速比较显示:
137 177 222
141 0 320
142 6 5
213 0 320
214 2 1
217 142 205
218 154 353
397 0 320
398 2 1
437 0 320
438 4 3
465 0 307
...
很多差异,尽管 ld
原则上应该只更改部分 header (.text) 的部分标志,即设置标志 IMAGE_SCN_MEM_WRITE
.
使用 HxD 手动更改标志,即将偏移量 0x19F 处的字节设置为 0xE0 可解决问题...
运行程序的试验 var
和 ret
的顺序互换(否则程序崩溃):
Breakpoint 1, 0x00401000 in start ()
(gdb) disassemble
Dump of assembler code for function start:
=> 0x00401000 <+0>: movl [=11=]x2,0x40100b
0x0040100a <+10>: ret
End of assembler dump.
(gdb) stepi
0x0040100a in start ()
(gdb) disassemble
Dump of assembler code for function start:
0x00401000 <+0>: movl [=11=]x2,0x40100b
=> 0x0040100a <+10>: ret
End of assembler dump.
(gdb) x/wx var
0x40100b <var>: 0x00000002
(gdb)
我们看到一切都按预期进行。
我的结论是 ld
以某种方式生成了格式错误的 PE 可执行文件,我看到@RossRidge 对此有答案(ld
不遵守文件的节对齐)。
在下面的玩具程序中,我在 .text 部分声明了一个变量并写入它,这给出了分段错误,因为 .text
部分被标记为只读:
Breakpoint 1, 0x00401000 in start ()
(gdb) disassemble
Dump of assembler code for function start:
=> 0x00401000 <+0>: movl [=11=]x2,0x40100a
End of assembler dump.
(gdb) stepi
Program received signal SIGSEGV, Segmentation fault.
0x00401000 in start ()
(gdb)
这是 objdump
输出:
test.exe: file format pei-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000001f 00401000 00401000 00000200 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .idata 00000014 00402000 00402000 00000400 2**2
CONTENTS, ALLOC, LOAD, DATA
但是,link使用 --omagic
开关(禁用只读 .text
部分)会产生以下结果:
ld --omagic -o test.exe test.obj
test.exe: file format pei-i386
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000001f 00401000 00401000 000001d0 2**4
CONTENTS, ALLOC, LOAD, CODE
1 .idata 00000014 00402000 00402000 000003d0 2**2
CONTENTS, ALLOC, LOAD, DATA
但是使用 GDB
进行调试会得到以下(奇怪的)结果:
Breakpoint 1, 0x00401000 in start ()
(gdb) disassemble
Dump of assembler code for function start:
=> 0x00401000 <+0>: dec %ebp
0x00401001 <+1>: pop %edx
0x00401002 <+2>: nop
0x00401003 <+3>: add %al,(%ebx)
0x00401005 <+5>: add %al,(%eax)
0x00401007 <+7>: add %al,(%eax,%eax,1)
End of assembler dump.
(gdb) stepi
0x00401001 in start ()
(gdb) stepi
0x00401002 in start ()
(gdb) stepi
0x00401003 in start ()
(gdb) stepi
0x00401005 in start ()
(gdb) stepi
Program received signal SIGSEGV, Segmentation fault.
0x00401005 in start ()
(gdb)
首先还是报了segmentation fault,但是汇编代码也改结构了?
如何 link .text
部分在 Windows 10 x64
上可写?
玩具程序:
BITS 32
section .text
global _start
_start:
mov [var], dword 2
var: dd 0
ret
--omagic
标志导致 GNU 链接器生成错误的 PECOFF 可执行文件。节必须在文件中对齐,最小文件对齐为 512 字节,但链接器将 .text
节放在文件偏移量 0x1d0 处。
不要使用 --omagic
标志,而是正常生成可执行文件,然后使用 objcopy
更改 header:
ld -o test-tmp.exe test.obj
$(OBJCOPY) --set-section-flags .text=code,data,alloc,contents,load test-tmp.exe test.exe
出于某种原因,ld
完全更改了使用 --omagic
选项链接的 PE 可执行文件。
使用 cmp
实用程序的文件快速比较显示:
137 177 222
141 0 320
142 6 5
213 0 320
214 2 1
217 142 205
218 154 353
397 0 320
398 2 1
437 0 320
438 4 3
465 0 307
...
很多差异,尽管 ld
原则上应该只更改部分 header (.text) 的部分标志,即设置标志 IMAGE_SCN_MEM_WRITE
.
使用 HxD 手动更改标志,即将偏移量 0x19F 处的字节设置为 0xE0 可解决问题...
运行程序的试验 var
和 ret
的顺序互换(否则程序崩溃):
Breakpoint 1, 0x00401000 in start ()
(gdb) disassemble
Dump of assembler code for function start:
=> 0x00401000 <+0>: movl [=11=]x2,0x40100b
0x0040100a <+10>: ret
End of assembler dump.
(gdb) stepi
0x0040100a in start ()
(gdb) disassemble
Dump of assembler code for function start:
0x00401000 <+0>: movl [=11=]x2,0x40100b
=> 0x0040100a <+10>: ret
End of assembler dump.
(gdb) x/wx var
0x40100b <var>: 0x00000002
(gdb)
我们看到一切都按预期进行。
我的结论是 ld
以某种方式生成了格式错误的 PE 可执行文件,我看到@RossRidge 对此有答案(ld
不遵守文件的节对齐)。