Исправление двоичного файла с помощью dd

32

Я читал эту цитату (ниже) несколько раз, совсем недавно здесь , и постоянно удивляюсь, как ddможно использовать для исправления чего-либо, не говоря уже о компиляторе:

Система Unix, которую я использовал в школе 30 лет назад, была очень ограничена в оперативной памяти и дисковом пространстве. Особенно /usr/tmpфайловая система была очень маленькой, что приводило к проблемам, когда кто-то пытался скомпилировать большую программу. Конечно, студенты не должны были писать «большие программы» в любом случае; большие программы обычно были исходными кодами, скопированными откуда-то. Многие из нас скопировали /usr/bin/ccв /home/<myname>/cc, и используются ddдля исправления двоичного файла использовать /tmpвместо/usr/tmp , который был больше. Конечно, это только усугубило проблему - дисковое пространство, занимаемое этими копиями, имело значение в те дни и теперь /tmpрегулярно заполнялось, не позволяя другим пользователям даже редактировать свои файлы. После того, как они узнали, что случилось, сисадмины сделалиchmod go-r /bin/* /usr/bin/* которая "исправила" проблему, и удалила все наши копии компилятора C.

(Акцент мой)

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

Могут ли быть исправлены двоичные файлы dd? Есть ли в этом историческое значение?

Amziraro
источник
3
Конечно - просто odфайл для байт-шестнадцатеричных кодов, найдите bs=$patchsize count=1 seek=$((offset/bs)) conv=notruncнужное смещение, решите, что вы хотите редактировать, и ваш патч прямо сейчас.
mikeserv
3
Кто-то никогда не перезаписывает загрузочный сектор. ;)
Парфянский выстрел
@ParthianShot На самом деле я однажды перезаписал первые ~ 260 МБ моего загрузочного диска (+ root) частью Debian LiveCD. О_о Но я не думаю, что это действительно исправление, хе-хе ...
Amziraro
1
Или, скорее, это ожидаемое и абсолютно нормальное поведение Разрушителя
Дисков

Ответы:

73

Давай попробуем. Вот тривиальная программа на C:

#include <stdio.h>
int main(int argc, char **argv) {
    puts("/usr/tmp");
}

Мы встроим это в test:

$ cc -o test test.c

Если мы запустим его, он напечатает «/ usr / tmp».

Давайте выясним, где находится " /usr/tmp" в двоичном файле:

$ strings -t d test | grep /usr/tmp
1460 /usr/tmp

-t d печатает смещение в десятичном виде в файл каждой найденной строки.

Теперь давайте создадим временный файл с просто " /tmp\0" в нем:

$ printf "/tmp\x00" > tmp

Итак, теперь у нас есть двоичный файл, мы знаем, где находится строка, которую мы хотим изменить, и у нас есть файл с замещающей строкой.

Теперь мы можем использовать dd:

$ dd if=tmp of=test obs=1 seek=1460 conv=notrunc

Он считывает данные из tmp(нашего " /tmp\0" файла), записывает их в наш двоичный файл, использует размер выходного блока 1 байт, пропускает смещение, найденное ранее, прежде чем что-то записывает, и явно не обрезает файл, когда это будет сделано.

Мы можем запустить пропатченный исполняемый файл:

$ ./test
/tmp

Строковый литерал, который выводит программа, был изменен, поэтому теперь он содержит " /tmp\0tmp\0", но строковые функции останавливаются, как только они видят первый нулевой байт. Это исправление позволяет только сделать строку короче или одинаковой длины, а не длиннее, но этого вполне достаточно для этих целей.

Так что мы можем не только исправлять вещи dd, но и делать это.

Майкл Гомер
источник
1
Это превосходно ... и я очень надеюсь, что никогда не столкнусь в производственной среде! В прошлом я использовал подобные методы, чтобы преобразовать серийные номера в шестнадцатеричные изображения для микроконтроллеров, но слишком легко выстрелить себе в ногу.
Майкл Шоу
Если я хотел бы дать письменные инструкции к кому - то , как залатать конкретный бинарный файл, я бы предпочел дать им командную строку , чтобы скопировать / вставить , чем сказать им «открыть файл в шестнадцатеричном редакторе, найти /usr/tmpстроку, заменить его /tmp, дон не забудьте завершающий \0байт, сохраните файл и скрестите пальцы ". Или, что еще лучше, сценарий оболочки, который сначала выполняет некоторую проверку работоспособности, а затем вызывает dd. К сожалению, потребность в таких вещи , как это часто возникает , когда старая часть программного обеспечения на ныне несуществующим поставщик только должна быть перенесена на новую систему.
Гунтрам Блом поддерживает Монику
Да, sed лучше для такого рода вещей. Но вы не совсем правы насчет всего: «Это исправление позволяет только сделать строку короче или одинаковой длины, а не длиннее». Вы предполагаете, что вы заботитесь о данных, следующих сразу за строкой, которую хотите изменить, или что следующая строка не может быть просто подстрокой исходной строки. Другими словами, если вы находитесь в разделе памяти .strings и у вас есть «/ usr \ 0 / bin / bash \ 0», вы можете превратить это в / usr / bin / bash, просто изменив это первое нулевой байт и создание его "/ usr // bin / bash" (например).
Парфянский выстрел
2
@ParthianShot - sed«S не лучше для такого рода вещи - вы не можете так expliciltly и точно предел sed» s для чтения / записи буфера в том , как вы могли бы с dd- что вся причина , она была когда - либо использовали для этого в первую очередь. С ddего помощью можно произвольно разместить произвольное количество произвольных байтов. Этого также нельзя сказать о sed. Если ddздесь используется как скальпель, вы бы применили sedкак разрушающий шар.
mikeserv
Это справедливо (хотя и довольно редко!) - будут случаи, когда вы можете сделать строку длиннее, не заботясь ни о результате, ни о другом произвольном, но конкретном фрагменте данных. Я буду придерживаться общего заявления, хотя.
Майкл Гомер
9

Это зависит от того, что вы подразумеваете под «патчем двоичного кода».

Я изменяю двоичные файлы, используя ddиногда. Конечно, такой функции нет dd, но она может открывать файлы, читать и записывать вещи с определенными смещениями, поэтому, если вы знаете, что и куда писать, вуаля, есть ваш патч.

Например, у меня был этот двоичный файл, который содержал некоторые данные PNG. Используйте, binwalkчтобы найти смещение, ddчтобы извлечь его (обычно binwalk также извлекает вещи, но моя копия была глючная), отредактируйте его gimp, убедитесь, что отредактированный файл имеет тот же размер или меньше, чем исходный (изменение смещений - это не то, что вы можете легко сделать ), а затем используйте, ddчтобы вернуть измененное изображение на место.

$ binwalk thebinary
[…]
4194643    0x400153     PNG image, 800 x 160, 8-bit/color RGB, non-interlaced
[…]
$ dd if=nickel bs=1 skip=4194641 count=2 conv=swab | od -i
21869 # file size in this case - depends on the binary format
$ dd if=thebinary bs=1 skip=4194643 count=21869 of=theimage.png
$ gimp theimage.png
$ pngcrush myimage.png myimage.crush.png
# make sure myimage.crush.png is smaller than the original
$ dd if=myimage.crush.png of=thebinary bs=1 seek=4194643 conv=notrunc

Иногда я также хочу заменить строки в двоичных файлах (например, пути или имена переменных). Хотя это также может быть сделано с помощью dd, это проще сделать с помощью sed. Вам просто нужно убедиться, что строка, которую вы заменяете, имеет ту же длину, что и исходная строка, чтобы вы не меняли смещения.

sed -e s@/the/old/save/path@/the/new/save/path@ -i thebinary

или взять пример @ MichaelHomer с добавленным 0-байтом в:

sed -e 's@/usr/tmp@/tmp\x00tmp@' -i test

Конечно, вы должны проверить, действительно ли это работает потом.

frostschutz
источник
... при условии, что у вас есть sedфайл, который хорошо обрабатывает двоичные файлы, что, похоже, имеет место с gnu sed, но не со многими более старыми, sedкоторые работали только с файлами ascii, перепутали с чем-то еще (особенно \0с входными данными), и имел ограничения по максимальной длине линии.
Гунтрам Блом поддерживает Монику
1
sedПохоже, busybox может нормально изменять двоичные файлы, но \x00в строке замены он не понимает, как это делает GNU sed. Это требует тестирования, но, тем не менее, я думаю, что стоит упомянуть, поскольку это намного проще, чем dd- в некоторых случаях. Исправление двоичных файлов в любом случае ненадежное дело.
frostschutz