C 在十六进制文件中我的函数在哪里?

C 在十六进制文件中我的函数在哪里?,c,binary,hex,hexdump,C,Binary,Hex,Hexdump,我试图了解更多关于二进制文件的知识。所以我写了一个指针*数据以二进制形式打印出函数。问题是我在hextump文件中找不到打印到stdio的十六进制值 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> /* Our old friend die from ex17. */ void die(const char *message){ i

我试图了解更多关于二进制文件的知识。所以我写了一个指针*数据以二进制形式打印出函数。问题是我在hextump文件中找不到打印到stdio的十六进制值

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

/* Our old friend die from ex17. */
void die(const char *message){
    if(errno){
        perror(message);
    }
    else{
        printf("ERROR: %s\n", message);
    }
    exit(1);
}

// a typedef creates a fake type, in this 
// case for a function pointer
typedef int (*compare_cb)(int a, int b);

/* A classic buble sort function that uses 
     the compare_cb to do the sorting */
int *bubble_sort(int *numbers, int count, compare_cb cmp){
    int temp = 0;
    int i = 0;
    int j = 0;
    int *target = malloc(count * sizeof(int));
    if(!target) die("Memory error.");

    memcpy(target, numbers, count * sizeof(int));
    for(i = 0; i < count; i++) {
        for(j = 0; j < count - 1; j++) {
            if(cmp(target[j], target[j+1])>0) {
                temp = target[j+1];
                target[j+1] = target[j];
                target[j] = temp;
            }
        }
    }
    return target;
}

int sorted_order(int a, int b){
    return a - b;
}

int reverse_order(int a, int b){
    return b - a;
}

int strange_order(int a, int b){
    if(a == 0 || b == 0){
        return 0;
    }
    else{
        return a % b;
    }
}

/* Used to test that we are sorting things correctly by doing the sort and printing it out. */
void test_sorting(int *numbers, int count, compare_cb cmp){
    int i = 0; 
    int *sorted = bubble_sort(numbers, count, cmp);
    if(!sorted) die("Failed to sort as requested.");

    for(i = 0; i < count; i++) {
        printf("%d ", sorted[i]);
    }
    printf("\n");
    free(sorted);

    // printing function pointers purposefully
    unsigned char *data = (unsigned char *)cmp;
    for(i = 0; i < 25; i++) {
        printf("%02x:", data[i]);
    }
    printf("\n");
}

int main(int argc, char *argv[]){
    if(argc < 2) die("USAGE: ex18 4 3 1 5 6");
    int count = argc - 1;
    int i = 0;
    char **inputs = argv + 1;

    int *numbers = malloc(count * sizeof(int));
    if(!numbers) die("Memory error.");

    for(i = 0; i < count; i++) {
        numbers[i] = atoi(inputs[i]);
    }

    test_sorting(numbers, count, sorted_order);
    test_sorting(numbers, count, reverse_order);
    test_sorting(numbers, count, strange_order);

    free(numbers);
    return 0;
}
我得到:

1 2 3 
55:48:89:e5:89:7d:fc:89:75:f8:8b:45:f8:8b:55:fc:29:c2:89:d0:5d:c3:55:48:89:
3 2 1 
55:48:89:e5:89:7d:fc:89:75:f8:8b:45:fc:8b:55:f8:29:c2:89:d0:5d:c3:55:48:89:
2 3 1 
55:48:89:e5:89:7d:fc:89:75:f8:83:7d:fc:00:74:06:83:7d:f8:00:75:07:b8:00:00:
然后我跑:

$hexdump ex18 > ex18_hexdump
ex18_hexdump中的内容是(根据注释使用正确的文件进行更新,由于帖子的限制而被截断):

我找不到55和48紧挨着

更新1:更新后的二进制文件具有以下模式,与我打印的函数类似:

第一种模式:

0000790 7be9 ffff 0fff 001f 73e9 ffff 55ff 8948
00007a0 48e5 ec83 4810 7d89 e8f8 fe72 ffff 008b
00007b0 c085 0e74 8b48 f845 8948 e8c7 fec0 ffff
第二种模式:

0000980 99fc 7df7 89f8 5dd0 55c3 8948 48e5 ec83
0000990 4840 7d89 89d8 d475 8948 c855 45c7 00ec
00009a0 0000 4800 558b 8bc8 d44d 8b48 d845 ce89
但是,根据我打印出来的图案: 55:48:89:e5:89:7d:fc:89:75:f8:8b:45:f8:8b:55:fc:29:c2:89:d0:5d:c3:55:48:89:

我应该能够在二进制文件中找到这个函数,也就是说,我在二进制文件中查找如下内容: …89fc7d89e5894855

但上述第一种模式有: …8348e5894855 这与我所寻找的模式不同

上述第二种模式有: …8348e5894855
这也是不同的。

如果您想知道编译器(或者严格地说是链接器)在二进制文件中的数据和函数的位置,您需要指示链接器生成,或者您可以在现有对象文件上使用和/或实用程序。

编译代码后,无需任何优化,我得到了大小为10136字节的可执行文件
ex18
<代码>文件将其报告为“Mach-O 64位可执行x86_64”,因为我在Mac上。系统上的某些工具名称和符号可能不同

让我们用Linux et fili的一些标准工具来看看这个文件

使用
hextump
我得到了363行hex,其中一些是
*
,表示重复了前一行。(如果没有默认设置,我将得到10136/16=821行。)

添加
-Wl,-map,ex18.map
(请记住:我使用的是OS X,此命令行语法不同于gcc常规版本的默认语法)之后,我得到一个以

(__TEXT,__text) section
start:
00000001000007a0    pushq   $0x00
00000001000007a2    movq    %rsp,%rbp
00000001000007a5    andq    $0xf0,%rsp
00000001000007a9    movq    0x08(%rbp),%rdi
00000001000007ad    leaq    0x10(%rbp),%rsi
00000001000007b1    movl    %edi,%edx
00000001000007b3    addl    $0x01,%edx
路径:ex18 #拱门:x86_64 #对象文件: [0]链接器已合成 [1]/usr/lib/crt1.10.6.o [2]/var/folders/b6/g3yv219j13v46njdytcd767w0000gn/T//ccoACNzu.o [3]/usr/lib/libSystem.dylib #章节: #地址大小段段 0x1000007A00x0000056F\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu 0x100000D10 0x00000036\uuuu文本\uuuu存根 0x100000D48 0x0000006C\uuuuu文本\uuuuu存根\uu帮助程序 0x100000DB4 0x00000056文本字符串 0x100000E0A 0x00000050\uuuu文本\uuuu展开\uu信息 0x100000E60 0x00000198文本框 0x100001000 0x00000028\u数据\u程序\u变量 0x100001028 0x00000010\u数据\u nl\u符号\u ptr 0x100001038 0x00000048_uuuu数据_uuula_u符号_uptr 0x100001080 0x00000020\u数据\u公共 #符号: #地址大小文件名 0x1000007A00x0000003C[1]启动 0x1000007E0 0x00000050[2]\u模具 0x100008300x000001C0[2]\u气泡\u排序 0x10000009F0 0x00000070[2]\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu内联\u memcpy\u chk 0x10000A60 0x00000020[2]\u排序\u顺序 0x100000A80 0x00000020[2]\u相反顺序 0x100000AA0 0x00000040[2]\u奇怪\u顺序 0x100000AE0 0x00000100[2]\u测试\u排序 0x100000BE0 0x0000012F[2]\u主 。。接下来是程序中使用的更多函数(
\u atoi
\u exit
)和文本字符串的地址等。这些信息告诉我们什么

  • 您可以忽略大偏移量
    0x100000000
  • 余数是可执行二进制文件中每个项的偏移量
  • 大多数名称都添加了一个或多个前导下划线(这是大多数链接器的一个特点,归结起来就是“有一个唯一的名称”——不用担心)
  • 广义而言,
    部分
    用于指示文件的每个部分的用途。有些部分是可执行的(在这种情况下,
    \uu text
    是可执行的);有些包含文本数据(
    \uu cstring
    仅包含程序中的文本字符串)。出于几个好的原因,可执行文件被划分为多个部分。例如,“代码”只能在可执行部分中运行,“只读数据”可能永远不会执行,以此类推
  • 符号是可执行文件中函数和数据的地址。在这个转储中,地址以最基本的形式出现:减去巨大的常量,作为可执行文件的偏移量
