วิธีการดัมพ์ไฟล์ไบนารีเป็นสตริงตัวอักษร C / C ++?


39

ฉันมีไฟล์ไบนารีที่ฉันต้องการรวมไว้ในซอร์สโค้ด C ของฉัน (เป็นการชั่วคราวเพื่อการทดสอบ) ดังนั้นฉันต้องการได้รับเนื้อหาไฟล์เป็นสตริง C เช่นนี้:

\x01\x02\x03\x04

เป็นไปได้นี้อาจโดยใช้odหรือhexdumpสาธารณูปโภค? ในขณะที่ไม่จำเป็นถ้าสตริงสามารถตัดบรรทัดถัดไปทุก 16 อินพุตไบต์และรวมเครื่องหมายคำพูดคู่ที่จุดเริ่มต้นและจุดสิ้นสุดของแต่ละบรรทัดนั่นจะยิ่งดีกว่า!

ฉันทราบว่าสตริงจะมีค่า null ที่ฝังอยู่ ( \x00) ดังนั้นฉันจะต้องระบุความยาวของสตริงในโค้ดเพื่อป้องกันไบต์เหล่านี้จากการยกเลิกสตริงก่อน



ฉันต้องการที่คล้ายกัน แต่รักษา glyph ที่พิมพ์ได้ของ ascii เพียงหนี 1-127, เครื่องหมายคำพูด, แบ็กสแลช, โมฆะ ฯลฯ
把把留留在无盐

คำตอบ:


10

คุณเกือบจะสามารถทำสิ่งที่คุณต้องการได้hexdumpแต่ฉันไม่สามารถหาวิธีรับเครื่องหมายคำพูด & แบ็กสแลชเดี่ยวลงในสตริงรูปแบบได้ sedดังนั้นผมจึงทำหลังการประมวลผลน้อยกับ เป็นโบนัสฉันได้เยื้องแต่ละบรรทัดด้วย 4 ช่องว่าง :)

hexdump -e '16/1 "_x%02X" "\n"' filename | sed 's/_/\\/g; s/.*/    "&"/'

แก้ไข

ดังที่ Cengiz สามารถชี้ให้เห็นได้บรรทัดคำสั่งด้านบนไม่สามารถทำงานได้ดีกับสายข้อมูลสั้น ๆ ดังนั้นนี่คือรุ่นปรับปรุงใหม่:

hexdump -e '16/1 "_x%02X" "\n"' filename | sed 's/_/\\/g; s/\\x  //g; s/.*/    "&"/'

ในฐานะที่เป็น Malvineous กล่าวถึงในความคิดเห็นที่เรายังต้องผ่าน-vตัวเลือกอย่างละเอียดเพื่อที่จะป้องกันไม่ให้มันย่อวิ่งยาวของไบต์เหมือนกับhexdump*

hexdump -v -e '16/1 "_x%02X" "\n"' filename | sed 's/_/\\/g; s/\\x  //g; s/.*/    "&"/'

สิ่งนี้จะสร้างองค์ประกอบที่ซ้ำซ้อนและไม่ถูกต้องหากอินพุตสั้นกว่า 16 ไบต์
Cengiz สามารถ

@CengizCan:: อ๊ะ:! มันดีกว่าไหม
PM 2Ring

1
จำเป็นที่จะต้องเพิ่ม-vตัวเลือกในการมิฉะนั้นยาวทำงานของเดียวกันการป้อนข้อมูลไบต์สาเหตุสายออกที่บอกว่าhexdump "*"
Malvineous

@ Malvineous จุดดี! ฉันแก้ไขคำตอบแล้ว ขอบคุณสำหรับการเฮด (และขอบคุณสำหรับการยอมรับคำตอบของฉัน)
PM 2Ring

66

xxdมีโหมดสำหรับสิ่งนี้ -i/ --includeตัวเลือกที่จะ:

เอาท์พุทใน C รวมถึงรูปแบบไฟล์ นิยามอาร์เรย์แบบสแตติกที่สมบูรณ์ถูกเขียน (ตั้งชื่อตามไฟล์อินพุต) ยกเว้นว่า xxd อ่านจาก stdin

คุณสามารถดัมพ์นั้นลงในไฟล์ให้เป็น#included จากนั้นก็เข้าถึงได้fooเช่นเดียวกับอาเรย์อักขระอื่น ๆ (หรือลิงก์ใน) นอกจากนี้ยังมีการประกาศความยาวของอาร์เรย์

เอาต์พุตถูกรวมไว้ที่ 80 ไบต์และมีลักษณะคล้ายกับสิ่งที่คุณอาจเขียนด้วยมือ:

$ xxd --include foo
unsigned char foo[] = {
  0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64,
  0x21, 0x0a, 0x0a, 0x59, 0x6f, 0x75, 0x27, 0x72, 0x65, 0x20, 0x76, 0x65,
  0x72, 0x79, 0x20, 0x63, 0x75, 0x72, 0x69, 0x6f, 0x75, 0x73, 0x21, 0x20,
  0x57, 0x65, 0x6c, 0x6c, 0x20, 0x64, 0x6f, 0x6e, 0x65, 0x2e, 0x0a
};
unsigned int foo_len = 47;

