Я пытаюсь выяснить, как агенты на самом деле записаны в скомпилированном двоичном файле программы 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?)). Числа представляют собой какой-то адрес для «плагина»? Значит, они разные в зависимости от позиции и их длина тоже различна?
Вот это да. Ваш вопрос показывает, что вы готовы экспериментировать и стремиться учиться, но есть намного больше, чтобы понять, чем обычно происходит в вопросе переполнения стека.
Первый, 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
Вы должны многому научиться, но продолжайте в том же духе! Я думаю, это замечательно, как вы хотите экспериментировать. Надеюсь это поможет.