bash — Бинарный в ASCII различается между C ++ и Grep?

Я пытаюсь выяснить, как агенты на самом деле записаны в скомпилированном двоичном файле программы 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

Но для того, чтобы что-то изучить, мне нужна точная позиция. Поэтому я запрограммировал другую программу, которая в основном печатает ASCII в соответствии с char.

#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 есть какой-то особый механизм (например, не побайтовый)? И какой из них правильный? Или кто-то может дать мне ответ:
КАК «1,2,3,4» в func (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» (little endian?)). Числа представляют собой какой-то адрес для «плагина»? Значит, они разные в зависимости от позиции и их длина тоже различна?

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 бита будут иметь значение, поэтому вы увидите много нулей:

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

Обратите внимание, что это становится действительно неуклюжим, поэтому мы склонны использовать шестнадцатеричное. Используя это, вы можете представить каждый 8-битный байт в 2 символа. В шестнадцатеричном виде ваш ответ будет следующим:

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

Вы должны многому научиться, но продолжайте в том же духе! Я думаю, это замечательно, как вы хотите экспериментировать. Надеюсь это поможет.

2

Другие решения


По вопросам рекламы [email protected]