zoukankan      html  css  js  c++  java
  • PE学习



    直接写一遍PE结构吧  看看记得住不?




    MS-DOS
     {
    MZ  
    `````
    e_lfanew
    }  64字节
    dos-stub 112字节  不定
    NT header 
    {
    Signature  4
    IMAGE_FILE_HEADER
    {
    machine
    NumberOfSections
    timedatastamp
    PointerToSymbolTable
    NumberOfSymbols
    sizeofOptionalHeader
    characteristics
    }
    OptionalHeader32
    {
    magic
    MajorLinker
    MinorLinker
    SizeofCode
    SizeOfInitializeData
    SizeOfUnInirializeData
    AddressOfEntryPoint
    BaseOfCpde
    BaseOfData
    ImageBase
    SectionAlignment
    FileAlignment
    Maior操作系统主
    操作系统次
    用户自定义主
    用户次版本
    子系统主
    子系统次
    Win32预留
    SizeOfImage 映像装入内存
    SizeOfHeader   DOS头+PE头+区块表
    校验和
    子系统
    DllCharactrstics
    SizeOfStackReserve 线程保留
    委派堆栈··
    进程保留
    委派堆保留内存
    LoaderFlag 调试有关
    数据目录项数
    DataDirectory 16个
    {
    IMAGE_DATA_DIRECTORY
    {
    VirtualAddress  数据块起始RVA
    SIZE 数据块长度
    }
    出 入 资 异       安 重 调 版 全     线 配 绑 导 延 R 预
    }
    }
    }




    IMAGE_SECTION_HEADER   数量由   NT_HEADER -> FILE_HEADER->第二元素 NumbersOfSection 指定
    {     两排半  
    name
    union{  padder,size  }
    VitualAddress
    SizeOfRawData
    PointerToRawData

    4个无用信息{  OBJ使用 重定位偏移 + 行号表偏移 + OBJ使用 重定位数目 + 行号数目 }
    Characteristics  代码/数据/可读可写 标志    60 00 00 20可读可运行    C0 00 00 20 可读可写  40 00 00 40 可读
    }
    在后面+一个空的 IMAGE_SECTION_HEADER
    最后补齐  200H 倍数


    手动添加节区并且以节区为起始位置的步骤:

    1)节区大小为    两行半,先添加名称  半排

    2)添加节大小,一般0x1000 字节,也就是对齐后长度,注意添加时字节顺序

    3)添加节相对虚拟地址,=  上一个节区的相对虚拟地址 + 上一个节对齐后长度(0x1000)

    4)添加节在磁盘上的大小,通常为对齐大小0x1000

    5)  添加节区在磁盘的偏移值 = 上一个节区的磁盘的偏移值 + 上一个节对齐后长度(0x1000)

    6)添加4个无用信息,12字节的都可以是 0 

    7)最后4个字节添加  节区的节区属性

    8)修改PE文件的节区数量,在OPT header 结构 后面 7 8字节上

    9) 修改映像大小  sizeOfImage, OPT header  结构中

    10)添加数据 0x1000  在这个区域修改代码即为区段的代码

    11)修改PE起始点为 节区代码段(10步骤添加的代码)

    代码学习··········································


    文件偏移  =  虚拟地址VA  - ImageBase - (所在块 IMAGE_SECTION_HEADER -> 内存偏移 -  所在块文件偏移)




    NT_HEADER->OPTIONAL_HEADER->BaseOfCode                       ->代码段



       数据目录中的各种表




    NT_HEADER->OPTIONAL_HEADER->BaseOfData                          -> 数据段


    基址重定位  主要用于DLL  因为DLL经常加载到不是预设基址的地址上

    typedef struct _IMAGE_BASE_RELOCATION

    {

    DWORD VirtualAddress; //需重定位数据的起始RVA

    DWORD SizeofBlock;    //本结构 8字节 + TypeOffset 总大小

    //WORD TypeOffset[1];//不属于 本结构    是需要进行重定位的具体偏移

    }

    TypeOffset 又定义为  

    struct{

    WORD Offset:12;   //后12位

    WORD Type:4; //前4位

    }TypeOffset;



    一个重定位区块     的      重定位项的数目  = (SizeOfBlock - IMAGE_SIZEOF_RELOCATION )/ 2    //IMAGE_SIZEOF_RELOCATION也就是8

    举个例子:

    TypeOffset  = 0x3006

    Type  =   0x3

    Offset  = 0x006

     VirtualAddress = 0x1000;

    重定位的RVA = VirtualAddress + 0x006 = 0x1006


    由此  推出需要重定位的数据RVA 为  0x1006

    示例代码如下:

    typedef struct {
    	PIMAGE_NT_HEADERS headers;
    	unsigned char *codeBase;
    	HMODULE *modules;
    	int numModules;
    	int initialized;
    } MEMORYMODULE, *PMEMORYMODULE;
    
    #define GET_HEADER_DICTIONARY(module, idx) &(module)->headers->OptionalHeader::DataDirectory[idx]
    
    void PerformBaseRelocation(PMEMORYMODULE module, DWORD delta) 
    //第二参数为 你把DLL导入到的内存基址 - DLL 文件的 ImageBase
    {
    	DWORD i;
    	unsigned char *codeBase = module->codeBase;
    
    	PIMAGE_DATA_DIRECTORY directory = GET_HEADER_DICTIONARY(module, IMAGE_DIRECTORY_ENTRY_BASERELOC);
    	if (directory->Size > 0)
    	{
    		PIMAGE_BASE_RELOCATION relocation = (PIMAGE_BASE_RELOCATION)(codeBase + directory->VirtualAddress);
    		for (; relocation->VirtualAddress > 0; )
    		{
    			unsigned char *dest = (unsigned char *)(codeBase + relocation->VirtualAddress);
    			unsigned short *relInfo = (unsigned short *)((unsigned char *)relocation + IMAGE_SIZEOF_BASE_RELOCATION);
    			for (i=0; i<((relocation->SizeOfBlock - IMAGE_SIZEOF_BASE_RELOCATION) / 2); i++, relInfo++)
    			{//164
    				DWORD *patchAddrHL;
    				int type, offset;
    
    				// the upper 4 bits define the type of relocation
    				type = *relInfo >> 12;   //4bit
    				// the lower 12 bits define the offset
    				offset = *relInfo & 0xfff;//12bit
    				
    				switch (type)
    				{
    				case IMAGE_REL_BASED_ABSOLUTE:
    					// skip relocation
    					break;
    
    				case IMAGE_REL_BASED_HIGHLOW:
    					// change complete 32 bit address
    					patchAddrHL = (DWORD *)(dest + offset);
    					*patchAddrHL += delta;
    					break;
    
    				default:
    					//printf("Unknown relocation: %d
    ", type);
    					break;
    				}
    			}
    
    			// advance to next relocation block 有很多个重定位表~~~
    			relocation = (PIMAGE_BASE_RELOCATION)(((DWORD)relocation) + relocation->SizeOfBlock);
    		}
    	}
    }




    EG:

    push ebp

    mov ebp,esp

    push 0xfffffffe

    push 0x10002210


    假如0x1006 + 0x10000000 对应着 0x10002210  

    如果没有加载 首选加载地址  而是加载了0x72AB0000 处,很明显要要对其进行重定位了。

    首先计算出他们的差值 为    0x62AB0000    再加上原地址0x10002210 就是重定位后的地址


    重定位后的地址  = (加载基址 - ImageBase) + 重定位前的地址

    上面的例子为 0x72AB0000 - 0x10000000 + 0x10002210 = 0x72AB2210 

    重定位后第四排为

    push 0x72AB2210 


    TLS 变量

    保存在一个.tls区段中

    TLS  —— 变量 ——静态模式 主要是指  __declspec (thread) 声明的TLS变量     不能使用在DLL中

       ——动态模式   主要TlsAlloc TlsFree TlsSrtValue TlsGetValue

     ——  回调函数

    当我们声明一个tls变量后,系统会自动生成.TLS节

    __declspec (thread) int tlsnum = 1;

    系统会保证对于每个线程的唯一性。

    #include "stdafx.h"
    #include <Windows.h>
    
    __declspec (thread) int g_num = 0x11111111;
    __declspec (thread) char g_str[] = "TLS g_num = 0x%p   ````
    ";
    
    
    void NTAPI t_tlscallback_A(PVOID DLLHandle, DWORD Reason, PVOID red)
    {
    	if (DLL_THREAD_DETACH == Reason)
    	{
    		printf("t_tlscallback_A -> threadDetach ! 
    ");
    	}
    	return ;
    }
    void NTAPI t_tlscallback_B(PVOID DLLHandle, DWORD Reason, PVOID red)
    {
    	if (DLL_THREAD_DETACH == Reason)
    	{
    		printf("t_tlscallback_B -> threadDetach ! 
    ");
    	}
    	return ;
    }
    
    
    #pragma data_seg(".CRT$XLB")
    PIMAGE_TLS_CALLBACK p_thread_callback[] = {
    	t_tlscallback_A,t_tlscallback_B,NULL
    };
    #pragma data_seg()
    
    
    DWORD WINAPI ThreadProc(
    	__in  LPVOID lpParameter
    	)
    {
    	printf("ThreadProc first printf
    ");
    	printf(g_str,g_num);
    	g_num = 0x22222222;
    	printf("ThreadProc second printf
    ");
    	printf(g_str,g_num);
    	return 0;
    }
    int _tmain(int argc, _TCHAR* argv[])
    {
    	printf("_tmain ``````````````````````````
    ");
    	CreateThread(NULL,0,ThreadProc,NULL,0,0);
    	Sleep(1000);
    	printf("
    ");
    	CreateThread(NULL,0,ThreadProc,NULL,0,0);
    	system("pause");
    	
    	return 0;
    }
    /*
    _tmain ``````````````````````````
    ThreadProc first printf
    TLS g_num = 0x11111111   ````
    ThreadProc second printf
    TLS g_num = 0x22222222   ````
    t_tlscallback_A -> threadDetach !
    t_tlscallback_B -> threadDetach !
    
    
    ThreadProc first printf
    TLS g_num = 0x11111111   ````
    ThreadProc second printf
    TLS g_num = 0x22222222   ````
    t_tlscallback_A -> threadDetach !
    t_tlscallback_B -> threadDetach !
    */

    另一个作用  就是在程序main函数运行前 进行操作

    #include "stdafx.h"
    #include <Windows.h>
    
    
    #pragma comment(linker,"/include:__tls_used")//这句很重要
    
    
    void NTAPI t_tlscallback_A(PVOID DLLHandle, DWORD Reason, PVOID red)
    {
    	if (DLL_PROCESS_ATTACH == Reason)
    	{
    		MessageBox(NULL,_T("hi,this is tls callback"),_T("title"),MB_OK); 
    	}
    	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)GetModuleHandle(NULL);
    
    
    	PIMAGE_NT_HEADERS32 pNt = (PIMAGE_NT_HEADERS32)((DWORD)pDos+(DWORD)pDos->e_lfanew);
    
    
    	BYTE* OEP = (BYTE*)(pNt->OptionalHeader.AddressOfEntryPoint + (DWORD)pDos);
    	
    	for (unsigned int i=0;i<200;i++)
    	{
    		if (OEP[i] == 0xcc)
    		{
    			MessageBox(NULL,_T("检测到CC断点"),_T("检测到CC断点"),MB_OK);
    		}
    		 
    	}
    	return ;
    }
    
    
    #pragma data_seg(".CRT$XLB")
    PIMAGE_TLS_CALLBACK p_thread_callback[] = {
    	t_tlscallback_A,NULL
    };
    #pragma data_seg()
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	printf("MAIN STARTN");
    	system("pause");
    	return 0;
    }
















  • 相关阅读:
    STM32CubeMX 使用
    Zookeeper集群搭建
    golang zookeeper监听事件报错
    git push 报错
    springboot使用postgresql模式下数据库表找不到
    不要在循环中访问数据库,这样会严重影响数据库性能
    SQL查询效率(Oracle)
    游标 数据集 效率比较
    oracle 视图
    INDEX SKIP SCAN 和 INDEX RANGE SCAN以及索引会失效
  • 原文地址:https://www.cnblogs.com/zcc1414/p/3982445.html
Copyright © 2011-2022 走看看