strings 命令详解与参数实测(Ubuntu 22.04)
strings 详解:长度阈值、文件偏移、编码扫描与 ELF 版本字符串提取实战,Ubuntu 22.04 实测
strings 命令详解与参数实测(Ubuntu 22.04)
目标:全面掌握 GNU strings 的每一个常用参数——默认提取 ≥4 字符的可打印串、
-n自定义最小长度、-t附带文件偏移、-e按编码扫描(UTF-16LE 实测)、-a全文件扫描与默认数据段扫描的差异、管道输入,以及从 ELF 二进制提取版本字符串的实战用法,全部命令与回显已在 Ubuntu 22.04(GNU Binutils 2.38)实测通过。
一、环境信息
本篇所有命令与回显均在以下环境实测采集,读者在同等版本环境可逐条复现。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- strings 版本:GNU strings(GNU Binutils for Ubuntu)2.38(
strings --version实测),由 binutils 包提供,Ubuntu 22.04 默认预装;若缺失可执行apt-get install binutils补齐 - 配套工具:GNU od(偏移核对)
- 演示目录:
/root/demo/strings/ - 测试数据:
s.txt(4 行混合长度的文本样例)、utf16le.bin(UTF-16LE 编码的 "Hello World" 样例)、系统二进制/bin/ls(ELF 实战对象)
二、命令概览
strings 的职责是从任意文件(通常是二进制)中提取连续的可打印字符序列。二进制文件中大量字符串(报错信息、版本号、配置键、符号名)以明文存放,strings 是逆向分析、排查配置、确认组件版本的第一把瑞士军刀。它对文本文件同样有效——本质上就是按规则筛出可打印片段。
💡 技术拓展:从二进制中提取文本线索的常用工具对比——
工具 来源 特点 适用场景 strings GNU binutils 按长度阈值筛可打印序列,支持多编码 通用文本线索提取 grep -a GNU grep 把二进制当文本按行正则搜索 已知关键字直接搜 readelf/objdump GNU binutils 解析 ELF 结构化段表/符号表 需要结构化信息的深度分析 hexdump/od util-linux/coreutils 逐字节转储 查看字符串周围的原始字节 本文以 strings 为例;拿到偏移后用 od 精确核对原始字节是常见组合(见 4.3 节)。
2.1 语法与测试数据准备
strings 的语法源自 --help 原文:strings [option(s)] [file(s)],不给文件时读标准输入。准备两个演示文件:一个混合 2/3/4/11 字符串行的文本样例(观察阈值效果),一个 UTF-16LE 编码样例:
mkdir -p /root/demo/strings
cd /root/demo/strings
printf 'ab\nabcdef\nxyz\nhello world\n' > s.txt
printf 'H\x00e\x00l\x00l\x00o\x00 \x00W\x00o\x00r\x00l\x00d\x00\n\x00' > utf16le.bin
ls -l s.txt utf16le.bin
数据就绪回显:
-rw-r--r-- 1 root root 26 Oct 15 07:42 s.txt
-rw-r--r-- 1 root root 24 Oct 15 07:42 utf16le.bin
s.txt 内容依次为 ab(2 字符)、abcdef(6)、xyz(3)、hello world(11);utf16le.bin 是 "Hello World" 的 UTF-16LE 字节流(每个 ASCII 字符后跟一个 0x00)。
2.2 --help 全量回显
strings --help 输出完整保留(末行会列出所有支持的二进制目标格式):
Usage: strings [option(s)] [file(s)]
Display printable strings in [file(s)] (stdin by default)
The options are:
-a - --all Scan the entire file, not just the data section [default]
-d --data Only scan the data sections in the file
-f --print-file-name Print the name of the file before each string
-n <number> Locate & print any sequence of at least <number>
--bytes=<number> displayable characters. (The default is 4).
-t --radix={o,d,x} Print the location of the string in base 8, 10 or 16
-w --include-all-whitespace Include all whitespace as valid string characters
-o An alias for --radix=o
-T --target=<BFDNAME> Specify the binary file format
-e --encoding={s,S,b,l,B,L} Select character size and endianness:
s = 7-bit, S = 8-bit, {b,l} = 16-bit, {B,L} = 32-bit
--unicode={default|show|invalid|hex|escape|highlight}
-U {d|s|i|x|e|h} Specify how to treat UTF-8 encoded unicode characters
-s --output-separator=<string> String used to separate strings in output.
@<file> Read options from <file>
-h --help Display this information
-v -V --version Print the program's version number
strings: supported targets: elf64-x86-64 elf32-i386 elf32-iamcu elf32-x86-64 pei-i386 pe-x86-64 pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big elf32-little elf32-big pe-bigobj-x86-64 pe-i386 srec symbolsrec verilog tekhex binary ihex plugin
Report bugs to <https://sourceware.org/bugzilla/>
2.3 版本确认
$ strings --version
GNU strings (GNU Binutils for Ubuntu) 2.38
Copyright (C) 2022 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) any later version.
This program has absolutely no warranty.
三、参数逐项实测
3.1 默认行为:提取长度 ≥ 4 的可打印字符串
$ strings s.txt
abcdef
hello world
说明:默认最小长度是 4(见 2.2 节 help 原文 The default is 4),因此 2 字符的 ab、3 字符的 xyz 被过滤,只输出 abcdef 与 hello world。
3.2 -n(--bytes):自定义最小长度阈值
把阈值降到 3:
$ strings -n 3 s.txt
abcdef
xyz
hello world
再降到 2:
$ strings -n 2 s.txt
ab
abcdef
xyz
hello world
说明:阈值每降 1,更短的串就进入结果。排查短标识符(如字段名、单字符标志)时用 -n 2 甚至 -n 1,代价是噪声增多。
3.3 -t(--radix):输出每个字符串的文件偏移
$ strings -t x s.txt
3 abcdef
e hello world
$ strings -t d s.txt
3 abcdef
14 hello world
$ strings -t o s.txt
3 abcdef
16 hello world
说明:-t x 十六进制偏移(e 即十进制 14)、-t d 十进制、-t o 八进制。偏移是从文件开头到该字符串首字节的距离:ab\n 占 3 字节所以 abcdef 在偏移 3;abcdef\n 占 7 字节,3+7=10(十六进制 e、八进制 16)。拿到偏移后可用 od 核对原始字节(见 od 篇 4.4 节)。另有短选项 -o 等价 -t o(见 2.2 节 help 原文)。
3.4 -e(--encoding):按指定编码扫描
先用默认 8 位编码扫 UTF-16LE 样例——每个 ASCII 字符被 0x00 隔开,连续可打印序列都不到 4 字符,结果为空:
$ strings utf16le.bin
指定 -e l(16 位小端,即 UTF-16LE)后正确解出:
$ strings -e l utf16le.bin
Hello World
对照 -e b(16 位大端):编码不匹配导致字符错位,从第二个字符开始才对齐出内容:
$ strings -e b utf16le.bin
ello World
说明:-e 可选值 s(7 位 ASCII)、S(8 位)、b/l(16 位大端/小端)、B/L(32 位大端/小端)。处理 Windows 的 UTF-16 文件(注册表导出、资源文件、Office 旧格式)时 -e l 是关键参数。
3.5 -a(--all)与默认行为:扫描范围差异
对 ELF 二进制 /bin/ls 实测。默认(等价 -a,扫描整个文件):
$ strings /bin/ls | head -5
/lib64/ld-linux-x86-64.so.2
__cxa_finalize
__libc_start_main
__cxa_atexit
obstack_alloc_failed_handler
$ strings -a /bin/ls | head -5
/lib64/ld-linux-x86-64.so.2
__cxa_finalize
__libc_start_main
__cxa_atexit
obstack_alloc_failed_handler
$ strings /bin/ls | wc -l
1465
$ strings -a /bin/ls | wc -l
1465
显式 -d(只扫描数据段)结果更精简。GNU strings 的 help 显示 -a(全文件扫描)在当前版本是默认值(见 2.2 节原文 [default] 标注),因此 -a 与默认输出完全一致;而 -d 只扫数据段,行数略少于全文件扫描:
$ strings -d /bin/ls | wc -l
1434
💡 说明:历史 BSD 版 strings 只扫数据段,GNU 版默认全文件扫描。需要「干净的数据段字符串」时显式加
-d;两个数值 1465 与 1434 的差异就是全文件与数据段的范围差。
3.6 管道输入:不给文件名时读标准输入
$ printf "pipe-test-string-abcd\nab\n" | strings
pipe-test-string-abcd
$ head -c 200 /bin/ls | strings -n 3
ELF
说明:第一条中 2 字符的 ab 因默认阈值被过滤;第二条对 /bin/ls 前 200 字节(ELF 头部)扫描,-n 3 下唯一命中是 ELF 魔数中的 ELF 三字符——头部其余字节多为不可打印。
3.7 组合参数与实战:从 ELF 提取版本字符串
最常用的实战组合:从系统命令里找版本信息线索:
$ strings /bin/ls | grep -i version | head -5
# slackware version of dircolors) are recognized but ignored.
sort_type != sort_version
time (-t), version (-v), extension (-X)
-v natural sort of (version) numbers within text
--version output version information and exit
-t x -n 6 组合:带十六进制偏移、只输出 6 字符以上的串,先看头部结构化字符串:
$ strings -t x -n 6 /bin/true | head -8
318 /lib64/ld-linux-x86-64.so.2
841 __cxa_finalize
850 __libc_start_main
862 __cxa_atexit
86f dcgettext
885 nl_langinfo
891 __fpending
89c fileno
说明:偏移 318 处的动态链接器路径、841 起的 glibc 符号名,都是 ELF 头部与动态段的固有内容;-n 6 过滤掉了短符号带来的噪声。
四、实战示例
以下示例均在本环境实测执行。
4.1 确认二进制来自哪个 libc / 链接器
$ strings /bin/ls | grep -m2 GLIBC
GLIBC_2.28
GLIBC_2.14
列出的 GLIBC_2.x 是 /bin/ls 依赖的 glibc 符号版本(-m2 让 grep 命中 2 条即停),排查「移植到老系统报 GLIBC 版本不足」时快速定位需求版本。
4.2 从崩溃日志/堆转储里捞线索
$ printf "segv at 0x7f\nuser=ops module=auth ok\n" > core-dump.txt
$ strings -n 3 core-dump.txt
segv at 0x7f
user=ops module=auth ok
对未知格式的二进制转储文件先跑 strings -n 3 文件 | head,往往能直接看到模块名、路径等明文线索。
4.3 与 od 配合验证字符串及其原始字节
$ strings -t x s.txt
3 abcdef
e hello world
$ od -j 3 -N 6 -t x1z s.txt
0000003 61 62 63 64 65 66 >abcdef<
0000011
strings 报告 abcdef 在十六进制偏移 3,od 从偏移 3 转储 6 字节正是 61 62 63 64 65 66("abcdef" 的 ASCII 码),闭环验证。
4.4 扫描 UTF-16 编码的 Windows 资源文件
$ strings -e l -n 3 utf16le.bin
Hello World
Windows 平台的 UTF-16LE 文本(注册表 .reg 导出、.lnk、资源段)用默认编码扫不出来,-e l 后即可正常提取。
五、注意事项与常见问题
Q1:strings 装不上或命令不存在?
strings 属 GNU binutils 包(apt-get install binutils 安装,Ubuntu 22.04 默认预装 2.38 版本)。本环境实测安装日志末尾为 Setting up binutils (2.38-4ubuntu2.12),安装后 strings --version 即 2.3 节回显。
Q2:明明有文本却提取不到?
三个最常见原因:① 文本不足 4 字符被默认阈值过滤,降 -n;② 文本是 UTF-16 编码,加 -e l;③ 字符串中间混了不可打印字节被截断,降 -n 分段观察。
Q3:strings 大文件 | wc -l 出来的行数每次不一样?
输出量取决于文件数据段与全文件扫描的范围(见 3.5 节 1465 与 1434 的实测差异);另外带 -a/-d 与默认值在不同 binutils 版本上的默认行为有差异,脚本依赖行数时显式写明 -a 或 -d。
Q4:为什么提取出来的字符串「断在奇怪的地方」?
strings 把「连续可打印字符」定义为序列,遇到任何不可打印字节就断开。长句子中间若包含制表符、特殊空格等就会分成两段;-w(--include-all-whitespace,见 2.2 节 help 原文)可把空白字符也纳入序列。
Q5:strings 能确认软件版本号吗?
能提供线索但非权威:strings /bin/ls | grep -i version 能找到 help 文本与符号名(见 3.7 节实测),但准确版本应优先用程序自带的 --version 输出;strings 更适合在无执行权限(如只拿到固件映像文件)的场景反查版本字符串。
六、参考链接
- GNU Binutils 官方文档(strings 手册):https://sourceware.org/binutils/docs/binutils/strings.html
- Binutils 源码与缺陷跟踪:https://sourceware.org/bugzilla/
- 配套工具详解见本专栏《od 命令详解与参数实测(Ubuntu 22.04)》《hexdump 命令详解与参数实测(Ubuntu 22.04)》