【问题标题】:Is there a way to find addresses of the code sections (.data, .text, etc) at runtime?有没有办法在运行时找到代码段(.data、.text 等)的地址?
【发布时间】:2020-02-02 17:30:09
【问题描述】:

我想编写一些代码,在运行时将在其自己的进程中打印每个代码段的地址和长度。是否有捷径可寻?我知道使用 void* main_address = main; 之类的代码找到 main 等函数的位置相对容易,但我想找到 .data 和 .text 之类的部分,我不知道我是否可以对它们做同样的事情编译。我正在 Windows 系统上编写这个实验并为 x86 架构进行编译。如果解决方案需要,我知道一点 x86 程序集。我真的很感激任何帮助或建议。谢谢!

【问题讨论】:

    标签: c windows x86 portable-executable


    【解决方案1】:

    只需经过PIMAGE_SECTION_HEADERs

    void DumpSections()
    {
        //PIMAGE_NT_HEADERS pinth = (PIMAGE_NT_HEADERS)((PBYTE)&__ImageBase + reinterpret_cast<PIMAGE_DOS_HEADER>(&__ImageBase)->e_lfanew);
        if (PIMAGE_NT_HEADERS pinth = RtlImageNtHeader(&__ImageBase))
        {
            if (ULONG NumberOfSections = pinth->FileHeader.NumberOfSections)
            {
                PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION(pinth);
    
                do 
                {
                    DbgPrint("%p %08x %.8s\n", (PBYTE)&__ImageBase + pish->VirtualAddress, pish->Misc.VirtualSize, pish->Name);
                } while (pish++, --NumberOfSections);
            }
        }
    }
    

    【讨论】:

    • 你真的需要进行函数调用吗?例如,@P__J__ 的回答显示了一种可以编译成 RIP 相关的 lea 的方法,以便编译器将 _text_start__ 的地址放入寄存器中。
    • @PeterCordes - 我不清楚为什么需要这样做以及最终目标是什么。我写的 - 简单地从 PE 中转储部分。所以很难说这里最好的解决方案是什么。 P__J__ 的解决方案对 link.exe 没有用。这里存在基于link.exe megre部分的方式,在第一个$符号之前具有通用名称,例如在$之后的a-z顺序可以定义“.text$a”和“.text$z”中的符号。但我会使用 char text_start[];char text_end[]; 例如 - 它的大小为零,不像 char text_start;char text_end;
    • 同意 C 类型:我建议在我的回答中也使用像 extern char text_end[] 这样的数组。但是extern char 编译相同,只要您从不加载/存储值,只需获取变量的地址。
    • @PeterCordes - 这非常依赖于工具集。 extern char x;给出错误LNK2001:无法解析的外部符号。为了获得更多控制权,需要在 extern asm 文件中声明它,但无论如何这种方式存在问题.. 但我首先要问 - 这到底需要什么。基于此可能发现|选择最佳解决方案
    • @PeterCordes - 不,在 asm 中我们也可以用 $ 设置部分名称并通过这个地方控制
    【解决方案2】:

    这取决于使用的编译器。这将是关于 gcc 和 binutils。

    1. 找到您的项目链接描述文件。链接描述文件通常具有.ld 文件扩展名。如果没有链接描述文件,您的项目将使用默认链接描述文件。要查看默认链接器脚本,您需要传递-Wl,--verbose。您会看到很多信息,但我们正在寻找类似的信息(这是我的):
    /* Default linker script, for normal executables */
    /* Copyright (C) 2014 Free Software Foundation, Inc.
       Copying and distribution of this script, with or without modification,
       are permitted in any medium without royalty provided the copyright
       notice and this notice are preserved.  */
    OUTPUT_FORMAT(pei-i386)
    SEARCH_DIR("/mingw/mingw32/lib"); SEARCH_DIR("/mingw/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
    SECTIONS
    {
      /* Make the virtual address and file offset synced if the alignment is
         lower than the target page size. */
      . = SIZEOF_HEADERS;
      . = ALIGN(__section_alignment__);
      .text  __image_base__ + ( __section_alignment__ < 0x1000 ? . : __section_alignment__ ) :
      {
         *(.init)
        *(.text)
        *(SORT(.text$*))
         *(.text.*)
         *(.gnu.linkonce.t.*)
        *(.glue_7t)
        *(.glue_7)
         ___CTOR_LIST__ = .; __CTOR_LIST__ = . ;
                LONG (-1);*(.ctors); *(.ctor); *(SORT(.ctors.*));  LONG (0);
         ___DTOR_LIST__ = .; __DTOR_LIST__ = . ;
                LONG (-1); *(.dtors); *(.dtor); *(SORT(.dtors.*));  LONG (0);
         *(.fini)
        /* ??? Why is .gcc_exc here?  */
         *(.gcc_exc)
        PROVIDE (etext = .);
        PROVIDE (_etext = .);
         *(.gcc_except_table)
      }
      /* The Cygwin32 library uses a section to avoid copying certain data
         on fork.  This used to be named ".data".  The linker used
         to include this between __data_start__ and __data_end__, but that
         breaks building the cygwin32 dll.  Instead, we name the section
         ".data_cygwin_nocopy" and explicitly include it after __data_end__. */
      .data BLOCK(__section_alignment__) :
      {
        __data_start__ = . ;
        *(.data)
        *(.data2)
        *(SORT(.data$*))
        *(.jcr)
        __data_end__ = . ;
        *(.data_cygwin_nocopy)
      }
      .rdata BLOCK(__section_alignment__) :
      {
        *(.rdata)
                 *(SORT(.rdata$*))
        __rt_psrelocs_start = .;
        *(.rdata_runtime_pseudo_reloc)
        __rt_psrelocs_end = .;
      }
      __rt_psrelocs_size = __rt_psrelocs_end - __rt_psrelocs_start;
      ___RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
      __RUNTIME_PSEUDO_RELOC_LIST_END__ = .;
      ___RUNTIME_PSEUDO_RELOC_LIST__ = . - __rt_psrelocs_size;
      __RUNTIME_PSEUDO_RELOC_LIST__ = . - __rt_psrelocs_size;
      .eh_frame BLOCK(__section_alignment__) :
      {
        *(.eh_frame*)
      }
      .pdata BLOCK(__section_alignment__) :
      {
        *(.pdata)
      }
      .bss BLOCK(__section_alignment__) :
      {
        __bss_start__ = . ;
        *(.bss)
        *(COMMON)
        __bss_end__ = . ;
      }
      .edata BLOCK(__section_alignment__) :
      {
        *(.edata)
      }
      /DISCARD/ :
      {
        *(.debug$S)
        *(.debug$T)
        *(.debug$F)
        *(.drectve)
         *(.note.GNU-stack)
         *(.gnu.lto_*)
      }
      .idata BLOCK(__section_alignment__) :
      {
        /* This cannot currently be handled with grouped sections.
        See pe.em:sort_sections.  */
        SORT(*)(.idata$2)
        SORT(*)(.idata$3)
        /* These zeroes mark the end of the import list.  */
        LONG (0); LONG (0); LONG (0); LONG (0); LONG (0);
        SORT(*)(.idata$4)
        __IAT_start__ = .;
        SORT(*)(.idata$5)
        __IAT_end__ = .;
        SORT(*)(.idata$6)
        SORT(*)(.idata$7)
      }
      .CRT BLOCK(__section_alignment__) :
      {
        ___crt_xc_start__ = . ;
        *(SORT(.CRT$XC*))  /* C initialization */
        ___crt_xc_end__ = . ;
        ___crt_xi_start__ = . ;
        *(SORT(.CRT$XI*))  /* C++ initialization */
        ___crt_xi_end__ = . ;
        ___crt_xl_start__ = . ;
        *(SORT(.CRT$XL*))  /* TLS callbacks */
        /* ___crt_xl_end__ is defined in the TLS Directory support code */
        ___crt_xp_start__ = . ;
        *(SORT(.CRT$XP*))  /* Pre-termination */
        ___crt_xp_end__ = . ;
        ___crt_xt_start__ = . ;
        *(SORT(.CRT$XT*))  /* Termination */
        ___crt_xt_end__ = . ;
      }
      /* Windows TLS expects .tls$AAA to be at the start and .tls$ZZZ to be
         at the end of section.  This is important because _tls_start MUST
         be at the beginning of the section to enable SECREL32 relocations with TLS
         data.  */
      .tls BLOCK(__section_alignment__) :
      {
        ___tls_start__ = . ;
        *(.tls$AAA)
        *(.tls)
        *(.tls$)
        *(SORT(.tls$*))
        *(.tls$ZZZ)
        ___tls_end__ = . ;
      }
      .endjunk BLOCK(__section_alignment__) :
      {
        /* end is deprecated, don't use it */
        PROVIDE (end = .);
        PROVIDE ( _end = .);
         __end__ = .;
      }
      .rsrc BLOCK(__section_alignment__) : SUBALIGN(4)
      {
        *(.rsrc)
        *(.rsrc$*)
      }
      .reloc BLOCK(__section_alignment__) :
      {
        *(.reloc)
      }
      .stab BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.stab)
      }
      .stabstr BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.stabstr)
      }
      /* DWARF debug sections.
         Symbols in the DWARF debugging sections are relative to the beginning
         of the section.  Unlike other targets that fake this by putting the
         section VMA at 0, the PE format will not allow it.  */
      /* DWARF 1.1 and DWARF 2.  */
      .debug_aranges BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_aranges)
      }
      .zdebug_aranges BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_aranges)
      }
      .debug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_pubnames)
      }
      .zdebug_pubnames BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_pubnames)
      }
      .debug_pubtypes BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_pubtypes)
      }
      .zdebug_pubtypes BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_pubtypes)
      }
      /* DWARF 2.  */
      .debug_info BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_info .gnu.linkonce.wi.*)
      }
      .zdebug_info BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_info .zdebug.gnu.linkonce.wi.*)
      }
      .debug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_abbrev)
      }
      .zdebug_abbrev BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_abbrev)
      }
      .debug_line BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_line)
      }
      .zdebug_line BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_line)
      }
      .debug_frame BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_frame*)
      }
      .zdebug_frame BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_frame*)
      }
      .debug_str BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_str)
      }
      .zdebug_str BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_str)
      }
      .debug_loc BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_loc)
      }
      .zdebug_loc BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_loc)
      }
      .debug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_macinfo)
      }
      .zdebug_macinfo BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_macinfo)
      }
      /* SGI/MIPS DWARF 2 extensions.  */
      .debug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_weaknames)
      }
      .zdebug_weaknames BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_weaknames)
      }
      .debug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_funcnames)
      }
      .zdebug_funcnames BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_funcnames)
      }
      .debug_typenames BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_typenames)
      }
      .zdebug_typenames BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_typenames)
      }
      .debug_varnames BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_varnames)
      }
      .zdebug_varnames BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_varnames)
      }
      .debug_macro BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_macro)
      }
      .zdebug_macro BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_macro)
      }
      /* DWARF 3.  */
      .debug_ranges BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_ranges)
      }
      .zdebug_ranges BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_ranges)
      }
      /* DWARF 4.  */
      .debug_types BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.debug_types .gnu.linkonce.wt.*)
      }
      .zdebug_types BLOCK(__section_alignment__) (NOLOAD) :
      {
        *(.zdebug_types .gnu.linkonce.wt.*)
      }
    }
    

    将其复制并粘贴到文本文件中(.ld 扩展名将帮助您或其他人了解此文件的作用)。

    正如我们所见,.text 部分的末尾确实提供了两个符号,但开头没有符号。我们可以自己添加这个符号:

      .text  __image_base__ + ( __section_alignment__ < 0x1000 ? . : __section_alignment__ ) :
      {
         .__text_start__ = .;
         *(.init)
    

    您需要将-Tpath_to_your_ld_file 传递给链接器

    然后在.c文件中我们可以测试它:

    extern char _text_start__, etext, _data_start__, _data_end__, _bss_start__, _bss_end__;
    
    int main()
    {
        printf("Text start: %p  Text end: %p\n", (void *)&_text_start__, (void *)&etext);
        printf("Data start: %p  Data end: %p\n", (void *)&_data_start__, (void *)&_data_end__);
        printf("BSS start: %p  BSS end: %p\n", (void *)&_bss_start__, (void *)&_bss_end__);
    }
    

    在我的电脑上是:

    Text start: 00401000  Text end: 00408FF4
    Data start: 00409000  Data end: 0040902C
    BSS start: 0040D000  BSS end: 0040DA34
    

    【讨论】:

      【解决方案3】:

      我不知道 Windows 的约定,但如果它像 Linux,您的编译器、汇编器或链接器可能会为节的开头定义一个符号。 (使用nm 或类似工具从.obj.exe 转储符号表。)

      如果是这样,extern const char that_symbol[] 将定义一个可能匹配名称的 C 变量。那么当然你可以使用that_symbol的地址。 (注意,不是 const char *that_symbol ;内存中没有存储指针。您正在定义一个静态变量,其 address 是您想要的地址。不要读取 value这个变量。)

      如果需要解决前导下划线或使用包含. 或C 不允许的内容的asm 符号名称,请使用GNU C extern char foo[] asm("real_name")https://gcc.gnu.org/onlinedocs/gcc/Asm-Labels.html 这当然是 GNU 扩展。


      请参阅 @P__J__'s answer 了解此答案所建议的完整 Windows 实现,包括一个 GNU binutils 链接器脚本,用于为工具链未定义的某些部分的开始/结束添加缺失符号在其自己的。它使用extern char 而不是extern char[] 作为C 名称,因此您必须始终使用&amp;name 来获取符号的地址。但是,定义具有您想要的地址的 C extern 全局变量的基本思想正是我所建议的。

      【讨论】:

      • 你能解释一下这个技巧,把真正的符号名称和汇编一起输入更多吗?我以前从未见过像这样在同一行上混合使用 C 的程序集
      • 操作系统确实很重要。工具链很重要
      • @kansas_bulldog382:添加了关于该 GNU 扩展的 GCC 手册的链接。
      猜你喜欢
      • 2010-12-29
      • 1970-01-01
      • 2014-05-25
      • 2018-07-30
      • 2021-10-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多