PE 文件静态注入 DLL 的精简实现
2007-10-26

简介

通过修改 PE 头,可以静态注入一个自己编写的 DLL。

至于可以做什么用? 从第三方插件(比如更改皮肤)到病毒。谁都不知道我在室友电脑的游戏程序里悄悄注入了怎样的 DLL。 应该能找到第三方库,然而我想要的是一个精简的实现,复制到U盘/软盘都可以足够快。

这段代码仅仅是从我的另一个程序中抠出来作个样例。稍作俢改便可用在其它项目中。

源码

  1 #define _WIN32 1
  2 #undef _WIN64
  3 
  4 
  5 #pragma comment(linker,"/merge:.data=.text")
  6 #pragma comment(linker,"/merge:.rdata=.text")
  7 #pragma comment(linker,"/SECTION:.text,ERW")
  8 
  9 
 10 #include <fcntl.h>
 11 #include <sys/types.h>
 12 #include <sys/stat.h>
 13 #include <io.h>
 14 
 15 #include <stdio.h>
 16 #include <stdlib.h>
 17 #include <windows.h>
 18 #include <winnt.h>
 19 #include <commdlg.h>
 20 /*
 21     DLL静态注入
 22     过程:
 23         1. 新建一个Sector。
 24         2. 把原来的Import Table指向的IMAGE_IMPORT_DESCRIPTOR结构数组复制到新的段中。
 25         3. 在新的Import Table中加入自己的DLL
 26 
 27     命名说明:
 28         网络上常常把“节”,“段”,“块”不分,在此认为分别是Section,Segment,Block的翻译
 29         以 RAW_ 开头的都是在文件的物理偏移
 30 
 31 */
 32 
 33 
 34 long RAW_GetNTHeader(int fin){
 35     DWORD PE_Flag;
 36     IMAGE_DOS_HEADER DosHeader;
 37     if(lseek(fin,0,SEEK_SET)==-1)return -1;
 38     if(read(fin,&DosHeader,sizeof(IMAGE_DOS_HEADER))!=sizeof(IMAGE_DOS_HEADER))return -1;
 39     if(IMAGE_DOS_SIGNATURE!=DosHeader.e_magic)return -1;
 40     if(lseek(fin,DosHeader.e_lfanew,SEEK_SET)==-1)return -1;
 41     if(read(fin,&PE_Flag,sizeof(DWORD))!=4)return -1;
 42     if(PE_Flag!=0x00004550)return -1;/*winnt.h中找不到定义的值,自己写*/
 43     return DosHeader.e_lfanew;
 44 }
 45 
 46 long RAW_AttachSection(int fin,PIMAGE_SECTION_HEADER pNewSectionHeader){
 47     /*
 48         如果pNewSectionHeader->VirtualAddress==0则由函数指定
 49         函数填充并返回pNewSectionHeader->PointToRawData
 50 
 51         Warn:
 52             函数可能会改变 *(pNewSectionHeader) 的
 53             pNewSectionHeader->PointToRawData , SizeOfRawData 和 VirtualAddress 域
 54     */
 55     #define WCRALIGN(GEGEBEN,NACH) \
 56         ((((DWORD)(GEGEBEN)%(DWORD)(NACH))?((DWORD)(GEGEBEN)/(DWORD)(NACH)+1):((DWORD)(GEGEBEN)/(DWORD)(NACH))) *(DWORD)(NACH))
 57     int i;
 58     //DEBUG!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 59     char localBuffer[0x200]; //和无意义的填充文件.
 60     IMAGE_NT_HEADERS NT_Header;
 61     IMAGE_SECTION_HEADER localSectionHeader;
 62     unsigned short NumberOfSections;
 63     DWORD ImageBase,SectionAlignment,FileAlignment,SizeOfOptionalHeader;
 64     long RAW_NTHeader,RAW_FirstSectionHeader,RAW_NewSectionHeader,RAW_NewSection;
 65     RAW_NTHeader=RAW_GetNTHeader(fin);
 66     if(RAW_NTHeader==-1)return -1;
 67     //读取PE头
 68     lseek(fin,RAW_NTHeader,SEEK_SET);
 69     if(read(fin,&NT_Header,sizeof(NT_Header))!=sizeof(NT_Header))return -1;
 70 
 71     //克隆本地数据
 72     NumberOfSections=NT_Header.FileHeader.NumberOfSections;
 73     ImageBase=NT_Header.OptionalHeader.ImageBase;
 74     SectionAlignment=NT_Header.OptionalHeader.SectionAlignment;
 75     FileAlignment=NT_Header.OptionalHeader.FileAlignment;
 76     SizeOfOptionalHeader=NT_Header.FileHeader.SizeOfOptionalHeader;
 77     //参照linux的内核链表结构结合winnt.h的做法,找到第一个节头的物理位置:
 78     RAW_FirstSectionHeader=RAW_NTHeader+(long)(&((PIMAGE_NT_HEADERS)NULL)->OptionalHeader)+SizeOfOptionalHeader;
 79     lseek(fin,RAW_FirstSectionHeader,SEEK_SET);
 80     //先读入第一个节头,看看在第一个节前是否有足够的空间
 81     //自己的程序中不用缓冲会导致速度变慢,但权衡程序的可扩展性后还是这么做了
 82     if(read(fin,&localSectionHeader,sizeof(IMAGE_SECTION_HEADER))!=(int)sizeof(IMAGE_SECTION_HEADER))return -1;
 83     //开始依次扫描区段,初始化:
 84     RAW_NewSectionHeader=RAW_FirstSectionHeader+sizeof(IMAGE_SECTION_HEADER)*(NumberOfSections);
 85     //offset=(PIMAGE_SECTION_HEADER)Buf;
 86     //连一个多余的节头的空间都没有,会导致插入失败
 87     if(localSectionHeader.PointerToRawData-RAW_FirstSectionHeader<sizeof(IMAGE_SECTION_HEADER)*(NumberOfSections+1))return -1;
 88     //定位到最后一个节头:
 89     lseek(fin,RAW_FirstSectionHeader+(NumberOfSections-1)*sizeof(IMAGE_SECTION_HEADER),SEEK_SET);
 90     read(fin,&localSectionHeader,sizeof(IMAGE_SECTION_HEADER));
 91     //offset=offset+NumberOfSections-1;
 92     if(localSectionHeader.VirtualAddress+WCRALIGN( localSectionHeader.Misc.VirtualSize,SectionAlignment)<pNewSectionHeader->VirtualAddress){
 93         pNewSectionHeader->VirtualAddress=WCRALIGN(pNewSectionHeader->VirtualAddress,SectionAlignment);
 94     }else if(pNewSectionHeader->VirtualAddress==0){
 95         pNewSectionHeader->VirtualAddress=localSectionHeader.VirtualAddress+WCRALIGN( localSectionHeader.Misc.VirtualSize,SectionAlignment);
 96     }else{
 97         return -1;
 98     }
 99     pNewSectionHeader->SizeOfRawData=WCRALIGN(pNewSectionHeader->SizeOfRawData,FileAlignment);
100     if(pNewSectionHeader->Misc.VirtualSize<pNewSectionHeader->SizeOfRawData)pNewSectionHeader->Misc.VirtualSize=WCRALIGN(pNewSectionHeader->SizeOfRawData,SectionAlignment);
101     //写入文件.........................
102 
103     //在附加新的SectionHeader之前先定下新节的物理地址
104     RAW_NewSection=filelength(fin);
105     RAW_NewSection=WCRALIGN(RAW_NewSection,FileAlignment);
106     lseek(fin,RAW_NewSection,SEEK_SET);
107     for(i=0;(unsigned)i<(pNewSectionHeader->SizeOfRawData)/0x200;(unsigned)i++)
108         if(write(fin,localBuffer,0x200)==-1)return -1;
109     //附加上一个新的SectionHeader
110     pNewSectionHeader->PointerToRawData=RAW_NewSection;
111     lseek(fin,RAW_NewSectionHeader,SEEK_SET);
112     if(write(fin,pNewSectionHeader,sizeof(IMAGE_SECTION_HEADER))==-1)return -1;
113     //添加SectionHeader个数
114     lseek(fin,RAW_NTHeader,SEEK_SET);
115     NT_Header.FileHeader.NumberOfSections++;
116     NT_Header.OptionalHeader.SizeOfImage+=pNewSectionHeader->Misc.VirtualSize;
117     if(write(fin,&NT_Header,sizeof(NT_Header))==-1)return -1;
118     return RAW_NewSection;
119     #undef WCRALIGN
120 }
121 /*inline*/ long RVA_To_RAW(int fin,DWORD RVA){ //自己写的纠错能力不强的地址转换方法
122     #define MAX_NUMBER_OF_SECTIONS 50
123     IMAGE_SECTION_HEADER localSectionHeaders[MAX_NUMBER_OF_SECTIONS];
124     IMAGE_NT_HEADERS NT_Header;
125     unsigned short NumberOfSections;
126     DWORD RAW_FirstSectionHeader;
127     int i;
128 
129     long RAW_NTHeader;
130     RAW_NTHeader= RAW_GetNTHeader(fin);
131     if(RAW_NTHeader==-1)return -1;
132     lseek(fin,RAW_NTHeader,SEEK_SET);
133     if(read(fin,&NT_Header,sizeof(NT_Header))!=sizeof(NT_Header))return -1;
134     NumberOfSections=NT_Header.FileHeader.NumberOfSections;
135     //参照linux的内核链表结构结合winnt.h的做法,找到第一个节头的物理位置:
136     RAW_FirstSectionHeader=RAW_NTHeader+(long)(&((PIMAGE_NT_HEADERS)NULL)->OptionalHeader)+NT_Header.FileHeader.SizeOfOptionalHeader;
137     lseek(fin,RAW_FirstSectionHeader,SEEK_SET);
138     //先读入第一个节头,看看在第一个节前是否有足够的空间
139     //自己的程序中不用缓冲会导致速度变慢,但权衡程序的可扩展性后还是这么做了
140     if(read(fin,localSectionHeaders,sizeof(IMAGE_SECTION_HEADER)*NumberOfSections)!=(int)sizeof(IMAGE_SECTION_HEADER)*NumberOfSections)return -1;
141     if(localSectionHeaders[0].VirtualAddress>RVA)return -1;
142     for(i=0;i<NumberOfSections;i++){
143         if(i==0)continue;//防止原来合并得只剩一个节
144         if(localSectionHeaders[i].VirtualAddress>RVA)break;
145     }
146     i--;
147     //当文件中对应的位置不存在时
148     if(localSectionHeaders[i].VirtualAddress+localSectionHeaders[0].SizeOfRawData<RVA)return -1;
149     //返回从文件开头的偏移
150     return localSectionHeaders[i].PointerToRawData+(RVA-localSectionHeaders[i].VirtualAddress);
151 }
152 
153 
154 
155 
156 
157 long CloneImportTable_Attach(int fin,char *szDllName){
158     #define MAX_NUMBER_OF_IMPORT_DESCRIPTORS 50
159     long offset,length;
160     long RAW_NewSection;
161     IMAGE_SECTION_HEADER NewSectionHeader;
162     IMAGE_NT_HEADERS NTHeader;
163     IMAGE_THUNK_DATA ThunkData[4];
164     DWORD RAW_OldImportTable;
165     long RAW_NTHeader;
166     long NumberOfImportTable;
167     IMAGE_IMPORT_DESCRIPTOR Buffer_ImportTables[MAX_NUMBER_OF_IMPORT_DESCRIPTORS];
168     //插入新节
169     NewSectionHeader.Name[0]=(char)'K';NewSectionHeader.Name[1]=(char)'u';NewSectionHeader.Name[2]=(char)'r';
170     NewSectionHeader.Name[3]=(char)'t';NewSectionHeader.Name[4]=(char)'\0';
171     NewSectionHeader.Characteristics=0xE0000060;
172     NewSectionHeader.Misc.VirtualSize=100; NewSectionHeader.NumberOfLinenumbers=0; NewSectionHeader.PointerToLinenumbers=0;
173     //暂时不支持重定位
174     NewSectionHeader.NumberOfRelocations=0; NewSectionHeader.PointerToRelocations;
175     NewSectionHeader.SizeOfRawData=200;
176     NewSectionHeader.VirtualAddress=0;
177     RAW_NewSection=RAW_AttachSection(fin,&NewSectionHeader);
178     if(RAW_NewSection==-1)return -1;
179     //clone输入表.............
180     RAW_NTHeader=RAW_GetNTHeader(fin);
181     lseek(fin,RAW_NTHeader,SEEK_SET);
182     read(fin,&NTHeader,sizeof(NTHeader));
183     //第二项是 "Import Table"
184     if(NTHeader.OptionalHeader.DataDirectory[1].VirtualAddress==0)return -1;
185     RAW_OldImportTable=RVA_To_RAW(fin,NTHeader.OptionalHeader.DataDirectory[1].VirtualAddress);
186     lseek(fin,RAW_OldImportTable,SEEK_SET);
187     //被PE说明忽悠了,windows根本不在乎这个参数:
188     //NumberOfImportTable=NTHeader.OptionalHeader.DataDirectory[1].Size/sizeof(IMAGE_IMPORT_DESCRIPTOR);
189     if(read(fin,Buffer_ImportTables,MAX_NUMBER_OF_IMPORT_DESCRIPTORS*sizeof(IMAGE_IMPORT_DESCRIPTOR))==-1)return -1;
190     NumberOfImportTable=0;
191     while(1){
192         if(Buffer_ImportTables[NumberOfImportTable].FirstThunk==0
193             &&Buffer_ImportTables[NumberOfImportTable].Name==0
194             &&Buffer_ImportTables[NumberOfImportTable].OriginalFirstThunk==0)break;
195         NumberOfImportTable++;
196     }
197     //加入新的动态连接库
198     //DLL文件名
199     offset=NewSectionHeader.VirtualAddress;
200     lseek(fin,RVA_To_RAW(fin,offset),SEEK_SET);
201     length=strlen(szDllName)+1;//length是二进制数据的长度,而不是字符串长度
202     write(fin,szDllName,length);
203     Buffer_ImportTables[NumberOfImportTable].Name=offset;
204     offset+=length;
205     //OriginalFirstThunk:
206     length=3;
207     write(fin,"\000\000\000",length);
208     ThunkData[0].u1.Ordinal=0x80000001;
209     ThunkData[1].u1.AddressOfData=0;
210     ThunkData[2].u1.Ordinal=0x80000001;
211     ThunkData[3].u1.AddressOfData=0;
212     /*ThunkData[0].u1.AddressOfData=(PIMAGE_IMPORT_BY_NAME)offset;
213     ThunkData[1].u1.AddressOfData=0;
214     ThunkData[2].u1.AddressOfData=(PIMAGE_IMPORT_BY_NAME)offset;
215     ThunkData[3].u1.AddressOfData=0;*/
216     offset+=length;
217     write(fin,ThunkData,sizeof(ThunkData));
218     Buffer_ImportTables[NumberOfImportTable].FirstThunk=offset+0*sizeof(IMAGE_THUNK_DATA);
219     Buffer_ImportTables[NumberOfImportTable].OriginalFirstThunk=offset+2*sizeof(IMAGE_THUNK_DATA);
220     offset+=sizeof(ThunkData);
221     //不重要的项
222     Buffer_ImportTables[NumberOfImportTable].TimeDateStamp=0;
223     Buffer_ImportTables[NumberOfImportTable].ForwarderChain=0;
224     NumberOfImportTable++;
225     //最后一项一定要为0
226     Buffer_ImportTables[NumberOfImportTable].TimeDateStamp=0;
227     Buffer_ImportTables[NumberOfImportTable].FirstThunk=0;
228     Buffer_ImportTables[NumberOfImportTable].ForwarderChain=0;
229     Buffer_ImportTables[NumberOfImportTable].Name=0;
230     Buffer_ImportTables[NumberOfImportTable].OriginalFirstThunk=0;
231     NumberOfImportTable++;
232 
233     lseek(fin,RVA_To_RAW(fin,offset),SEEK_SET);
234     write(fin,Buffer_ImportTables,NumberOfImportTable*sizeof(IMAGE_IMPORT_DESCRIPTOR));
235     lseek(fin,RAW_NTHeader,SEEK_SET);
236     NTHeader.OptionalHeader.DataDirectory[1].VirtualAddress=offset;
237     NTHeader.OptionalHeader.DataDirectory[1].Size+=sizeof(IMAGE_IMPORT_DESCRIPTOR);
238     write(fin,&NTHeader,sizeof(NTHeader));
239     return 1;
240 }
241 int __stdcall WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLinene,int nShowCmd){
242     int fin;
243     char filename[MAX_PATH];
244     OPENFILENAME fn={0};
245     fn.lStructSize = sizeof( OPENFILENAME) ;
246     fn.hwndOwner = HWND_DESKTOP;
247     fn.lpstrFilter = "程序文件\0*.exe\0" ;
248     fn.nFilterIndex = 0 ;
249     fn.nMaxFile = MAX_PATH ;
250     fn.lpstrTitle = "请选择目标应用程序:" ;
251     fn.lpstrFile = filename;
252     fn.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_EXPLORER ;
253 
254     filename[0]=0;
255     if(!GetOpenFileName(&fn))return -1;
256     fin=open(filename,O_RDWR|O_BINARY);
257     if(fin==NULL)return 1;
258 
259 
260     if(CloneImportTable_Attach(fin,"Winnt.dll")==-1){
261         close(fin);
262         return -1;
263     }
264     close(fin);
265     return 0;
266 }