最近深感自己基础不牢,回头学了一遍PE,顺手做了个导入表注入的小练习
首先准备一个DLL用来测试
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
init();
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
void init() {
MessageBoxA(0, "Success", "Test", MB_OK);
}
int plus(int a, int b) {
return a + b;
}
再准备一个exe
.386
.MODEL flat,stdcall
option casemap:none
include windows.inc
include gdi32.inc
includelib gdi32.lib
include user32.inc
includelib user32.lib
include kernel32.inc
includelib kernel32.lib
.data?
hInstance dd ?
hWinMain dd ?
.const
szClassName db 'Myclass',0
szCaptionMain db 'My first window',0
szText db 'win32 Assembly,simple and powerful !',0
szButton db 'button',0
szButtonText db '&ok',0
.code
_ProcWinMain proc uses ebx edi esi,hWnd,uMsg,wParam,lParam
local @stPs:PAINTSTRUCT
local @stRect:RECT
local @hDc
mov eax,uMsg
.if eax == WM_PAINT
invoke BeginPaint,hWnd,addr @stPs
mov @hDc,eax
invoke GetClientRect,hWnd,addr @stRect
invoke DrawText,@hDc,addr szText,-1,addr @stRect,DT_SINGLELINE or DT_CENTER or DT_VCENTER
invoke EndPaint,hWnd,addr @stPs
.elseif eax == WM_CLOSE
invoke DestroyWindow,hWinMain
invoke PostQuitMessage,NULL
.else
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.endif
xor eax,eax
ret
_ProcWinMain endp
_WinMain proc
local @stWndClass:WNDCLASSEX
local @stMsg:MSG
invoke GetModuleHandle,NULL
mov hInstance,eax
invoke RtlZeroMemory,addr @stWndClass,sizeof @stWndClass
invoke LoadCursor,0,IDC_ARROW
mov @stWndClass.hCursor,eax
push hInstance
pop @stWndClass.hInstance
mov @stWndClass.cbSize,sizeof WNDCLASSEX
mov @stWndClass.style,CS_HREDRAW or CS_VREDRAW
mov @stWndClass.lpfnWndProc,offset _ProcWinMain
mov @stWndClass.hbrBackground,COLOR_WINDOW + 1
mov @stWndClass.lpszClassName,offset szClassName
invoke RegisterClassEx,addr @stWndClass
invoke CreateWindowEx,WS_EX_CLIENTEDGE,offset szClassName,offset szCaptionMain,
WS_OVERLAPPEDWINDOW,100,100,600,400,NULL,NULL,hInstance,NULL
mov hWinMain,eax
invoke ShowWindow,hWinMain,SW_SHOWNORMAL
invoke UpdateWindow,hWinMain
.while TRUE
invoke GetMessage,addr @stMsg,NULL,0,0
.break .if eax == 0
invoke TranslateMessage,addr @stMsg
invoke DispatchMessage,addr @stMsg
.endw
ret
_WinMain endp
start:
call _WinMain
invoke ExitProcess,NULL
end start
运行效果
要完成导入表注入,需要以下几步
1 打开一个exe
2 新增一个节
3 把导入表移进去
4 写入自己的导入表
5 存盘
关于导入表的资料网上有很多我就不细说了,这里主要就放一下代码,这个代码我写的很随意,看着应该比较丑hhhhh
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
FILE* fp = fopen(fileName, "rb");
if (fp == NULL) {
printf("open failed
");
exit(0);
}
fseek(fp, 0, SEEK_END);
unsigned int len = ftell(fp);
fseek(fp, 0, SEEK_SET);
char* buf = (char*)malloc(2 * len);//因为exe要扩充,所以这里直接扩了一倍,简单暴力hhh
if (buf != NULL) {
memset(buf, 'x00', 2 * len);
fread(buf, len, 1, fp);
}
fclose(fp);
char* lpnewSec = buf + newSection(buf);//指向新增的节
inject(buf, lpnewSec, fileName);
free(buf);
buf = NULL;
新增节:
unsigned __int32 newSection(char* buf) {
unsigned __int32 e_lfanew = *(unsigned __int32*)(buf + 0x3c);
char* FIheader = buf + e_lfanew + 4;
char* OPheader = FIheader+0x14;
//扩大地址,我直接加了一个内存对齐的大小
unsigned __int32 SectionAlignment = *(unsigned __int32*)(OPheader + 0x20);
unsigned __int32 SizeOfImage = *(unsigned __int32*)(OPheader + 0x38);
*(unsigned __int32*)(buf + 0x38 + (e_lfanew + 0x18)) = SizeOfImage + SectionAlignment;
SizeOfImage = *(unsigned __int32*)(OPheader + 0x38);
unsigned __int32 SizeoOfHeaders = *(unsigned __int32*)(OPheader + 0x3c);
unsigned __int16 NumberOfSections = *(unsigned __int16*)(FIheader + 0x2);
__int16 SizeOfOptionalHeader = *(unsigned __int16*)(FIheader + 0x10);
char* Sections_addr = OPheader + SizeOfOptionalHeader;
//遍历节表
unsigned __int32 Misc = 0;
unsigned __int32 VirtualAddress = 0;
unsigned __int32 SizeOfRawData = 0;
unsigned __int32 PointerToRawData = 0;
unsigned __int32 Characteristics = 0;
for (int j = 0; j < NumberOfSections; j++) {
Misc = *(unsigned __int32*)(Sections_addr + 0x8);
VirtualAddress = *(unsigned __int32*)(Sections_addr + 0xc);
SizeOfRawData = *(unsigned __int32*)(Sections_addr + 0x10);
PointerToRawData = *(unsigned __int32*)(Sections_addr + 0x14);
Characteristics |= *(unsigned __int32*)(Sections_addr + 0x24);
Sections_addr = Sections_addr + 0x28;
}
unsigned int check = SizeoOfHeaders - (Sections_addr - buf);
//当节表与第一个节的距离小于0x50时不能加节表,可以通过合并节等方式处理,这里没管
if (check < 0x50) {
printf("can't add new Section");
exit(0);
}
*(unsigned __int16*)(FIheader + 0x2) += 1;//节表长度加一
//加入新节表
unsigned __int32 newSec_addr = VirtualAddress + SectionAlignment;
unsigned __int32 newSec_PointerToRawData = PointerToRawData + SizeOfRawData;
*(unsigned __int64*)Sections_addr = 0x000000006362612e;//节名我直接取abc了
*(unsigned __int32*)(Sections_addr + 0x8) = SectionAlignment;
*(unsigned __int32*)(Sections_addr + 0xc) = newSec_addr;
*(unsigned __int32*)(Sections_addr + 0x10) = SectionAlignment;
*(unsigned __int32*)(Sections_addr + 0x14) = newSec_PointerToRawData;
*(unsigned __int32*)(Sections_addr + 0x24) = Characteristics;
return newSec_PointerToRawData;
}
注入
void inject(char* buf, char* newSec,const char* fileName) {
unsigned __int32 e_lfanew = *(unsigned __int32*)(buf + 0x3c);
char* FIheader = buf + e_lfanew + 4;
char* OPheader = FIheader + 0x14;
unsigned __int16 NumberOfSections = *(unsigned __int16*)(FIheader + 0x2);
__int16 SizeOfOptionalHeader = *(unsigned __int16*)(FIheader + 0x10);
char* Sections_addr = OPheader + SizeOfOptionalHeader;
unsigned __int32 VirtualAddress = 0;
unsigned __int32 SizeOfRawData = 0;
unsigned __int32 PointerToRawData = 0;
for (int j = 0; j < NumberOfSections; j++) {
VirtualAddress = *(unsigned __int32*)(Sections_addr + 0xc);
SizeOfRawData = *(unsigned __int32*)(Sections_addr + 0x10);
PointerToRawData = *(unsigned __int32*)(Sections_addr + 0x14);
Sections_addr = Sections_addr + 0x28;
}
char* lpDirectory = OPheader + SizeOfOptionalHeader - 0x80;
unsigned __int32 ImportRVA = *(unsigned __int32*)(lpDirectory + 0x8);
*(unsigned __int32*)(lpDirectory + 0x8) = VirtualAddress;
unsigned __int32 ImportFOA = RVA_to_FOA(buf, ImportRVA);
char* importAddr = buf + ImportFOA;
char* newImport = newSec;
unsigned __int32 count = 0;
//移动导入表,没有动INT和IAT
while (*(unsigned __int32*)importAddr || *(unsigned __int32*)(importAddr + 0x4) || *(unsigned __int32*)(importAddr + 0x8) || *(unsigned __int32*)(importAddr + 0xc) || *(unsigned __int32*)(importAddr + 0x4)) {
for (unsigned __int32 i = 0; i < 0x14; i++) {
*(newImport + i) = *(importAddr + i);
}
count += 0x14;
newImport += 0x14;
importAddr += 0x14;
}
//new import
*(unsigned __int32*)(newImport + 0x0) = VirtualAddress + count + 0x14;//指向新的INT
*(unsigned __int32*)(newImport + 0x4) = 0;
*(unsigned __int32*)(newImport + 0x8) = 0;
*(unsigned __int32*)(newImport + 0xc) = VirtualAddress + count + 0x2b;//指向注入的DLL名
*(unsigned __int32*)(newImport + 0x10) = VirtualAddress + count + 0x1c;//指向IAT
//INT
*(unsigned __int32*)(newImport + 0x14) = VirtualAddress + count + 0x24;//指向导入函数名,下面IAT也指向此处
*(unsigned __int32*)(newImport + 0x18) = 0;
//IAT
*(unsigned __int32*)(newImport + 0x1c) = VirtualAddress + count + 0x24;
*(unsigned __int32*)(newImport + 0x20) = 0;
//这里必须要有导入的函数,否则不会加载dll
*(unsigned __int16*)(newImport + 0x24) = 0;
*(unsigned __int32*)(newImport + 0x26) = 0x73756c70;//plus
*(newImport + 0x2a) = (char)0;
//dLL name:testDLL.dll
*(unsigned __int32*)(newImport + 0x2b) = 0x74736574;
*(unsigned __int32*)(newImport + 0x2f) = 0x2e6c6c44;
*(unsigned __int32*)(newImport + 0x33) = 0x006c6c64;
FILE* fp = fopen(fileName, "wb");
if (fp == NULL) {
printf("open failed
");
exit(0);
}
fwrite(buf, SizeOfRawData + PointerToRawData, 1, fp);
fclose(fp);
}
RVA转为FOA
unsigned __int32 RVA_to_FOA(char* buf, unsigned __int32 RVA) {
unsigned __int32 e_lfanew = *(unsigned __int32*)(buf + 0x3c);
unsigned __int32 SizeoOfHeaders = *(unsigned __int32*)(buf + 0x3c + (e_lfanew + 0x18));
unsigned __int32 FOA = 0;
//在文件头中不拉伸
if (RVA <= SizeoOfHeaders) {
FOA = RVA;
return FOA;
}
unsigned __int16 NumberOfSections = *(unsigned __int16*)(buf + e_lfanew + 0x4 + 0x2);
__int16 SizeOfOptionalHeader = *(unsigned __int16*)(buf + e_lfanew + 0x4 + 0x10);
char* Sections_addr = buf + e_lfanew + 0x18 + SizeOfOptionalHeader;
//遍历节表判断在那个节中,FOA=RVA-节的RVA+节的FOA
unsigned __int32 VirtualAddress = *(unsigned __int32*)(Sections_addr + 0xc);
unsigned __int32 PointerToRawData = *(unsigned __int32*)(Sections_addr + 0x14);
unsigned __int32 PrevVirtualAddress = *(unsigned __int32*)(Sections_addr + 0xc);
unsigned __int32 PrevPointerToRawData = *(unsigned __int32*)(Sections_addr + 0x14);
Sections_addr = Sections_addr + 0x28;
for (int j = 0; j < NumberOfSections - 1; j++) {
VirtualAddress = *(unsigned __int32*)(Sections_addr + 0xc);
PointerToRawData = *(unsigned __int32*)(Sections_addr + 0x14);
if (PrevVirtualAddress <= RVA && VirtualAddress > RVA) {
FOA = PrevPointerToRawData + (RVA - PrevVirtualAddress);
return FOA;
}
PrevVirtualAddress = VirtualAddress;
PrevPointerToRawData = PointerToRawData;
Sections_addr = Sections_addr + 0x28;
}
FOA = PrevPointerToRawData + (RVA - PrevVirtualAddress);
return FOA;
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
注入后
运行