Skip to content

Commit 8d470b3

Browse files
committed
patcher: Sort code, add remove and repatch function.
1 parent 76f4352 commit 8d470b3

5 files changed

Lines changed: 286 additions & 118 deletions

File tree

CMakeLists.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
cmake_minimum_required(VERSION 3.27)
1+
cmake_minimum_required(VERSION 3.18)
22
project(DualBootKernelPatcher C)
33

44
set(CMAKE_C_STANDARD 11)
5+
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux")
6+
set(CMAKE_EXE_LINKER_FLAGS "-static")
7+
endif ()
58

69
# Compile the patcher.
7-
add_executable(DualBootKernelPatcher patcher.c)
10+
add_executable(DualBootKernelPatcher patcher.c utils.c)
11+
add_executable(DualBootPatchRemover remover.c utils.c)
812

913
# Compile Shell Codes with aarch64 gcc.
1014
add_subdirectory(ShellCode)

patcher.c

Lines changed: 60 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,8 @@
1212
* MIT License
1313
*
1414
*/
15-
#include <stdio.h>
16-
#include <malloc.h>
17-
#include <string.h>
18-
#include <stdint.h>
19-
#include <errno.h>
20-
#include "patcher.h"
15+
16+
#include "utils.h"
2117

2218
/**
2319
* The main function will check and read given files,
@@ -31,7 +27,7 @@
3127
*/
3228
int main(int argc, char *argv[]) {
3329
// Print hello message.
34-
printf("WOA-msmnile DualBoot Kernel Image Patcher v1.1.0.0\n");
30+
printf("WOA-msmnile DualBoot Kernel Image Patcher v1.2.0.0\n");
3531
printf("Copyright (c) 2021-2024 The DuoWoA authors\n\n");
3632
if (argc != 6) {
3733
// Print usage if arg numbers not meet.
@@ -96,92 +92,7 @@ int main(int argc, char *argv[]) {
9692

9793
// Print end message.
9894
printf("Image successfully patched.\n");
99-
printf("Please find the newly made kernel image at %s.\n", outputImage.filePath);
100-
return 0;
101-
}
102-
103-
/**
104-
* Get file size based on given fileContent.
105-
*
106-
* @param fileContent provide filePath, will also set fileSize in it.
107-
* @retval 0 File does not exist.
108-
* @retval size_t File size
109-
*
110-
*/
111-
size_t get_file_size(FileContent *fileContent) {
112-
FILE *pFile = fopen(fileContent->filePath, "r");
113-
if (pFile == NULL) {
114-
printf("Error: %s not found\n", fileContent->filePath);
115-
return 0;
116-
}
117-
fseek(pFile, 0, SEEK_END);
118-
size_t len = ftell(pFile);
119-
fclose(pFile);
120-
fileContent->fileSize = len;
121-
return len;
122-
}
123-
124-
/**
125-
* Read File buffer based on given fileContent.
126-
*
127-
* @param fileContent provide filePath, will also set fileBuffer in it.
128-
* @return Buffer read from file.
129-
*/
130-
uint8_t *read_file_content(FileContent *fileContent) {
131-
if (fileContent->fileBuffer == NULL)
132-
return NULL;
133-
FILE *pFile = fopen(fileContent->filePath, "rb");
134-
if (pFile == NULL)
135-
return NULL;
136-
fread(fileContent->fileBuffer, fileContent->fileSize, 1, pFile);
137-
fclose(pFile);
138-
return fileContent->fileBuffer;
139-
}
140-
141-
/**
142-
* Write buffer to filePath given by fileContent.
143-
*
144-
* @param fileContent Contains file information.
145-
* @retval -EBADF Failed to write file
146-
*
147-
*/
148-
int write_file_content(pFileContent fileContent) {
149-
FILE *pFile = fopen(fileContent->filePath, "wb");
150-
if (pFile == NULL)
151-
return -EBADF;
152-
fwrite(fileContent->fileBuffer, fileContent->fileSize, 1, pFile);
153-
fclose(pFile);
154-
return 0;
155-
}
156-
157-
/**
158-
* Parse given config.
159-
*
160-
* @param fileContent
161-
* @param config Config info read from config file
162-
* @retval -EINVAL Give File not found.
163-
*
164-
*/
165-
int parse_config(FileContent *fileContent, pConfig config) {
166-
// Check file size
167-
if (!get_file_size(fileContent))
168-
return -EINVAL;
169-
170-
// Open config file
171-
FILE *pConfigFile = fopen(fileContent->filePath, "r");
172-
char key[256];
173-
uint32_t value = 0;
174-
175-
// Parse
176-
while (fscanf(pConfigFile, "%[^=]=%x\n", key, &value) != EOF) {
177-
if (strcmp(key, "StackBase") == 0) {
178-
config->StackBase = value;
179-
} else if (strcmp(key, "StackSize") == 0) {
180-
config->StackSize = value;
181-
}
182-
}
183-
184-
fclose(pConfigFile);
95+
printf("Please check the patched kernel image at %s.\n", outputImage.filePath);
18596
return 0;
18697
}
18798

@@ -209,7 +120,7 @@ uint8_t *PatchKernel(pFileContent kernel, pFileContent uefi, pFileContent shellC
209120
memcpy(patchedKernel->fileBuffer + kernel->fileSize, uefi->fileBuffer, uefi->fileSize);
210121

211122
// Check ShellCode binary magic.
212-
if (uefi->fileSize > 0x40 && strcmp((char *) (shellCode->fileBuffer + 8), "SHLLCOD") != 0) {
123+
if (shellCode->fileSize > 0x40 && strcmp((char *) (shellCode->fileBuffer + 8), "SHLLCOD") != 0) {
213124
printf("Error: shell code binary format not recognize.\n");
214125
return NULL;
215126
}
@@ -224,45 +135,65 @@ uint8_t *PatchKernel(pFileContent kernel, pFileContent uefi, pFileContent shellC
224135
hasHeader |= 0b1;
225136
}
226137

227-
// Check if kernel size % 0x10 == 0, which will cause copy loop issue.
228-
if (kernel->fileSize % 0x10 != 0) {
138+
// Check if kernel size % 0x10 == 0, which will cause copy loop(in shellcode) issue.
139+
if (kernel->fileSize % 0x10) {
229140
printf("Align kernel size to 0x10.\n");
141+
142+
// Calculate align padding
143+
uint8_t padding = 0x10 - (kernel->fileSize % 0x10);
144+
145+
// New kernel size and output size
146+
kernel->fileSize += padding;
147+
patchedKernel->fileSize += padding;
148+
230149
// Reallocate patched kernel.
231-
void *ptr = realloc(patchedKernel->fileBuffer, patchedKernel->fileSize + (kernel->fileSize % 10));
150+
void *ptr = realloc(patchedKernel->fileBuffer, patchedKernel->fileSize);
232151
if (ptr == NULL) {
233152
printf("Failed to reallocate patched kernel buffer.");
234153
return NULL;
235154
} else patchedKernel->fileBuffer = ptr;
236-
// Copy uefi image to 0x10 align place.
237-
uint8_t padding = 0x10 - (kernel->fileSize % 0x10);
238-
memmove(patchedKernel->fileBuffer + kernel->fileSize + padding,
239-
patchedKernel->fileBuffer + kernel->fileSize, uefi->fileSize);
240-
kernel->fileSize += padding;
241-
patchedKernel->fileSize += padding;
242-
}
243155

244-
// Check if kernel has EFI Stub, 4D5A0091, "MZ"
245-
// Note: This magic is part of kernel
246-
// We wil rewrite file header here.
247-
// For kernel with EFI stub, we only need to patch the efi header.
248-
memcpy(kernelHeader, kernel->fileBuffer, 0x4);
249-
if (*(uint32_t *) kernelHeader == 0x91005A4D) {
250-
printf("Kernel has EFI header.\n");
251-
hasHeader |= 0b10;
156+
// Copy uefi image to 0x10 align place and fill padding part to 0.
157+
if (hasHeader & 0b1) // Kernel has header, need to add 0x14 offset while copying.
158+
{
159+
memmove(patchedKernel->fileBuffer + kernel->fileSize + 0x14,
160+
patchedKernel->fileBuffer + kernel->fileSize - padding + 0x14, uefi->fileSize);
161+
memset(patchedKernel->fileBuffer + kernel->fileSize - padding + 0x14, 0, padding);
162+
} else {
163+
memmove(patchedKernel->fileBuffer + kernel->fileSize,
164+
patchedKernel->fileBuffer + kernel->fileSize - padding, uefi->fileSize);
165+
memset(patchedKernel->fileBuffer + kernel->fileSize - padding, 0, padding);
166+
}
252167
}
253168

254169
// Kernel has uncompressed_img header,
255-
if(hasHeader & 1)
170+
if (hasHeader & 1)
256171
// Move our pointer after UNCOMPRESSED_IMG header before further processing,
257172
patchedKernel->fileBuffer += 0x14;
258173

174+
/* Reserved for feature use */
175+
// Check if kernel was already patched.
176+
// if config in kernel was same with the config provided here,
177+
// set kernel size to previous kernel size. (which skipped copying previously uefi fd)
178+
// if (*(uint64_t *) (kernel->fileBuffer + 0x20) == config->StackBase &&
179+
// *(uint64_t *) (kernel->fileBuffer + 0x28) == config->StackSize) {
180+
// printf("Kernel was already patched previously!\n"
181+
// "Re-patching with new UEFI...\n");
182+
// kernel->fileSize = *(uint64_t *) (kernel->fileBuffer + 0x30);
183+
// patchedKernel->fileSize = kernel->fileSize + uefi->fileSize; // Update output kernel size
184+
// printf("Old kernel size %llx\n", *(uint64_t *) (kernel->fileBuffer + 0x30));
185+
// hasHeader |= 0b10;
186+
// }
187+
259188
// Determine the loading offset of the kernel first,
260189
// we are either going to find a b instruction on the
261190
// first instruction or the second one. First is problematic,
262191
// second is fine.
263192

264193
// 0x14 is AArch64 b opcode
265-
if (patchedKernel->fileBuffer[3] == 0x14) {
194+
// If Code1 is jump instruction, the kernel is an original kernel or a patched kernel.
195+
// However, if Code2 is also jump instruction, the kernel should be a patched kernel.
196+
if (patchedKernel->fileBuffer[3] == 0x14 && patchedKernel->fileBuffer[7] != 0x14) {
266197
// For kernel without EFI stub, we have to move the jump kernel instruction forward to Code2.
267198
// We have a branch instruction first, we need to fix things a bit.
268199

@@ -282,8 +213,21 @@ uint8_t *PatchKernel(pFileContent kernel, pFileContent uefi, pFileContent shellC
282213
patchedKernel->fileBuffer[5] = offsetInstructionBuffer[1];
283214
patchedKernel->fileBuffer[6] = offsetInstructionBuffer[2];
284215
patchedKernel->fileBuffer[7] = 0x14;
285-
}
286-
else if (patchedKernel->fileBuffer[7] != 0x14) {
216+
} else // Patched Kernel
217+
if (patchedKernel->fileBuffer[3] == 0x14 && patchedKernel->fileBuffer[7] == 0x14) {
218+
printf("Patched kernel detected, redo patch with new fd.\n");
219+
kernel->fileSize = *(uint64_t *) (kernel->fileBuffer + 0x30);
220+
patchedKernel->fileSize = kernel->fileSize + uefi->fileSize; // Update output kernel size
221+
} else // Kernel with EFI Stub
222+
if (*(uint16_t *) (patchedKernel->fileBuffer) == 0x5A4D && patchedKernel->fileBuffer[7] == 0x14) {
223+
// Check if kernel has EFI Stub, 4D5A, "MZ"
224+
// Note:
225+
// The magic is also an arm64 instruction, and the second instruction jump to kernel.
226+
// We will only rewrite EFI header here. (This will break the kernel)
227+
// For kernel with EFI stub, we only need to patch the efi header.
228+
printf("Kernel has EFI header.\n");
229+
} else // Unknown stuff
230+
if (patchedKernel->fileBuffer[7] != 0x14) {
287231
// There is no branch instruction!
288232
printf("Error: Invalid Kernel Image. Branch instruction not found within first two instruction slots.\n");
289233
return NULL;

remover.c

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/** @file
2+
* Patch Remover Source File.
3+
*
4+
* This Program will help you revert patched instructions in a patched kernel.
5+
*
6+
* It only supports several formats of kernel:
7+
* 1. Image file compile from source
8+
* 2. Qualcomm patched kernel.
9+
* 3. Image file kernel with efi stub.
10+
*
11+
* Copyright (c) 2021-2024 The DuoWoa authors. All rights reserved.
12+
* MIT License
13+
*
14+
*/
15+
16+
#include "utils.h"
17+
18+
/**
19+
* Receive args and handle status.
20+
*
21+
* @param argc argc is numbers of argues given in cmdline.
22+
* @param argv argv is a array that contains all given values.
23+
* @return program status
24+
*/
25+
int main(int argc, char *argv[]) {
26+
printf("WOA-msmnile DualBoot Patch Remover v1.2.0.0\n");
27+
printf("Copyright (c) 2021-2024 The DuoWoA authors\n\n");
28+
if (argc != 3) {
29+
// Print usage if arg numbers not meet.
30+
printf("args: %d\n", argc);
31+
printf("Usage: <Input Patched Kernel> <Output Kernel>\n");
32+
return -EINVAL;
33+
}
34+
35+
// Program status.
36+
int status = 0;
37+
38+
// Get file paths.
39+
FileContent patchedImage = {.filePath = argv[1]};
40+
FileContent outputImage = {.filePath = argv[2]};
41+
42+
// Read buffer from patched kernel.
43+
if (!get_file_size(&patchedImage)) {
44+
status = -EINVAL;
45+
goto free_and_exit;
46+
}
47+
patchedImage.fileBuffer = malloc(patchedImage.fileSize);
48+
read_file_content(&patchedImage);
49+
50+
// Remove!
51+
Remove(&patchedImage, &outputImage);
52+
53+
// Output buffer to new kernel.
54+
if (outputImage.fileBuffer != NULL) {
55+
write_file_content(&outputImage);
56+
} else {
57+
printf("Error Removing Patch.\n");
58+
status = -EINVAL;
59+
goto free_and_exit;
60+
}
61+
62+
// Free buffers we allocated.
63+
free_and_exit:
64+
free(patchedImage.fileBuffer);
65+
free(outputImage.fileBuffer);
66+
67+
// Everything goes well
68+
printf("Patch successfully removed.\n");
69+
printf("Please check the unpatched kernel image at %s.\n", outputImage.filePath);
70+
return status;
71+
}
72+
73+
/**
74+
* Revert patch in kernel and return output file buffer.
75+
*
76+
* @param patchedKernel Input Patched kernel.
77+
* @param outputKernel Output Reverted kernel.
78+
* @return Reverted kernel buffer, NULL if error processing.
79+
*/
80+
uint8_t *Remove(pFileContent patchedKernel, pFileContent outputKernel) {
81+
// Get previous config from patched kernel.
82+
size_t originKernelSize = *(uint64_t *) (patchedKernel->fileBuffer + 0x30);
83+
84+
// Check if kernel has UNCOMPRESSED_IMG Header.
85+
char kernelHeader[0x11] = {0};
86+
uint8_t hasHeader = 0;
87+
memcpy(kernelHeader, patchedKernel->fileBuffer, 0x10);
88+
if (strcmp(kernelHeader, "UNCOMPRESSED_IMG") == 0) {
89+
printf("Kernel has UNCOMPRESSED_IMG header.\n");
90+
originKernelSize = *(uint64_t *) (patchedKernel->fileBuffer + 0x30 + 0x14) + 0x14;
91+
hasHeader |= 0b1;
92+
}
93+
94+
// Allocate output buffer
95+
outputKernel->fileSize = originKernelSize;
96+
outputKernel->fileBuffer = malloc(outputKernel->fileSize);
97+
98+
// Copy new buffer into outputBuffer.
99+
memcpy(outputKernel->fileBuffer, patchedKernel->fileBuffer, outputKernel->fileSize);
100+
101+
// After copying, jump over header
102+
if (hasHeader & 0b1)
103+
outputKernel->fileBuffer += 0x14;
104+
105+
// Now check if it is a patched kernel
106+
if (outputKernel->fileBuffer[3] == 0x14 && outputKernel->fileBuffer[7] == 0x14) {
107+
printf("Patched kernel detected.");
108+
// Recover Code1 jump instruction, jump to linux kernel directly.
109+
*(uint32_t *) outputKernel->fileBuffer =
110+
((*(uint32_t *) (outputKernel->fileBuffer + 4) & ~(0xFF << 24)) + 1) | (0x14 << 24);
111+
// Clean Code2
112+
*(uint32_t *) (outputKernel->fileBuffer + 4) = 0;
113+
} else {
114+
printf("Not an valid kernel.");
115+
return NULL;
116+
}
117+
118+
// Move back our pointer and recalculate kernel size in kernel
119+
// header if the patched kernel buffer has UNCOMPRESSED_IMG header.
120+
if (hasHeader & 0b1) {
121+
outputKernel->fileBuffer -= 0x14;
122+
size_t newKernelSize = outputKernel->fileSize - 0x14;
123+
outputKernel->fileBuffer[0x10] = newKernelSize >> 0 & 0xFF;
124+
outputKernel->fileBuffer[0x11] = newKernelSize >> 8 & 0xFF;
125+
outputKernel->fileBuffer[0x12] = newKernelSize >> 16 & 0xFF;
126+
outputKernel->fileBuffer[0x13] = newKernelSize >> 24 & 0xFF;
127+
}
128+
return outputKernel->fileBuffer;
129+
}

0 commit comments

Comments
 (0)