• ShellCode之寻找Debug下真实函数地址


    ShellCode之寻找Debug下真实函数地址

    一丶简介与原理

    ​ 在Debug模式下,函数地址与真实函数地址不一致. 这导致我们在写类似于ShellCode的代码的时候会产生问题.比如远程线程代码注入.

    产生这一原因是因为在Debug模式下,我们的函数地址是一层跳转表.是编译器维护的.名字叫做ILT,所以对函数名的直接访问都被映射了.映射为了修饰后的函数名.而真实函数地址在其跳转表之后.

    如: JMP 偏移 JMP之后才是真正的函数地址.

    在ILT表中的一般都是我们自定义的函数. 我们自定义的函数才会在其表中.

    下面看一下代码与调试.

    int fun1()
    {
    	int a = 10 * 10;
    	scanf("%d", &a);
    	return a;
    }
    
    int fun2()
    {
    	int b = 10 * 10;
    	int c = b * 2;
    	scanf("%d", &b);
    	return c;
    }
    int main()
    {
    	fun1();
    	fun2();
    	printf("fun1 = %x 
    ",(DWORD)fun1);
    	printf("fun2 = %x 
    ",(DWORD)fun2);
    	/*printf("va fun1 %x 
    ", (DWORD)get_DebugVaAddress(fun1));
    	printf("va fun2 %x 
    ", (DWORD)get_DebugVaAddress(fun2));*/
    	system("pause");
    	return 0;
    }
    

    可以看到定义了两个函数,分别是 fun1 以及 fun2 并且分别进行了调用. 下面我们就其调试看一下ILT表.

    真实的函数地址

    在调用fun1 函数之前我们进入了反汇编进行查看. 可以看到 现在的fun1地址其实是 0x00A3109B 而真实的fun1函数地址就是 0XA31750

    这就会导致我们一个问题.当我们写ShellCode的时候.想要将fun1写入到内存中 一般都会进行如下几个步骤:

    • 1.定义fun1 并且在fun1中写好地址无关代码
    • 2.在fun1下面定义一个fun2函数.fun2可以什么都不做.或者加点标记
    • 3.利用 fun2 - fun1 得出 fun1函数的字节大小(ShellCode)大小
    • 4.利用写内存函数将fun1 写入到内存.

    伪代码如下:

    void fun1()
    {
      //你的ShellCode代码
    }
    void fun2()
    {
    //做结束指令的fun2
       __asm{
         nop
         nop
       }
    }
    DWORD dwShellCodeSize = (DWORD)fun2 - (DWORD)fun1;
    WriteProcessMemory(Process,BaseAdddr,fun1,dwShellCodeSize...);
    

    如果程序运行在Release下是没有问题的.如果是Debug就会有问题了. 在Debug下函数都是ILT表. 两者相减就会出错.

    而真实函数我们也知道是 ILT表中之后记录的函数地址. 也就是跳转之后的地址.

    二丶原理与代码解决方式

    在ILT表中记录的是 JMP + 偏移的方式 来进行跳转的. 而我们学过HOOK的伙伴们应该知道偏移是怎么的出来的.

    偏移 = 目的地址 - 源地址 - 指令长度 而反过来说 目的地址 = 源地址 + 偏移 + 指令长度

    比如上面的 伪函数fun1地址(0x00A3109B 记录的偏移为: 0x6B0) 真实fun1 0XA31750

    代入指令:

    • 偏移 = 目的地址 - 源地址 - 指令长度

      0XA31750 - 0x00A3109B - 5(jmp占5个字节指令长度) = 0x6B0

    • 目的地址 = 源地址 + 偏移 + 指令长度

      0x00A3109B + 0x6B0 + 5 = 0XA31750

    代码实现方式就很简单了.首先取出 伪函数地址. 对其按照 unsigned char * 类型解析. 判断首字节是否是E9(JMP)

    如果是则取出后面的偏移,让当前地址 + 偏移 + 指令长度来得出真实地址.

    注意因为你要判断16进制的0xE9 那么一定要按照UCHAR类型解析 否则高位会认为是符号位.

    代码如下:

    void* get_DebugVaAddress(void* procFunction)
    {
        unsigned int VaAddr = 0;
        unsigned int VaOffset = 0;
        unsigned char* DebugCalcProc = NULL;
        if (procFunction == NULL)
        {
            return procFunction;
        }
        /*
        1.得出当前带有jmp跳转表的函数
            E9 XX XX XX XX
        2.判断是否是E9 如果是继续进行计算,不是返回自己函数本身
        3.是的情况下 从当前函数地址位置取出其偏移
          偏移在HOOK中是 目的地址 - 源地址 - 指令长度填写上的
          反过来讲 当前函数地址 + 指令长度 + 偏移则得到真实的目的地址
          计算真实地址即可。
        */
        unsigned int DebugVaAddr = (unsigned int)procFunction;
        DebugCalcProc = (unsigned char*)procFunction; //一定要uchar类型 要判断数值的
        if (DebugCalcProc[0] == 0xE9) //jmp
        {
            VaOffset = *(unsigned int*)(DebugCalcProc + 1);
            //当前地址 + 指令长度 + 偏移  = 目的地址
            VaAddr = DebugVaAddr + 5 + VaOffset;
            return (void*)VaAddr;
        }
        else
        {
            return (void*)procFunction;
        }
        return (void*)procFunction;
    }
    

    下面看下实战:

    可以看出在Debug下我们自己进行转化可以得到真实函数地址. 这样在调试ShellCode的时候也更加方便.

    当然如果你也可以加条件宏进行编译.这样Release下就不试用GetDebugVaAddress

  • 相关阅读:
    Dynamics CRM 请求服务时报access is denied错误
    (转载)表服务器无法打开与报表服务器数据库的连接。所有请求和处理都要求与数据库建立连接。
    如何将sqlserver的windows验证模式改为SQL Server 和 Windows 混合身份验证模式
    (转载)SQL Server2008附加数据库之后显示为只读时解决方法
    CRM导入组织报实例名称必须与计算机名称相同的问题
    The ENU localization is not supported by this SQL Server media
    js验证input输入框(字母,数字,符号,中文)
    Microsoft Dynamics CRM 2011 JS操作集锦
    一入python深似海--class
    TLB的作用及工作过程
  • 原文地址:https://www.cnblogs.com/iBinary/p/14148137.html
Copyright © 2020-2023  润新知