Como editar arquivos binários com o Vim?

77

Existe uma maneira de editar arquivos binários em algum tipo de modo hexadecimal?

Por exemplo, se eu tiver alguns dados binários mostrados por xxdou hexdump -Cassim:

$ hexdump -C a.bin | head -n 5
00000000  cf fa ed fe 07 00 00 01  03 00 00 80 02 00 00 00  |................|
00000010  12 00 00 00 40 05 00 00  85 00 20 00 00 00 00 00  |....@..... .....|
00000020  19 00 00 00 48 00 00 00  5f 5f 50 41 47 45 5a 45  |....H...__PAGEZE|
00000030  52 4f 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |RO..............|
00000040  00 00 00 00 01 00 00 00  00 00 00 00 00 00 00 00  |................|

$ xxd a.bin | head -n 5
0000000: cffa edfe 0700 0001 0300 0080 0200 0000  ................
0000010: 1200 0000 4005 0000 8500 2000 0000 0000  ....@..... .....
0000020: 1900 0000 4800 0000 5f5f 5041 4745 5a45  ....H...__PAGEZE
0000030: 524f 0000 0000 0000 0000 0000 0000 0000  RO..............
0000040: 0000 0000 0100 0000 0000 0000 0000 0000  ................

Se eu quiser alterar o valor em uma posição específica, esse tipo de visualização ajudará a encontrar o lugar certo, por exemplo, quando a posição a mudar estiver próxima de alguma string conhecida.

janos
fonte

Respostas:

89

A maneira mais simples é usar a binaryopção De :help binary:

This option should be set before editing a binary file.  You can also
use the -b Vim argument.  When this option is switched on a few
options will be changed (also when it already was on):
        'textwidth'  will be set to 0
        'wrapmargin' will be set to 0
        'modeline'   will be off
        'expandtab'  will be off
Also, 'fileformat' and 'fileformats' options will not be used, the
file is read and written like 'fileformat' was "unix" (a single <NL>
separates lines).
The 'fileencoding' and 'fileencodings' options will not be used, the
file is read without conversion.

[..]

When writing a file the <EOL> for the last line is only written if
there was one in the original file (normally Vim appends an <EOL> to
the last line if there is none; this would make the file longer).  See
the 'endofline' option.

Se você não fizer isso, e seu ambiente estiver usando uma codificação multibyte (por exemplo, UTF-8, como a maioria das pessoas usa), o Vim tenta codificar o texto como tal, geralmente causando corrupção de arquivo.

Você pode verificar isso abrindo um arquivo e apenas usando :w. Agora está alterado.
Se você definir LANGe LC_ALLpara C(ASCII), o Vim não converterá nada e os arquivos permanecerão os mesmos (ainda adiciona uma nova linha), pois o Vim não precisará fazer nenhuma codificação multibyte.

Pessoalmente, também prefiro desativar o set wrap binário, embora outros possam preferir ativá- lo. YMMV. Outra coisa útil a fazer é :set display=uhex. De :help 'display':

uhex            Show unprintable characters hexadecimal as <xx>
                instead of using ^C and ~C.

E como última dica, você pode mostrar o valor hexadecimal do caractere sob o cursor na régua com %B( :set rulerformat=0x%B).

Mais avancado: xxd

Você pode usar a xxd(1)ferramenta para converter um arquivo em um formato mais legível e (esse é o bit mais importante), analisar o "formato legível" editado e gravá-lo como dados binários. xxdfaz parte vim, portanto, se você viminstalou, também deve xxd.

Para usá-lo:

$ xxd /bin/ls | vi -

Ou, se você já abriu o arquivo, pode usar:

:%!xxd

Agora faça as alterações, você precisa fazer isso no lado esquerdo da tela (os números hexadecimais), as alterações no lado direito (representação imprimível) são ignoradas na gravação.

Para salvá-lo, use xxd -r:

:%!xxd -r > new-ls

Isso salvará o arquivo em new-ls.

Ou para carregar o binário no buffer atual:

:%!xxd -r

De xxd(1):

   -r | -revert
          reverse operation: convert (or patch) hexdump into  binary.   If
          not  writing  to stdout, xxd writes into its output file without
          truncating it. Use the combination -r -p to read plain hexadeci‐
          mal dumps without line number information and without a particu‐
          lar column layout. Additional  Whitespace  and  line-breaks  are
          allowed anywhere.

E então apenas use :wpara escrevê-lo. ( cuidado : você deseja definir a binary opção antes de gravar no arquivo, pelos mesmos motivos descritos acima).

Teclados complementares para facilitar um pouco isso:

" Hex read
nmap <Leader>hr :%!xxd<CR> :set filetype=xxd<CR>

" Hex write
nmap <Leader>hw :%!xxd -r<CR> :set binary<CR> :set filetype=<CR>

Isso também está disponível no menu, se você estiver usando o gVim, em 'Ferramentas ➙ Converter em HEX' e 'Ferramentas ➙ Converter de volta'.

