4月5日,学习shellcode,用peb找函数地址

虽然最后事情的结果很郁闷,但中间的过程还是有些收获的,
在编shellcode的时候,很重要的就是要找到要用的函数在内存中的位置,网上有很多写好的模板,方法就

是通过peb和SEH找,找到的模板是用peb的。
主要目的就是找到kernel.dll在内存中的位置,进而找到GetProcAddress()的位置,有了这两个,就可以

有LoAdLibrAry(),然后其他的函数地址问题就都解决了

FS:0是teb的位置,teb偏移0x30的地方是peb
typedef struct _PEB {
 BOOLEAN InheritedAddressSpace;
 BOOLEAN ReadImageFileExecOptions;
 BOOLEAN BeingDebugged;
 BOOLEAN Spare;
 HANDLE Mutant;
 PVOID ImageBaseAddress;
 PPEB_LDR_DATA LoaderData;
 PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
 PVOID SubSystemData;
 PVOID ProcessHeap;
 PVOID FastPebLock;
 PPEBLOCKROUTINE FastPebLockRoutine;
 PPEBLOCKROUTINE FastPebUnlockRoutine;
 ULONG EnvironmentUpdateCount;
 PVOID *KernelCallbackTable;
 PVOID EventLogSection;
 PVOID EventLog;
 PPEB_FREE_BLOCK FreeList;
 ULONG TlsExpansionCounter;
 PVOID TlsBitmap;
 ULONG TlsBitmapBits[0x2];
 PVOID ReadOnlySharedMemoryBase;
 PVOID ReadOnlySharedMemoryHeap;
 PVOID *ReadOnlyStaticServerData;
 PVOID AnsiCodePageData;
 PVOID OemCodePageData;
 PVOID UnicodeCaseTableData;
 ULONG NumberOfProcessors;
 ULONG NtGlobalFlag;
 BYTE Spare2[0x4];
 LARGE_INTEGER CriticalSectionTimeout;
 ULONG HeapSegmentReserve;
 ULONG HeapSegmentCommit;
 ULONG HeapDeCommitTotalFreeThreshold;
 ULONG HeapDeCommitFreeBlockThreshold;
 ULONG NumberOfHeaps;
 ULONG MaximumNumberOfHeaps;
 PVOID **ProcessHeaps;
 PVOID GdiSharedHandleTable;
 PVOID ProcessStarterHelper;
 PVOID GdiDCAttributeList;
 PVOID LoaderLock;
 ULONG OSMajorVersion;
 ULONG OSMinorVersion;
 ULONG OSBuildNumber;
 ULONG OSPlatformId;
 ULONG ImageSubSystem;
 ULONG ImageSubSystemMajorVersion;
 ULONG ImageSubSystemMinorVersion;
 ULONG GdiHandleBuffer[0x22];
 ULONG PostProcessInitRoutine;
 ULONG TlsExpansionBitmap;
 BYTE TlsExpansionBitmapBits[0x80];
 ULONG SessionId;
} PEB, *PPEB;

