Как редактировать двоичные файлы с помощью Vim?

77

Есть ли способ редактировать двоичные файлы в каком-то шестнадцатеричном режиме?

Например, если у меня есть некоторые двоичные данные, показанные xxdили hexdump -Cкак это:

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

Если я хочу изменить значение в определенной позиции, этот вид просмотра поможет найти правильное место, например, когда позиция для изменения находится рядом с некоторой известной строкой.

Янош
источник

Ответы:

89

Самый простой способ - использовать binaryопцию. От :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.

Если вы этого не делаете, и ваша среда использует многобайтовую кодировку (например, UTF-8, которую использует большинство людей), Vim пытается закодировать текст как таковой, что обычно приводит к повреждению файла.

Вы можете убедиться в этом, открыв файл и просто используя :w. Сейчас это изменилось.
Если вы установите LANGи LC_ALLв C(ASCII), Vim ничего не конвертирует, а файлы остаются прежними (хотя он все равно добавляет новую строку), поскольку Vim не нужно выполнять многобайтовое кодирование.

Я лично также предпочитаю отключить set wrap для двоичного файла, хотя другие могут предпочесть включить его. YMMV. Еще одна полезная вещь, чтобы сделать это :set display=uhex. От :help 'display':

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

И, как последний совет, вы можете показать шестнадцатеричное значение символа под курсором в линейке с помощью %B( :set rulerformat=0x%B).

Более продвинутый: xxd

Вы можете использовать xxd(1)инструмент для преобразования файла в более читаемый формат и (это важный бит), проанализировать отредактированный «читаемый формат» и записать его обратно как двоичные данные. xxdявляется частью vim, поэтому, если вы vimустановили, вы также должны иметь xxd.

Чтобы использовать это:

$ xxd /bin/ls | vi -

Или, если вы уже открыли файл, вы можете использовать:

:%!xxd

Теперь внесите изменения, вам нужно сделать это в левой части экрана (шестнадцатеричные числа), изменения в правой части (печатное представление) игнорируются при записи.

Чтобы сохранить его, используйте xxd -r:

:%!xxd -r > new-ls

Это сохранит файл в new-ls.

Или загрузить двоичный файл в текущий буфер:

:%!xxd -r

От 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.

А потом просто используйте, :wчтобы написать это. ( будьте осторожны : вы хотите установить binary опцию перед записью в файл, по тем же причинам, изложенным выше).

Дополнительные сочетания клавиш, чтобы сделать это немного проще:

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

Это также доступно из меню, если вы используете gVim, в «Инструменты ➙ Преобразовать в HEX» и «Инструменты ➙ Преобразовать обратно».

В вики подсказках есть страница с дополнительной информацией и некоторыми вспомогательными скриптами. Лично я думаю, что вам лучше использовать настоящий шестнадцатеричный редактор, если вы часто редактируете двоичные файлы. Vim может выполнять свою работу, но он явно не предназначен для этого, и если вы когда-либо будете писать без :set binaryVim, вы можете уничтожить ваши двоичные файлы!

Мартин Турной
источник
4
Прекрасный ответ, но, вероятно, следует начать с "Не пытайтесь делать это дома, дети!"
MSS
Что если мне нужно удалить несколько байтов? например, в середине двоичного файла.
Антон К
Я не знаю, что делает Vim, но он добавляет 95 КБ текста в двоичный файл размером 200 КБ, хотя я ничего не изменил. Даже с :set binary noeol fenc=utf-8. На самом деле, он делает это сразу после открытия файла, прежде чем он говорит [noeol] [converted]. Почему vim нужно увеличить буфер на 150%? Как я могу предотвратить повреждение файлов?
Брэден Бест
Единственное, что работает - это :r !xxd <file>(или $ xxd <file> | vim -) читать и :w !xxd -r > <file>писать, но это не идеально.
Брэден Бест
Отличный ответ. Обратите внимание, что URL для благословения не работает; Я нашел это (я думаю) на github на github.com/bwrsandman/Bless .
sonofagun
19

Чтобы просмотреть содержимое двоичного файла в шестнадцатеричном представлении, откройте файл, включите двоичный режим и отфильтруйте буфер с помощью xxdкоманды:

:set binary
:%!xxd

Вы можете внести изменения в левой области (отредактировать шестнадцатеричные числа), а когда будете готовы, отфильтровать xxd -rи, наконец, сохранить файл:

:%!xxd -r
:w

Если этап фильтрации после открытия и перед закрытием звучит утомительно, и вы часто делаете это с файлами с .binрасширением, вы можете добавить это в свой vimrc, чтобы сделать процесс автоматическим:

" 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
Янош
источник
Если я следовать этим инструкциям (открыть бинарный файл, :%!xxd, :%!xxd -r, :w, не сделало никаких изменений!) , То бинарный файл , написанный является не то же самое, что и оригинал ... Является ли это дело для вас (я тестировал с /bin/ls). Мне нужно использовать :set binaryперед сохранением (см. Также мой ответ, который объясняет, почему) ... Может быть, это что-то в моем vimrc? Но независимо от того, я бы всегда использовал set binaryдля безопасности ...
Мартин Турной
1
Вместо этого вы можете добавить augroupскрипт, ~/.vim/plugin/binary.vimесли не хотите загромождать свой.vimrc
thom_nic
Если вы используете иностранную установку, этот augroup Binaryсписок находится в Vim :help hex-editingили :help using-xxdв любом из них начиная с 5.5 (сентябрь 1999 г.).
bb010g
6

Используйте редактор "bvi". http://bvi.sourceforge.net/ (это есть в каждом репозитории 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
источник
1
Более продвинутой альтернативой является bviplus, который имеет элементы управления vim.
Антон К
Домашняя страница Bviplus и скриншоты .
Юлиан Онофрей
3

TL; DR Ответ

Откройте файл с помощью Vim в двоичном режиме:

vim -b <file_to_edit>

В Vim перейдите в режим редактирования в шестнадцатеричном формате так:

:%!xxd -p

Сохранить:

:%!xxd -p -r
:w

Это преобразует буфер обратно из шестнадцатеричного режима и затем сохраняет файл как обычно.

Обратите внимание на параметр -p. Это позволяет избежать лишнего печатного и адресного пуха и просто показывает вам гекс. Просто опустите -p, если вы хотите дополнительный контекст.

Будьте осторожны, открывая файл с Vim не в двоичном режиме, так как он добавит (обычно непреднамеренный) символ LF в конец файла при его сохранении.

Hintron
источник
Это действительно не добавляет ничего, чего нет в других ответах.
Херб Вульф
5
Настоящий TL; DR присутствует :h using-xxdи существует уже с тех пор v7.0001и, вероятно, дольше. Этот сайт будет менее активным, если люди будут искать документы.
Томми А
1

Это похоже на удобный маленький плагин vim, который выполняет работу, используя временный файл, который он записывает для вас автоматически.

Несколько лет назад я нашел похожий плагин, который я адаптировал и улучшил для собственного использования. Я включил соответствующий код здесь, на случай, если кто-то захочет. Это тоже основано на инструменте xxd. Я уверен, что версия GitHub, которую я связал выше, работает лучше, но я на самом деле не использовал ее сам, поэтому я решил, что я также опубликую эту, которая, я знаю, работает точно.

Источником для этой другой версии была vim wikia, а именно эта страница .

Вот код:

"-------------------------------------------------------------------------------  
" 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
Тим
источник