GreenHandX1h 4.5 代码阅读①
2016-04-06 19:45
141 查看
经过一番挣扎之后我决定开始重新把代码读一遍写一个完美一点的注释
BOOL IsPeFile(LPVOID ImageBase)//这里我们收取一个PE文件 //判断是否是PE文件结构 //首先检验文件头部的第一个字是否是PRIMAGE_DOS_SIGNATURE //然后定位PE头,如果PE头符合IMAGE_NT_SIGNATURE则可以判断是有效的PE文件 //经过检验没有任何问题 { PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNtHeader = NULL;//置空两个属性分别为Dos头跟NT头的变量 if(!ImageBase)//这里如果ImageBase传过来一个不存在的东西那么就直接传递False回去了直接判断不是PE文件了 return FALSE; pDosHeader = (PIMAGE_DOS_HEADER) ImageBase;//我们将ImageBase的Dos_Header提取出来 if(pDosHeader->e_magic != IMAGE_DOS_SIGNATURE) return FALSE ;//判定e_magic是否是"MZ" pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader+pDosHeader->e_lfanew);//这个是固定获得NT头的方式 if(pNtHeader->Signature != IMAGE_NT_SIGNATURE ) return FALSE; return TRUE; } PIMAGE_NT_HEADERS GetNtHeader(LPVOID ImageBase) //获取NT结构指针 //即返回pNtHeader这里就是个固定的程式 { PIMAGE_DOS_HEADER pDosHeader = NULL; PIMAGE_NT_HEADERS pNtHeader = NULL; if(!IsPeFile(ImageBase)) return NULL; pDosHeader = (PIMAGE_DOS_HEADER)ImageBase; pNtHeader = (PIMAGE_NT_HEADERS32)((DWORD)pDosHeader+pDosHeader->e_lfanew); return pNtHeader; } PIMAGE_FILE_HEADER WINAPI GetFileHeader(LPVOID Imagebase) { //为什么有个winapi 这里我们用来调用系统函数所以我们用到了winapi // 获取FileHeader PIMAGE_FILE_HEADER pFileHeader; PIMAGE_NT_HEADERS pNtHeader = NULL; pNtHeader = GetNtHeader(Imagebase); if(!pNtHeader) return NULL; pFileHeader = & pNtHeader->FileHeader; return pFileHeader; } PIMAGE_OPTIONAL_HEADER GetOptionalHeader(LPVOID ImageBase) { //获取PE可选头 PIMAGE_OPTIONAL_HEADER pOptionHeader = NULL; PIMAGE_NT_HEADERS pNtHeader = NULL; pNtHeader = GetNtHeader(ImageBase); //无论获取什么头,方式都是一样的,先获取了dos头,再通过dos头找到NT头,找到NT头就可以得到可选头 if(!pNtHeader) return NULL; pOptionHeader = & pNtHeader->OptionalHeader; return pOptionHeader; } //将RVA地址转换为磁盘文件中的偏移 BOOL RvaToOffset(LPVOID lpMoudle,DWORD Rva) { DWORD FileOffset; WORD nSectionNum; IMAGE_NT_HEADERS *pNTHead; pNTHead=GetNtHeader(lpMoudle); //老规矩我们还是先要求得NT头 nSectionNum=pNTHead->FileHeader.NumberOfSections; //通过NT头找到PE文件头,然后通过文件头找到节的数目NumberOfSections //NumberOfSections:定义PE文件Section的个数。如果对PE文件新增或删除Section的话,一定要记的修改此域。 //取节表结构头(紧接在IMAGE_NT_HEADERS后面就是IMAGE_SECTION_HEADER) IMAGE_SECTION_HEADER *pSectionHead; pSectionHead=(IMAGE_SECTION_HEADER *)((DWORD)pNTHead+sizeof(IMAGE_NT_HEADERS));//我们在这里进行运算的时候用到结构名称的时候他都唯一指代着他这个结构开始的那个绝对地址。是绝对地址而不是偏移量 //循环比较Rva值所对应节表的偏移 for(int i=0; i<nSectionNum; i++) { if((pSectionHead->VirtualAddress<=Rva) && (Rva<(pSectionHead->SizeOfRawData+pSectionHead->VirtualAddress))) { FileOffset=Rva-pSectionHead->VirtualAddress+pSectionHead->PointerToRawData; //获得rva相对应在文件当中的偏移 //切记这里PointerToRawData是该节的节头在磁盘中的相对偏移量 return FileOffset; } pSectionHead++; } return FALSE; } BOOL RvaToVirtualAddress(LPVOID lpMoudle,DWORD Rva) { DWORD offect=RvaToOffset(lpMoudle,Rva); /*if(offect==NULL||offect==FALSE) return FALSE;*/ return (DWORD)lpMoudle+offect; //这里我们通过偏移量加上基址地址就把VA求出来了 } //下面这段程序就是把关键程序代码插进去了 VOID HandleSessionTable(LPVOID file,LPVOID base) { char funcname[16]="MessageBoxA"; char DLLname[16]="user32.dll"; char Caption[16]="Warning"; char Content[16]="This is test"; DWORD LoadLibraryAAddr=0x1f864; DWORD GetProcAddress=0x24c46; //这里就是我们要插入的数据 char codes[]="\x60\xe8\x0\x0\x0\x0\x5f\x83\xef\x6\x8b\x4f\xf8\x8b" //////这里的数据就只插入代码 "\x5f\xfc\x64\x8b\x15\x30\x0\x0\x0\x8b\x52\xc\x8b\x52\x1c\x8b" //////的二进制机器码 "\x12\x8b\x42\x8\x8b\x42\x50\x3\xc8\x50\x8b\xd7\x83\xea\x38\x52" "\xff\xd1\x8b\xc8\x58\x3\xd8\x8b\xd7\x83\xea\x48\x52\x51\xff\xd3" "\x8b\xcf\x83\xe9\x18\x6a\x0\x51\x83\xe9\x10\x51\x6a\x0\xff\xd0\x61" "\xe9\x00\x00\x00\x00"; //以上就是我们要插入的代码 int datalength=16*4+8; int codeslength=sizeof(codes)-1; //不太明白这里为什么-1呢 IMAGE_NT_HEADERS *nthead=GetNtHeader(base); IMAGE_SECTION_HEADER *sessionhead=(IMAGE_SECTION_HEADER*)((DWORD)nthead+sizeof(IMAGE_NT_HEADERS)); if(sessionhead->VirtualAddress== 4000 NULL) return; DWORD sessionnum=nthead->FileHeader.NumberOfSections; //获取节的数量 IMAGE_SECTION_HEADER *p=sessionhead; //这个地方一个超级脱裤子放屁的操作,又定义了一个新的指针☞到了节表上 DWORD sFileSize=GetFileSize(base,NULL); //不知道为啥突然调用了这个GetFileSize!!! for(int i=0;i<sessionnum;i++) { cout<<(char*)p->Name<<" " <<(int)p->SizeOfRawData-(int)p->Misc.VirtualSize<<endl; //先输出p这个节头的名字,然后输出这个节的碎片区域的大小 IMAGE_SECTION_HEADER tmp=sessionhead; //再次脱裤子放屁 memcpy(&tmp,p,sizeof(IMAGE_SECTION_HEADER)); //到底为啥脱裤子放屁还不知道 ,我觉得往下会用到。可能。。应该是备份作用的 if((int)p->SizeOfRawData-(int)p->Misc.VirtualSize>codeslength+datalength&&(p->Characteristics&IMAGE_SCN_MEM_EXECUTE)) //这个条件的意思是说如果这个碎片区域可以放得下数据段跟代码段并且这个节是可写的话那么我们就可以进行以下的操作了 { DWORD datavirtualbase=p->VirtualAddress+p->Misc.VirtualSize; //在内存中这段空隙开始的RVA,也就是给我们data安家的地方 DWORD datafileoffect=p->PointerToRawData+p->Misc.VirtualSize; //这个狗叼的地方竟然名把offset又尼玛写错了让我看的一蒙一蒙的,这两个相加我们得到我们插入的data的rva SetFilePointer(file,datafileoffect,NULL,FILE_BEGIN); WriteFile(file,funcname,16,0,0); WriteFile(file,DLLname,16,0,0); WriteFile(file,Caption,16,0,0); WriteFile(file,Content,16,0,0); WriteFile(file,&LoadLibraryAAddr,4,0,0); WriteFile(file,&GetProcAddress,4,0,0); //看到了么在这个地方这两个东西被写入了磁盘文件当中,不知为什么,到底干啥用的还是个迷 DWORD codevirtualbase=p->VirtualAddress+p->Misc.VirtualSize+datalength; //这个地方是给我们code安家的位置 DWORD cedefileoffset=p->PointerToRawData+p->Misc.VirtualSize+datalength; //安家之后code开始处的rva p->Misc.VirtualSize+=(codeslength+datalength); //那么他的实际占用的空间就变成了原来的VirtualSize加上data还有code长度的和 SetFilePointer(file,cedefileoffset,NULL,FILE_BEGIN); //定位到要插入code的位置即codefileoffset DWORD oldentry=nthead->OptionalHeader.AddressOfEntryPoint; //获取AOP的位置 DWORD JMPOffset=oldentry-(codevirtualbase+codeslength-5)-5; memcpy(codes+codeslength-4,&JMPOffset,sizeof(DWORD)); //上面两行看起来好像是在备份到底要干嘛不太清楚 nthead->OptionalHeader.AddressOfEntryPoint=codevirtualbase; //将程序的新AOP移动到新插入的代码段的开始 DWORD writesize=0; SetFilePointer(file,cedefileoffset,NULL,FILE_BEGIN); if(!WriteFile(file,codes,codeslength,&writesize,0) ) //倒数第二个参数为什么这个s b不写成0 { TCHAR *buffer; ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,NULL,GetLastError(),0,( LPTSTR )&buffer,0,NULL ); MessageBox(0,buffer,L"ok",0); } //这里就是说我们加入了这段弹框的程序 cout<<"success"<<endl; break; } p++; //如果不能break就转到下一个节头位置 } } void main() //接下来就是主程序的 { HANDLE hFile = CreateFile(L"qq.exe", // open pe file GENERIC_READ|GENERIC_WRITE, // open for reading NULL, // share for reading NULL, // no security OPEN_EXISTING, // existing file only FILE_ATTRIBUTE_NORMAL, // normal file NULL); // no attr. template HANDLE hFileMap = CreateFileMapping(hFile,NULL,PAGE_READWRITE,0,0,NULL); if(!hFileMap ) { TCHAR *buffer ; ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,NULL,GetLastError(),0,( LPTSTR )&buffer,0,NULL ); MessageBox(0,buffer,L"ok",0); } LPVOID lpMemory = MapViewOfFile(hFileMap,FILE_MAP_READ|FILE_MAP_WRITE ,NULL,NULL,NULL); if(IsPeFile(lpMemory)) { //AnalyzeNTHEADER(lpMemory); cout<<"yes"<<endl; IMAGE_NT_HEADERS *nthead=GetNtHeader(lpMemory); IMAGE_OPTIONAL_HEADER32 *image=GetOptionalHeader(lpMemory); cout<<"DataDirectory num:"<<image->NumberOfRvaAndSizes<<endl; HandleSessionTable(hFile,lpMemory); } else cout<<"no"<<endl; UnmapViewOfFile(lpMemory); CloseHandle(hFileMap); CloseHandle(hFile); system("pause"); } //最后面的main没有太弄明白不过问题不大。解决起来也很容易,大概就是调试一个PE程序然后对齐进行一番修改
相关文章推荐
- 编程确定系统是32位还是64位
- 我 && yii2 (二)
- php安装mamcache扩展时报错
- Java String 转 long
- matlab与c混合编程
- lamda:和c++差不多
- 0406复利计算程序5.0-结对编程
- Java String 转 long
- 如何在 Github 打造你的爆款开源项目
- Sublime text2 C/C++ 编译环境设置
- POJ 2236-Wireless Network(并查集)
- 【软件工程】复利计算器--结对编程
- Java学习·final
- 作业四:结对编程项目——四则运算
- 面向对象编程学习一点感悟(java)
- Qt多线程编程一:使用QSemaphore和waitCondition管理一系列资源
- java学习第七天
- 20160406javaweb JDBC 实例工具类
- zoj 1073 Round and Round We Go
- scrapy爬取智联招聘中python工程师的招聘信息