1.修改导入表,即添加一个新的导入表描述符及其iat,int. 这样系统加载该pe文件时将自动加载添加的dll,从而实现dll注入

2.思路:

可以手工修改,但是复杂程度一点也不比写代码低,而且低效. 通过代码实现可以一劳永逸.

新增一个节来存储新的导入表. 其实也可以在原来的pe文件找空隙插入进去,但是又很难实现通用,因为不同的pe文件空隙位置大小不同.容易出错

新增的导入表描述符通过序号导入,这样更简单一些

基本步骤: 修改节区数量,在原来的节区头中添加一个节,如果空间不够就不行了,但绝大多数pe文件的节区空间都是足够的 ->

填好新节头的字段->申请节内存->复制原来的导入表到新节中->增加新的导入表描述符并填写相应字段->增加该导入表描述符的iat和int->将新增数据

写入到文件尾部-> 修改pe文件中重要字段

以上步骤并不是严格顺序的,因为涉及到rva, 文件偏移,各种大小的计算.

 

3.代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//导入表注入
 
DWORD rva2offset(LPVOID base, DWORD rva)
{
    IMAGE_DOS_HEADER* dosHeader = (IMAGE_DOS_HEADER*)base;
    IMAGE_NT_HEADERS32* ntHeader = (IMAGE_NT_HEADERS32*)(dosHeader->e_lfanew + (DWORD)base);
    IMAGE_SECTION_HEADER* sectionHeader = (IMAGE_SECTION_HEADER*)((DWORD)ntHeader + sizeof(IMAGE_NT_HEADERS32));
    if (rva<ntHeader->OptionalHeader.SizeOfHeaders)
    {
        return rva;
    }
    for (DWORD i = 1; i <= ntHeader->FileHeader.NumberOfSections; i++)
    {
        //如果到了最后一个节时,就可以直接计算了,否则可以通过前后节头的VirtualAddress确定在哪个节中
        if (i == ntHeader->FileHeader.NumberOfSections)
        {
            return rva - sectionHeader->VirtualAddress + sectionHeader->PointerToRawData;
        }
        else if (rva >= sectionHeader->VirtualAddress && rva < (sectionHeader + 1)->VirtualAddress)
        {
            return rva - sectionHeader->VirtualAddress + sectionHeader->PointerToRawData;
        }
        sectionHeader++;
    }
    return 0;
}
//文件或者内存对齐
DWORD PEAlign(DWORD size, DWORD dwAlignTo)
{
    return(((size + dwAlignTo - 1) / dwAlignTo)*dwAlignTo);
}
 
