您的位置:首页 > 编程语言

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程序然后对齐进行一番修改
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: