วิธีแก้ไขไฟล์ไบนารีด้วย 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ดังนั้นหากคุณได้ติดตั้งแล้วคุณควรจะมีvimxxd

วิธีใช้:

$ 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' และ 'เครื่องมือ➙แปลงกลับ'

เคล็ดลับที่เป็นกลุ่มวิกิพีเดียมีหน้าเว็บที่มีข้อมูลเพิ่มเติมและบางสคริปต์ผู้ช่วย โดยส่วนตัวแล้วฉันคิดว่าคุณน่าจะใช้โปรแกรมแก้ไข hex จริงถ้าคุณแก้ไขไฟล์ไบนารีที่มักจะ เป็นกลุ่มสามารถ เรียงลำดับของงาน แต่เห็นได้ชัดว่ามันไม่ได้ออกแบบมาสำหรับมันและถ้าคุณเคยเขียนโดยไม่ต้องเป็น:set binaryกลุ่มอาจทำลายไฟล์ไบนารีของคุณ!


4
คำตอบที่น่ารัก แต่น่าจะเริ่มต้นด้วย "อย่าลองทำที่บ้านกับเด็ก ๆ !"
msw

จะทำอย่างไรถ้าฉันต้องการลบบางไบต์ เช่นในช่วงกลางของไบนารี
Anton K

ฉันไม่รู้ว่า Vim กำลังทำอะไร แต่มันเพิ่ม 95KB ของข้อความลงในไฟล์ไบนารี 200KB แม้ว่าฉันจะไม่ได้เปลี่ยนอะไรเลย :set binary noeol fenc=utf-8ถึงแม้จะมี [noeol] [converted]ในความเป็นจริงมันทำมันได้ทันทีเมื่อเปิดแฟ้มก่อนที่จะกล่าวว่า ทำไมกลุ่มต้องใช้ทำให้บัฟเฟอร์ใหญ่ขึ้น 150% ฉันจะป้องกันไม่ให้ไฟล์เสียหายเช่นนั้นได้อย่างไร
Braden สุดยอด

สิ่งเดียวที่ใช้ได้ผลก็คือ:r !xxd <file>(หรือ$ xxd <file> | vim -) อ่านและ:w !xxd -r > <file>เขียน แต่สิ่งนี้ไม่เหมาะ
Braden สุดยอด

คำตอบที่ยอดเยี่ยม โปรดทราบว่า URL สำหรับคำอวยพรไม่ได้ผล ผมพบว่ามัน (ฉันคิด) บน GitHub ที่github.com/bwrsandman/Bless
sonofagun

19

หากต้องการดูเนื้อหาของไฟล์ไบนารีในมุมมอง hex ให้เปิดไฟล์เปิดโหมดไบนารีและกรองบัฟเฟอร์ผ่าน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เพื่อความปลอดภัยเสมอหรือไม่...
Martin Tournoij

1
คุณสามารถเพิ่มaugroupสคริปต์แทน~/.vim/plugin/binary.vimหากคุณไม่ต้องการถ่วงของคุณ.vimrc
thom_nic

หากคุณติดตั้งในต่างประเทศaugroup Binaryรายชื่อนั้นจะอยู่ที่: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.

1
ทางเลือกที่สูงขึ้นคือ bviplus ซึ่งมีปุ่มควบคุมเป็นกลุ่ม
Anton K

Bviplus หน้าแรกและหน้าจอ
Iulian Onofrei

3

TL; DR คำตอบ

เปิดไฟล์ด้วย Vim ในโหมดไบนารี:

vim -b <file_to_edit>

ในกลุ่มให้เข้าสู่โหมดการแก้ไข hex ดังนี้:

:%!xxd -p

เพื่อบันทึก:

:%!xxd -p -r
:w

การดำเนินการนี้จะแปลงบัฟเฟอร์กลับจากโหมด hex และบันทึกไฟล์เหมือนปกติ

หมายเหตุตัวเลือก -p นี่เป็นการหลีกเลี่ยงการพิมพ์และที่อยู่พิเศษทั้งหมดและเพียงแค่แสดงให้คุณเห็นเลขฐานสิบหก เพียงละเว้น -p หากคุณต้องการบริบทเพิ่มเติม

ระวังการเปิดไฟล์ด้วย Vim ไม่ได้อยู่ในโหมดไบนารีเพราะมันจะผนวกอักขระ LF (ปกติโดยไม่ได้ตั้งใจ) ไว้ท้ายไฟล์เมื่อคุณบันทึก


นี่ไม่ได้เพิ่มอะไรเลยที่ไม่ได้อยู่ในคำตอบอื่น ๆ
สมุนไพร Wolfe

5
TL; DR ที่แท้จริงอยู่ใน:h using-xxdและได้รับมาตั้งแต่v7.0001และอาจนานกว่านั้น ไซต์นี้จะใช้งานน้อยลงถ้ามีคนค้นหาเอกสาร
ทอมมี่ A

1

นี้ดูเหมือนปลั๊กอินเป็นกลุ่มที่มีประโยชน์เล็ก ๆ น้อย ๆ ที่จะทำงานโดยใช้ไฟล์ชั่วคราวที่มันเขียนกลับมาให้คุณโดยอัตโนมัติ

หลายปีก่อนฉันพบปลั๊กอินที่คล้ายกันซึ่งฉันดัดแปลงและปรับปรุงเพื่อการใช้งานของฉันเอง ฉันได้รวมรหัสที่เกี่ยวข้องสำหรับที่นี่ในกรณีที่ทุกคนต้องการมัน มันก็ขึ้นอยู่กับเครื่องมือ xxd ฉันแน่ใจว่ารุ่น GitHub ที่ฉันลิงค์ด้านบนใช้งานได้ดีกว่า แต่ฉันไม่ได้ใช้ด้วยตัวเองจริง ๆ ดังนั้นฉันจึงคิดว่าฉันจะโพสต์สิ่งนี้ซึ่งฉันรู้ว่าใช้ได้แน่นอน

แหล่งที่มาสำหรับรุ่นอื่น ๆ นี้มี 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.