ELF 文件中的字符串是如何编码的?
How are strings encoded in an ELF file?
我想证明明文密码很容易从程序中读取:
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
char password[] = "a big refreshing lemonade";
return strcmp(argv[1], password);
}
但它没有按预期工作:
$ gcc foo.c
$ hexdump -C a.out | grep -C2 'lem'
000006c0 00 00 00 48 89 45 f8 31 c0 48 b8 61 20 62 69 67 |...H.E.1.H.a big|
000006d0 20 72 65 48 ba 66 72 65 73 68 69 6e 67 48 89 45 | reH.freshingH.E|
000006e0 d0 48 89 55 d8 48 b8 20 6c 65 6d 6f 6e 61 64 48 |.H.U.H. lemonadH|
000006f0 89 45 e0 66 c7 45 e8 65 00 48 8b 45 c0 48 83 c0 |.E.f.E.e.H.E.H..|
00000700 08 48 8b 00 48 8d 55 d0 48 89 d6 48 89 c7 e8 6d |.H..H.U.H..H...m|
我注意到一些奇怪的字符。这是为什么?
这是因为字符串没有存储为静态数据。
例如,如果你有这个:
const char* password = "a big refreshing lemonade";
甚至这样:
static char password[] = "a big refreshing lemonade";
它连续存储在常量部分的二进制文件中(您会看到彼此相邻 "a big refreshing lemonade")。
如果您查看程序集输出,您会看到:
6:test.c **** char password[] = "a big refreshing lemonade";
23 .loc 1 6 0
24 001e 48B86120 movabsq 09940773697495137, %rax
24 62696720
24 7265
25 0028 48BA6672 movabsq 53010330678293094, %rdx
25 65736869
25 6E67
26 0032 488945D0 movq %rax, -48(%rbp)
27 0036 488955D8 movq %rdx, -40(%rbp)
28 003a 48B8206C movabsq 33183901389515808, %rax
28 656D6F6E
28 6164
29 0044 488945E0 movq %rax, -32(%rbp)
30 0048 66C745E8 movw 1, -24(%rbp)
30 6500
你看到很多movabsq
的地方,它加载了一个64位常量。所以,它一次加载 8 个字节到 password
.
您会注意到第一个常量 (7309940773697495137) 是 "a big re"
的 little-endian 形式
I wanted to demonstrate that passwords in clear are easy to read from a program...
可以轻松地使用 strings
来查找此信息。
strings - print the strings of printable characters in files.
根据你的例子,
strings a.out
[ . . . TRUNCATED . . . ]
a big reH
freshingH
lemonadH
[ . . . TRUNCATED . . . ]
满输出,
strings a.out
/lib64/ld-linux-x86-64.so.2
libc.so.6
__stack_chk_fail
__cxa_finalize
strcmp
__libc_start_main
GLIBC_2.2.5
GLIBC_2.4
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
a big reH
freshingH
lemonadH
AWAVI
AUATL
[]A\A]A^A_
;*3$"
GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.7697
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
foo.c
__FRAME_END__
__init_array_end
_DYNAMIC
__init_array_start
__GNU_EH_FRAME_HDR
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
_ITM_deregisterTMCloneTable
_edata
__stack_chk_fail@@GLIBC_2.4
__libc_start_main@@GLIBC_2.2.5
__data_start
strcmp@@GLIBC_2.2.5
__gmon_start__
__dso_handle
_IO_stdin_used
__libc_csu_init
__bss_start
main
__TMC_END__
_ITM_registerTMCloneTable
__cxa_finalize@@GLIBC_2.2.5
.symtab
.strtab
.shstrtab
.interp
.note.ABI-tag
.note.gnu.build-id
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.plt.got
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.data
.bss
.comment
root@42d62eac5ccf:~#
root@42d62eac5ccf:~# strings a.out
/lib64/ld-linux-x86-64.so.2
libc.so.6
__stack_chk_fail
__cxa_finalize
strcmp
__libc_start_main
GLIBC_2.2.5
GLIBC_2.4
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
a big reH
freshingH
lemonadH
AWAVI
AUATL
[]A\A]A^A_
;*3$"
GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.7697
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
foo.c
__FRAME_END__
__init_array_end
_DYNAMIC
__init_array_start
__GNU_EH_FRAME_HDR
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
_ITM_deregisterTMCloneTable
_edata
__stack_chk_fail@@GLIBC_2.4
__libc_start_main@@GLIBC_2.2.5
__data_start
strcmp@@GLIBC_2.2.5
__gmon_start__
__dso_handle
_IO_stdin_used
__libc_csu_init
__bss_start
main
__TMC_END__
_ITM_registerTMCloneTable
__cxa_finalize@@GLIBC_2.2.5
.symtab
.strtab
.shstrtab
.interp
.note.ABI-tag
.note.gnu.build-id
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.plt.got
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.data
.bss
.comment
我想证明明文密码很容易从程序中读取:
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv)
{
char password[] = "a big refreshing lemonade";
return strcmp(argv[1], password);
}
但它没有按预期工作:
$ gcc foo.c
$ hexdump -C a.out | grep -C2 'lem'
000006c0 00 00 00 48 89 45 f8 31 c0 48 b8 61 20 62 69 67 |...H.E.1.H.a big|
000006d0 20 72 65 48 ba 66 72 65 73 68 69 6e 67 48 89 45 | reH.freshingH.E|
000006e0 d0 48 89 55 d8 48 b8 20 6c 65 6d 6f 6e 61 64 48 |.H.U.H. lemonadH|
000006f0 89 45 e0 66 c7 45 e8 65 00 48 8b 45 c0 48 83 c0 |.E.f.E.e.H.E.H..|
00000700 08 48 8b 00 48 8d 55 d0 48 89 d6 48 89 c7 e8 6d |.H..H.U.H..H...m|
我注意到一些奇怪的字符。这是为什么?
这是因为字符串没有存储为静态数据。
例如,如果你有这个:
const char* password = "a big refreshing lemonade";
甚至这样:
static char password[] = "a big refreshing lemonade";
它连续存储在常量部分的二进制文件中(您会看到彼此相邻 "a big refreshing lemonade")。
如果您查看程序集输出,您会看到:
6:test.c **** char password[] = "a big refreshing lemonade";
23 .loc 1 6 0
24 001e 48B86120 movabsq 09940773697495137, %rax
24 62696720
24 7265
25 0028 48BA6672 movabsq 53010330678293094, %rdx
25 65736869
25 6E67
26 0032 488945D0 movq %rax, -48(%rbp)
27 0036 488955D8 movq %rdx, -40(%rbp)
28 003a 48B8206C movabsq 33183901389515808, %rax
28 656D6F6E
28 6164
29 0044 488945E0 movq %rax, -32(%rbp)
30 0048 66C745E8 movw 1, -24(%rbp)
30 6500
你看到很多movabsq
的地方,它加载了一个64位常量。所以,它一次加载 8 个字节到 password
.
您会注意到第一个常量 (7309940773697495137) 是 "a big re"
的 little-endian 形式I wanted to demonstrate that passwords in clear are easy to read from a program...
可以轻松地使用 strings
来查找此信息。
strings - print the strings of printable characters in files.
根据你的例子,
strings a.out
[ . . . TRUNCATED . . . ]
a big reH
freshingH
lemonadH
[ . . . TRUNCATED . . . ]
满输出,
strings a.out
/lib64/ld-linux-x86-64.so.2
libc.so.6
__stack_chk_fail
__cxa_finalize
strcmp
__libc_start_main
GLIBC_2.2.5
GLIBC_2.4
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
a big reH
freshingH
lemonadH
AWAVI
AUATL
[]A\A]A^A_
;*3$"
GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.7697
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
foo.c
__FRAME_END__
__init_array_end
_DYNAMIC
__init_array_start
__GNU_EH_FRAME_HDR
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
_ITM_deregisterTMCloneTable
_edata
__stack_chk_fail@@GLIBC_2.4
__libc_start_main@@GLIBC_2.2.5
__data_start
strcmp@@GLIBC_2.2.5
__gmon_start__
__dso_handle
_IO_stdin_used
__libc_csu_init
__bss_start
main
__TMC_END__
_ITM_registerTMCloneTable
__cxa_finalize@@GLIBC_2.2.5
.symtab
.strtab
.shstrtab
.interp
.note.ABI-tag
.note.gnu.build-id
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.plt.got
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.data
.bss
.comment
root@42d62eac5ccf:~#
root@42d62eac5ccf:~# strings a.out
/lib64/ld-linux-x86-64.so.2
libc.so.6
__stack_chk_fail
__cxa_finalize
strcmp
__libc_start_main
GLIBC_2.2.5
GLIBC_2.4
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
a big reH
freshingH
lemonadH
AWAVI
AUATL
[]A\A]A^A_
;*3$"
GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.7697
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
foo.c
__FRAME_END__
__init_array_end
_DYNAMIC
__init_array_start
__GNU_EH_FRAME_HDR
_GLOBAL_OFFSET_TABLE_
__libc_csu_fini
_ITM_deregisterTMCloneTable
_edata
__stack_chk_fail@@GLIBC_2.4
__libc_start_main@@GLIBC_2.2.5
__data_start
strcmp@@GLIBC_2.2.5
__gmon_start__
__dso_handle
_IO_stdin_used
__libc_csu_init
__bss_start
main
__TMC_END__
_ITM_registerTMCloneTable
__cxa_finalize@@GLIBC_2.2.5
.symtab
.strtab
.shstrtab
.interp
.note.ABI-tag
.note.gnu.build-id
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.plt.got
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.data
.bss
.comment