O wiki de dicas do vim tem uma página com mais informações e alguns scripts auxiliares. Pessoalmente, acho que é melhor você usar um editor hexadecimal real se estiver editando arquivos binários com tanta frequência. O Vim pode meio que fazer o trabalho, mas obviamente não foi projetado para isso, e se você escrever sem o :set binaryVim poderá destruir seus arquivos binários!

Martin Tournoij
fonte
4
Resposta adorável, mas provavelmente deve começar com "Não tente fazer isso em casa, crianças!"
msw
E se eu precisar remover alguns bytes? por exemplo, no meio do binário.
Anton K
Não sei o que o Vim está fazendo, mas está adicionando 95 KB de texto a um arquivo binário de 200 KB, apesar de eu não ter alterado nada. Mesmo com :set binary noeol fenc=utf-8. Na verdade, ele está fazendo isso imediatamente após abrir o arquivo antes que ele diga [noeol] [converted]. Por que o vim precisa aumentar o buffer em 150%? Como evito que ele corrompa arquivos como esse?
Braden Best
A única coisa que funciona é :r !xxd <file>(ou $ xxd <file> | vim -) ler e :w !xxd -r > <file>escrever, mas isso não é o ideal.
Braden Best
Excelente resposta. Observe que o URL para abençoar não funciona; Encontrei (acho) no github em github.com/bwrsandman/Bless .
sonofagun 10/01
19

Para visualizar o conteúdo de um arquivo binário em uma visualização hexadecimal, abra o arquivo, ative o modo binário e filtre o buffer através do xxdcomando:

:set binary
:%!xxd

Você pode fazer alterações na área esquerda (editar os números hexadecimais) e, quando estiver pronto, filtrar xxd -re finalmente salvar o arquivo:

:%!xxd -r
:w

Se a etapa de filtragem após a abertura e antes do fechamento parecer entediante, e você costuma fazer isso com arquivos com .binextensão, pode adicioná-lo ao seu vimrc para tornar o processo automático:

" for hex editing
augroup Binary
  au!
  au BufReadPre  *.bin let &bin=1
  au BufReadPost *.bin if &bin | %!xxd
  au BufReadPost *.bin set ft=xxd | endif
  au BufWritePre *.bin if &bin | %!xxd -r
  au BufWritePre *.bin endif
  au BufWritePost *.bin if &bin | %!xxd
  au BufWritePost *.bin set nomod | endif
augroup END
janos
fonte
Se eu siga estas instruções (abrir arquivo binário, :%!xxd, :%!xxd -r, :w, didnt fazer qualquer alteração!), Em seguida, o arquivo binário escrito é não o mesmo que o original ... É este o caso para você (eu testei com /bin/ls). Preciso usar :set binaryantes de salvar (veja também minha resposta que explica o porquê) ... Talvez seja algo no meu vimrc? Mas, independentemente disso, eu sempre uso set binarypara a segurança ...
Martin Tournoij
1
Você pode, em vez adicionar o augroupscript para ~/.vim/plugin/binary.vimse você não quiser encher o seu.vimrc
thom_nic
Se você estiver em uma instalação estrangeira, essa augroup Binarylistagem estará localizada em :help hex-editingou :help using-xxdem qualquer Vim desde 5.5 (setembro de 1999).
bb010g
6

Use o editor "bvi". http://bvi.sourceforge.net/ (Está em todos os repositórios Linux.)

$ apt-cache show bvi
[snip]
Description-en: binary file editor
 The bvi is a display-oriented editor for binary files, based on the vi
 text editor. If you are familiar with vi, just start the editor and begin to
 edit! If you never heard about vi, maybe bvi is not the best choice for you.
RonJohn
fonte
1
Uma alternativa mais avançada é o bviplus, que possui controles vim.
Anton K
Homepage e screenshots do Bviplus .
Iulian Onofrei
3

Resposta TL; DR

Abra o arquivo com o Vim no modo binário:

vim -b <file_to_edit>

No Vim, entre no modo de edição hexadecimal da seguinte forma:

:%!xxd -p

Salvar:

:%!xxd -p -r
:w

Isso converterá o buffer novamente do modo hexadecimal e salvará o arquivo normalmente.

Observe a opção -p. Isso evita todo o fluff extra de impressão e endereço e mostra apenas o hexadecimal. Apenas omita -p se quiser o contexto extra.

Cuidado ao abrir o arquivo com o Vim não no modo binário, pois ele anexará um caractere LF (geralmente não intencional) ao final do arquivo quando você o salvar.

Hintron
fonte
Isso realmente não adiciona nada que não esteja nas outras respostas.
Herb Wolfe
5
O verdadeiro TL; DR está presente :h using-xxde existe desde então v7.0001e provavelmente por mais tempo. Este site ficaria menos ativo se as pessoas pesquisassem os documentos.
Tommy A
1

Este parece um pouco útil plug-in vim que faz o trabalho usando um arquivo temporário que escreve e para trás para você automaticamente.