DWORD importTableInject(WCHAR* modulepath,char* dllpath)//dllpath传入dll的名字,而不是路径
{
    //先备份源文件
    WCHAR newFile[MAX_PATH];
 
    wsprintf(newFile, L"%s.bak", modulepath);
    CopyFileW(modulepath, newFile, 0);
    HANDLE hFile = CreateFileW(modulepath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile==0||hFile==INVALID_HANDLE_VALUE)
    {
        return 0;
    }
    DWORD fileSize = GetFileSize(hFile, 0);
    HANDLE hMap = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0 , 0);
    if (hMap<=0)
    {
        CloseHandle(hFile);
        return 0;
    }
    LPVOID imagebase = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
    if (imagebase==0)
    {
        CloseHandle(hFile);
        CloseHandle(hMap);
        return 0;
    }
    PIMAGE_NT_HEADERS32 ntHeader = (PIMAGE_NT_HEADERS32)((DWORD)imagebase + ((PIMAGE_DOS_HEADER)(imagebase))->e_lfanew);
    if ((ntHeader->FileHeader.NumberOfSections+1)*sizeof(IMAGE_SECTION_HEADER)>ntHeader->OptionalHeader.SizeOfHeaders)
    {
        CloseHandle(hFile);
        CloseHandle(hMap);
        return 0;
    }
    //定位到最后一个节区的最外面地址,就是nt头最后的尾部
    PIMAGE_SECTION_HEADER newSection = (PIMAGE_SECTION_HEADER)(ntHeader + 1) + ntHeader->FileHeader.NumberOfSections;
 
    //添加节区头
    memcpy(newSection->Name, "freesec", 8); //节区头名字最多为8个字节,包括结尾的\0
    newSection->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
    newSection->Misc.VirtualSize =  //添加一个dll信息,所以在原来大小的基础上加一个导入表描述符大小加dll名字字符串大小+4个IMAGE_THUNK_DATA32大小
        ntHeader->OptionalHeader.DataDirectory[1].Size + sizeof(IMAGE_IMPORT_DESCRIPTOR) + strlen(dllpath) + 1  + sizeof(IMAGE_THUNK_DATA32) * 4;
    newSection->NumberOfLinenumbers = 0;
    newSection->NumberOfRelocations = 0;
    newSection->PointerToLinenumbers = 0;
    newSection->PointerToRawData = (newSection-1)->PointerToRawData+(newSection-1)->SizeOfRawData;
    newSection->PointerToRelocations = 0;
    newSection->VirtualAddress = (newSection - 1)->VirtualAddress + PEAlign((newSection - 1)->SizeOfRawData, ntHeader->OptionalHeader.SectionAlignment);
    newSection->SizeOfRawData = PEAlign(newSection->Misc.VirtualSize, ntHeader->OptionalHeader.FileAlignment);
    DWORD sectionSize = newSection->SizeOfRawData;
    //添加节表
    SetFilePointer(hFile, 0, 0, FILE_END); //文件指针向文件尾部
    LPVOID content = malloc(newSection->SizeOfRawData);
    memset(content, 0, newSection->SizeOfRawData);
    char* p = (char*)content;
    memcpy(content, (LPVOID)((DWORD)imagebase+rva2offset(imagebase, ntHeader->OptionalHeader.DataDirectory[1].VirtualAddress)), ntHeader->OptionalHeader.DataDirectory[1].Size-sizeof(IMAGE_IMPORT_DESCRIPTOR));
     
    p =(char*)((DWORD)p + ntHeader->OptionalHeader.DataDirectory[1].Size - sizeof(IMAGE_IMPORT_DESCRIPTOR));
    ((PIMAGE_IMPORT_DESCRIPTOR)p)->OriginalFirstThunk = newSection->VirtualAddress + ntHeader->OptionalHeader.DataDirectory[1].Size + sizeof(IMAGE_IMPORT_DESCRIPTOR) + strlen(dllpath) + 1;
    ((PIMAGE_IMPORT_DESCRIPTOR)p)->FirstThunk = ((PIMAGE_IMPORT_DESCRIPTOR)p)->OriginalFirstThunk + sizeof(IMAGE_THUNK_DATA32)*2;
    ((PIMAGE_IMPORT_DESCRIPTOR)p)->ForwarderChain = 0;
    ((PIMAGE_IMPORT_DESCRIPTOR)p)->Name = newSection->VirtualAddress + ntHeader->OptionalHeader.DataDirectory[1].Size + sizeof(IMAGE_IMPORT_DESCRIPTOR);
    ((PIMAGE_IMPORT_DESCRIPTOR)p)->TimeDateStamp = 0;
    p += sizeof(IMAGE_IMPORT_DESCRIPTOR)*2; //越过新增节和空节尾
     
    //导入表描述符中的name字段,注意2者之间rva的关联
    memcpy(p, dllpath, strlen(dllpath)+1);
    p += strlen(dllpath) + 1;
    //添加注入的dll的iat和int.4个元素,前2个是给iat的,后2个给int的均以
    IMAGE_THUNK_DATA32 ixt[4] = { 0 };
    ixt[0].u1.AddressOfData |= 0x80000000; //将最高位置1,表示是以序号导入
    ixt[0].u1.AddressOfData += 1;
    ixt[2].u1.AddressOfData |= 0x80000000; //将最高位置1,表示是以序号导入
    ixt[2].u1.AddressOfData += 1;
    memcpy(p, ixt, 4 * sizeof(IMAGE_THUNK_DATA32));
 
    //修改必要字段
    ntHeader->FileHeader.NumberOfSections++;
    ntHeader->OptionalHeader.SizeOfImage += newSection->SizeOfRawData;
    //绑定导入表改为0,保险一些
    ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;
    ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
    ntHeader->OptionalHeader.DataDirectory[1].VirtualAddress = newSection->VirtualAddress;
    ntHeader->OptionalHeader.DataDirectory[1].Size = newSection->Misc.VirtualSize;
    UnmapViewOfFile(imagebase); //这个操作需要在writefile之前调用
    if (!WriteFile(hFile, content, sectionSize, 0, 0))
    {
         
        CloseHandle(hMap);
        CloseHandle(hFile);
        free(content);
        return 0;
    }
    CloseHandle(hMap);
    CloseHandle(hFile);
    free(content);
    return 1;
}
//end 导入表注入