虽然最后事情的结果很郁闷,但中间的过程还是有些收获的,
在编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里导出的
函数,,然后有了这两个函数,,其他啥函数都不发愁了