最后一项,即
符号
列表,似乎是您感兴趣的地方<例如,代码>开始
在我的二进制文件中位于
7A0
。检查十六进制转储,我在该位置找到以下字节序列:
6A 00 48 89 E5 48 83 E4 F0..
。现在,这些是对CPU的文字指令。我们可以在
排序顺序
反向顺序
奇怪顺序
中找到相同的字节,并确认它们与从程序“内部”打印时得到的字节几乎相同

但是这些字节意味着什么呢?转到下一个工具

Clifford提到了
objdump
,我的OSX有
otool
。使用
otool-tvex18
,我得到了404行长的文本部分反汇编。拆解从以下步骤开始:

(__TEXT,__text) section
start:
00000001000007a0    pushq   $0x00
00000001000007a2    movq    %rsp,%rbp
00000001000007a5    andq    $0xf0,%rsp
00000001000007a9    movq    0x08(%rbp),%rdi
00000001000007ad    leaq    0x10(%rbp),%rsi
00000001000007b1    movl    %edi,%edx
00000001000007b3    addl    $0x01,%edx
这是我上面展示的十六进制字节的写出形式(“反汇编”)
otool
包含非常有用的函数名作为标签,因此我们可以滚动列表,再次找到您的
\u排序顺序
\u反向顺序
\u奇怪顺序
函数。以下是我得到的:

