¿Cómo editar archivos binarios con Vim?

77

¿Hay alguna manera de editar archivos binarios en algún tipo de modo hexadecimal?

Por ejemplo, si tengo algunos datos binarios mostrados por xxdo hexdump -Ccomo este:

$ 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  ................

Si quiero cambiar el valor en una posición específica, este tipo de vista ayudará a encontrar el lugar correcto, por ejemplo, cuando la posición para cambiar está cerca de una cadena conocida.

janos
fuente

Respuestas:

89

La forma más simple es usar la binaryopción. 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.

Si no hace esto, y su entorno está usando una codificación multibyte (por ejemplo, UTF-8, como la mayoría de la gente usa), Vim intenta codificar el texto como tal, lo que generalmente daña los archivos.

Puede verificar esto abriendo un archivo y simplemente usando :w. Ahora está cambiado.
Si configura LANGy LC_ALLen C(ASCII), Vim no convierte nada y los archivos permanecen igual (aunque aún agrega una nueva línea) ya que Vim no necesitará hacer ninguna codificación multibyte.

Personalmente, también prefiero deshabilitar set wrap para binario, aunque otros prefieren habilitarlo . YMMV. Otra cosa útil que hacer es :set display=uhex. De :help 'display':

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

Y como último consejo, puede mostrar el valor hexadecimal del personaje debajo del cursor en la regla con %B( :set rulerformat=0x%B).

Más avanzado: xxd

Puede usar la xxd(1)herramienta para convertir un archivo a un formato más legible y (este es el bit importante), analizar el "formato legible" editado y volver a escribirlo como datos binarios. xxdes parte de vim, por lo que si ha viminstalado también debería tener xxd.

Para usarlo:

$ xxd /bin/ls | vi -

O si ya ha abierto el archivo, puede usar:

:%!xxd

Ahora haga sus cambios, debe hacerlo en el lado izquierdo de la pantalla (los números hexadecimales), los cambios en el lado derecho (representación imprimible) se ignoran al escribir.

Para guardarlo, use xxd -r:

:%!xxd -r > new-ls

Esto guardará el archivo en new-ls.

O para cargar el binario en el búfer actual:

:%!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.

Y luego solo usa :wpara escribirlo. ( cuidado : desea configurar la binary opción antes de escribir en el archivo, por los mismos motivos descritos anteriormente).

Teclas complementarias para hacer esto un poco más fácil:

" 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>

Esto también está disponible en el menú si está utilizando gVim, en 'Herramientas ➙ Convertir a HEX' y 'Herramientas ➙ Convertir de nuevo'.

El wiki de vim tips tiene una página con más información y algunos scripts de ayuda. Personalmente, creo que probablemente sea mejor usar un editor hexadecimal real si está editando archivos binarios con tanta frecuencia. Vim puede hacer el trabajo, pero obviamente no está diseñado para ello, y si alguna vez escribe sin :set binaryVim podría destruir sus archivos binarios.

Martin Tournoij
fuente
44
Encantadora respuesta, pero probablemente debería comenzar con "¡No intenten esto en casa, niños!"
msw
¿Qué sucede si necesito eliminar algunos bytes? Por ejemplo, en el medio de binario.
Anton K
No sé qué está haciendo Vim, pero está agregando 95 KB de texto a un archivo binario de 200 KB a pesar de que no he cambiado nada. Incluso con :set binary noeol fenc=utf-8. De hecho, lo está haciendo inmediatamente al abrir el archivo antes de que lo diga [noeol] [converted]. ¿Por qué vim necesita hacer el búfer 150% más grande? ¿Cómo evito que corrompa archivos como ese?
Braden Best
Lo único que funciona es :r !xxd <file>(o $ xxd <file> | vim -) leer y :w !xxd -r > <file>escribir, pero esto no es lo ideal.
Braden Best
Excelente respuesta Tenga en cuenta que la url para bendecir no funciona; Lo encontré (creo) en github en github.com/bwrsandman/Bless .
sonofagun
19

Para ver el contenido de un archivo binario en una vista hexadecimal, abra el archivo, active el modo binario y filtre el búfer mediante el xxdcomando:

:set binary
:%!xxd

Puede realizar cambios en el área izquierda (editar los números hexadecimales) y, cuando esté listo, filtrar xxd -ry finalmente guardar el archivo:

:%!xxd -r
:w

Si el paso de filtrado después de abrir y antes de cerrar suena tedioso, y a menudo lo hace con archivos con .binextensión, puede agregar esto a su vimrc para que el proceso sea 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
fuente
Si sigo estas instrucciones (abrir archivo binario, :%!xxd, :%!xxd -r, :w, ni de realizar ningún cambio!), Entonces el archivo binario escrito es no el mismo que el original ... ¿Es este el caso para usted (he probado con /bin/ls). Necesito usar :set binaryantes de guardar (también vea mi respuesta que explica por qué) ... ¿Tal vez es algo en mi vimrc? Pero independientemente, siempre lo usaría set binarypor seguridad ...
Martin Tournoij
1
En su lugar, puede agregar el augroupscript ~/.vim/plugin/binary.vimsi no desea .vimrc
abarrotar
Si está en una instalación extranjera, esa augroup Binarylista se encuentra en :help hex-editingo :help using-xxden cualquier Vim desde 5.5 (septiembre de 1999).
bb010g
6

Utilice el editor "bvi". http://bvi.sourceforge.net/ (está en todos los repositorios de 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
fuente
1
La alternativa más avanzada es bviplus, que tiene controles vim.
Anton K
Página de inicio de Bviplus y capturas de pantalla .
Iulian Onofrei
3

TL; DR Respuesta

Abra el archivo con Vim en modo binario:

vim -b <file_to_edit>

En Vim, ingrese al modo de edición hexadecimal de esta manera:

:%!xxd -p

Ahorrar:

:%!xxd -p -r
:w

Esto convertirá el búfer de vuelta del modo hexadecimal y luego guardará el archivo como de costumbre.

Tenga en cuenta la opción -p. Esto evita toda la pelusa adicional de impresión y dirección y solo le muestra el hexadecimal. Simplemente omita -p si desea el contexto adicional.

Tenga cuidado al abrir el archivo con Vim no en modo binario, ya que agregará un carácter LF (generalmente no intencionado) al final del archivo cuando lo guarde.

Hintron
fuente
Esto realmente no agrega nada que no esté en las otras respuestas.
Herb Wolfe
55
El verdadero TL; DR está dentro :h using-xxdy ha estado presente desde entonces v7.0001y probablemente por más tiempo. Este sitio estaría menos activo si la gente buscara en los documentos.
Tommy A
1

Esto parece un pequeño y práctico complemento vim que hace el trabajo usando un archivo temporal que escribe de un lado a otro automáticamente.

Hace algunos años encontré un complemento similar que adapté y mejoré para mi propio uso. He incluido el código relevante para eso aquí, en caso de que alguien lo quiera. También se basa en la herramienta xxd. Estoy seguro de que la versión de GitHub que vinculé anteriormente funciona mejor, pero en realidad no la he utilizado, así que pensé que también publicaría esta, que sé que funciona con seguridad.

La fuente de esta otra versión fue el vim wikia, específicamente esta página .

Aquí está el 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
fuente