原理

分析PE结构,找到第一个节,假设它是代码节(后面的实验都是基于这个前提),获取其内存偏移 VirtualAddress,计算它最后一条指令的偏移的下一个字节,作为代码插入点。插入调用MessageBoxA的硬编码,和jmp到原入口点的代码。跳转地址计算公式是:X = 要跳转的地址 - 下一条指令的地址。要注意,公式中涉及的“地址”指的都是运行时的地址,即通过 [ImageBase + 偏移] 得到。
修改程序入口点,使运行时先执行我插入的代码,然后跳转到原来的入口点。

代码使用vc6开发,如果要迁移到其他环境如vs,则需要注意字符串参数的类型,要做相应的调整。另外,只适用于32位程序。

向代码节添加代码的函数

// 向代码节添加MessageBox代码
// 向代码节添加代码不需要担心内存对齐后大小发生变化
// 默认第一个节是代码节,但是这样判断不一定准确,应该遍历节表,根据属性找代码节
BOOL AddCodeToCodeSec(LPCSTR lpszFile, LPCSTR lpszOutFile)
{BYTE shellcode[] ={0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, // push 0 push 0 push 0 push 00xE8, 0x00, 0x00, 0x00, 0x00,                    // call MessageBoxA0xE9, 0x00, 0x00, 0x00, 0x00                 // jmp OEP};DWORD dwShellCodeSize = 18;DWORD dwCodeRva = 0; // 插入的位置RVALPVOID pFileBuffer = NULL;LPVOID pImageBuffer = NULL;LPVOID pNewBuffer = NULL;DWORD dwFileBufferSize = 0;DWORD dwImageBufferSize = 0;DWORD dwNewBufferSize = 0;// 读取PE到内存中if ((dwFileBufferSize = ReadPEFile(lpszFile, &pFileBuffer)) == 0){printf("读取失败\n");return FALSE;}
// 拉伸成内存映像
dwImageBufferSize = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);
if (0 == dwImageBufferSize)
{free(pFileBuffer);return FALSE;
}
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionHeader = \
(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);DWORD dwCodeSecIndex = -1;
// 遍历节表,找到代码节
for (int i = 0; i < pPEHeader->NumberOfSections; i++)
{if ((pSectionHeader[i].Characteristics & 0x60000020) == 0x60000020){dwCodeSecIndex = i;break;}
}
if (dwCodeSecIndex == -1)
{printf("找不到代码节\n");free(pFileBuffer);free(pImageBuffer);return FALSE;
}// 计算插入点RVA
dwCodeRva = pSectionHeader[dwCodeSecIndex].VirtualAddress + pSectionHeader[dwCodeSecIndex].Misc.VirtualSize;
// 是否有足够的空间插入代码,要考虑到代码节是最后一个节的情况
if (dwCodeSecIndex + 1 == pPEHeader->NumberOfSections)
{if (dwCodeRva + dwShellCodeSize > pOptionHeader->SizeOfImage){printf("代码节没有足够的空间插入代码\n");free(pFileBuffer);free(pImageBuffer);return FALSE;}
}
else
{DWORD dwUnuseSize = pSectionHeader[dwCodeSecIndex + 1].VirtualAddress - \pSectionHeader[dwCodeSecIndex].VirtualAddress - pSectionHeader[dwCodeSecIndex].Misc.VirtualSize;if (dwUnuseSize < dwShellCodeSize){printf("代码节没有足够的空间插入代码\n");free(pFileBuffer);free(pImageBuffer);return FALSE;}
}// 代码插入点偏移 = VA + VSIZE
dwCodeRva = pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize;
// 代码插入
memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva), shellcode, dwShellCodeSize);
// 修正地址
DWORD MsgBoxAddr = (DWORD)&MessageBoxA; // 获取MessageBox的地址
DWORD hardCodeAddr = MsgBoxAddr - (pOptionHeader->ImageBase + dwCodeRva + 13);
memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva + 9), &hardCodeAddr, 4);
hardCodeAddr = pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint \
- (pOptionHeader->ImageBase + dwCodeRva + 18);
memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva + 14), &hardCodeAddr, 4);
// 修改入口点
pOptionHeader->AddressOfEntryPoint = dwCodeRva;
// 转成文件对齐
dwNewBufferSize = CopyImageBufferToFileBuffer(pImageBuffer, &pNewBuffer);
if (dwNewBufferSize != dwFileBufferSize)
{printf("可能丢失数据\n");
}
MemoryToFile(pNewBuffer, dwNewBufferSize, lpszOutFile);
free(pFileBuffer);
free(pImageBuffer);
free(pNewBuffer);
printf("插入代码成功\n");
return TRUE;
}

完整代码

