windows下shellcode基本编写方法

注:本实验涉及的代码等仅限于学习目的,请勿用于非法目的,违者后果自负,与本网站无关。

必备API相关知识参考《windows中重要的API略解》

基本原理:

    1、程序一定调用了kernel32.dll

    2、利用Loadlibrary可以载入其他dll

    3、结合GetProcAddress可以获得任意API调用的具体地址

    4、使用DLL显式调用API(参考《DLL的两种调用方法》)

基本思路:

    1、获得kernel32.dll的地址

    2、获得LoadLibrary和GetProcAddress的地址

    3、显式调用LoadLibrary和GetProcAddress获得其他API地址

    4、显式调用得到的API

关键函数的编写

    1、GetKernel32Addr()

    用途:获取Kernel32.dll载入到进程中的位置

    参数:无参数

    输出:返回值为Kernerl32.dll载入的基址

    原理:fs寄存器指向进程的TEB块,TEB的0x30位置为指向PEB的指针

                PEB的Ldr(0x0c)为指向 _PEB_LDR_DATA结构体的指针

                _PEB_LDR_DATA的InMemoryOrderModuleList(0x1c)为一个

                双向链表

                双向链表Flink指向_LDR_DATA_TABLE_ENTRY机构体的某个成

                员(0x08)

                _LDR_DATA_TABLE_ENTRY包含FullDllName和DllBase等信息

                FullDllName即为模块名,DllBase即为模块载入基址

                比较模块名即可找到kernel32.dll的基址

                判断尾节点是否等于开始节点可以判断是否查找结束

    原理图示意:

    代码实现:

    DWORD GetKernel32Addr()
    {
        WCHAR kernel32[]={'k','e','r','n','e','l','3','2','.','d','l','l','\x00'};
        DWORD i=0;
        PPEB peb=(PPEB)__readfsdword(0x30);//获得PE头
        LIST_ENTRY *first=peb->Ldr->InMemoryOrderModuleList.Flink;
        //模块头指针,指向下一个模块
        LIST_ENTRY *pDataTableEntry=first;//载入模块
         do{
            LDR_DATA_TABLE_ENTRY *dte=
                (LDR_DATA_TABLE_ENTRY*)((BYTE*)pDataTableEntry-0x8);
            //dte为当前运行的模块,遍历模块找到kernel32.dll
            pDataTableEntry=pDataTableEntry->Flink;//下一个模块
            const wchar_t *dllname=
                ((decltype(dte->FullDllName)*)(DWORD*)&(dte->Reserved4))->Buffer;
                //截取模块名
            //下面为自己做的unicode字符串比较方法,可以采用hash方法比较
            const wchar_t *dllname_t=dllname;
            while(*dllname_t){//模块名比较
                if(kernel32[i]==(BYTE)*dllname_t)i++;
                else if((BYTE)*dllname_t<'a') 
                    if((BYTE)*dllname_t+0x20==kernel32[i])i++;//统一大小写
                dllname_t++;
            }
            if(i==12){
    
                return (DWORD)dte->DllBase;
                break;
            }
        }
        while(pDataTableEntry!=first)
         return 0;
    }

    2、GetAPIAddress(unsigned char * pDllBase,char * ApiName)

    用途:获取API函数地址(主要是Loadlibrary和GetProcAddress,其他在kernel32.dll中的API也可以通过此办法获取)

    原理:

            获得kernel32.dll的PE头(dos头位置为GetKernel32Addr()返回的地

            址)

            获得导出表的地址(PE头知识参看《PE文件结构初探》)

            遍历查找函数名表获得相应函数的函数名序号

            函数名序号(从0开始)等于NumberOfNames时表示未找到并结束

            将函数名序号带入函数序号表查找对应的函数序号

            Tips:因为导出方式不止有名字导出,所以函数名序号不能直接用于查找

            函数地址,应通过函数序号表转化后查找

            将函数序号带入函数地址表查找对应的函数地址

            函数地址加上基址即为函数的RVA

    原理图示意:

导出表结构:

    typedef struct _IMAGE_EXPORT_DIRECTORY {    
        DWORD   Characteristics;
        DWORD   TimeDateStamp;
        WORD    MajorVersion;
        WORD    MinorVersion;
        DWORD   Name;
        DWORD   Base;
        DWORD   NumberOfFunctions;      //导出函数总数
        DWORD   NumberOfNames;          //通过名称导出的函数总数
        DWORD   AddressOfFunctions;     // 指向导出函数表的RVA
        DWORD   AddressOfNames;         // 指向函数名表的RVA
        DWORD   AddressOfNameOrdinals;  // 指向函数序号表的RVA
    } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

代码实现:

    DWORD GetAPIAddress(unsigned char * baseAddress,char * ApiName)
    {
        unsigned char * DllApiName;
        BYTE*baseAddress = (BYTE*)Kernel32Addr;
        PIMAGE_DOS_HEADER dosHeader=(PIMAGE_DOS_HEADER)baseAddress;
        PIMAGE_NT_HEADERS peHeader = (PIMAGE_NT_HEADERS)
        (baseAddress+ dosHeader->e_lfanew);
        //获得PE头
        PIMAGE_EXPORT_DIRECTORY ied = 
            (PIMAGE_EXPORT_DIRECTORY)&baseAddress
            [peHeader->OptionalHeader.DataDirectory[0].VirtualAddress];
         //获得导出表
        int index=0;
        while(index<ied->NumberOfNames)
        {
            DllApiName=baseAddress+*((DWORD *)
            &baseAddress[ied->AddressOfNames + (index << 2)]) ;
             //遍历函数名表
            int i=0;//实现字符串对比
            char *p=ApiName;
            while(*p){
                if((BYTE)*p!=(BYTE)*DllApiName)break;
                p++;
                DllApiName++;
                i++;
            }
            if (i==strlen(ApiName)) break;
            index ++;
        }
        if (index == ied->NumberOfNames)  return 0;
        index = ((WORD *)&baseAddress[ied->AddressOfNameOrdinals])[index];
        return ((DWORD *)&baseAddress[ied->AddressOfFunctions])[index] + 
                peHeader->OptionalHeader.ImageBase;
    }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值