55 48 89 E5 89 7D FC 89 75 F8 ...
或者,在更可读的反汇编中:

_sorted_order:
0000000100000a60    pushq   %rbp
0000000100000a61    movq    %rsp,%rbp
0000000100000a64    movl    %edi,0xfc(%rbp)
0000000100000a67    movl    %esi,0xf8(%rbp)
0000000100000a6a    movl    0xfc(%rbp),%eax
0000000100000a6d    movl    0xf8(%rbp),%ecx
0000000100000a70    subl    %ecx,%eax
0000000100000a72    movl    %eax,0xf0(%rbp)
0000000100000a75    movl    0xf0(%rbp),%eax
0000000100000a78    movl    %eax,0xf4(%rbp)
0000000100000a7b    movl    0xf4(%rbp),%eax
0000000100000a7e    popq    %rbp
0000000100000a7f    ret
关于这一点的确切含义,您可能需要查阅汇编程序网站。组装可能看起来很吓人,但即使是一本简短的入门读物也足以理解一般要点。我,我能像读小说一样读这本书(又短又无聊)
;)
它从内部堆栈中读取两个值,从另一个值中减去一个值,然后在
%eax
中返回该值。看起来差不多是什么
_sorted_order:
0000000100000a60    pushq   %rbp
0000000100000a61    movq    %rsp,%rbp
0000000100000a64    movl    %edi,0xfc(%rbp)
0000000100000a67    movl    %esi,0xf8(%rbp)
0000000100000a6a    movl    0xfc(%rbp),%eax
0000000100000a6d    movl    0xf8(%rbp),%ecx
0000000100000a70    subl    %ecx,%eax
0000000100000a72    movl    %eax,0xf0(%rbp)
0000000100000a75    movl    0xf0(%rbp),%eax
0000000100000a78    movl    %eax,0xf4(%rbp)
0000000100000a7b    movl    0xf4(%rbp),%eax
0000000100000a7e    popq    %rbp
0000000100000a7f    ret
test_sorting (numbers, count, sorted_order);
unsigned char *data = (unsigned char *)test_sorting;
for(i = 0; i < 25; i++) {
    printf("%02x:", data[i]);
}
printf("\n");
0x100000DB4 0x0000000B  [  2] literal string: ERROR: %s