#include "headers.h"// 读取PE文件到内存中,返回读取的字节数;读取失败返回0
DWORD ReadPEFile(LPCSTR lpszFile, LPVOID *pFileBuffer)
{FILE *pFile = NULL;DWORD dwFileSize = 0;pFile = fopen(lpszFile, "rb");if (pFile == NULL) {printf("打开文件失败\n");return 0;}fseek(pFile, 0, SEEK_END);dwFileSize = ftell(pFile);fseek(pFile, 0, SEEK_SET);*pFileBuffer = malloc(dwFileSize);if (*pFileBuffer == NULL){printf("分配内存失败\n");fclose(pFile);return 0;}    DWORD dwRead = fread(*pFileBuffer, 1, dwFileSize, pFile);fclose(pFile);if (dwRead != dwFileSize){printf("文件大小 = %d\t实际写入内存字节 = %d\t写入失败\n", dwFileSize, dwRead);return 0;}if (!IsPEFile(*pFileBuffer, dwRead)){printf("不是有效的PE文件\n");return 0;}return dwRead;
}// 验证是否PE文件
BOOL IsPEFile(LPVOID pFileBuffer, DWORD dwSize)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);if (*((PWORD)pDosHeader) != IMAGE_DOS_SIGNATURE){printf("不是有效的MZ标志\n");return FALSE;}if (pNTHeader->Signature != IMAGE_NT_SIGNATURE){printf("不是有效的PE标记\n");return FALSE;}return TRUE;
}// 打印PE头信息
VOID PrintNTHeaders(LPCSTR lpszFile)
{LPVOID pFileBuffer = NULL;DWORD dwFileSize = ReadPEFile(lpszFile, &pFileBuffer);PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);if (dwFileSize == 0){printf("读取文件失败\n");return;}//打印DOS头puts("----DOS HEADER----");printf("e_magic = %x\n", pDosHeader->e_magic);printf("e_lfanew = %x\n", pDosHeader->e_lfanew); //打印NT头 printf("----标准PE头----\n");printf("Machine = %x\n", pPEHeader->Machine);printf("NumberOfSections = %x\n", pPEHeader->NumberOfSections);printf("TimeDateStamp = %x\n", pPEHeader->TimeDateStamp);printf("PointerToSymbolTable = %x\n", pPEHeader->PointerToSymbolTable);printf("NumberOfSymbols = %x\n", pPEHeader->NumberOfSymbols);printf("SizeOfOptionalHeader = %x\n", pPEHeader->SizeOfOptionalHeader);printf("Characteristics = %x\n", pPEHeader->Characteristics);//可选PE头  printf("----可选PE头----\n");printf("Magic = %x\n", pOptionHeader->Magic);printf("MajorLinkerVersion = %x\n", pOptionHeader->MajorLinkerVersion);printf("MinorLinkerVersion = %x\n", pOptionHeader->MinorLinkerVersion);printf("SizeOfCode = %x\n", pOptionHeader->SizeOfCode);printf("SizeOfInitializedData = %x\n", pOptionHeader->SizeOfInitializedData);printf("SizeOfUninitializedData = %x\n", pOptionHeader->SizeOfUninitializedData);printf("AddressOfEntryPoint = %x\n", pOptionHeader->AddressOfEntryPoint);printf("BaseOfCode = %x\n", pOptionHeader->BaseOfCode);printf("BaseOfData = %x\n", pOptionHeader->BaseOfData);printf("ImageBase = %x\n", pOptionHeader->ImageBase);printf("SectionAlignment = %x\n", pOptionHeader->SectionAlignment);printf("FileAlignment = %x\n", pOptionHeader->FileAlignment);printf("MajorOperatingSystemVersion = %x\n", pOptionHeader->MajorOperatingSystemVersion);printf("MinorOperatingSystemVersion = %x\n", pOptionHeader->MinorOperatingSystemVersion);printf("MajorImageVersion = %x\n", pOptionHeader->MajorImageVersion);printf("MinorImageVersion = %x\n", pOptionHeader->MinorImageVersion);printf("MajorSubsystemVersion = %x\n", pOptionHeader->MajorSubsystemVersion);printf("MinorSubsystemVersion = %x\n", pOptionHeader->MinorSubsystemVersion);printf("Win32VersionValue = %x\n", pOptionHeader->Win32VersionValue);printf("SizeOfImage = %x\n", pOptionHeader->SizeOfImage);printf("SizeOfHeaders = %x\n", pOptionHeader->SizeOfHeaders);printf("CheckSum = %x\n", pOptionHeader->CheckSum);printf("Subsystem = %x\n", pOptionHeader->Subsystem);printf("DllCharacteristics = %x\n", pOptionHeader->DllCharacteristics);printf("SizeOfStackReserve = %x\n", pOptionHeader->SizeOfStackReserve);printf("SizeOfStackCommit = %x\n", pOptionHeader->SizeOfStackCommit);printf("SizeOfHeapReserve = %x\n", pOptionHeader->SizeOfHeapReserve);printf("SizeOfHeapCommit = %x\n", pOptionHeader->SizeOfHeapCommit);printf("LoaderFlags = %x\n", pOptionHeader->LoaderFlags);printf("NumberOfRvaAndSizes = %x\n", pOptionHeader->NumberOfRvaAndSizes);//打印节表    char sectionName[9];for (int i = 0; i < pPEHeader->NumberOfSections; i++){memset(sectionName, 0, 9);memcpy(sectionName, pSectionHeader->Name, 8);printf("----节表: [%s]----\n", sectionName);printf("VirtualSize = %x\n", pSectionHeader->Misc.VirtualSize);printf("VirtualAddress = %x\n", pSectionHeader->VirtualAddress);printf("SizeOfRawData = %x\n", pSectionHeader->SizeOfRawData);printf("PointerToRawData = %x\n", pSectionHeader->PointerToRawData);printf("Characteristics = %x\n", pSectionHeader->Characteristics);pSectionHeader++;}//释放内存free(pFileBuffer);
}// 将 FileBuffer 拉伸成 ImageBuffer 并写入到新的缓冲区
// 返回 ImageBuffer 的大小;失败返回0
DWORD CopyFileBufferToImageBuffer(LPVOID pFileBuffer, LPVOID *pImageBuffer)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);*pImageBuffer = malloc(pOptionHeader->SizeOfImage);if (*pImageBuffer == NULL){printf("分配内存失败\n");return 0;}memset(*pImageBuffer, 0, pOptionHeader->SizeOfImage);// 复制DOS头+PE头+可选PE头+节表+文件对齐memcpy(*pImageBuffer, pFileBuffer, pOptionHeader->SizeOfHeaders);// 遍历节表,复制所有节    for (int i = 0; i < pPEHeader->NumberOfSections; i++){memcpy((LPVOID)((DWORD)(*pImageBuffer) + pSectionHeader[i].VirtualAddress), \(LPVOID)((DWORD)pFileBuffer + pSectionHeader[i].PointerToRawData), \pSectionHeader[i].SizeOfRawData);}return pOptionHeader->SizeOfImage;
}// 将 ImageBuffer 变成文件对齐的 FileBuffer 写入新的缓冲区
// 返回复制的大小,失败返回0
DWORD CopyImageBufferToFileBuffer(LPVOID pImageBuffer, LPVOID *pNewBuffer)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);// 最后一个节表PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + pPEHeader->NumberOfSections - 1;// 计算要复制的字节// 这一步有BUG,当最后一个节后面还有数据时(多见于控制台程序),丢失数据DWORD dwFileBufferSize = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;*pNewBuffer = malloc(dwFileBufferSize);   if (*pNewBuffer == NULL){printf("分配内存失败\n");return 0;}memset(*pNewBuffer, 0, dwFileBufferSize);// 复制DOS头+PE头+可选PE头+节表+文件对齐memcpy(*pNewBuffer, pImageBuffer, pOptionHeader->SizeOfHeaders);// 遍历节表,复制文件对齐后的节   for (int i = 0; i < pPEHeader->NumberOfSections; i++){memcpy((LPVOID)((DWORD)(*pNewBuffer) + pSectionHeader[i].PointerToRawData), \(LPVOID)((DWORD)pImageBuffer + pSectionHeader[i].VirtualAddress), \pSectionHeader[i].SizeOfRawData);}return dwFileBufferSize;
}// 内存数据写入文件
BOOL MemoryToFile(LPVOID pMemBuffer, DWORD dwSize, LPCSTR lpszFile)
{FILE *fp = NULL;fp = fopen(lpszFile, "wb+");if (fp == NULL){printf("打开文件失败\n");return FALSE;}DWORD dwWritten = fwrite(pMemBuffer, 1, dwSize, fp);if (dwWritten != dwSize){printf("写入了 %d 字节,不等于 %d\n", dwWritten, dwSize);fclose(fp);return FALSE;}fclose(fp);return TRUE;
}// 向代码节添加MessageBox代码
// 向代码节添加代码不需要担心内存对齐后大小发生变化
// 默认第一个节是代码节,但是这样判断不一定准确,应该遍历节表,根据属性找代码节
BOOL AddCodeToCodeSec(LPCSTR lpszFile, LPCSTR lpszOutFile)
{BYTE shellcode[] ={0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, // push 0 push 0 push 0 push 00xE8, 0x00, 0x00, 0x00, 0x00,                    // call MessageBoxA0xE9, 0x00, 0x00, 0x00, 0x00                 // jmp OEP};DWORD dwShellCodeSize = 18;DWORD dwCodeRva = 0; // 插入的位置RVALPVOID pFileBuffer = NULL;LPVOID pImageBuffer = NULL;LPVOID pNewBuffer = NULL;DWORD dwFileBufferSize = 0;DWORD dwImageBufferSize = 0;DWORD dwNewBufferSize = 0;// 读取PE到内存中if ((dwFileBufferSize = ReadPEFile(lpszFile, &pFileBuffer)) == 0){printf("读取失败\n");return FALSE;}
// 拉伸成内存映像
dwImageBufferSize = CopyFileBufferToImageBuffer(pFileBuffer, &pImageBuffer);
if (0 == dwImageBufferSize)
{free(pFileBuffer);return FALSE;
}
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
PIMAGE_SECTION_HEADER pSectionHeader = \
(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);DWORD dwCodeSecIndex = -1;
// 遍历节表,找到代码节
for (int i = 0; i < pPEHeader->NumberOfSections; i++)
{if ((pSectionHeader[i].Characteristics & 0x60000020) == 0x60000020){dwCodeSecIndex = i;break;}
}
if (dwCodeSecIndex == -1)
{printf("找不到代码节\n");free(pFileBuffer);free(pImageBuffer);return FALSE;
}// 计算插入点RVA
dwCodeRva = pSectionHeader[dwCodeSecIndex].VirtualAddress + pSectionHeader[dwCodeSecIndex].Misc.VirtualSize;
// 是否有足够的空间插入代码,要考虑到代码节是最后一个节的情况
if (dwCodeSecIndex + 1 == pPEHeader->NumberOfSections)
{if (dwCodeRva + dwShellCodeSize > pOptionHeader->SizeOfImage){printf("代码节没有足够的空间插入代码\n");free(pFileBuffer);free(pImageBuffer);return FALSE;}
}
else
{DWORD dwUnuseSize = pSectionHeader[dwCodeSecIndex + 1].VirtualAddress - \pSectionHeader[dwCodeSecIndex].VirtualAddress - pSectionHeader[dwCodeSecIndex].Misc.VirtualSize;if (dwUnuseSize < dwShellCodeSize){printf("代码节没有足够的空间插入代码\n");free(pFileBuffer);free(pImageBuffer);return FALSE;}
}// 代码插入点偏移 = VA + VSIZE
dwCodeRva = pSectionHeader->VirtualAddress + pSectionHeader->Misc.VirtualSize;
// 代码插入
memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva), shellcode, dwShellCodeSize);
// 修正地址
DWORD MsgBoxAddr = (DWORD)&MessageBoxA; // 获取MessageBox的地址
DWORD hardCodeAddr = MsgBoxAddr - (pOptionHeader->ImageBase + dwCodeRva + 13);
memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva + 9), &hardCodeAddr, 4);
hardCodeAddr = pOptionHeader->ImageBase + pOptionHeader->AddressOfEntryPoint \
- (pOptionHeader->ImageBase + dwCodeRva + 18);
memcpy((LPVOID)((DWORD)pImageBuffer + dwCodeRva + 14), &hardCodeAddr, 4);
// 修改入口点
pOptionHeader->AddressOfEntryPoint = dwCodeRva;
// 转成文件对齐
dwNewBufferSize = CopyImageBufferToFileBuffer(pImageBuffer, &pNewBuffer);
if (dwNewBufferSize != dwFileBufferSize)
{printf("可能丢失数据\n");
}
MemoryToFile(pNewBuffer, dwNewBufferSize, lpszOutFile);
free(pFileBuffer);
free(pImageBuffer);
free(pNewBuffer);
printf("插入代码成功\n");
return TRUE;
}// 计算对齐的函数,如偏移为900,对齐为1000h,返回1000h
DWORD Align(DWORD dwOffset, DWORD dwAlign)
{// 如果偏移小于对齐,向上取整if (dwOffset <= dwAlign) return dwAlign;// 如果偏移大于对齐且不能除尽,向上取整if (dwOffset % dwAlign){return (dwOffset / dwAlign + 1) * dwAlign;}// 如果能除尽,直接返回offsetreturn dwOffset;
}// RVA 转 FOA
DWORD RvaToFoa(LPVOID pFileBuffer, DWORD dwRva)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pFileBuffer + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);// RVA在文件头中或者文件对齐==内存对齐时,RVA==FOA  错!第一句是对的,第二句是错的if (dwRva < pOptionHeader->SizeOfHeaders){return dwRva;}// 遍历节表,确定偏移属于哪一个节    for (int i = 0; i < pPEHeader->NumberOfSections; i++){if (dwRva >= pSectionHeader[i].VirtualAddress && \dwRva < pSectionHeader[i].VirtualAddress + pSectionHeader[i].Misc.VirtualSize){int offset = dwRva - pSectionHeader[i].VirtualAddress;return pSectionHeader[i].PointerToRawData + offset;}}printf("找不到RVA %x 对应的 FOA,转换失败\n", dwRva);return 0;
}// 移动NT头和节表到DOS STUB,该函数在新增节时节表空间不足的情况下调用;返回地址减小值
DWORD MoveNTHeaderAndSectionHeadersToDosStub(LPVOID pFileBuffer)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);LPVOID pDst = (LPVOID)((DWORD)pDosHeader + sizeof(IMAGE_DOS_HEADER)); // NT头插入点DWORD dwRet = (DWORD)pNTHeader - (DWORD)pDst; // 返回地址减小的值DWORD dwSize = 4 + sizeof(IMAGE_FILE_HEADER) + pPEHeader->SizeOfOptionalHeader +\sizeof(IMAGE_SECTION_HEADER) * pPEHeader->NumberOfSections; // 移动的字节数LPVOID pSrc = malloc(dwSize);if (pSrc == NULL){printf("分配内存失败\n");return 0;}memcpy(pSrc, (LPVOID)pNTHeader, dwSize); // 保存要复制的数据memset((LPVOID)pNTHeader, 0, dwSize); // 清空原数据memcpy(pDst, pSrc, dwSize); // 移动数据free(pSrc);pDosHeader->e_lfanew = sizeof(IMAGE_DOS_HEADER); // 更新 e_lfanewreturn dwRet;
}// 新增一个大小为 newSectionSize 的代码节
// dwFileBufferSize 是原来的文件大小
// 返回新缓冲区的大小,失败返回0
DWORD AddSection(LPVOID pFileBuffer, LPVOID *pNewFileBuffer, DWORD dwFileBufferSize, DWORD dwNewSectionSize)
{// 复制一份 pFileBuffer,不要修改原来的数据LPVOID pFileBuffer2 = malloc(dwFileBufferSize);memcpy(pFileBuffer2, pFileBuffer, dwFileBufferSize);pFileBuffer = pFileBuffer2;PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PWORD pNumberOfSections = &(pPEHeader->NumberOfSections); // 节的数量PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + *pNumberOfSections - 1; // 最后一个节表PIMAGE_SECTION_HEADER pNewSectionHeader = pSectionHeader + *pNumberOfSections; // 新节表插入点DWORD newFileBufferSize = 0; // 新文件的大小// 判断最后一个节表后面是否有空闲的80字节if (80 > (DWORD)pFileBuffer + pOptionHeader->SizeOfHeaders - (DWORD)pNewSectionHeader){printf("没有足够的80字节插入新节表\n");free(pFileBuffer2);return 0;}// 判断空闲的80字节是否全为0,如果不是,则把整个NT头往上挪覆盖dos stub以空出空间插入节表for (int i = 0; i < 80; i++){if (((PBYTE)pNewSectionHeader)[i] != 0){            DWORD dwRet = MoveNTHeaderAndSectionHeadersToDosStub(pFileBuffer);printf("节表空间不足,NT头和节表向低地址移动了 %d 字节\n", dwRet);if (dwRet < 80){printf("移动后仍没有足够的80字节空间插入新节表\n");free(pFileBuffer2);return 0;}// 更新指针pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);pNumberOfSections = &(pPEHeader->NumberOfSections); // 节的数量pLastSectionHeader = pSectionHeader + *pNumberOfSections - 1; // 最后一个节表pNewSectionHeader = pSectionHeader + *pNumberOfSections; // 新节表插入点break;}}// 定义一个 IMAGE_SECTION_HEADER 结构,计算里面的属性IMAGE_SECTION_HEADER newSectionHeader;memcpy(newSectionHeader.Name, ".newsec", 8);newSectionHeader.Misc.VirtualSize = Align(dwNewSectionSize, pOptionHeader->SectionAlignment);newSectionHeader.VirtualAddress = pLastSectionHeader->VirtualAddress + \Align(pLastSectionHeader->Misc.VirtualSize, pOptionHeader->SectionAlignment);    newSectionHeader.SizeOfRawData = Align(dwNewSectionSize, pOptionHeader->FileAlignment);newSectionHeader.PointerToRawData = pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData;newSectionHeader.PointerToRelocations = 0;newSectionHeader.PointerToLinenumbers = 0;newSectionHeader.NumberOfRelocations = 0;newSectionHeader.NumberOfLinenumbers = 0;newSectionHeader.Characteristics = 0x60000020;// pNewFileBuffer 分配内存,把 pFileBuffer 复制过去,后面的修改都在 pNewFileBuffer 进行*pNewFileBuffer = malloc(dwFileBufferSize + newSectionHeader.SizeOfRawData);memcpy(*pNewFileBuffer, pFileBuffer, dwFileBufferSize);memset((LPVOID)((DWORD)*pNewFileBuffer + dwFileBufferSize), 0, newSectionHeader.SizeOfRawData); // 新增节数据清0// 更新指针,指向新内存  pDosHeader = (PIMAGE_DOS_HEADER)*pNewFileBuffer;pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);pNumberOfSections = &(pPEHeader->NumberOfSections);pLastSectionHeader = pSectionHeader + *pNumberOfSections - 1;pNewSectionHeader = pSectionHeader + *pNumberOfSections;// 节的数量+1,SizeOfImage是内存中拉伸后的大小*pNumberOfSections += 1; pOptionHeader->SizeOfImage += Align(newSectionHeader.Misc.VirtualSize, pOptionHeader->SectionAlignment);// 拷贝 newSectionHeadermemcpy(pNewSectionHeader, &newSectionHeader, sizeof(newSectionHeader));printf("插入成功\n");free(pFileBuffer2);return dwFileBufferSize + newSectionHeader.SizeOfRawData;
}// 扩大最后一个节
// 返回新文件的大小,失败返回0
DWORD ExpandLastSection(LPVOID pFileBuffer, LPVOID *pNewFileBuffer, DWORD dwOldSize, DWORD dwExpandSize)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);DWORD dwVirtualSizeExpand = Align(dwExpandSize, pOptionHeader->SectionAlignment);DWORD dwRawDataExpand = Align(dwExpandSize, pOptionHeader->FileAlignment);*pNewFileBuffer = malloc(dwOldSize + dwRawDataExpand);memcpy(*pNewFileBuffer, pFileBuffer, dwOldSize);memset((LPVOID)((DWORD)(*pNewFileBuffer) + dwOldSize), 0, dwRawDataExpand);pDosHeader = (PIMAGE_DOS_HEADER)*pNewFileBuffer;pNTHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + (DWORD)*pNewFileBuffer);pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pNTHeader + 4);pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pNTHeader + 0x18);pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);// 修改新内存的属性pSectionHeader[pPEHeader->NumberOfSections - 1].Misc.VirtualSize += dwVirtualSizeExpand;pSectionHeader[pPEHeader->NumberOfSections - 1].SizeOfRawData += dwRawDataExpand;pOptionHeader->SizeOfImage += dwVirtualSizeExpand;return dwOldSize + dwRawDataExpand;
}// 合并所有节
BOOL MergeSection(LPVOID pImageBuffer, LPVOID *pNewImageBuffer, DWORD dwImageSize)
{*pNewImageBuffer = malloc(dwImageSize);if (*pNewImageBuffer == NULL){printf("分配内存失败\n");return FALSE;}memcpy(*pNewImageBuffer, pImageBuffer, dwImageSize);PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)*pNewImageBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_SECTION_HEADER pLastSectionHeader = pSectionHeader + pPEHeader->NumberOfSections - 1;// 修改第一个节的范围以覆盖其他所有节pSectionHeader->SizeOfRawData = pSectionHeader->Misc.VirtualSize = \pOptionHeader->SizeOfImage - pSectionHeader->VirtualAddress;pSectionHeader->SizeOfRawData = Align(pSectionHeader->SizeOfRawData, pOptionHeader->FileAlignment);// 属性包含所有节的属性for (int i = 1; i < pPEHeader->NumberOfSections; i++){pSectionHeader[0].Characteristics |= pSectionHeader[i].Characteristics;}// 清空其他节表的数据,这步是为了合并节后新增节方便memset(pSectionHeader + 1, 0, sizeof(IMAGE_SECTION_HEADER) * (pPEHeader->NumberOfSections - 1));// 节的数量 = 1pPEHeader->NumberOfSections = 1;return TRUE;
}// 打印数据目录
DWORD TestPrintDataDirectory(LPCSTR lpszFile)
{LPVOID pFileBuffer = NULL;ReadPEFile(lpszFile, &pFileBuffer);PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);LPCSTR data[16] ={"导出表","导入表","资源表","异常信息表","安全证书表","重定位表","调试信息表","版权表","全局指针表","TLS表","加载配置表","绑定导入表","IAT表","延迟导入表","COM信息表","保留"};for (int i = 0; i < 16; i++){puts(data[i]);printf("VirtualAddress = %x\n", (pOptionHeader->DataDirectory)[i].VirtualAddress);printf("Size = %x\n", (pOptionHeader->DataDirectory)[i].Size);puts("-------------------------");}free(pFileBuffer);return 0;
}// 打印导出表
VOID PrintExportTable(LPVOID pFileBuffer)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_EXPORT_DIRECTORY pExportDirectory = \(PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));printf("Base = %x\n", pExportDirectory->Base);printf("NumberOfFunctions = %x\n", pExportDirectory->NumberOfFunctions);printf("NumberOfNames = %x\n", pExportDirectory->NumberOfNames);printf("----AddressOfFunctions----\n");PDWORD AddressOfFunctions = (PDWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfFunctions));for (int i = 0; i < (int)pExportDirectory->NumberOfFunctions; i++){printf("AddressOfFunctions[%d] = %x\n", i, AddressOfFunctions[i]);// 测试:调用导出序号为12的Plus函数,VC6可以这样调用,VS不行,要VirtualAlloc一块可执行内存//if (i + pExportDirectory->Base == 12)//{// int (*Plus)(int x, int y) = (int (*)(int x, int y))((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, AddressOfFunctions[i]));// printf("2+3=%d\n", Plus(2, 3));//}}printf("----AddressOfNames & AddressOfNameOridinals----\n");PDWORD AddressOfNames = (PDWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfNames));PWORD AddressOfNameOridinals = (PWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfNameOrdinals));for (int i = 0; i < (int)pExportDirectory->NumberOfNames; i++){printf("AddressOfNames[%d] = %s, AddressOfOrdinals[%d] = %d\n",i, (char *)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, AddressOfNames[i])), i, AddressOfNameOridinals[i]);}
}// 根据函数名返回函数入口点FOA
DWORD GetFunctionAddrByName(LPVOID pFileBuffer, LPCSTR lpszFunc)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_EXPORT_DIRECTORY pExportDirectory = \(PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));PDWORD AddressOfFunctions = (PDWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfFunctions));PDWORD AddressOfNames = (PDWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfNames));PWORD AddressOfNameOridinals = (PWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfNameOrdinals));for (int i = 0; i < (int)pExportDirectory->NumberOfNames; i++){if (!strcmp((char *)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, AddressOfNames[i])), lpszFunc)){return AddressOfFunctions[AddressOfNameOridinals[i]];}}return NULL;
}// 根据导出序号返回函数入口点FOA
DWORD GetFunctionAddrByOrdinals(LPVOID pFileBuffer, DWORD dwOrdinal)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_EXPORT_DIRECTORY pExportDirectory = \(PIMAGE_EXPORT_DIRECTORY)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pOptionHeader->DataDirectory[0].VirtualAddress));PDWORD AddressOfFunctions = (PDWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfFunctions));PDWORD AddressOfNames = (PDWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfNames));PWORD AddressOfNameOridinals = (PWORD)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pExportDirectory->AddressOfNameOrdinals));return AddressOfFunctions[dwOrdinal - pExportDirectory->Base];
}// 打印重定位表
VOID PrintRelocationTable(LPVOID pFileBuffer)
{PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));PIMAGE_SECTION_HEADER pSectionHeader = \(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);PIMAGE_BASE_RELOCATION pBaseRelocation = \(PIMAGE_BASE_RELOCATION)((DWORD)pFileBuffer + RvaToFoa(pFileBuffer, pOptionHeader->DataDirectory[5].VirtualAddress));while (pBaseRelocation->VirtualAddress || pBaseRelocation->SizeOfBlock){puts("-------------------------------------");printf("VirtualAddress = %08x\n", pBaseRelocation->VirtualAddress);printf("SizeOfBlock = %08x\n", pBaseRelocation->SizeOfBlock);PWORD pwAddr = (PWORD)((DWORD)pBaseRelocation + 8);int n = (pBaseRelocation->SizeOfBlock - 8) / 2;printf("要修改的地址个数 = %d\n", n);for (int i = 0; i < n; i++){WORD wProp = (0xF000 & pwAddr[i]) >> 12;WORD wAddr = 0x0FFF & pwAddr[i];printf("[%d]:RVA = %08x\t属性 = %d\n", i + 1, pBaseRelocation->VirtualAddress + wAddr, wProp);}pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pBaseRelocation + pBaseRelocation->SizeOfBlock);}
}