Há alguns anos, encontrei um plugin semelhante que adaptei e aprimorei para meu próprio uso. Eu incluí o código relevante para isso aqui, caso alguém queira. Também é baseado na ferramenta xxd. Tenho certeza de que a versão do GitHub que eu vinculei acima funciona melhor, mas na verdade eu não a usei, então imaginei que também postaria essa que sei que com certeza funciona.

A fonte desta outra versão foi a vim wikia, especificamente esta página .

Aqui está o código:

"-------------------------------------------------------------------------------  
" Hexmode  
"-------------------------------------------------------------------------------  
" Creates an automatic hex viewing mode for vim by converting between hex dump  
" and binary formats. Makes editing binary files a breeze.  
"-------------------------------------------------------------------------------  
" Source: vim.wikia.com/wiki/Improved_Hex_editing  
" Author: Fritzophrenic, Tim Baker  
" Version: 7.1  
"-------------------------------------------------------------------------------  
" Configurable Options {{{1  
"-------------------------------------------------------------------------------  

" Automatically recognized extensions  
let s:hexmode_extensions = "*.bin,*.exe,*.hex"  

"-------------------------------------------------------------------------------
" Commands and Mappings {{{1
"-------------------------------------------------------------------------------

" ex command for toggling hex mode - define mapping if desired
command! -bar Hexmode call ToggleHex()
command! -nargs=0 Hexconfig edit $VIM\vimfiles\plugin\hexmode.vim | exe "normal 11G" | exe "normal zo"

nnoremap <C-H> :Hexmode<CR>
inoremap <C-H> <Esc>:Hexmode<CR>
vnoremap <C-H> :<C-U>Hexmode<CR>

"-------------------------------------------------------------------------------    
" Autocommands {{{1  
"-------------------------------------------------------------------------------  

if exists("loaded_hexmode")  
    finish  
endif  
let loaded_hexmode = 1  

" Automatically enter hex mode and handle file writes properly  
if has("autocmd")  
  " vim -b : edit binary using xxd-format  
  augroup Binary  
    au!  

    " set binary option for all binary files before reading them  
    exe "au! BufReadPre " . s:hexmode_extensions . " setlocal binary"

    " if on a fresh read the buffer variable is already set, it's wrong
    au BufReadPost *
          \ if exists('b:editHex') && b:editHex |
          \   let b:editHex = 0 |
          \ endif

    " convert to hex on startup for binary files automatically
    au BufReadPost *
          \ if &binary | Hexmode | endif

    " When the text is freed, the next time the buffer is made active it will
    " re-read the text and thus not match the correct mode, we will need to
    " convert it again if the buffer is again loaded.
    au BufUnload *
          \ if getbufvar(expand("<afile>"), 'editHex') == 1 |
          \   call setbufvar(expand("<afile>"), 'editHex', 0) |
          \ endif

    " before writing a file when editing in hex mode, convert back to non-hex
    au BufWritePre *
          \ if exists("b:editHex") && b:editHex && &binary |
          \  let oldro=&ro | let &ro=0 |
          \  let oldma=&ma | let &ma=1 |
          \  silent exe "%!xxd -r" |
          \  let &ma=oldma | let &ro=oldro |
          \  unlet oldma | unlet oldro |
          \ endif

    " after writing a binary file, if we're in hex mode, restore hex mode
    au BufWritePost *
          \ if exists("b:editHex") && b:editHex && &binary |
          \  let oldro=&ro | let &ro=0 |
          \  let oldma=&ma | let &ma=1 |
          \  silent exe "%!xxd" |
          \  exe "set nomod" |
          \  let &ma=oldma | let &ro=oldro |
          \  unlet oldma | unlet oldro |
          \ endif
  augroup END  
endif  

"-------------------------------------------------------------------------------
" Functions {{{1
"-------------------------------------------------------------------------------

" helper function to toggle hex mode
function! ToggleHex()
  " hex mode should be considered a read-only operation
  " save values for modified and read-only for restoration later,
  " and clear the read-only flag for now
  let l:modified=&mod
  let l:oldreadonly=&readonly
  let &readonly=0
  let l:oldmodifiable=&modifiable
  let &modifiable=1
  if !exists("b:editHex") || !b:editHex
    " save old options
    let b:oldft=&ft
    let b:oldbin=&bin
    " set new options
    setlocal binary " make sure it overrides any textwidth, etc.
    let &ft="xxd"
    " set status
    let b:editHex=1
    " switch to hex editor
    set sh=C:/cygwin/bin/bash
    %!xxd
  else
    " restore old options
    let &ft=b:oldft
    if !b:oldbin
      setlocal nobinary
    endif
    " set status
    let b:editHex=0
    " return to normal editing
    %!xxd -r
  endif
  " restore values for modified and read only state
  let &mod=l:modified
  let &readonly=l:oldreadonly
  let &modifiable=l:oldmodifiable
endfunction

" vim: ft=vim:fdc=2:fdm=marker
Tim
fonte