Corrigindo um binário com dd

32

Eu li essa citação (abaixo) várias vezes, mais recentemente aqui , e estou constantemente intrigado com a forma como ddpode ser usado para corrigir qualquer coisa e muito menos um compilador:

O sistema Unix que usei na escola, há 30 anos, era muito limitado em RAM e espaço em disco. Especialmente, o /usr/tmpsistema de arquivos era muito pequeno, o que causava problemas quando alguém tentava compilar um programa grande. Obviamente, os alunos não deveriam escrever "grandes programas" de qualquer maneira; programas grandes geralmente eram códigos-fonte copiados de "algum lugar". Muitos de nós copiados /usr/bin/ccpara /home/<myname>/cc, e utilizado ddpara corrigir o binário para usar /tmpem vez de/usr/tmp , que era maior. Obviamente, isso apenas piorou o problema - o espaço em disco ocupado por essas cópias importava naqueles dias e agora era /tmppreenchido regularmente, impedindo que outros usuários editassem seus arquivos. Depois que descobriram o que aconteceu, os administradores do sistema fizeram umachmod go-r /bin/* /usr/bin/* que "corrigiu" o problema e excluiu todas as nossas cópias do compilador C.

(Ênfase minha)

A ddpágina de manual não diz nada sobre correções e não acha que poderia ser re-proposto de qualquer maneira.

Os binários poderiam realmente ser corrigidos dd? Existe algum significado histórico para isso?

Amziraro
fonte
3
Claro - apenas odum arquivo para os códigos hexadecimais bytes, encontrar o deslocamento que você precisa, decidir sobre a sua edição, e bs=$patchsize count=1 seek=$((offset/bs)) conv=notruncsua direita patch no no.
mikeserv
3
Alguém nunca substituiu um setor de inicialização. ;)
Parthian Shot
@ParthianShot Na verdade, sobrescrevi os primeiros ~ 260 MB da minha unidade de inicialização (+ root) por parte de um LiveCD Debian uma vez. O_o Mas eu não acho que é realmente remendar, hehehe ...
Amziraro
1
Ou melhor, que é o comportamento esperado e totalmente normal do Destroyer disco: D
Amziraro

Respostas:

73

Vamos tentar. Aqui está um programa C trivial:

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

Vamos incorporar isso em test:

$ cc -o test test.c

Se o rodarmos, ele imprimirá "/ usr / tmp".

Vamos descobrir onde " /usr/tmp" está no binário:

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

-t d imprime o deslocamento em decimal no arquivo de cada sequência encontrada.

Agora vamos criar um arquivo temporário apenas com " /tmp\0":

$ printf "/tmp\x00" > tmp

Portanto, agora temos o binário, sabemos onde está a cadeia que queremos alterar e temos um arquivo com a cadeia de substituição.

Agora podemos usar dd:

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

Isso lê dados de tmp(nosso " /tmp\0" arquivo), gravando-os em nosso binário, usando um tamanho de bloco de saída de 1 byte, pulando para o deslocamento que encontramos anteriormente antes de gravar qualquer coisa, e explicitamente não truncando o arquivo quando terminar.

Podemos executar o executável corrigido:

$ ./test
/tmp

A string literal impressa pelo programa foi alterada, portanto agora contém " /tmp\0tmp\0", mas as funções da string param assim que vêem o primeiro byte nulo. Esse patch permite apenas tornar a string mais curta ou com o mesmo comprimento, e não mais, mas é adequada para esses fins.

Portanto, não apenas podemos consertar as coisas dd, como acabamos de fazê-lo.

Michael Homer
fonte
1
Isso é excelente ... e algo que espero nunca encontrar em um ambiente de produção! Eu usei métodos semelhantes no passado para obter números de série em imagens hexadecimais para microcontroladores, embora seja fácil demais dar um tiro no pé.
22815 Michael Shaw
Se eu quisesse dar instruções escritas a alguém sobre como corrigir um binário específico, prefiro fornecer uma linha de comando para copiar / colar do que dizer "abra o arquivo em um editor hexadecimal, encontre a /usr/tmpstring, substitua-a por /tmp, não esqueça o \0byte à direita , salve o arquivo e cruze os dedos ". Ou, melhor ainda, um script de shell que verifica primeiro a sanidade e depois chama dd. Infelizmente, a necessidade de coisas como essa surge com frequência quando um software antigo de um fornecedor agora extinto apenas precisa ser migrado para um novo sistema.
Guntram Blohm apoia Monica
Sim, sed é melhor para esse tipo de coisa. Mas você não está completamente certo sobre o conjunto "Esse patch permite apenas tornar a corda mais curta ou com o mesmo comprimento, e não mais". Você supõe que se preocupa com os dados imediatamente após a sequência que deseja modificar ou que não pode ter a próxima sequência simplesmente como uma substring da sequência original. Em outras palavras, se você estiver na seção .strings da memória e tiver "/ usr \ 0 / bin / bash \ 0", poderá transformá-lo em / usr / bin / bash simplesmente mudando primeiro byte nulo e tornando-o "/ usr // bin / bash" (por exemplo).
Parthian Shot
2
@ParthianShot - nãosed é melhor para esse tipo de coisa - você não pode limitar de forma explícita e precisa os buffers de leitura / gravação da maneira que você poderia com - o que é todo o motivo pelo qual ele foi usado para isso em primeiro lugar. Com você pode arbitrariamente colocar uma contagem arbitrária de bytes arbitrários. Isso também não pode ser dito . Se for usado como um bisturi aqui, você aplicaria como uma bola de demolição. sedddddsedddsed
mikeserv
Esse é um ponto justo (embora bastante raro!) - haverá ocasiões em que você poderá prolongar a sequência não se importando com o resultado ou com outro dado arbitrário, mas específico. Eu vou manter a afirmação geral, no entanto.
Michael Homer
9

Depende do que você quer dizer com "corrigir o binário".

Eu mudo binários usando ddàs vezes. É claro que não existe esse recurso dd, mas ele pode abrir arquivos, ler e escrever coisas com desvios específicos; portanto, se você sabe o que escrever, onde está, seu patch está pronto.

Por exemplo, eu tinha esse binário que continha alguns dados PNG. Use binwalkpara encontrar o deslocamento, ddpara extraí-lo (geralmente o binwalk também extrai coisas, mas minha cópia estava com bugs), edite-o com gimp, verifique se o arquivo editado é do mesmo tamanho ou menor que o original (alterar deslocamentos não é algo que você possa fazer facilmente) ) e use ddpara colocar a imagem alterada novamente no lugar.

$ 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

Às vezes, também desejo substituir cadeias de caracteres em binários (como nomes de caminho ou variável). Embora isso também possa ser feito dd, é mais simples fazê-lo sed. Você só precisa garantir que a string substituída tenha o mesmo comprimento da string original para não alterar as compensações.

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

ou para pegar o exemplo do @ MichaelHomer com um byte de 0 bytes adicionado em:

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

Claro que você precisa verificar se realmente funciona depois.

frostschutz
fonte
... assumindo que você tenha um sedque lida bem com arquivos binários, o que parece ser o caso do gnu sed, mas não com muitos dos seds mais antigos que funcionavam apenas em arquivos ascii, ficou confuso com qualquer outra coisa (especialmente \0s na entrada), e tinha restrições no comprimento máximo da linha.
Guntram Blohm suporta Monica
1
O busybox sedparece capaz de alterar bem os arquivos binários, mas não entende \x00na cadeia de substituição a maneira como o GNU sed. Requer testes, mas mesmo assim acho que vale a pena mencionar, pois é muito mais simples do que dd- em alguns casos. Os binários de aplicação de patches são um negócio confuso, de qualquer maneira.
frostschutz