Seregon/ShadPKG

A tool for deriving PKG packet encryption keys for ps4 written in c++

C++/47.3 KB/No license
core/patcher/elf_reconstruct.cpp
ShadPKG / core / patcher / elf_reconstruct.cpp
1#include "elf_reconstruct.h"
2#include <algorithm>
3#include <cstring>
4#include <fstream>
5#include <iostream>
6 
7namespace ShadPKG::Patcher {
8 
9ElfReconstructor::ElfReconstructor() {}
10 
11bool ElfReconstructor::loadFile(const std::string &path) {
12 std::ifstream file(path, std::ios::binary | std::ios::ate);
13 if (!file.is_open()) {
14 std::cerr << "[ElfReconstructor] Failed to open: " << path << std::endl;
15 return false;
16 }
17 
18 std::streamsize size = file.tellg();
19 file.seekg(0, std::ios::beg);
20 
21 rawData_.resize(size);
22 if (!file.read(reinterpret_cast<char *>(rawData_.data()), size)) {
23 return false;
24 }
25 
26 isLoaded_ = true;
27 return true;
28}
29 
30bool ElfReconstructor::loadData(const std::vector<uint8_t> &data) {
31 rawData_ = data;
32 isLoaded_ = true;
33 return true;
34}
35 
36bool ElfReconstructor::isSelf() const {
37 if (rawData_.size() < sizeof(SelfHeader))
38 return false;
39 const auto *header = reinterpret_cast<const SelfHeader *>(rawData_.data());
40 // Check magic 0x1D3D154F (Little Endian: 4F 15 3D 1D)
41 return header->magic == 0x1D3D154F;
42}
43 
44bool ElfReconstructor::isElf() const {
45 if (rawData_.size() < 4)
46 return false;
47 // ELF magic: 0x7F 'E' 'L' 'F'
48 return rawData_[0] == 0x7F && rawData_[1] == 'E' && rawData_[2] == 'L' &&
49 rawData_[3] == 'F';
50}
51 
52bool ElfReconstructor::reconstructElf(const std::string &outputPath) {
53 if (!isLoaded_)
54 return false;
55 
56 if (isElf()) {
57 std::cout << "[ElfReconstructor] Already an ELF file." << std::endl;
58 elfData_ = rawData_;
59 if (!outputPath.empty()) {
60 std::ofstream out(outputPath, std::ios::binary);
61 out.write(reinterpret_cast<const char *>(elfData_.data()),
62 elfData_.size());
63 }
64 return true;
65 }
66 
67 if (!isSelf()) {
68 std::cerr << "[ElfReconstructor] Input is not a valid SELF or ELF."
69 << std::endl;
70 return false;
71 }
72 
73 std::cout << "[ElfReconstructor] Converting SELF to ELF..." << std::endl;
74 
75 const auto *selfHeader =
76 reinterpret_cast<const SelfHeader *>(rawData_.data());
77 
78 std::vector<Elf64_Phdr> phdrs;
79 std::vector<std::vector<uint8_t>> segmentData;
80 
81 size_t segmentTableOffset = sizeof(SelfHeader);
82 
83 const auto *segments = reinterpret_cast<const SelfSegment *>(
84 rawData_.data() + 0x20); // 32 bytes offset
85 
86 // Prepare ELF Header
87 Elf64_Ehdr ehdr = {};
88 ehdr.e_ident[0] = 0x7F;
89 ehdr.e_ident[1] = 'E';
90 ehdr.e_ident[2] = 'L';
91 ehdr.e_ident[3] = 'F';
92 ehdr.e_ident[4] = 2; // ELFCLASS64
93 ehdr.e_ident[5] = 1; // ELFDATA2LSB
94 ehdr.e_ident[6] = 1; // EV_CURRENT
95 ehdr.e_ident[7] = 9; // FreeBSD ABI (PS4 uses this)
96 ehdr.e_type = 2; // ET_EXEC
97 ehdr.e_machine = 62; // EM_X86_64
98 ehdr.e_version = 1;
99 ehdr.e_phentsize = sizeof(Elf64_Phdr);
100 ehdr.e_phnum = 0;
101 ehdr.e_shentsize = 0;
102 ehdr.e_shnum = 0;
103 ehdr.e_shstrndx = 0;
104 ehdr.e_ehsize = sizeof(Elf64_Ehdr);
105 
106 // Calculate global offset for data
107 // ELF Header + Phdrs
108 uint64_t currentOffset =
109 sizeof(Elf64_Ehdr) + (selfHeader->segment_count * sizeof(Elf64_Phdr));
110 // Align to page size?
111 currentOffset = (currentOffset + 0xFFF) & ~0xFFF;
112 
113 ehdr.e_phoff = sizeof(Elf64_Ehdr); // Phdrs follow Ehdr
114 
115 for (int i = 0; i < selfHeader->segment_count; ++i) {
116 const auto &seg = segments[i];
117 
118 if (seg.file_size == 0 && seg.memory_size == 0)
119 continue;
120 
121 // Define ELF Phdr
122 Elf64_Phdr phdr = {};
123 
124 phdr.p_type = 1; // PT_LOAD default
125 phdr.p_flags = 0;
126 if (seg.flags & 4)
127 phdr.p_flags |= 4; // R
128 if (seg.flags & 2)
129 phdr.p_flags |= 2; // W
130 if (seg.flags & 1)
131 phdr.p_flags |= 1; // X
132 
133 // Use SELF segment properties
134 phdr.p_offset = currentOffset; // We will pack data here
135 phdr.p_vaddr = seg.vaddr;
136 phdr.p_paddr = seg.vaddr;
137 phdr.p_filesz = seg.file_size;
138 phdr.p_memsz = seg.memory_size;
139 phdr.p_align = seg.align;
140 
141 // Need to read data from SELF
142 // seg.offset in SELF file -> data
143 if (seg.offset + seg.file_size > rawData_.size()) {
144 std::cerr << "[ElfReconstructor] Segment " << i << " out of bounds!"
145 << std::endl;
146 continue;
147 }
148 
149 std::vector<uint8_t> data(seg.file_size);
150 std::memcpy(data.data(), rawData_.data() + seg.offset, seg.file_size);
151 segmentData.push_back(std::move(data));
152 
153 phdrs.push_back(phdr);
154 
155 // Update offset for next segment
156 currentOffset += seg.file_size;
157 }
158 
159 ehdr.e_phnum = phdrs.size();
160 
161 for (size_t i = 0; i < rawData_.size() - 4; ++i) {
162 if (rawData_[i] == 0x7F && rawData_[i + 1] == 'E' &&
163 rawData_[i + 2] == 'L' && rawData_[i + 3] == 'F') {
164 // Found internal ELF!
165 std::cout << "[ElfReconstructor] Found internal ELF at offset 0x"
166 << std::hex << i << std::dec << std::endl;
167 
168 const auto *internalEhdr =
169 reinterpret_cast<const Elf64_Ehdr *>(rawData_.data() + i);
170 // Check if it looks valid
171 if (internalEhdr->e_machine == 62) {
172 
173 std::cout << "[ElfReconstructor] Found embedded ELF64 header."
174 << std::endl;
175 
176 // 1. Validate Program Headers
177 uint64_t phoff = internalEhdr->e_phoff;
178 uint16_t phnum = internalEhdr->e_phnum;
179 
180 // Calculate where the PHT end is relative to the ELF start
181 uint64_t phtEnd = phoff + (phnum * internalEhdr->e_phentsize);
182 
183 if (i + phtEnd > rawData_.size()) {
184 std::cerr << "[ElfReconstructor] Error: Program Headers extend "
185 "beyond file bounds."
186 << std::endl;
187 continue;
188 }
189 
190 // 2. Scan Segments to find the actual file size required
191 // The ELF file size on disk is determined by the segment with the
192 // highest (p_offset + p_filesz)
193 uint64_t maxOffset = phtEnd; // Min size is up to PHT
194 
195 const Elf64_Phdr *phdrs =
196 reinterpret_cast<const Elf64_Phdr *>(rawData_.data() + i + phoff);
197 
198 for (int k = 0; k < phnum; ++k) {
199 uint64_t segEnd = phdrs[k].p_offset + phdrs[k].p_filesz;
200 if (segEnd > maxOffset) {
201 maxOffset = segEnd;
202 }
203 }
204 
205 // 3. Validate Section Headers (Optional for execution, but good for
206 // tools)
207 bool stripSections = false;
208 uint64_t shoff = internalEhdr->e_shoff;
209 uint16_t shnum = internalEhdr->e_shnum;
210 uint64_t shtEnd = shoff + (shnum * internalEhdr->e_shentsize);
211 
212 if (shoff != 0 && (i + shtEnd > rawData_.size())) {
213 std::cerr << "[ElfReconstructor] Warning: Section Headers point "
214 "outside file (Offset: "
215 << shoff << "). Stripping..." << std::endl;
216 stripSections = true;
217 } else if (shoff != 0) {
218 // If sections exist and are valid, include them in size
219 if (shtEnd > maxOffset) {
220 maxOffset = shtEnd;
221 }
222 }
223 
224 std::cout << "[ElfReconstructor] Calculated actual ELF size: "
225 << maxOffset << " bytes." << std::endl;
226 
227 // Double check bounds against input file
228 if (i + maxOffset > rawData_.size()) {
229 std::cerr << "[ElfReconstructor] Warning: Segments extend beyond "
230 "input file! Truncating to available data."
231 << std::endl;
232 maxOffset = rawData_.size() - i;
233 }
234 
235 // 4. Create new ELF buffer
236 elfData_.resize(maxOffset);
237 
238 // Copy data from input
239 // We copy byte-for-byte from the start of the embedded ELF to the
240 // calculated end
241 std::memcpy(elfData_.data(), rawData_.data() + i, maxOffset);
242 
243 // 5. Patch header if we stripped sections
244 if (stripSections) {
245 auto *newEhdr = reinterpret_cast<Elf64_Ehdr *>(elfData_.data());
246 newEhdr->e_shoff = 0;
247 newEhdr->e_shnum = 0;
248 newEhdr->e_shentsize = 0;
249 newEhdr->e_shstrndx = 0;
250 }
251 
252 if (!outputPath.empty()) {
253 std::ofstream out(outputPath, std::ios::binary);
254 out.write(reinterpret_cast<const char *>(elfData_.data()),
255 elfData_.size());
256 std::cout << "[ElfReconstructor] Saved reconstructed ELF ("
257 << elfData_.size() << " bytes)" << std::endl;
258 }
259 return true;
260 }
261 }
262 }
263 
264 ehdr.e_entry = 0x400000;
265 
266 std::cerr << "[ElfReconstructor] Could not find embedded ELF header."
267 << std::endl;
268 return false;
269}
270 
271} // namespace ShadPKG::Patcher
272