xxdเป็นส่วนหนึ่งของการvimกระจายดังนั้นคุณน่าจะมีอยู่แล้ว หากไม่ใช่นั่นคือสิ่งที่คุณจะได้รับ - คุณสามารถสร้างเครื่องมือด้วยตัวเองจากvimแหล่งที่มา


ดี! ฉันไม่รู้ด้วยซ้ำว่าฉันมี xxd ตอนนี้ฉันต้องจำไว้ว่ามันมีอยู่ในครั้งต่อไปที่ฉันต้องการ ... หรือฉันอาจจะทำซ้ำฟังก์ชั่นที่ต้องการใน Python :)
PM 2Ring


@LightnessRacesinOrbit objcopyจะอนุญาตให้ OP เชื่อมโยงข้อมูลไบนารี่กับไฟล์ที่เรียกใช้งานได้ซึ่งมีประโยชน์ แต่ไม่ใช่สิ่งที่ถูกถามที่นี่
Wander Nauta

1
@WanderNauta: คุณจะเข้าถึงมันในแบบเดียวกับที่คุณเข้าถึงfoo/ foo_lenที่นี่และคุณจะไม่ต้องสูญเสียพื้นที่เก็บข้อมูลอย่างมากมาย ฉันเชื่อว่า OP จะดีกว่าด้วยobjcopyและเหมาะสมกับความต้องการของเขาหรือเธอ
การแข่งขัน Lightness กับ Monica

2
objcopyใช้ได้เมื่ออยู่ใกล้ ๆ แต่ไม่สามารถพกพาได้และให้ผลผลิตน้อยลง แน่นอนว่ามันสามารถเป็นส่วนหนึ่งของทางออกถาวรที่ดี แต่นั่นไม่ใช่คำถามที่นี่
Michael Homer

3

xxd ดี แต่ผลลัพธ์นั้นละเอียดมากและใช้พื้นที่เก็บข้อมูลมากมาย

คุณสามารถบรรลุถึงสิ่งเดียวกันโดยใช้objcopy; เช่น

objcopy --input binary \
    --output elf32-i386 \
    --binary-architecture i386 foo foo.o

จากนั้นลิงก์foo.oไปยังโปรแกรมของคุณและใช้สัญลักษณ์ต่อไปนี้:

00000550 D _binary_foo_end
00000550 A _binary_foo_size 
00000000 D _binary_foo_start

นี่ไม่ใช่ตัวอักษรสตริง แต่โดยพื้นฐานแล้วสิ่งเดียวกับที่สตริงตัวอักษรเปลี่ยนเป็นระหว่างการรวบรวม (พิจารณาว่าสตริงตัวอักษรไม่มีอยู่จริงในขณะใช้งานจริง ๆ แล้วไม่มีคำตอบอื่นใดที่ให้สตริงตัวอักษรจริง ๆ แม้ในเวลารวบรวม) และสามารถเข้าถึงได้ในลักษณะเดียวกัน:

unsigned char* ptr = _binary_foo_start;
int i;
for (i = 0; i < _binary_foo_size; i++, ptr++)
   putc(*ptr);

ข้อเสียคือคุณต้องระบุสถาปัตยกรรมเป้าหมายของคุณเพื่อให้ไฟล์วัตถุเข้ากันได้และสิ่งนี้อาจไม่สำคัญในระบบการสร้างของคุณ



0

นี่เป็นยูทิลิตี้สั้น ๆ ที่ฉันเขียนว่าทำสิ่งเดียวกัน (โดยเดิมโพสต์บนStack Overflow ):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_LENGTH 80

int main(void)
{
    FILE *fout = fopen("out.txt", "w");

    if(ferror(fout))
    {
        fprintf(stderr, "Error opening output file");
        return 1;
    }
    char init_line[]  = {"char hex_array[] = { "};
    const int offset_length = strlen(init_line);

    char offset_spc[offset_length];

    unsigned char buff[1024];
    char curr_out[64];

    int count, i;
    int line_length = 0;

    memset((void*)offset_spc, (char)32, sizeof(char) * offset_length - 1);
    offset_spc[offset_length - 1] = '\0';

    fprintf(fout, "%s", init_line);

    while(!feof(stdin))
    {
        count = fread(buff, sizeof(char), sizeof(buff) / sizeof(char), stdin);

        for(i = 0; i < count; i++)
        {
            line_length += sprintf(curr_out, "%#x, ", buff[i]);

            fprintf(fout, "%s", curr_out);
            if(line_length >= MAX_LENGTH - offset_length)
            {
                fprintf(fout, "\n%s", offset_spc);
                line_length = 0;
            }
        }
    }
    fseek(fout, -2, SEEK_CUR);
    fprintf(fout, " };");

    fclose(fout);

    return EXIT_SUCCESS;
}

1
คำตอบของคุณจะมีประโยชน์มากขึ้นถ้าคุณให้ตัวอย่างอินพุตและเอาต์พุตด้วย
not2qubit

0

หากคุณเป็นงูหลามให้โหลดมันลงในตัวแปร "buff" และใช้สิ่งนี้:

buff2 = buff.encode("hex")
print ("0x"+", 0x".join([buff2[i:i+2] for i in range(0,len(buff2),2)]))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.