Создать двоичные файлы в UNIX
Этот вопрос был на некоторое время, и я подумал, что должен предложить несколько бонусных баллов, если я смогу заставить его работать.
Что я сделал...
Недавно на работе я написал парсер, который преобразует двоичный файл в читаемый формат. Двоичный файл не является файлом Ascii с 10101010
персонажи. Он был закодирован в двоичном формате. Так что если я сделаю cat
на файл, я получаю следующее -
[jaypal~/Temp/GTP]$ cat T20111017153052.NEW
==?sGTP?ղ?N????W????&Xx1?T?&Xx1?;
?d@#e?
?0H????????|?X?@@(?ղ??VtPOC01
cceE??k@9??W傇??R?K?i2??d@#e???&Xx1&Xx??!?
blackberrynet?/??!
??!
??#ripassword??W傅?W傆??0H??
#R??@Vtc@@(?ղ??n?POC01
Так что я использовал hexdump
Утилита для отображения файла и последующего перенаправления его в файл. Теперь у меня был выходной файл, который представлял собой текстовый файл, содержащий значения Hex.
[jaypal~/Temp/GTP]$ hexdump -C T20111017153052.NEW
00000000 3d 3d 01 f8 73 47 54 50 02 f1 d5 b2 be 4e e4 d7 |==..sGTP.....N..|
00000010 00 01 01 00 01 80 00 cc 57 e5 82 00 00 00 00 00 |........W.......|
00000020 00 00 00 00 00 00 00 00 87 d3 f5 13 00 00 00 00 |................|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 10 |................|
00000040 01 01 0f 00 00 00 00 00 26 58 78 31 00 b3 54 c5 |........&Xx1..T.|
00000050 26 58 78 31 00 b4 3b 0a 00 00 ad 64 13 40 01 03 |&Xx1..;....d.@..|
00000060 23 16 65 f3 01 01 0b 91 30 19 48 99 f2 ff ff ff |#.e.....0.H.....|
00000070 ff ff ff 02 00 7c 00 dc 01 58 00 a0 40 40 28 02 |.....|...X..@@(.|
00000080 f1 d5 b2 b8 ca 56 74 50 4f 43 30 31 00 00 00 00 |.....VtPOC01....|
00000090 00 04 0a 63 63 07 00 00 00 00 00 00 00 00 00 00 |...cc...........|
000000a0 00 00 00 65 45 00 00 b4 fb 6b 40 00 39 11 16 cd |...eE....k@.9...|
000000b0 cc 57 e5 82 87 d3 f5 52 85 a1 08 4b 00 a0 69 02 |.W.....R...K..i.|
000000c0 32 10 00 90 00 00 00 00 ad 64 00 00 02 13 40 01 |2........d....@.|
После тонны awk
, sed
а также cut
, скрипт преобразовал шестнадцатеричные значения в читаемый текст. Для этого я использовал позиционирование смещения, которое будет отмечать начальную и конечную позиции каждого преобразованного параметра. Полученный файл после всех преобразований выглядит так
[jaypal:~/Temp/GTP] cat textfile.txt
Beginning of DB Package Identifier: ==
Total Package Length: 508
Offset to Data Record Count field: 115
Data Source: GTP
Timestamp: 2011-10-25
Matching Site Processor ID: 1
DB Package format version: 1
DB Package Resolution Type: 0
DB Package Resolution Value: 1
DB Package Resolution Cause Value: 128
Transport Protocol: 0
SGSN IP Address: 220.206.129.47
GGSN IP Address: 202.4.210.51
Почему я это сделал
Я - инженер по тестированию, и ручная проверка двоичных файлов была серьезной проблемой. Мне пришлось вручную анализировать смещения и использовать калькулятор для их преобразования и проверки на соответствие Wireshark и GUI.
Теперь вопрос часть
Я хочу сделать обратное тому, что я сделал. Это был мой план -
- Иметь легкий для чтения входной текстовый файл, который будет иметь
Parameters : Values
, - Пользователь может просто поместить значения рядом с ними (например, Date будет параметром, а пользователь может указать дату, которую он хочет иметь в файле данных).
- Сценарий вырезает всю необходимую информацию (предоставленную пользователем) из входного текстового файла и преобразует их в шестнадцатеричные значения.
- Как только файл был преобразован в шестнадцатеричные значения, я хочу закодировать его обратно в двоичный файл.
Первые три шага сделаны
проблема
Как только мой скрипт преобразует входной текстовый файл в текстовый файл с шестнадцатеричными значениями, я получаю файл следующим образом (обратите внимание, я могу сделать cat
на это).
[visdba@hw-diam-test01 ParserDump]$ cat temp_file | sed 's/.\{32\}/&\n/g' | sed 's/../& /g'
3d 3d 01 fc 73 47 54 50 02 f1 d6 55 3c 9f 49 9c
00 01 01 00 01 80 00 dc ce 81 2f 00 00 00 00 00
00 00 00 00 00 00 00 00 ca 04 d2 33 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10
01 01 0f 00 00 07 04 ea 00 00 ff ff 00 00 14 b7
00 00 ff ff 00 00 83 ec 00 00 83 62 54 14 59 00
60 38 34 f5 01 01 0b 58 62 70 11 60 f6 ff ff ff
ff ff ff 02 00 7c 00 d0 01 4c 00 b0 40 40 28 02
f1 d6 55 38 cb 2b 23 50 4f 43 30 31 00 00 00 00
00 04 0a 63 63 07 00 00 00 00 00 00 00 00 00 00
Мое намерение состоит в том, чтобы закодировать этот преобразованный файл в двоичный файл, чтобы, когда я делаю cat
в файле я получаю кучу мусорных значений.
[jaypal~/Temp/GTP]$ cat temp.file
==?sGTP?ղ?N????W????&Xx1?T?&Xx1?;
?d@#e?
?0H????????|?X?@@(?ղ??VtPOC01
cceE??k@9??W傇??R?K?i2??d@#e???&Xx1&Xx??!?
blackberrynet?/??!
??!
Так что вопрос такой. Как мне закодировать это в этой форме?
Почему я хочу это сделать?
У нас не так много GTP (GPRS Tunneling Protocol) сообщений на производстве. Я подумал, что если я перепроектирую это, я смогу эффективно создать генератор данных и создать свои собственные данные.
Подводя итог
Там могут быть сложные инструменты, но я не хочу тратить слишком много времени на их изучение. Прошло около 2 месяцев, я начал работать на платформе *nix и просто начал изучать такие мощные инструменты, как sed
а также awk
,
Что я действительно хочу, так это некоторую помощь и руководство, чтобы это произошло.
Еще раз спасибо за чтение! 200 очков ждут того, кто может направить меня в правильном направлении.:)
Примеры файлов
Вот образец оригинального двоичного файла
Вот пример входного текстового файла, который позволит пользователю вводить значения
Вот пример файла, который мой сценарий создает после того, как все преобразования из входного текстового файла завершены.
Как мне изменить кодировку File 3
в File 1
?
5 ответов
Вы можете использовать xxd для преобразования в / из двоичных файлов / hexdumps довольно просто.
данные в гекс
echo Hello | xxd -p
48656c6c6f0a
шестнадцатеричный к данным
echo 48656c6c6f0a | xxd -r -p
Hello
или же
echo 48 65 6c 6c 6f 0a | xxd -r -p
Hello
-p
это постскриптумный режим, который позволяет более свободно вводить данные
Это выход из xxd -r -p text
где текст - данные, которые вы даете выше
==▒sGTP▒▒U<▒I▒▒▒/▒▒3▒▒▒▒▒▒▒▒▒bTY`84▒
Xbp`▒▒▒▒▒▒▒|▒L▒@@(▒▒U8▒+#POC01
:▒ިv▒b▒▒▒▒TY`84Ud▒▒▒▒>▒▒▒▒▒▒▒!▒
blackberrynet▒/▒▒!
M
▒▒!
N
▒▒#Oripassword▒▒/▒▒/▒▒Xbp`▒@@(▒▒U8▒IvPOC01
:qU▒b▒▒▒▒▒▒TY`84U▒▒▒*:▒▒!
▒k▒▒▒#O Welcmme!
▒!
M
С помощью cut
а также awk
, вы можете сделать это довольно просто, используя gawk
(GNU Awk) функция расширения, strtonum()
:
cut -c11-60 inputfile |
awk '{ for (i = 1; i <= NF; i++)
{
c = strtonum("0x" $i)
printf("%c", c);
}
}' > outputfile
Или, если вы используете не-GNU версию 'new awk
', тогда вы можете использовать:
cut -c11-60 inputfile |
awk '{ for (i = 1; i <= NF; i++)
{
s = toupper($i)
c0 = index("0123456789ABCDEF", substr(s, 1, 1)) - 1
c1 = index("0123456789ABCDEF", substr(s, 2, 1)) - 1
printf("%c", c0*16 + c1);
}
}' > outputfile
Если вы хотите использовать другие инструменты (например, Perl и Python sprint; Ruby был бы другой возможностью), вы можете сделать это достаточно легко.
odx
это программа, похожая на hexdump
программа. Приведенный выше сценарий был изменен для чтения "hexdump.out" в качестве входного файла, и вывод передается в odx
вместо файла, и дает следующий вывод:
$ cat hexdump.out
00000000 3d 3d 01 fc 73 47 54 50 02 f1 d6 55 3c 9f 49 9c |==..sGTP...U<.I.|
00000010 00 01 01 00 01 80 00 dc ce 81 2f 00 00 00 00 00 |........../.....|
00000020 00 00 00 00 00 00 00 00 ca 04 d2 33 00 00 00 00 |...........3....|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 |................|
00000040 01 01 0f 00 00 07 04 ea 00 00 ff ff 00 00 14 b7 |................|
00000050 00 00 ff ff 00 00 83 ec 00 00 83 62 54 14 59 00 |...........bT.Y.|
00000060 60 38 34 f5 01 01 0b 58 62 70 11 60 f6 ff ff ff |`84....Xbp.`....|
00000070 ff ff ff 02 00 7c 00 d0 01 4c 00 b0 40 40 28 02 |.....|...L..@@(.|
$ sh -x revdump.sh | odx
+ cut -c11-60 hexdump.out
+ awk '{ for (i = 1; i <= NF; i++)
{
#c = strtonum("0x" $i)
#printf("%c", c);
s = toupper($i)
c0 = index("0123456789ABCDEF", substr(s, 1, 1)) - 1
c1 = index("0123456789ABCDEF", substr(s, 2, 1)) - 1
printf("%c", c0*16 + c1);
}
}'
0x0000: 3D 3D 01 FC 73 47 54 50 02 F1 D6 55 3C 9F 49 9C ==..sGTP...U<.I.
0x0010: 00 01 01 00 01 80 00 DC CE 81 2F 00 00 00 00 00 ........../.....
0x0020: 00 00 00 00 00 00 00 00 CA 04 D2 33 00 00 00 00 ...........3....
0x0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 ................
0x0040: 01 01 0F 00 00 07 04 EA 00 00 FF FF 00 00 14 B7 ................
0x0050: 00 00 FF FF 00 00 83 EC 00 00 83 62 54 14 59 00 ...........bT.Y.
0x0060: 60 38 34 F5 01 01 0B 58 62 70 11 60 F6 FF FF FF `84....Xbp.`....
0x0070: FF FF FF 02 00 7C 00 D0 01 4C 00 B0 40 40 28 02 .....|...L..@@(.
0x0080:
$
Или, используя hexdump -C
на месте odx
:
$ sh -x revdump.sh | hexdump -C
+ cut -c11-60 hexdump.out
+ awk '{ for (i = 1; i <= NF; i++)
{
#c = strtonum("0x" $i)
#printf("%c", c);
s = toupper($i)
c0 = index("0123456789ABCDEF", substr(s, 1, 1)) - 1
c1 = index("0123456789ABCDEF", substr(s, 2, 1)) - 1
printf("%c", c0*16 + c1);
}
}'
00000000 3d 3d 01 fc 73 47 54 50 02 f1 d6 55 3c 9f 49 9c |==..sGTP...U<.I.|
00000010 00 01 01 00 01 80 00 dc ce 81 2f 00 00 00 00 00 |........../.....|
00000020 00 00 00 00 00 00 00 00 ca 04 d2 33 00 00 00 00 |...........3....|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 10 |................|
00000040 01 01 0f 00 00 07 04 ea 00 00 ff ff 00 00 14 b7 |................|
00000050 00 00 ff ff 00 00 83 ec 00 00 83 62 54 14 59 00 |...........bT.Y.|
00000060 60 38 34 f5 01 01 0b 58 62 70 11 60 f6 ff ff ff |`84....Xbp.`....|
00000070 ff ff ff 02 00 7c 00 d0 01 4c 00 b0 40 40 28 02 |.....|...L..@@(.|
00000080
$
Чтобы изменить кодировку с File3 на File1, вы используете такой скрипт:
#!/bin/bash
# file name: tobin.sh
fileName="tobin.txt" # todo: pass it as parameter
# or prepare it to be used via the pipe...
while read line; do
for hexValue in $line; do
echo -n -e "\x$hexValue"
done
done < $fileName
Или, если вы просто хотите передать это, и используйте как пример xxd в этом потоке:
#!/bin/bash
# file name: tobin.sh
# usage: cat file3.txt | ./tobin.sh > file1.bin
while read line; do
for hexValue in $line; do
echo -n -e "\x$hexValue"
done
done
Если вы действительно хотите использовать BASH для этого, тогда я предлагаю вам начать использовать массив для создания вашего пакета. Вот стартовый код:
#!/bin/sh
# We assume the script will run on a LSB architecture.
hexDump() {
for idx in $(seq 0 ${#buffer[@]}); do
printf "%02X", ${buffer[$idx]}
done
} # hexDump() function
###
# dump() dumps the current content of the buffer[] array to the STDOUT.
#
dump() {
# or, use $ptr here...
for idx in $(seq 0 ${#buffer[@]}); do
printf "%c" ${buffer[$idx]}
done
} # dump() function
# Beginning of DB Package Identifier: ==
buffer[0]=$'\x3d' # =
buffer[1]=$'\x3d' # =
size=2
# Total Package Length: 2
# We start with 2, and later on we update it once we know the exact size...
# Assuming 32bit architecture, LSB, this is how we encode number 2 (that is our current size of the packet)
buffer[2]=$'\x02'
buffer[3]=$'\x00'
buffer[4]=$'\x00'
buffer[5]=$'\x00'
# Offset to Data Record Count field: 115
# I assume this is also a 32bit field of unsigned int type
ptr=5
buffer[++ptr]=$'\x73' # 115
buffer[++ptr]=$'\x00'
buffer[++ptr]=$'\x00'
buffer[++ptr]=$'\x00'
#hexDump
dump
Выход:
$ ./tobin2.sh | hexdump -C
00000000 3d 3d 02 00 00 00 73 00 00 00 00 |==....s....|
0000000b
Конечно, это не решение оригинальной статьи... Решение будет использовать что-то вроде этого для генерации двоичного вывода. Самая большая проблема заключается в том, что мы до сих пор не знаем типы полей в пакете. Мы также не знаем архитектуру (будь то бигендовская или малоизвестная, 32-битная или 64-битная). Вы должны дать нам спецификацию. Например, длина пакета какого типа? Мы не знаем, что из этого файла TXT!
Чтобы помочь вам сделать то, что вам нужно сделать, вы должны найти нам спецификацию размеров этих полей.
Обратите внимание, что это хорошее начало. Вам необходимо реализовать вспомогательные функции, например, для автоматического заполнения буфера [] значениями из строки, закодированной шестнадцатеричными значениями. Таким образом, вы можете сделать что-то вроде write $offset "ff c0 d3 ba be"
,
Существует инструмент binmake, позволяющий описывать в текстовом формате некоторые двоичные данные и генерировать двоичный файл (или выводить в stdout). Позволяет изменять порядковый номер и числовые форматы и принимает комментарии.
Сначала получите и скомпилируйте binmake (двоичная программа будет bin/
):
$ git clone https://github.com/dadadel/binmake
$ cd binmake
$ make
Создайте свой текстовый файл file.txt
:
# an exemple of file description of binary data to generate
# set endianess to big-endian
big-endian
# default number is hexadecimal
00112233
# man can explicit a number type: %b means binary number
%b0100110111100000
# change endianess to little-endian
little-endian
# if no explicit, use default
44556677
# bytes are not concerned by endianess
88 99 aa bb
# change default to decimal
decimal
# following number is now decimal
0123
# strings are delimited by " or '
"this is some raw string"
# explicit hexa number starts with %x
%xff
Создайте свой двоичный файл file.bin
:
$ ./binmake file.txt file.bin
$ hexdump file.bin -C
00000000 00 11 22 33 4d e0 77 66 55 44 88 99 aa bb 7b 74 |.."3M.wfUD....{t|
00000010 68 69 73 20 69 73 20 73 6f 6d 65 20 72 61 77 20 |his is some raw |
00000020 73 74 72 69 6e 67 ff |string.|
00000027
Вы также можете передать это, используя stdin
а также stdout
:
$ echo '32 decimal 32 %x61 61' | ./binmake | hexdump -C
00000000 32 20 61 3d |2 a=|
00000004
awk - неподходящий инструмент для этой работы, но есть тысячи способов сделать это. Самым простым способом часто является небольшая программа на Си или любой другой язык, который явно делает различие между символом и строкой десятичных цифр.
Однако, чтобы сделать это в awk, используйте формат "%c" printf.