实现我自己的“字符串”工具-缺少序列GNU字符串发现

时间:2018-07-17 20:48:42

标签: c linux string shell binary

我想以编程方式读取二进制文件中的文本/字符串。

我的目标的确切替代方法是Linux中的strings shell命令。

当我运行strings -n 4 /bin/dd shell命令时,它会打印818行文本。

如何像strings命令一样找到二进制形式的所有字符串?


我的代码使用read而不是fgetc,并在找到EOF之后为其余文本添加了打印块。

它在/bin/dd中可以找到813个单词,但是strings仍然可以找到818个单词。有什么区别?

另一个问题;您可以为此代码提出性能改进建议吗?我猜read(1)并不是最快的方法。

最新更新的代码

#include <stdio.h>
#include <stdbool.h>
#include <unistd.h>
#include <fcntl.h>

bool isPrintable(unsigned char c)
{
    if(c >= 0x20 && c <= 0x7e || c == 0x09)
    {
        return true;
    }
    return false;
}

int main(int argc, char * argv [])
{
    char buffer[300];
    char *p = buffer;
    char ch;
    int fd;

    if(argc < 2)
    {
        printf("Usage: %s file", argv[0]);
        return 1;
    }

    fd = open(argv[1], O_RDONLY);
    if(0 <= fd)
    {
        while(1 == read(fd, &ch, 1))
        {
            if(isPrintable(ch) && (p - buffer < sizeof(buffer) - 3))
            {
                *p++ = ch;
            }
            else
            {
                if(p - buffer >= 4) // print collected text
                {
                    *p++ = '\n';
                    *p++ = '\0';
                    printf("%s", buffer);
                }
                p = buffer;
            }
        }
        if(p - buffer >= 4) // print the rest, if any
        {
            *p++ = '\n';
            *p++ = '\0';
            printf("%s", buffer);
        }
        close(fd);
    }
    else
    {
        printf("Could not open %s\n", argv[1]);
        return 1;
    }

    return 0;
}

这是mystringsstrings的性能指标。 strings可以在更短的时间内找到更多文本。

$ time ./mystrings /lib/i386-linux-gnu/libc-2.27.so | wc -l
11852
real    0m0,917s
user    0m0,271s
sys 0m0,629s

$ time strings /lib/i386-linux-gnu/libc-2.27.so | wc -l
12026
real    0m0,028s
user    0m0,027s
sys 0m0,000s

即使我使用fopenfreadfclose也不是那么快:

$ time ./mystrings2 /lib/i386-linux-gnu/libc-2.27.so | wc -l
11852
real    0m0,084s
user    0m0,070s
sys 0m0,004s

我也欢迎任何有关性能改进的建议。

1 个答案:

答案 0 :(得分:3)

您必须包含制表符。它们的十六进制代码为0x09。

您可以通过将其添加到可打印的测试中来对其进行修复:

if(c >= 0x20 && c <= 0x7e || c == 0x09)

提前十分钟:

哦,我不知道为什么该程序在此人的/bin/dd中找到813个单词,而strings找到818个单词。为什么有人会认为我会这么做?

但是,我确实有一个编译器和一个Unix系统,所以我可以做一些研究以找出答案。

首先,我在系统上进行了尝试:

$ ./yourprogram /bin/dd > yours && wc -l yours
807 yours

$ strings -n 4 /bin/dd > theirs && wc -l theirs
812 theirs

好吧,数字不同但还是有所不同。然后,我查看了差异:

$ diff -u yours theirs
--- yours       2018-07-17 15:13:27.188357492 -0700
+++ theirs      2018-07-17 15:13:56.905429280 -0700
@@ -182,7 +182,7 @@
 ATUH
 t9[]A\
 []A\
-[]A\
+8      []A\
 AUAT1
 []A\A]
 HiD$
@@ -210,7 +210,9 @@
 XZL;t$
 \$ I
 AUATI
+;'u    H
 []A\A]
+       v*H

这很麻烦,但是它表明您发现[]A\strings发现8 []A\。检查文件将其显示为Tab字符。然后,我可以创建一个测试用例:

$ printf 'hello\tworld' > file

$ strings file
hello    world

$ ./yourprogram file
hello
world

因此,程序似乎无法识别Tab,而strings可以识别。程序为什么不认为它可打印?

我在man ascii中进行了查找:

Oct   Dec   Hex   Char
───────────────────────────────────────
011   9     09    HT  '\t' (horizontal tab)

我将其与代码查找的内容进行了比较。我可以在调试器中运行它,也可以添加printf语句来尝试确定为什么它不能识别0x09,但是我可以看到它要求字符至少为0x20才能使其可打印。

我更新了isPrintable,将其添加为特殊情况:

    if(c >= 0x20 && c <= 0x7e || c == 0x09)

然后重新编译并重新运行:

$ ./yourprogram /bin/dd | wc -l
812

现在计数匹配,我可以将其发布为答案,并假装就像我使用了一些哈利波特修补魅力或秘密关卡锁定功能,而不仅仅是研究和测试。