【问题标题】:Binary to ASCII different between C++ and Grep?C ++和Grep之间的二进制到ASCII不同?
【发布时间】:2014-11-01 00:50:39
【问题描述】:

我试图弄清楚 agument 是如何实际记录在 c/c++ 程序的编译二进制文件中的。以下是我的程序。我只是想让它尽可能简单

void f(char a,char b){}
int main(){f(12,23);}

为了真正能够“读取”二进制文件,我需要将其转换为某种 ASCII“可表示”形式。我发现了

grep $'\xx' a.out

实际上使用 a.out 作为二进制文件和 xx 作为十进制 ascii 代码。但是 grep 不能告诉我任何事情,因为它只会输出“二进制匹配”。如果我强迫它用'-a'打印出来,它只会打印出所有东西。不过,我可以使用 -c 选项来查看其中有多少:

grep $'\12' b.out (I renamed the file) ==> 4
grep $'\23' b.out                      ==> 3

但是为了研究一些东西,我需要确切的位置。所以我编写了另一个程序,它基本上打印出符合 char 的 ASCII。

#include<iostream>
using namespace std;
int main(){char c;
    while(cin>>c)cout<<(int)c<<' ';}

但是当我运行以下命令时,结果实际上不匹配:

./a.out<./b.out|tr ' ' '\n'|grep -c '^12$' ==> 0
./a.out<./b.out|tr ' ' '\n'|grep -c '^23$' ==> 4

我想知道我在测试程序中写错了什么吗?还是 grep 有某种特殊的机制(比如不是逐字节)?哪一个是正确的?或者有人可以直接给我答案: func(1,2,3,4) 中的“1,2,3,4”如何以二进制形式记录


EDT1 感谢您的建议,我使用“od -tu1”来替换我的测试程序,效果非常好。而且我稍微增强了我的测试程序,这样论点会更明显,数字不会“消失”:

void f(int a,int b,int c,int d,int e,int f,int g,int h,int i,int j,int k,int l,int m,int n,int o,int p,int q,int r,int s,int t){a+=b+c+d+e+f+g+h+i+j+k+l+m+n+o+p+q+r+s+t;}
int main(){f(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19);}

通过更改这些参数并使用“diff”命令,我终于找出了这些数字在二进制文件中的位置:

0002560  68  36 104  19   0   0   0 199  68  36  96  18   0   0   0 199
0002600  68  36  88  17   0   0   0 199  68  36  80  16   0   0   0 199
0002620  68  36  72  15   0   0   0 199  68  36  64  14   0   0   0 199
0002640  68  36  56  13   0   0   0 199  68  36  48  12   0   0   0 199
0002660  68  36  40  11   0   0   0 199  68  36  32  10   0   0   0 199
0002700  68  36  24   9   0   0   0 199  68  36  16   8   0   0   0 199
0002720  68  36   8   7   0   0   0 199   4  36   6   0   0   0  65 185
0002740   5   0   0   0  65 184   4   0   0   0 185   3   0   0   0 186
0002760   2   0   0   0 190   1   0   0   0 191   0   0   0   0 232 234

如你所见,19~9都写得很清楚了。但是从 8 到 0,事情开始以一种无法理解的方式发生变化。数字之间的位移变得越来越小。而且我也不明白它们之间的数字是多少(我明白 0 是“int”部分(小端序?))。这些数字是否代表某种“插件”地址?那么它们根据不同的位置是不同的,它们的长度也不同?

【问题讨论】:

  • 你可以编译成汇编器使用:g++ -S -o prog.s prog.cpp
  • 有了cout&lt;&lt;hex;,事情应该会更好……
  • 然而确切地数字出现在您的可执行文件中,它们肯定不会以文字文本"12""23" 的形式出现。它们将出现在 binary 中(尽管不是 00001100),而 GREP 在执行二进制时非常糟糕(它是为纯文本设计的)。即使这样,它也会发现 lots 只出现字节“12”。
  • @Galik,assemble do 看起来很清晰,但我对可执行文件中的实际位置(或结构)更感兴趣
  • “之间的数字”是实际的 CPU 操作码,它们将您的参数移动到进一步处理的位置。将您的普通转储与生成的汇编程序进行比较;一些操作码长 1 个字节(加上参数字节),一些占用更多空间。

标签: c++ bash grep bin


【解决方案1】:

哇。您的问题表明您愿意尝试并渴望学习,但与堆栈溢出问题相比,您需要理解的内容要多得多。

首先,grep 是一个非常强大的工具,但不适合您的任务。你会对od 更感兴趣,它将为你提供文件的原始二进制转储。 (查看其标志以了解如何输出为十六进制、十进制甚至纯二进制。)

接下来,如果你想写一个二进制文件,如果你把它写在一个可执行文件中,你会看到一堆乱七八糟的东西。除了您要存储的变量外,可执行文件还将包含您正在编译的所有代码。很难隔离代表变量的(大概)四个字节,并且您需要大量阅读 a.out 可执行文件背后的格式才能做到这一点。

编写一个将写入二进制文件的 C 程序会更简洁,例如:

#include <stdio.h>
int main() {
    int one;
    int two;
    int three;
    int four;
    one = 1;
    two = 2;
    three = 3;
    four = 4;
    FILE* fp = fopen("test.dat", "wb");
    fwrite(&one, sizeof(int), 1, fp); 
    fwrite(&two, sizeof(int), 1, fp); 
    fwrite(&three, sizeof(int), 1, fp); 
    fwrite(&four, sizeof(int), 1, fp); 
    fclose(fp);
    return 0;
}

有很多其他方法可以编写相同的代码,并且一些好人可以纠正我犯的任何明显错误(我已经有一段时间没有在没有编译器的情况下编写 C 代码了),但这应该只写 4 个整数.

最后,快速回答您的问题。假设 int 是 32 位,您将用二进制写入这些数字。您必须查看“big-endian vs. little-endian”才能理解下一部分,但根据您的架构,您将成为其中之一。 Big-endian 更直观,所以我将使用这个概念来回答。

数字存储为 32 位二进制值。 (int 中的第一位是符号位。如果为 1,则值为负数,您必须查找“二进制补码”才能理解该符号。)在您的情况下,对于“1、2、3 , 4",只有最后 3 位很重要,所以你会看到很多 0:

1: 00000000 0000000 00000000 00000001
2: 00000000 0000000 00000000 00000010
3: 00000000 0000000 00000000 00000011
4: 00000000 0000000 00000000 00000100

注意,这真的很笨重,所以我们倾向于使用十六进制。使用它,您可以用 2 个字符表示每个 8 位字节。在十六进制中,您的答案是:

1:   00 00 00 01
2:   00 00 00 02
3:   00 00 00 03
4:   00 00 00 04
17:  00 00 00 11
255: 00 00 00 FF

你有很多事情要做,但要坚持下去!我认为你是多么渴望实验真是太好了。希望这会有所帮助。

【讨论】:

  • 嗯,感谢您写了这么多,但我认为我的观点更多的是阅读编译后的文件,而不是阅读一些随机的可执行文件。 BYW,“od”真的很有用,谢谢!我终于找出了这些论点,虽然它们看起来很奇怪......我将把更新粘贴到问题中
猜你喜欢
  • 1970-01-01
  • 2011-04-14
  • 2011-05-29
  • 2011-05-30
  • 1970-01-01
  • 2016-05-05
  • 1970-01-01
  • 2020-10-05
  • 1970-01-01
相关资源
最近更新 更多