(byte[] payload, PeSection[] sections, int requiredSize)
| 1049 | const uint SCN_MEM_EXECUTE = 0x20000000; |
| 1050 | |
| 1051 | int EnsureExecutableCave(byte[] payload, PeSection[] sections, int requiredSize) |
| 1052 | { |
| 1053 | var peOff = BitConverter.ToInt32(payload, 0x3C); |
| 1054 | ushort numSections = BitConverter.ToUInt16(payload, peOff + 6); |
| 1055 | ushort optHdrSize = BitConverter.ToUInt16(payload, peOff + 20); |
| 1056 | int secHdrStart = peOff + 24 + optHdrSize; |
| 1057 | int sectionAlignment = BitConverter.ToInt32(payload, peOff + 0x38); |
| 1058 | |
| 1059 | // Find a read-only section with alignment padding after VSize, extend |
| 1060 | // VSize to cover the cave, and add EXECUTE. |
| 1061 | for (int s = 0; s < numSections; s++) |
| 1062 | { |
| 1063 | int hdrOff = secHdrStart + s * 40; |
| 1064 | uint chars = BitConverter.ToUInt32(payload, hdrOff + 36); |
| 1065 | if ((chars & SCN_MEM_EXECUTE) != 0) |
| 1066 | continue; |
| 1067 | if ((chars & 0x80000000) != 0) // skip writable sections |
| 1068 | |
| 1069 | continue; |
| 1070 | |
| 1071 | int va = BitConverter.ToInt32(payload, hdrOff + 12); |
| 1072 | int vSize = BitConverter.ToInt32(payload, hdrOff + 8); |
| 1073 | |
| 1074 | int alignedEnd = ((va + vSize) + sectionAlignment - 1) & ~(sectionAlignment - 1); |
| 1075 | int padGap = alignedEnd - (va + vSize); |
| 1076 | |
| 1077 | if (padGap < requiredSize) |
| 1078 | continue; |
| 1079 | |
| 1080 | int caveRva = va + vSize; |
| 1081 | int newVSize = vSize + requiredSize; |
| 1082 | BitConverter.GetBytes(newVSize).CopyTo(payload, hdrOff + 8); |
| 1083 | uint newChars = chars | SCN_MEM_EXECUTE; |
| 1084 | BitConverter.GetBytes(newChars).CopyTo(payload, hdrOff + 36); |
| 1085 | |
| 1086 | int rawOff = BitConverter.ToInt32(payload, hdrOff + 20); |
| 1087 | int rawSize = BitConverter.ToInt32(payload, hdrOff + 16); |
| 1088 | int caveFileOff = rawOff + (caveRva - va); |
| 1089 | |
| 1090 | if (caveFileOff + requiredSize > rawOff + rawSize) |
| 1091 | { |
| 1092 | if (caveFileOff + requiredSize > payload.Length) |
| 1093 | continue; |
| 1094 | int newRawSize = ((caveRva - va) + requiredSize + 0x1FF) & ~0x1FF; |
| 1095 | BitConverter.GetBytes(newRawSize).CopyTo(payload, hdrOff + 16); |
| 1096 | } |
| 1097 | |
| 1098 | string secName = System.Text.Encoding.ASCII.GetString(payload, hdrOff, 8).TrimEnd('\0'); |
| 1099 | Log($" Cave target: section '{secName}' padding gap ({padGap} bytes)"); |
| 1100 | Log($" VSize 0x{vSize:X} -> 0x{newVSize:X}, Chars 0x{chars:X8} -> 0x{newChars:X8}"); |
| 1101 | Log($" Cave RVA 0x{caveRva:X}, file offset 0x{caveFileOff:X}"); |
| 1102 | |
| 1103 | return caveFileOff; |
| 1104 | } |
| 1105 | return -1; |
| 1106 | } |
| 1107 | |
| 1108 | PatchEntry[] ResolvePayloadPatchOffsets(byte[] payload) |
nothing calls this directly
no outgoing calls
no test coverage detected