淺談程式碼段加密原理(防止靜態分析)
在軟體安全裡,有一種保護手段叫加密,一般情況下都是為程式碼段加密,使原本的程式碼無法被靜態分析,只能動態除錯。
涉及到的知識有:PE檔案結構,程式碼重定位,shellcode。
程式碼加密時可用各種演算法組合起來使用,只要保證解密時用逆推的方法還原成原始碼即可,下面例子當中用的是最簡單的異或加密
由於博主也是第一次接觸這個程式碼段加密,有寫的不對或者不夠的地方,還麻煩大佬指正。感謝!!
思路:
1.獲取程式碼段內容,進行加密後再覆蓋回去
2.把重定位去掉,目的使重定位發生的時機在解密之後,否則解密的資料是已經被重定位過的
3.新增一個節,在這個節中編寫解密步驟,並且要自己實現程式碼節的重定位,理由同2。並將OEP改寫為當前節的解密位置。
主要步驟:
步驟:
1.通過讀取內容,找到程式碼段位置(如果沒被修改過,用oep所在的節作為程式碼節來判斷比較科學,而不是單單靠.text),儲存程式碼段的偏移和長度,後續解密需要,然後進行異或加密。
2.通過可選PE頭的資料目錄成員,找到儲存重定位表的資料 (直接宣告一個IMAGE_SECTION_HEADER來存放),再分別申請2個記憶體來存放屬於程式碼節的重定位資料和非程式碼節的資料,並記錄其長度。
3.刪除重定位相關的資料,刪除節表,檔案內容,各頭裡面的相關資料
4.獲取到當前節表的末尾,建立一個新的節,以當前檔案的對映大小(SizeOfImage)為節記憶體起點,以當前檔案大小為節檔案起點。節屬性為可讀可學可執行已被初始化((0x20000000 | 0x40000000 | 0x80000000 | 0x40) = E0000040)
5.根據2儲存的程式碼節的重定位資料長度+4(在該節找到一個4位元組的位置(可緊接著重定位資料末尾)作為標記位,存放4個0x00進去,以便後面shellcode用來獲取目標基址與當前基址的差值,以便重定位時使用))擴充記憶體,並把增加了標誌位的重定位資料賦值進去。
6.把原oep儲存下來,並把新增節的RVA+程式碼段的重定位資料長度+4(標誌位)作為新的OEP。
7.編寫shellcode:先通過E8,00,00,00,00方法來獲取當前新增節所在的地址,再通過節地址獲得當前程序的基址(1.當前指令地址-指令偏移;2.根據重定位後的資料),然後通過偏移找到程式碼段進行解密(注意是記憶體偏移而不是檔案偏移),解密後,獲取程式碼節的重定位資料(該節開始資料就是重定位資料,長度為需要重定位的程式碼節資料長度+4),進行重定位,重定位完成後跳轉到原OEP進行檔案正常執行流程(此處要自己進行程式碼段的重定位是因為程式碼段的資料已經自己加密過了,如果不自己實現解密後再重定位,那麼解密出來的資料是加密後且重定位的資料,無法正常執行)
8.完善新增節後的檔案資料(檔案大小、檔案對映大小、檔案程式碼大小(SizeOfCode,這個節將作為解密使用,所以也是程式碼類)、節表數量等),並將原先存放檔案內容的空間進行擴容realloc,擴容大小為原大小+這個節的檔案大小。(擴容後可能返回一個新的地址,原地址被收回,所以要使用各種頭的資料的時候,要重新獲取)
注意新增節的長度為程式碼段重定位資料長度+4 +5(e8 00 00 00 00)+shellcode長度,且注意記憶體對齊和檔案對齊。
9.完善完新節後,用同樣的方法,修復原重定位節,並增加5.中的標誌位的位置到重定位表資料中(重定位塊為4位元組塊RVA+4位元組塊長度+2位元組塊中偏移(0x3XXX開頭有效)組成,但重定位塊為4位元組對齊,所以最小的重定位塊為12位元組),根據2.中所求得的非程式碼段的重定位的資料長度,再+12+8(8位元組0,用來結尾)的長度進行非程式碼節的重定位資料的擴容,並根據上一個節的大小,完善該節的相關資料。記得檔案大小和記憶體大小的對齊。並更新可選PE頭當中的相關資料。
10.將8.的非程式碼段的重定位資料,拼接到檔案內容的末尾。拼接前記得先對檔案內容大小進行擴容。
附上程式碼:
1 #include <Windows.h> 2 #include <iostream> 3 #include <stdio.h> 4 #include <tlhelp32.h> 5 #include <io.h> 6 #include <string.h> 7 #include "Asmjit\\asmjit.h" 8 9 using namespace std; 10 using namespace asmjit; 11 using namespace asmjit::x86; 12 13 char *g_pFileSrc = NULL; 14 int g_iTextVirtualAddr = 0; //程式碼節在記憶體中的地址 15 int g_iTextVirtualLen = 0; //程式碼節在記憶體中的長度 16 17 18 int main() 19 { 20 char szFileName[] = { "C:\\Users\\Admin\\Desktop\\彈窗.exe" }; 21 HANDLE hFile = CreateFileA(szFileName, 22 GENERIC_READ | GENERIC_WRITE, 23 FILE_SHARE_READ | FILE_SHARE_WRITE, 24 NULL, 25 OPEN_EXISTING, //獲取檔案內容 26 FILE_ATTRIBUTE_ARCHIVE, 27 NULL); 28 29 if (hFile == INVALID_HANDLE_VALUE ) 30 { 31 printf("檔案開啟失敗\n"); 32 return 1; 33 } 34 35 //獲取檔案大小 36 DWORD dwFileSize = GetFileSize(hFile, NULL); 37 38 //申請空間將exe讀取到記憶體中 39 g_pFileSrc = new char[dwFileSize]; 40 if (NULL == g_pFileSrc) 41 { 42 printf("空間申請失敗\n"); 43 return false; 44 } 45 ReadFile(hFile, g_pFileSrc, dwFileSize, NULL, NULL); 46 CloseHandle(hFile); 47 48 PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)g_pFileSrc; 49 PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew); 50 DWORD dwSectionCount = pNtHead->FileHeader.NumberOfSections;//節表數量 51 //通過可選PE頭的地址+可選PE頭的長度,得到節表位置 52 PIMAGE_SECTION_HEADER pSection = (PIMAGE_SECTION_HEADER)((DWORD)(&pNtHead->OptionalHeader) + pNtHead->FileHeader.SizeOfOptionalHeader); 53 54 55 56 ///////////////////////////////////// 57 /////////////加密程式碼節////////////// 58 ///////////////////////////////////// 59 for (int i = 0; i < dwSectionCount; i++) 60 { 61 //通過OEP的地址所在的節確定程式碼節位置 62 if (pNtHead->OptionalHeader.AddressOfEntryPoint >= pSection->VirtualAddress 63 && pNtHead->OptionalHeader.AddressOfEntryPoint < (pSection->VirtualAddress + pSection->Misc.VirtualSize)) 64 //if (!strcmp((char*)pSection->Name,".text")) //找到程式碼節 65 { 66 g_iTextVirtualAddr = pSection->VirtualAddress; //後續解密需要 67 g_iTextVirtualLen = pSection->Misc.VirtualSize; 68 cout << pSection->Name; 69 for (int iCount = 0; iCount < pSection->Misc.VirtualSize; iCount++) 70 { 71 *(char*)(g_pFileSrc + pSection->PointerToRawData + iCount) ^= 0x35; 72 } 73 pSection->Characteristics |= 0x80000000; //讓程式碼節可寫,以便後續解密 74 break; 75 } 76 pSection++; 77 } 78 79 80 81 ///////////////////////////////////// 82 ///////////儲存重定位表資訊////////// 83 ///////////////////////////////////// 84 DWORD dwRelocRVA = pNtHead->OptionalHeader.DataDirectory[5].VirtualAddress; //得到重定位表的RVA 85 pNtHead->OptionalHeader.DataDirectory[5].VirtualAddress = 0; 86 DWORD dwRelocVirtualSize = 0;//重定位表的長度 87 pNtHead->OptionalHeader.DataDirectory[5].Size = 0; 88 DWORD dwRelocFileAddr = 0;//重定位表的檔案位置 89 DWORD dwRelocFileLen = 0;//重定位表的檔案長度 90 char szRelocName[8] = { 0 }; //重定位表名字(可能被改名混淆,做記錄) 91 DWORD dwRelocTab = 0; //重定位節屬性 92 93 94 int iTextRelocSize = 0; //程式碼節塊的大小 95 int iNotTextRelocSize = 0;//非程式碼節塊的大小 96 char *szNotTextRelocSrc = NULL;//重定位表非程式碼段資料 97 char *szTextRelocSrc = NULL; //重定位表程式碼段資料 98 99 DWORD dwReloctOffset = 0; //當前遍歷的偏移 100 101 102 103 104 ///////////////////////////////////// 105 ////////拷貝並刪除重定位表資料/////// 106 ///////////////////////////////////// 107 pSection = (PIMAGE_SECTION_HEADER)((DWORD)(&pNtHead->OptionalHeader) + pNtHead->FileHeader.SizeOfOptionalHeader);//重新找到表頭 108 for (int i = 0; i < dwSectionCount; i++) 109 { 110 if (pSection->VirtualAddress == dwRelocRVA) //找到重定位節表,保留重定位表相關資料到記憶體中,隨後刪除重定位表 111 { 112 //儲存重定位表資料 113 dwRelocVirtualSize = pSection->Misc.VirtualSize; 114 dwRelocFileAddr = pSection->PointerToRawData; 115 dwRelocFileLen = pSection->SizeOfRawData; 116 dwRelocTab = pSection->Characteristics; 117 strcpy(szRelocName, (char *)pSection->Name); 118 119 PIMAGE_BASE_RELOCATION pBaseRelocation = (PIMAGE_BASE_RELOCATION)(g_pFileSrc + dwRelocFileAddr);//重定位表當前塊 120 while ((pBaseRelocation->VirtualAddress != 0) && (pBaseRelocation->SizeOfBlock != 0)) //遍歷到重定位表末尾為止 121 { 122 //如果當前重定位範圍在程式碼段內 則記錄起來 123 if (pBaseRelocation->VirtualAddress >= g_iTextVirtualAddr & pBaseRelocation->VirtualAddress < g_iTextVirtualAddr + g_iTextVirtualLen) 124 { 125 while (pBaseRelocation->VirtualAddress < g_iTextVirtualAddr + g_iTextVirtualLen) //由於程式碼段是連續的,一直遍歷到非程式碼段 126 { 127 iTextRelocSize += pBaseRelocation->SizeOfBlock; //記錄長度 128 pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pBaseRelocation + pBaseRelocation->SizeOfBlock); 129 } 130 szTextRelocSrc = (char *)realloc(szTextRelocSrc, iTextRelocSize); 131 //從末尾-重定位長度=重定位資料 再+遍歷的偏移 得到需要複製的資料的地址 複製長度為所以程式碼節範圍的重定位資料長度 132 memcpy(szTextRelocSrc, g_pFileSrc + dwFileSize - dwRelocFileLen + dwReloctOffset, iTextRelocSize); 133 dwReloctOffset += iTextRelocSize;//複製完之後 更新偏移(由於這個這段資料連續的 全部複製完再更新) 134 } 135 else 136 { 137 szNotTextRelocSrc = (char*)realloc(szNotTextRelocSrc, iNotTextRelocSize + pBaseRelocation->SizeOfBlock); //將原本的存放重定位的空間擴容 138 /*非程式碼塊的複製源起點=重定位表位置(起始位置+末尾-重定位長度)+偏移 139 目標位置為剛申請出來的空間的空閒起點(起點+已賦值的長度)*/ 140 memcpy(szNotTextRelocSrc + iNotTextRelocSize , 141 g_pFileSrc + dwFileSize - dwRelocFileLen + dwReloctOffset, 142 pBaseRelocation->SizeOfBlock); 143 dwReloctOffset += pBaseRelocation->SizeOfBlock;//此時檔案偏移增加 144 iNotTextRelocSize += pBaseRelocation->SizeOfBlock;//記錄非程式碼段的長度 145 pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pBaseRelocation + pBaseRelocation->SizeOfBlock); 146 } 147 148 } 149 /*此時,原重定位的資料已經分成了非程式碼段(szNotTextRelocSrc)和程式碼段(szTextRelocSrc)兩部分 150 還原時,還原非程式碼段的資料即可*/ 151 152 //檔案內容+檔案長度 = 取到檔案末尾 再-當前重定位表的長度,取到重定位表的起始位置 153 memset(g_pFileSrc + dwFileSize - dwRelocFileLen, 0, dwRelocFileLen);//把重定位表資料用0覆蓋 154 memset(pSection, 0, sizeof(IMAGE_SECTION_HEADER)); 155 pNtHead->FileHeader.NumberOfSections--; 156 //調整檔案記憶體大小 157 int iAlignmentSize = dwRelocVirtualSize % pNtHead->OptionalHeader.SectionAlignment; 158 if (!iAlignmentSize) 159 { 160 pNtHead->OptionalHeader.SizeOfImage -= dwRelocVirtualSize; //調整檔案記憶體大小 161 } 162 else 163 { 164 pNtHead->OptionalHeader.SizeOfImage = 165 pNtHead->OptionalHeader.SizeOfImage 166 - (dwRelocVirtualSize - iAlignmentSize + pNtHead->OptionalHeader.SectionAlignment); 167 } 168 dwFileSize -= dwRelocFileLen; 169 break; 170 } 171 pSection++; 172 } 173 174 175 176 //找到最後一個節表,得到新增節的檔案起點和對映到記憶體中的起點 177 pSection = (PIMAGE_SECTION_HEADER)((DWORD)(&pNtHead->OptionalHeader) + pNtHead->FileHeader.SizeOfOptionalHeader); 178 pSection += (pNtHead->FileHeader.NumberOfSections - 1); 179 //檔案起點 //檔案長度原本就已經對齊 無需再對齊 180 int iNewSectionFileAddr = pSection->PointerToRawData + pSection->SizeOfRawData; 181 182 //檔案對映起點 183 int iNewSectionFileMapAddr = 0; 184 int iMemoryAlignment = pSection->Misc.VirtualSize % pNtHead->OptionalHeader.SectionAlignment; 185 if (!iMemoryAlignment) 186 { 187 iNewSectionFileMapAddr = pSection->VirtualAddress + pSection->Misc.VirtualSize; 188 } 189 else 190 { 191 iNewSectionFileMapAddr = pSection->VirtualAddress 192 + pSection->Misc.VirtualSize 193 + (pNtHead->OptionalHeader.SectionAlignment - iMemoryAlignment); 194 } 195 196 197 ///////////////////////////////////// 198 ////////////開始新增節/////////////// 199 ///////////////////////////////////// 200 pSection++; 201 /*記錄當前屬性 方便除錯*/ 202 memset(pSection, 0, sizeof(IMAGE_SECTION_HEADER)); //初始化當前節屬性 203 memcpy(pSection->Name, ".kd", 3); //節名字 204 pSection->VirtualAddress = iNewSectionFileMapAddr; //節的記憶體起始地址 205 pSection->PointerToRawData = iNewSectionFileAddr; //節的檔案起始地址 206 207 pSection->Characteristics = (0x20000000 | 0x40000000 | 0x80000000 | 0x40); //節屬性 可讀可寫可執行已被初始化 208 pNtHead->FileHeader.NumberOfSections += 1; //節表數量+1 209 210 211 //作為標誌位 後面獲取記憶體基址使用 212 int iTabOffset = iTextRelocSize; //標誌位偏移 213 int iTabRva = pSection->VirtualAddress;//標誌位所在的RVA起點 214 int iTextAlignment = iTextRelocSize + 4 ; //站位後的程式碼節重定位資料長度 215 216 szTextRelocSrc = (char*)realloc(szTextRelocSrc,iTextAlignment); 217 memset(szTextRelocSrc + iTextRelocSize, 0, 4);//重定位標記 218 //g_pFileSrc = (char*)realloc(g_pFileSrc, dwFileSize + iTextAlignment); 219 //memcpy(g_pFileSrc + dwFileSize , szTextRelocSrc, iTextAlignment);//新增程式碼節的重定位資料 220 //dwFileSize += iTextAlignment; 221 222 int iOldOEP = pNtHead->OptionalHeader.AddressOfEntryPoint;//原來的oep 223 //將OEP改為新增節偏移 (原始的記憶體長度(對映末尾)-當前對映基址) 224 pNtHead->OptionalHeader.AddressOfEntryPoint = pSection->VirtualAddress + iTextAlignment; 225 int iNewOEP = pNtHead->OptionalHeader.AddressOfEntryPoint; 226 ///////////////////////////////////// 227 ////////////編寫shellcode/////////// 228 ///////////////////////////////////// 229 JitRuntime _x86RunTimeObject; 230 X86Assembler a(&_x86RunTimeObject); //重定位 231 232 233 // 動態編譯過程 234 // 也就是組織shellcode的過程 235 //得到實際載入基址:1.利用重定位 (還可以直接利用shellcode自定位獲得) 236 int ioldIB = pNtHead->OptionalHeader.ImageBase;//目標基址 237 extern int g_iTextVirtualAddr;//解密需要 238 extern int g_iTextVirtualLen; 239 240 //獲取程序基址 241 char szGetOEP[] = { 0xE8,0x00,0x00,0x00,0x00 }; //同理jit.call(0x00000000); 242 a.pop(eax); 243 a.sub(eax, 5);//此時得到oep所在的地址 oep距離基址隔著 244 a.mov(ebx, eax);//備份一個OEP所在程序地址 245 a.sub(eax, iNewOEP);//OEP所在的基址-偏移 得到基址 246 a.mov(ebp, eax); //儲存程序基址到ebp 247 248 a.sub(ebx, iTextAlignment - iTabOffset);//得到標誌塊的位置 249 a.mov(edx, dword_ptr(ebx));//得到重定位後該地址的值 該值為目標基址與實際基址的差值(由於以前是0,重定位後的值=0-目標+實際) 250 251 252 //解密程式碼段 253 Label begin = a.newLabel(); 254 Label over = a.newLabel(); 255 //此時ebp為實際載入基址 256 a.mov(esi, ebp); 257 a.add(esi, g_iTextVirtualAddr);//esi為程式碼段的起點 258 a.mov(edi, 0);//edi為迴圈偏移 259 a.bind(begin); 260 a.cmp(edi, g_iTextVirtualLen); 261 a.jnl(over); 262 a.mov(ecx, esi); 263 a.add(ecx, edi); //開始解密 264 a.movsx(al, byte_ptr(ecx)); 265 a.xor_(al, 0x35); 266 a.mov(byte_ptr(ecx), al); 267 a.inc(edi); 268 a.jmp(begin); 269 a.bind(over);//解密完畢 270 271 272 //程式碼節重定位 273 /*基址為程式碼節的重定位資料、iTextRelocSize程式碼節的長度、ebp程式基址、edi遍歷偏移*/ 274 Label TextBegin = a.newLabel(); //程式碼節遍歷起點 275 Label TextOver = a.newLabel(); //程式碼節遍歷終點 276 Label LumpBegin =a.newLabel(); //程式碼節的塊遍歷起點 277 Label LumpOver = a.newLabel(); //程式碼節的塊的遍歷終點 278 279 /*重定位資料:基址+iTextbAddr、資料長度:iTextRelocSize 280 edi程式基址、esi重定位資料基址(基址+iTextbAddr)*/ 281 int iTextOffset = pSection->VirtualAddress; //重定位所在的偏移 282 283 284 a.push(ebp); 285 a.mov(ebp, esp); 286 a.sub(esp, 16);//-16塊RVA -12塊長度 -8塊迴圈下標 -4塊內偏移(0x3XXX) ebp即程式基址 287 a.mov(dword_ptr(ebp, -4), edx);//把差值保留在edx中 288 289 a.cmp(dword_ptr(ebp), ioldIB); 290 a.je(TextOver);//當前程序基址=預計基址 則無需重定位 291 a.mov(edi, 0); //edi遍歷偏移 292 a.bind(TextBegin);//節迴圈起點 293 a.cmp(edi, iTextRelocSize);//遍歷下標大於等於重定位長度 則遍歷完成 294 a.jnl(TextOver); 295 a.mov(esi, dword_ptr(ebp));//得到程序基址 296 a.add(esi, iTextOffset);//esi為重定位資料的基址 297 a.mov(eax, dword_ptr(esi, edi)); 298 a.cmp(eax, 0); //如果為0 則遍歷完畢 299 a.je(TextOver); 300 a.mov(dword_ptr(ebp, -16), eax);//塊RVA 301 a.mov(edx, 4); 302 a.add(edx, edi); 303 a.mov(eax, dword_ptr(esi, edx)); 304 a.mov(dword_ptr(ebp, -12), eax);//塊長度 305 a.mov(dword_ptr(ebp,-8), 8); //塊內迴圈偏移 從第8位開始 4RVA+4SIZE 306 307 a.bind(LumpBegin);//節迴圈起點 308 a.mov(ebx, dword_ptr(ebp, -8)); //ebx塊內偏移 309 a.cmp(ebx, dword_ptr(ebp, -12)); 310 a.jnl(LumpOver); 311 a.mov(ecx, esi);//重定位資料基址 312 a.add(ecx, edi);//當前遍歷偏移 313 a.movzx(eax, word_ptr(ecx, ebx)); 314 a.add(dword_ptr(ebp, -8), 2);//塊內偏移+2 315 a.cmp(eax, 0); //為0 則遍歷完畢當前塊 316 a.je(LumpOver); 317 a.xor_(eax, 0x3000); 318 a.add(eax, dword_ptr(ebp, -16));//+塊RVA 319 a.add(eax, dword_ptr(ebp));//得到重定位地址 320 a.mov(edx, dword_ptr(eax));//獲得裡面的資料 321 a.add(edx, dword_ptr(ebp, -4)); 322 /*a.sub(edx, ioldIB); 323 a.add(edx, dword_ptr(ebp));*/ 324 a.mov(dword_ptr(eax), edx);//重定位成功 325 a.jmp(LumpBegin); 326 a.bind(LumpOver);//節迴圈終點 327 a.add(edi, dword_ptr(ebp, -12)); 328 a.jmp(TextBegin); 329 a.bind(TextOver);//節迴圈終點 330 331 a.add(esp, 16);//-16 332 a.pop(ebp); 333 //回到原OEP 此時ebp為程式基址 334 a.mov(ebx, ebp); 335 a.add(ebx, iOldOEP);//當前程序基址+原OEP 336 a.jmp(ebx); 337 338 // 生成機器碼 339 // 也叫生成shellcode 340 // 使用一個函式指標指向 341 PVOID pCode = a.make(); 342 int iJitSize = a.getCodeSize() + sizeof(szGetOEP); 343 iTextAlignment += iJitSize; 344 345 if (!(iTextAlignment % pNtHead->OptionalHeader.FileAlignment)) //節檔案長度 根據程式碼段重定位資料長度 346 { 347 pSection->SizeOfRawData = iTextAlignment; 348 } 349 else 350 { 351 pSection->SizeOfRawData = iTextAlignment 352 + pNtHead->OptionalHeader.FileAlignment 353 - (iTextAlignment % pNtHead->OptionalHeader.FileAlignment); 354 } 355 if (!(iTextAlignment % pNtHead->OptionalHeader.SectionAlignment)) //節記憶體長度 356 { 357 pSection->Misc.VirtualSize = iTextAlignment; 358 } 359 else 360 { 361 pSection->Misc.VirtualSize = iTextAlignment 362 + pNtHead->OptionalHeader.SectionAlignment 363 - (iTextAlignment % pNtHead->OptionalHeader.SectionAlignment); 364 } 365 pNtHead->OptionalHeader.SizeOfCode += pSection->SizeOfRawData; //新增節的內容同樣為程式碼 366 pNtHead->OptionalHeader.SizeOfImage += pSection->Misc.VirtualSize; 367 368 int iNewSecionFileSize = pSection->SizeOfRawData; 369 g_pFileSrc = (char*)realloc(g_pFileSrc, dwFileSize + iNewSecionFileSize); 370 memcpy(g_pFileSrc + dwFileSize, szTextRelocSrc, iTextRelocSize + 4);//新增程式碼節的重定位資料 371 memcpy(g_pFileSrc + dwFileSize + iTextRelocSize + 4, szGetOEP, sizeof(szGetOEP));//壓入求基址指令 372 memcpy(g_pFileSrc + dwFileSize + iTextRelocSize + 4 + sizeof(szGetOEP), pCode, a.getCodeSize()); //壓入shellcode 373 dwFileSize += iNewSecionFileSize; 374 375 376 377 ///////////////////////////////////// 378 //////////修復重定位表////////////// 379 ///////////////////////////////////// 380 pDosHead = (PIMAGE_DOS_HEADER)g_pFileSrc; 381 pNtHead = (PIMAGE_NT_HEADERS)((DWORD)pDosHead + pDosHead->e_lfanew); 382 dwSectionCount = pNtHead->FileHeader.NumberOfSections;//節表數量 383 //通過可選PE頭的地址+可選PE頭的長度,得到節表位置 384 pSection = (PIMAGE_SECTION_HEADER)((DWORD)(&pNtHead->OptionalHeader) + pNtHead->FileHeader.SizeOfOptionalHeader); 385 pSection += dwSectionCount; //pSection的首地址+(節表數量*40) 386 memset(pSection, 0, sizeof(IMAGE_SECTION_HEADER)); //初始化 387 388 //重定位表屬性 389 strcpy((char*)pSection->Name, szRelocName); //節名 390 /*如果原有的重定位表的虛擬空間大小比實際存放資料的空間大10位元組(用於新增新的重定位塊),則無需擴大,否則則要按節對齊尺寸擴大 391 前12位元組為資料,後40為一個重定位塊的長度 (以全為0的重定位塊為結尾) */ 392 pSection->PointerToRawData = dwFileSize; 393 int iRelocSize = iNotTextRelocSize + 20; 394 if (!(iRelocSize % pNtHead->OptionalHeader.FileAlignment)) 395 { 396 pSection->SizeOfRawData = iRelocSize; 397 } 398 else 399 { 400 pSection->SizeOfRawData = iRelocSize 401 + pNtHead->OptionalHeader.FileAlignment 402 - (iRelocSize % pNtHead->OptionalHeader.FileAlignment); 403 } 404 int iAddrSize = pSection->SizeOfRawData; //這個節的檔案長度 405 pSection->VirtualAddress = pNtHead->OptionalHeader.SizeOfImage;//節RVA起始位置 406 if (!(iRelocSize % pNtHead->OptionalHeader.SectionAlignment)) 407 { 408 pSection->Misc.VirtualSize = iRelocSize; 409 } 410 else 411 { 412 pSection->Misc.VirtualSize = iRelocSize 413 + pNtHead->OptionalHeader.SectionAlignment 414 - (iRelocSize % pNtHead->OptionalHeader.SectionAlignment); 415 } 416 pSection->Characteristics = dwRelocTab | 0x80000000; 417 pNtHead->OptionalHeader.DataDirectory[5].VirtualAddress = pSection->VirtualAddress; 418 pNtHead->OptionalHeader.DataDirectory[5].Size = iNotTextRelocSize + 12 + 8; 419 pNtHead->FileHeader.NumberOfSections++; 420 pNtHead->OptionalHeader.SizeOfImage += pSection->Misc.VirtualSize; 421 422 423 //重定位的塊以4位元組對齊 424 char szAddReloc[12] = { 0 }; 425 int iTabLen = sizeof(szAddReloc); 426 int iTabAddr = iTabRva + iTabOffset; 427 iTabRva = iTabAddr & 0xfff000; 428 iTabOffset = (iTabAddr & 0xfff) | 0x3000; 429 memcpy(szAddReloc, &iTabRva, 12);//RVA 430 memcpy(szAddReloc + 4, &iTabLen, 4);//size 431 memcpy(szAddReloc + 8, &iTabOffset, 4);//偏移 位於第8個位元組後面 432 433 szNotTextRelocSrc = (char *)realloc(szNotTextRelocSrc, iAddrSize);//增加重定位資料 434 memcpy(szNotTextRelocSrc + iNotTextRelocSize, szAddReloc, sizeof(szAddReloc));//把新的重定位塊資料加到原重定位塊資料上 435 memset(szNotTextRelocSrc + iNotTextRelocSize + sizeof(szAddReloc), 0, 8); 436 437 g_pFileSrc = (char*)realloc(g_pFileSrc, dwFileSize + iAddrSize);//把空間大小增加重定位節所佔檔案大小 438 439 memcpy(g_pFileSrc + dwFileSize, szNotTextRelocSrc, iAddrSize); 440 dwFileSize += iAddrSize;//更新檔案大小 441 442 443 HANDLE hNewFile = CreateFileA("C:\\Users\\Admin\\Desktop\\彈窗測試.exe", 444 GENERIC_READ | GENERIC_WRITE, 445 FILE_SHARE_READ | FILE_SHARE_WRITE, 446 NULL, 447 CREATE_ALWAYS, //建立並覆蓋上一個檔案 448 FILE_ATTRIBUTE_ARCHIVE, 449 NULL); 450 451 if (hNewFile == INVALID_HANDLE_VALUE) 452 { 453 printf("檔案儲存失敗\n"); 454 return 1; 455 } 456 int iError = GetLastError(); 457 LPDWORD iNum = NULL; 458 WriteFile(hNewFile, g_pFileSrc, dwFileSize, iNum, NULL); //寫入檔案 459 CloseHandle(hNewFile); 460 461 MessageBoxA(0, "by:阿怪\n 2020.8.25", "加密成功", 0); 462 system("pause"); 463 }
執行效果:
shellcode的例子各位可以在網上搜索或者自己想辦法用匯編轉換成機器碼輸入。
有疑惑或者不對的地方歡迎在評論指出,感謝
&n