向代码节添加代码编程实现相关推荐

  1. 代码节空白区添加代码

    文章目录 一. 找个可以写二进制的区域 二.寻找本机MessageBoxA(最终要跳到的地址) 三.计算 四.修改入口地址 一. 找个可以写二进制的区域 二.寻找本机MessageBoxA(最终要跳到 ...

  2. Python代码列主元消去法matlab编程_当面试官要求现场手敲代码,如何体现你对Python的编程能力?...

    如果你已经通过了招聘人员的电话面试,那么下面正是该展现你代码能力的时候了.无论是练习,作业,还是现场白板面试,这都是你证明自己的代码技巧的时刻.我们知道面试官常常会出一些题让你来解决,作为一名程序员, ...

  3. 《Adobe Flash CS5 ActionScript 3.0中文版经典教程》——1.3 使用代码片断添加ActionScript...

    本节书摘来自异步社区<Adobe Flash CS5 ActionScript 3.0中文版经典教程>一书中的第1章,第1.3节,作者: [美]Adobe公司 更多章节内容可以访问云栖社区 ...

  4. (20/24) webpack实战技巧:watch实现热打包和添加代码备注

    (20/24) webpack实战技巧:watch实现热打包和添加代码备注 在前面的学习中,我们一直使用webpack-dev-server充当(本地)服务器和完成打包任务,但是当出项目团队联合开发, ...

  5. 向PE文件中空白处添加代码

    // mem.cpp : 定义控制台应用程序的入口点. //PE文件从文件加载到内存,再从内存读取,然后存盘到文件#include "stdafx.h" #include < ...

  6. 简单的计算机程序代码,优秀程序员通过简单代码,窥探电脑编程中强大的数组操作功能...

    优秀程序员通过简单代码,窥探电脑编程中强大的数组操作功能.编程语言中,数组是一个非常重要的概念,也是一种很常用的类型.本文中通过javascript语言的代码实例,展现编程中数组的魅力.在javasc ...

  7. 从代码到 Docker、Kubernetes、Istio、Knative……,或许是时候重新思考从代码到云的编程了...

    作者 | Lakmal Warusawithana 译者 |弯月 责编 | 徐威龙 封图| CSDN 下载于视觉中国 早些时候,开发人员只需编写程序.构建,然后运行.如今,开发人员还需要考虑各种运行方 ...

  8. android 按钮顶级效果_人人都可写代码-Android零基础编程-相对布局11

    欢迎来到人人都可写代码,大家好,我是杨晓华,今天我们的课程内容是:Android界面布局中的相对布局. 1.RelativeLayout的概述 (1) RelativeLayout是一个允许子视图相对 ...

  9. datagridview 绑定list 不能刷新界面_人人都可写代码-H5零基础编程-首页界面实操06...

    欢迎来到人人都可写代码,大家好,我是杨晓华,今天我们的课程内容是,项目实操,开发一个首页界面的H5. 这是要实现的H5首页界面展示效果,下面就是教大家如何制作的步骤: 1.在views的项目smart ...

最新文章

  1. 做好数据可视化的技巧和原则!
  2. 机构押注ABS云平台 融资成本下降1%~3%
  3. 【算法】一个简单的ISODATA原理
  4. python mysqldb安装_解决centos7 安装MySQLdb-python 报错 方案
  5. iOS之深入解析Runtime的objc_msgSend“快速查找”底层原理
  6. websocket binary 数据解析_WebSocket实现原理相关知识点
  7. 九城最多多少组服务器,历经4年之久 九城WOW服务器价值几何
  8. java无限循环可变参数,Java可变参数、加强for循环
  9. CentOS 6.3(x86_32)下安装Oracle 10g R2
  10. myEclipse的subversion插件Subclipse
  11. qqxml图片代码_QQXML代码大全(持续更新)
  12. 【Pr】视频剪辑学习记录——导出
  13. dw怎么做html鼠标变化,dw鼠标经过单元格变色 DW 鼠标经过表格 背景变色
  14. Java—mysql缓存导致查询结果与数据库不一致
  15. 微信公众号html怎么做的,微信公众平台页面模板怎么用?分类目录页面是如何制作的?...
  16. 熬夜肝出 3w 字测试开发学习路线
  17. python面向对象编程的思想0727
  18. 【C语言你真的学会了吗】C语言深度剖析(1)【关键字深度剖析】
  19. 自动化构建:gitlab gitlab-run ,maven的缓存 和 gitea drone drone-run
  20. css中left属性,CSS left属性用法及代码示例

热门文章

  1. python打包工具报错_Python打包发布神器—Pyinstaller
  2. mysql严格模式 报错_代码一上传服务器就报错-尝试开启 MySQL 严格模式
  3. ML之FE:特征工程中常用的一些处理手段(缺失值填充、异常值检测等)及其对应的底层代码的实现
  4. Java:Java的jar包之POI的简介、安装、使用方法(基于POI将Word、Excel、PPT转换为html)之详细攻略
  5. Algorithm:【Algorithm算法进阶之路】之数据结构基础知识
  6. DL之DNN优化技术:利用Dropout(简介、使用、应用)优化方法提高DNN模型的性能
  7. HiveSQL中复杂数据类型操作
  8. 设置redis能远程访问
  9. 猴子吃桃问题 python
  10. CSS基础必备盒模型及清除浮动