PEB偏移0x0c的地方就是PPEB_LDR_DATA LoaderData;
typedef struct _PEB_LDR_DATA {
 ULONG Length;
 BOOLEAN Initialized;
 PVOID SsHandle;
 LIST_ENTRY InLoadOrderModuleList;
 LIST_ENTRY InMemoryOrderModuleList;
 LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;
其中有三个链表,每个元素都是结构
typedef struct _LDR_MODULE {
 LIST_ENTRY InLoadOrderModuleList;
 LIST_ENTRY InMemoryOrderModuleList;
 LIST_ENTRY InInitializationOrderModuleList;
 PVOID BaseAddress;
 PVOID EntryPoint;
 ULONG SizeOfImage;
 UNICODE_STRING FullDllName;
 UNICODE_STRING BaseDllName;
 ULONG Flags;
 SHORT LoadCount;
 SHORT TlsIndex;
 LIST_ENTRY HashTableEntry;
 ULONG TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;
通过LIST_ENTRY把每个进程用到的MODULE串起来,每个MODULE就是可执行文件或dll在内存中的映像,通过这

个连可以把进程用到的dll全都走一遍,而shellcode需要的是kernel32.dll的,它总是位于链表InInitializa

tionOrderModuleList的第一个元素
 mov    eax, fs:0x30                  // PEB
        mov    eax, [eax + 0x0c]            // PROCESS_MODULE_INFO
        mov    esi, [eax + 0x1c]            // InInitOrder.flink
        lodsd       

//得到kernel.dll所在的LDR_MODULE结构的InInitializationOrderModuleList地址
        mov    eax,[eax+8]      //一个LIST_ENTRY8个字节
现在的eAx中就是kernel.dll在内存映像的开始位置,也就是它的hAndle值

找到了kernel.dll,下面的部分就和PE文件的格式有关了,

 mov    ebx,eax                    // 取kernel32.dll的起始地址
        mov    esi,dword ptr [ebx+0x3C]  //u 在e_lfanew中得到pe heAder
        mov    esi,dword ptr [esi+ebx+0x78] //u export directory rvA
        add     esi,ebx     
        mov    edi,dword ptr [esi+0x20]   //u struct _IMAGE_EXPORT_DIRECTORY    

    //中AddressOfNames; // RVA from base of image
        add    edi,ebx
        mov    ecx,dword ptr [esi+0x14] //u AddressOfFunctions; // RVA from base of image
        xor    ebp,ebp
        push    esi
search_GetProcAddress:
        push    edi
        push    ecx
        mov    edi,dword ptr [edi]
        add    edi,ebx                    // 把输出函数名表起始地址存人edi
        mov    esi,edx                    // 指令表起始地址存入esi
        //mov    ecx,0Eh                    // 函数getprocAddress长度为0Eh
        push    0xE
        pop    ecx
        repe    cmps byte ptr [esi],byte ptr [edi]
        je    search_GetProcAddress_ok
       
        pop    ecx
        pop    edi
        add    edi,4
        inc    ebp
        loop    search_GetProcAddress

search_GetProcAddress_ok:
        pop    ecx  
        pop    edi
        pop    esi
        mov    ecx,ebp
        mov    eax,dword ptr [esi+24h] //u AddressOfNameOrdinals; // RVA from base of image
        add    eax,ebx
        shl    ecx,1
        add    eax,ecx
        xor    ecx,ecx
        mov    cx,word ptr [eax]
        mov    eax,dword ptr [esi+1Ch] //AddressOfFunctions; // RVA from base of image
        add    eax,ebx
        shl    ecx,2
        add    eax,ecx
        mov    eax,dword ptr [eax]
        add    eax,ebx

在pe文件中,最开始是IMAGE_DOS_HEADER结构
typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
    WORD   e_magic;                     // Magic number
    WORD   e_cblp;                      // Bytes on last page of file
    WORD   e_cp;                        // Pages in file
    WORD   e_crlc;                      // Relocations
    WORD   e_cparhdr;                   // Size of header in paragraphs
    WORD   e_minalloc;                  // Minimum extra paragraphs needed
    WORD   e_maxalloc;                  // Maximum extra paragraphs needed
    WORD   e_ss;                        // Initial (relative) SS value
    WORD   e_sp;                        // Initial SP value
    WORD   e_csum;                      // Checksum
    WORD   e_ip;                        // Initial IP value
    WORD   e_cs;                        // Initial (relative) CS value
    WORD   e_lfarlc;                    // File address of relocation table
    WORD   e_ovno;                      // Overlay number
    WORD   e_res[4];                    // Reserved words
    WORD   e_oemid;                     // OEM identifier (for e_oeminfo)
    WORD   e_oeminfo;                   // OEM information; e_oemid specific
    WORD   e_res2[10];                  // Reserved words
    LONG   e_lfanew;                    // File address of new exe header
  } IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

mov    esi,dword ptr [ebx+0x3C]这一句就是得到LONG   e_lfanew,esi指向pe头,由于是相对与文件开始

的偏移,所以esi+ebx才是kernell.dll的pe头在内存中的位置,再加0x78,就指向DAtADirectory,
typedef struct _IMAGE_NT_HEADERS {
    DWORD Signature;
    IMAGE_FILE_HEADER FileHeader;
    IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
typedef struct _IMAGE_OPTIONAL_HEADER {
    //
    // Standard fields.
    //

    WORD    Magic;
    BYTE    MajorLinkerVersion;
    BYTE    MinorLinkerVersion;
    DWORD   SizeOfCode;
    DWORD   SizeOfInitializedData;
    DWORD   SizeOfUninitializedData;
    DWORD   AddressOfEntryPoint;
    DWORD   BaseOfCode;
    DWORD   BaseOfData;

    //
    // NT additional fields.
    //

    DWORD   ImageBase;
    DWORD   SectionAlignment;
    DWORD   FileAlignment;
    WORD    MajorOperatingSystemVersion;
    WORD    MinorOperatingSystemVersion;
    WORD    MajorImageVersion;
    WORD    MinorImageVersion;
    WORD    MajorSubsystemVersion;
    WORD    MinorSubsystemVersion;
    DWORD   Win32VersionValue;
    DWORD   SizeOfImage;
    DWORD   SizeOfHeaders;
    DWORD   CheckSum;
    WORD    Subsystem;
    WORD    DllCharacteristics;
    DWORD   SizeOfStackReserve;
    DWORD   SizeOfStackCommit;
    DWORD   SizeOfHeapReserve;
    DWORD   SizeOfHeapCommit;
    DWORD   LoaderFlags;
    DWORD   NumberOfRvaAndSizes;
    IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;

DAtADirectory第一个就是ExportDirectory,,加ebx就是在内存中的位置,每个Directory的开头都是结构
typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;
    DWORD   Base;
    DWORD   NumberOfFunctions;
    DWORD   NumberOfNames;
    DWORD   AddressOfFunctions;     // RVA from base of image
    DWORD   AddressOfNames;         // RVA from base of image
    DWORD   AddressOfNameOrdinals;  // RVA from base of image
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

AddressOfNAmes和它后面的地址是一个指针数组,每个元素都指向一个函数名字符串的rvA,用的时候可以这


(char*)((*(int*)Addr)+bAseAddress)
AddressOfNAmeOrdinAls与AddressOfNAmes一一对应,是函数在AddressOfFunctions里的序号


在调用这里面的函数时,如果是用函数名在找的话,先在AddressOfNAmes找到对应的项,得到在这个指针
数组中的位置,用这个位置在AddressOfNAmeOrdinAls中索引出响应的元素,
  mov    ecx,ebp
        mov    eax,dword ptr [esi+24h] //u AddressOfNameOrdinals;
        add    eax,ebx
        shl    ecx,1
        add    eax,ecx
也就是这几句的意思,AddressOfNAmeOrdinAls数组中每个元素大小都是WORD,所以shl ecx,1
 xor    ecx,ecx
        mov    cx,word ptr [eax]
得到cx用来在AddressOfFunctions;中索引,AddressOfFunctions中每一个元素的内容就是函数的rvA了,加上

kernell.dll的基址就得到在内存中地址了
 add    eax,ebx
        shl    ecx,1
        add    eax,ecx
        xor    ecx,ecx
        mov    cx,word ptr [eax]
        mov    eax,dword ptr [esi+1Ch] //AddressOfFunctions; // RVA from base of image
        add    eax,ebx
        shl    ecx,2
        add    eax,ecx
        mov    eax,dword ptr [eax]
        add    eax,ebx

得到GetProcAddress的地址后,用它可以得到LoAdLibrAry的地址,因为LoAdLibrAry也是kernel.dll里导出的

函数,,然后有了这两个函数,,其他啥函数都不发愁了


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值