Skip to content

Instantly share code, notes, and snippets.

@tewilove
Created March 3, 2022 10:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tewilove/437ae0d161141795a50b3ebac3d49537 to your computer and use it in GitHub Desktop.
Save tewilove/437ae0d161141795a50b3ebac3d49537 to your computer and use it in GitHub Desktop.
My own elf packer for ARM/AARCH64.
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <elf.h>
/*
* Memory layout:
*
* Basicly 3 sections, text goes to RX, rodata goes to RO,
* data and bss goes to RW. For imported functions, generate
* bridge code for it, appending to the end of text section and
* make corresponding calls point to its bridge.
*
*/
/* target */
#define __ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask))
#define __ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
#define ALIGN(x, a) __ALIGN_KERNEL((x), (a))
#define PAGE_SIZE 4096
#define PAGE_ALIGN(addr) ALIGN(addr, PAGE_SIZE)
#define KOJI_MAGIC 0x494a4f4b
#define KOJI_VERSION 0x00000001
/* main file structure */
struct koji_head {
uint32_t magic;
uint32_t version;
uint32_t machine; // EM_XXX
uint32_t entry; // entry offset in text
uint32_t rx_offset; // text offset in file
uint32_t rx_size;
uint32_t ro_offset; // rodata offset in file
uint32_t ro_size;
uint32_t rw_offset; // data offset in file
uint32_t rw_size;
uint32_t rw_bss_offset; // bss offset in rw
uint32_t symbol_offset; // koji_symbol structure offset in file
uint32_t symbol_count;
uint32_t relocate_offset; // koji_relocate structure offset in file
uint32_t relocate_count;
uint32_t size; // size of the file
};
/* resolve address of name to offset */
struct koji_symbol
{
unsigned long type;
unsigned long offset;
const char *name;
struct koji_symbol *next;
};
/* add PC to value */
struct koji_relocate
{
unsigned long type;
unsigned long offset;
unsigned long value;
struct koji_relocate *next;
};
struct arch_symbol {
uint32_t offset;
char name[1];
} __attribute__((packed));
struct arch_relocate {
uint16_t type;
uint32_t offset;
uint32_t value;
} __attribute__((packed));
static struct koji_symbol *koji_symbol_append(struct koji_symbol **head)
{
struct koji_symbol *it;
struct koji_symbol *tmp;
it = calloc(1, sizeof(*it));
tmp = *head;
it->next = tmp;
*head = it;
return it;
}
static unsigned long koji_symbol_offset(struct koji_symbol *head, const char *name)
{
while (head) {
if (!strcmp(head->name, name))
return head->offset;
head = head->next;
}
return (unsigned long)(-1);
}
static struct koji_relocate *koji_relocate_append(struct koji_relocate **head)
{
struct koji_relocate *it;
struct koji_relocate *tmp;
it = calloc(1, sizeof(*it));
tmp = *head;
it->next = tmp;
*head = it;
return it;
}
struct aarch64_bridge {
uint32_t ldr_x16_val;
uint32_t br_x16;
uint64_t val;
} __attribute__((packed));
static int aarch64_is_sym_entry(Elf64_Sym *s, const char *strtab)
{
return (ELF64_ST_BIND(s->st_info) == STB_GLOBAL) &&
(ELF64_ST_TYPE(s->st_info) == STT_FUNC) &&
!strcmp(strtab + s->st_name, "koji_main");
}
static int aarch64_is_sym_und(Elf64_Sym *s)
{
return (ELF64_ST_BIND(s->st_info) == STB_GLOBAL) &&
(s->st_shndx == STN_UNDEF);
}
static int aarch64_is_rela_function(Elf64_Rela *r)
{
return (ELF64_R_TYPE(r->r_info) == R_AARCH64_CALL26) ||
(ELF64_R_TYPE(r->r_info) == R_AARCH64_JUMP26);
}
static int aarch64_is_type_function(unsigned long type)
{
return (type == R_AARCH64_CALL26) ||
(type == R_AARCH64_JUMP26);
}
static int aarch64_is_type_global(unsigned long type)
{
return !aarch64_is_type_function(type);
}
/* C6.2.24 */
static uint32_t aarch64_encode_b(unsigned long from, unsigned long to)
{
int64_t offset;
offset = (signed) to - (signed) from;
if (offset < 0x10000000 &&
offset >= -0x10000000) {
return 0x14000000u | ((offset & 0x0fffffff) >> 2);
}
return 0xdeadbeefu;
}
/* C6.2.31 */
static uint32_t aarch64_encode_bl(unsigned long from, unsigned long to)
{
int64_t offset;
offset = (signed) to - (signed) from;
if (offset < 0x10000000 &&
offset >= -0x10000000) {
return 0x94000000u | ((offset & 0x0fffffff) >> 2);
}
return 0xdeadbeefu;
}
static int aarch64_generate(const char *file, void *data, size_t size)
{
Elf64_Ehdr *ehdr = data;
int i, j, n;
Elf64_Shdr *ssec;
Elf64_Shdr *shdr;
char *shstrtab = NULL;
char *strtab = NULL;
Elf64_Sym *symtab = NULL;
int symnum;
Elf64_Shdr s_zero;
Elf64_Shdr *s_text = &s_zero, *s_rodata = &s_zero, *s_rodata_str = &s_zero, *s_data = &s_zero, *s_bss = &s_zero;
Elf64_Shdr *s_rela_text = &s_zero, *s_rela_data = &s_zero, *s_rela_rodata = &s_zero;
Elf64_Shdr *s_rela[3] = { &s_zero, &s_zero, &s_zero };
int text_size, rodata_size, rodata_str_size, data_size, bss_size;
int text_offset, rodata_offset, rodata_str_offset, data_offset, bss_offset;
int bridge_count, bridge_size, bridge_offset;
int global_count, global_size, global_offset;
int rx_size, ro_size, rw_size;
int rx_offset, ro_offset, rw_offset;
int xx_offset[3];
int total_size;
int entry = -1;
void *core_data;
char *rx_data, *ro_data, *rw_data;
char *xx_data[3];
void *tmp;
struct koji_symbol *koji_symbol = NULL;
struct koji_symbol *ks;
int symbol_count = 0;
int symbol_size;
char *symbol_data;
struct koji_relocate *koji_relocate = NULL;
struct koji_relocate *kr;
int relocate_count = 0;
int relocate_size;
char *relocate_data;
struct arch_symbol *a_sym;
struct arch_relocate *a_rel;
struct koji_head head;
int fd;
shdr = data + ehdr->e_shoff;
ssec = &shdr[ehdr->e_shstrndx];
shstrtab = data + ssec->sh_offset;
/* sections */
memset(&s_zero, 0, sizeof(s_zero));
s_zero.sh_entsize = 1;
for (i = 0; i < ehdr->e_shnum; i++) {
const char *name = shstrtab + shdr[i].sh_name;
if (shdr[i].sh_type == SHT_NULL)
continue;
if (!strcmp(name, ".strtab"))
strtab = data + shdr[i].sh_offset;
if (!strcmp(name, ".symtab")) {
symtab = data + shdr[i].sh_offset;
symnum = shdr[i].sh_size / shdr[i].sh_entsize;
}
if (!strcmp(name, ".text"))
s_text = &shdr[i];
if (!strcmp(name, ".rodata"))
s_rodata = &shdr[i];
if (!strcmp(name, ".rodata.str1.1"))
s_rodata_str = &shdr[i];
if (!strcmp(name, ".data"))
s_data = &shdr[i];
if (!strcmp(name, ".bss"))
s_bss = &shdr[i];
if (!strcmp(name, ".rela.text")) {
s_rela_text = &shdr[i];
s_rela[0] = s_rela_text;
}
if (!strcmp(name, ".rela.rodata")) {
s_rela_rodata = &shdr[i];
s_rela[1] = s_rela_rodata;
}
if (!strcmp(name, ".rela.data")) {
s_rela_data = &shdr[i];
s_rela[2] = s_rela_data;
}
}
if (symtab == NULL) {
errx(1, "No .symtab.");
return 1;
}
bridge_count = 0;
global_count = 0;
rx_offset = 0;
xx_offset[0] = rx_offset;
text_offset = 0;
text_size = s_text->sh_size;
bridge_offset = ALIGN(text_offset + text_size, sizeof(struct aarch64_bridge));
for (i = 0; i < symnum; i++) {
Elf64_Sym *sym;
sym = &symtab[i];
/* entry */
if (entry < 0 && aarch64_is_sym_entry(sym, strtab)) {
entry = (int)sym->st_value;
continue;
}
if (aarch64_is_sym_und(sym)) {
ks = koji_symbol_append(&koji_symbol);
symbol_count += 1;
ks->name = strtab + sym->st_name;
for (j = 0; j < s_rela_text->sh_size / s_rela_text->sh_entsize; j++) {
Elf64_Rela *rela;
rela = (Elf64_Rela *)(data + s_rela_text->sh_offset) + j;
if (sym->st_name == symtab[ELF64_R_SYM(rela->r_info)].st_name) {
ks->type = ELF64_R_TYPE(rela->r_info);
if (aarch64_is_type_function(ks->type)) {
ks->offset = bridge_offset + bridge_count * sizeof(struct aarch64_bridge);
bridge_count += 1;
} else {
global_count += 1;
}
break;
}
}
// printf(" %s %ld\n", ks->name, ks->type);
continue;
}
}
if (entry < 0) {
errx(1, "No entry point.");
return 1;
}
bridge_size = bridge_count * sizeof(struct aarch64_bridge);
rx_size = bridge_offset + bridge_size;
printf("RX %d | text %d, function %d\n", rx_size, text_size, bridge_size);
ro_offset = PAGE_ALIGN(rx_size);
xx_offset[1] = ro_offset;
rodata_offset = 0;
rodata_size = s_rodata->sh_size;
rodata_str_offset = ALIGN(rodata_offset + rodata_size, s_rodata_str->sh_addralign);
rodata_str_size = s_rodata_str->sh_size;
global_offset = ALIGN(rodata_str_offset + rodata_str_size, sizeof(long));
global_size = global_count * sizeof(long);
ro_size = global_offset + global_size;
if (global_count) {
int i;
ks = koji_symbol;
i = 0;
while (ks) {
if (aarch64_is_type_global(ks->type)) {
ks->offset = global_offset + i * sizeof(long);
i++;
}
ks = ks->next;
}
}
printf("RO %d | rodata %d, string %d, global %d\n",
ro_size, rodata_size, rodata_str_size, global_size);
rw_offset = PAGE_ALIGN(ro_offset + ro_size);
xx_offset[2] = rw_offset;
data_offset = 0;
data_size = s_data->sh_size;
bss_offset = ALIGN(data_offset + data_size, s_bss->sh_addralign);
bss_size = s_bss->sh_size;
rw_size = bss_offset + bss_size;
printf("RW %d | data %d, bss %d\n", rw_size, data_size, bss_size);
/* link */
total_size = rx_size + ro_size + bss_offset;
core_data = malloc(total_size);
memset(core_data, 0, total_size);
rx_data = core_data;
xx_data[0] = rx_data;
memcpy(rx_data + text_offset, data + s_text->sh_offset, s_text->sh_size);
if (ro_size) {
ro_data = rx_data + rx_size;
memcpy(ro_data + rodata_offset, data + s_rodata->sh_offset, s_rodata->sh_size);
memcpy(ro_data + rodata_str_offset, data + s_rodata_str->sh_offset, s_rodata_str->sh_size);
xx_data[1] = ro_data;
}
if (rw_size) {
rw_data = ro_data + ro_size;
memcpy(rw_data + data_offset, data + s_data->sh_offset, s_data->sh_size);
xx_data[2] = rw_data;
}
for (n = 0; n < 3; n++) {
for (i = 0; i < s_rela[n]->sh_size / s_rela[n]->sh_entsize; i++) {
Elf64_Rela *rela;
unsigned long type;
unsigned long addr;
void *out;
Elf64_Sym *sym;
char *name;
rela = (Elf64_Rela *)(data + s_rela[n]->sh_offset) + i;
type = ELF64_R_TYPE(rela->r_info);
out = xx_data[n] + rela->r_offset;
sym = &symtab[ELF64_R_SYM(rela->r_info)];
name = strtab + sym->st_name;
if (!aarch64_is_sym_und(sym)) {
char *section;
section = shstrtab + shdr[sym->st_shndx].sh_name;
if (!strcmp(section, ".text")) {
addr = rx_offset + text_offset;
} else if (!strcmp(section, ".rodata.str1.1")) {
addr = ro_offset + rodata_str_offset;
} else if (!strcmp(section, ".bss")) {
addr = rw_offset + bss_offset;
} else if (!strcmp(section, ".rodata")) {
addr = ro_offset + rodata_offset;
} else if (!strcmp(section, ".data")) {
addr = rw_offset + data_offset;
} else {
errx(1, "Unsupported section name %s\n", section);
return 1;
}
addr += sym->st_value + rela->r_addend;
} else {
if (aarch64_is_rela_function(rela)) {
addr = rx_offset;
} else {
addr = ro_offset;
addr |= 0x80000000u;
}
addr += koji_symbol_offset(koji_symbol, name);
}
switch (type) {
case R_AARCH64_CALL26: {
*((uint32_t *)(out)) = aarch64_encode_bl(rela->r_offset, addr);
break;
}
case R_AARCH64_JUMP26: {
*((uint32_t *)(out)) = aarch64_encode_b(rela->r_offset, addr);
break;
}
case R_AARCH64_ABS64:
case R_AARCH64_MOVW_UABS_G0_NC:
case R_AARCH64_MOVW_UABS_G1_NC:
case R_AARCH64_MOVW_UABS_G2_NC:
case R_AARCH64_MOVW_UABS_G3:
kr = koji_relocate_append(&koji_relocate);
kr->type = type;
kr->offset = xx_offset[n] + rela->r_offset;
kr->value = addr;
relocate_count += 1;
break;
default: {
errx(1, "Unknown relocation type %ld for %s\n", type, strtab + sym->st_name);
return 1;
}
}
}
}
symbol_size = 0;
ks = koji_symbol;
while (ks) {
symbol_size += sizeof(struct arch_symbol);
symbol_size += strlen(ks->name);
ks = ks->next;
}
printf("symbol %d | %d\n", symbol_size, symbol_count);
symbol_data = malloc(symbol_size);
tmp = symbol_data;
ks = koji_symbol;
while (ks) {
a_sym = tmp;
if (aarch64_is_type_function(ks->type)) {
struct aarch64_bridge *br;
br = (struct aarch64_bridge *)(rx_data + ks->offset);
br->ldr_x16_val = (uint32_t)0x58000050;
br->br_x16 = (uint32_t)0xd61f0200;
a_sym->offset = rx_offset + ks->offset + offsetof(struct aarch64_bridge, val);
} else {
a_sym->offset = ro_offset + ks->offset;
}
strcpy(a_sym->name, ks->name);
tmp += sizeof(struct arch_symbol);
tmp += strlen(ks->name);
ks = ks->next;
}
relocate_size = relocate_count * sizeof(struct arch_relocate);
printf("relocate %d | %d\n", relocate_size, relocate_count);
relocate_data = malloc(relocate_size);
kr = koji_relocate;
a_rel = (struct arch_relocate *)relocate_data;
while (kr) {
a_rel->type = kr->type;
a_rel->offset = kr->offset;
a_rel->value = kr->value;
kr = kr->next;
a_rel++;
}
head.magic = KOJI_MAGIC;
head.version = KOJI_VERSION;
head.machine = EM_AARCH64;
head.rx_offset = sizeof(head);
head.rx_size = rx_size;
head.ro_offset = head.rx_offset + head.rx_size;
head.ro_size = ro_size;
head.rw_offset = head.ro_offset + head.ro_size;
head.rw_size = rw_size;
head.rw_bss_offset = bss_offset;
head.symbol_offset = head.rw_offset + head.rw_bss_offset;
head.symbol_count = symbol_count;
head.relocate_offset = head.symbol_offset + symbol_size;
head.relocate_count = relocate_count;
head.size = sizeof(head) + total_size + symbol_size + relocate_size;
head.entry = entry;
fd = open(file, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (fd < 0) {
errx(1, "Could not open %s", file);
return 1;
}
write(fd, &head, sizeof(head));
write(fd, core_data, total_size);
write(fd, symbol_data, symbol_size);
write(fd, relocate_data, relocate_size);
close(fd);
return 0;
}
struct arm_bridge {
uint32_t ldr_pc_val;
uint32_t val;
} __attribute__((packed));
static int arm_is_sym_entry(Elf32_Sym *s, const char *strtab)
{
return (ELF32_ST_BIND(s->st_info) == STB_GLOBAL) &&
(ELF32_ST_TYPE(s->st_info) == STT_FUNC) &&
!strcmp(strtab + s->st_name, "koji_main");
}
static int arm_is_sym_und(Elf32_Sym *s)
{
return (ELF32_ST_BIND(s->st_info) == STB_GLOBAL) &&
(s->st_shndx == STN_UNDEF);
}
static int arm_is_rel_function(Elf32_Rel *r)
{
return (ELF32_R_TYPE(r->r_info) == R_ARM_CALL) ||
(ELF32_R_TYPE(r->r_info) == R_ARM_JUMP24);
}
static int arm_is_type_function(unsigned long type)
{
return (type == R_ARM_CALL) ||
(type == R_ARM_JUMP24);
}
static int arm_is_type_global(unsigned long type)
{
return !arm_is_type_function(type);
}
/* A8.8.25 */
static uint32_t arm_encode_bl(unsigned long from, unsigned long to)
{
int64_t offset;
offset = (signed) to - (signed) from - 8;
if (offset < 0x00800000 &&
offset >= -0x00800000) {
return 0xeb000000u | ((offset >> 2 ) & 0x00ffffff);
}
return 0xdeadbeefu;
}
/* A8.8.18 */
static uint32_t arm_encode_b(unsigned long from, unsigned long to)
{
int64_t offset;
offset = (signed) to - (signed) from - 8;
if (offset < 0x00800000 &&
offset >= -0x00800000) {
return 0xea000000u | ((offset >> 2) & 0x00ffffff);
}
return 0xdeadbeefu;
}
static int arm_generate(const char *file, void *data, size_t size)
{
Elf32_Ehdr *ehdr = data;
int i, j, n;
Elf32_Shdr *ssec;
Elf32_Shdr *shdr;
char *shstrtab = NULL;
char *strtab = NULL;
Elf32_Sym *symtab = NULL;
int symnum;
Elf32_Shdr s_zero;
Elf32_Shdr *s_text = &s_zero, *s_rodata = &s_zero, *s_rodata_str = &s_zero, *s_data = &s_zero, *s_bss = &s_zero;
Elf32_Shdr *s_rel_text = &s_zero, *s_rel_data = &s_zero, *s_rel_rodata = &s_zero;
Elf32_Shdr *s_rel[3] = { &s_zero, &s_zero, &s_zero };
int text_size, rodata_size, rodata_str_size, data_size, bss_size;
int text_offset, rodata_offset, rodata_str_offset, data_offset, bss_offset;
int bridge_count, bridge_size, bridge_offset;
int global_count, global_size, global_offset;
int rx_size, ro_size, rw_size;
int rx_offset, ro_offset, rw_offset;
int xx_offset[3];
int total_size;
int entry = -1;
void *core_data;
char *rx_data, *ro_data, *rw_data;
char *xx_data[3];
void *tmp;
struct koji_symbol *koji_symbol = NULL;
struct koji_symbol *ks;
int symbol_count = 0;
int symbol_size;
char *symbol_data;
struct koji_relocate *koji_relocate = NULL;
struct koji_relocate *kr;
int relocate_count = 0;
int relocate_size;
char *relocate_data;
struct arch_symbol *a_sym;
struct arch_relocate *a_rel;
struct koji_head head;
int fd;
shdr = data + ehdr->e_shoff;
ssec = &shdr[ehdr->e_shstrndx];
shstrtab = data + ssec->sh_offset;
/* sections */
memset(&s_zero, 0, sizeof(s_zero));
s_zero.sh_entsize = 1;
for (i = 0; i < ehdr->e_shnum; i++) {
const char *name = shstrtab + shdr[i].sh_name;
if (shdr[i].sh_type == SHT_NULL)
continue;
if (!strcmp(name, ".strtab"))
strtab = data + shdr[i].sh_offset;
if (!strcmp(name, ".symtab")) {
symtab = data + shdr[i].sh_offset;
symnum = shdr[i].sh_size / shdr[i].sh_entsize;
}
if (!strcmp(name, ".text"))
s_text = &shdr[i];
if (!strcmp(name, ".rodata"))
s_rodata = &shdr[i];
if (!strcmp(name, ".rodata.str1.1"))
s_rodata_str = &shdr[i];
if (!strcmp(name, ".data"))
s_data = &shdr[i];
if (!strcmp(name, ".bss"))
s_bss = &shdr[i];
if (!strcmp(name, ".rel.text")) {
s_rel_text = &shdr[i];
s_rel[0] = s_rel_text;
}
if (!strcmp(name, ".rel.rodata")) {
s_rel_rodata = &shdr[i];
s_rel[1] = s_rel_rodata;
}
if (!strcmp(name, ".rel.data")) {
s_rel_data = &shdr[i];
s_rel[2] = s_rel_data;
}
}
if (symtab == NULL) {
errx(1, "No .symtab.");
return 1;
}
bridge_count = 0;
global_count = 0;
rx_offset = 0;
xx_offset[0] = rx_offset;
text_offset = 0;
text_size = s_text->sh_size;
bridge_offset = ALIGN(text_offset + text_size, sizeof(struct arm_bridge));
for (i = 0; i < symnum; i++) {
Elf32_Sym *sym;
sym = &symtab[i];
/* entry */
if (entry < 0 && arm_is_sym_entry(sym, strtab)) {
entry = (int)sym->st_value;
continue;
}
if (arm_is_sym_und(sym)) {
ks = koji_symbol_append(&koji_symbol);
symbol_count += 1;
ks->name = strtab + sym->st_name;
for (j = 0; j < s_rel_text->sh_size / s_rel_text->sh_entsize; j++) {
Elf32_Rel *rel;
rel = (Elf32_Rel *)(data + s_rel_text->sh_offset) + j;
if (sym->st_name == symtab[ELF32_R_SYM(rel->r_info)].st_name) {
ks->type = ELF32_R_TYPE(rel->r_info);
if (arm_is_type_function(ks->type)) {
ks->offset = bridge_offset + bridge_count * sizeof(struct arm_bridge);
bridge_count += 1;
} else {
global_count += 1;
}
break;
}
}
// printf(" %s %ld\n", ks->name, ks->type);
continue;
}
}
if (entry < 0) {
errx(1, "No entry point.");
return 1;
}
bridge_size = bridge_count * sizeof(struct arm_bridge);
rx_size = bridge_offset + bridge_size;
printf("RX %d | text %d, function %d\n", rx_size, text_size, bridge_size);
ro_offset = PAGE_ALIGN(rx_size);
xx_offset[1] = ro_offset;
rodata_offset = 0;
rodata_size = s_rodata->sh_size;
rodata_str_offset = ALIGN(rodata_offset + rodata_size, s_rodata_str->sh_addralign);
rodata_str_size = s_rodata_str->sh_size;
global_offset = ALIGN(rodata_str_offset + rodata_str_size, sizeof(long));
global_size = global_count * sizeof(long);
ro_size = global_offset + global_size;
if (global_count) {
int i;
ks = koji_symbol;
i = 0;
while (ks) {
if (arm_is_type_global(ks->type)) {
ks->offset = global_offset + i * sizeof(long);
i++;
}
ks = ks->next;
}
}
printf("RO %d | rodata %d, string %d, global %d\n",
ro_size, rodata_size, rodata_str_size, global_size);
rw_offset = PAGE_ALIGN(ro_offset + ro_size);
xx_offset[2] = rw_offset;
data_offset = 0;
data_size = s_data->sh_size;
bss_offset = ALIGN(data_offset + data_size, s_bss->sh_addralign);
bss_size = s_bss->sh_size;
rw_size = bss_offset + bss_size;
printf("RW %d | data %d, bss %d\n", rw_size, data_size, bss_size);
/* link */
total_size = rx_size + ro_size + bss_offset;
core_data = malloc(total_size);
memset(core_data, 0, total_size);
rx_data = core_data;
xx_data[0] = rx_data;
memcpy(rx_data + text_offset, data + s_text->sh_offset, s_text->sh_size);
if (ro_size) {
ro_data = rx_data + rx_size;
memcpy(ro_data + rodata_offset, data + s_rodata->sh_offset, s_rodata->sh_size);
memcpy(ro_data + rodata_str_offset, data + s_rodata_str->sh_offset, s_rodata_str->sh_size);
xx_data[1] = ro_data;
}
if (rw_size) {
rw_data = ro_data + ro_size;
memcpy(rw_data + data_offset, data + s_data->sh_offset, s_data->sh_size);
xx_data[2] = rw_data;
}
for (n = 0; n < 3; n++) {
for (i = 0; i < s_rel[n]->sh_size / s_rel[n]->sh_entsize; i++) {
Elf32_Rel *rel;
unsigned long type;
unsigned long addr;
void *out;
Elf32_Sym *sym;
char *name;
rel = (Elf32_Rel *)(data + s_rel[n]->sh_offset) + i;
type = ELF32_R_TYPE(rel->r_info);
out = xx_data[n] + rel->r_offset;
sym = &symtab[ELF32_R_SYM(rel->r_info)];
name = strtab + sym->st_name;
if (!arm_is_sym_und(sym)) {
char *section;
section = shstrtab + shdr[sym->st_shndx].sh_name;
if (!strcmp(section, ".text")) {
addr = rx_offset + text_offset;
} else if (!strcmp(section, ".rodata.str1.1")) {
addr = ro_offset + rodata_str_offset;
} else if (!strcmp(section, ".bss")) {
addr = rw_offset + bss_offset;
} else if (!strcmp(section, ".rodata")) {
addr = ro_offset + rodata_offset;
} else if (!strcmp(section, ".data")) {
addr = rw_offset + data_offset;
} else {
errx(1, "Unsupported section name %s\n", section);
return 1;
}
addr += sym->st_value;
} else {
if (arm_is_rel_function(rel)) {
addr = rx_offset;
} else {
addr = ro_offset;
addr |= 0x80000000u;
}
addr += koji_symbol_offset(koji_symbol, name);
}
switch (type) {
case R_ARM_CALL: {
*((uint32_t *)(out)) = arm_encode_bl(rel->r_offset, addr);
break;
}
case R_ARM_JUMP24: {
*((uint32_t *)(out)) = arm_encode_b(rel->r_offset, addr);
break;
}
case R_ARM_ABS32:
case R_ARM_MOVW_ABS_NC:
case R_ARM_MOVT_ABS:
kr = koji_relocate_append(&koji_relocate);
kr->type = type;
kr->offset = xx_offset[n] + rel->r_offset;
kr->value = addr;
relocate_count += 1;
break;
default: {
errx(1, "Unknown relocation type %ld for %s\n", type, strtab + sym->st_name);
return 1;
}
}
}
}
symbol_size = 0;
ks = koji_symbol;
while (ks) {
symbol_size += sizeof(struct arch_symbol);
symbol_size += strlen(ks->name);
ks = ks->next;
}
printf("symbol %d | %d\n", symbol_size, symbol_count);
symbol_data = malloc(symbol_size);
tmp = symbol_data;
ks = koji_symbol;
while (ks) {
a_sym = tmp;
if (arm_is_type_function(ks->type)) {
struct arm_bridge *br;
br = (struct arm_bridge *)(rx_data + ks->offset);
br->ldr_pc_val = (uint32_t)0xe51ff004u;
a_sym->offset = rx_offset + ks->offset + offsetof(struct arm_bridge, val);
} else {
a_sym->offset = ro_offset + ks->offset;
}
strcpy(a_sym->name, ks->name);
tmp += sizeof(struct arch_symbol);
tmp += strlen(ks->name);
ks = ks->next;
}
relocate_size = relocate_count * sizeof(struct arch_relocate);
printf("relocate %d | %d\n", relocate_size, relocate_count);
relocate_data = malloc(relocate_size);
kr = koji_relocate;
a_rel = (struct arch_relocate *)relocate_data;
while (kr) {
a_rel->type = kr->type;
a_rel->offset = kr->offset;
a_rel->value = kr->value;
kr = kr->next;
a_rel++;
}
head.magic = KOJI_MAGIC;
head.version = KOJI_VERSION;
head.machine = EM_ARM;
head.rx_offset = sizeof(head);
head.rx_size = rx_size;
head.ro_offset = head.rx_offset + head.rx_size;
head.ro_size = ro_size;
head.rw_offset = head.ro_offset + head.ro_size;
head.rw_size = rw_size;
head.rw_bss_offset = bss_offset;
head.symbol_offset = head.rw_offset + head.rw_bss_offset;
head.symbol_count = symbol_count;
head.relocate_offset = head.symbol_offset + symbol_size;
head.relocate_count = relocate_count;
head.size = sizeof(head) + total_size + symbol_size + relocate_size;
head.entry = entry;
fd = open(file, O_WRONLY|O_CREAT|O_TRUNC, 0644);
if (fd < 0) {
errx(1, "Could not open %s", file);
return 1;
}
write(fd, &head, sizeof(head));
write(fd, core_data, total_size);
write(fd, symbol_data, symbol_size);
write(fd, relocate_data, relocate_size);
close(fd);
return 0;
}
static int is_aarch64(const void *data, size_t size)
{
const Elf64_Ehdr *ehdr = data;
if (memcmp(ehdr, ELFMAG, 4))
return 0;
if (ehdr->e_type != ET_REL)
return 0;
return ehdr->e_machine == EM_AARCH64;
}
static int is_arm(const void *data, size_t size)
{
const Elf32_Ehdr *ehdr = data;
if (memcmp(ehdr, ELFMAG, 4))
return 0;
if (ehdr->e_type != ET_REL)
return 0;
return ehdr->e_machine == EM_ARM;
}
int main(int argc, char *argv[])
{
int rc;
int fd;
struct stat fs;
void *data;
size_t size;
if (argc != 3)
return 1;
fd = open(argv[1], O_RDONLY);
if (fd < 0) {
perror("open");
return 1;
}
rc = fstat(fd, &fs);
if (rc < 0) {
perror("fstat");
close(fd);
return 1;
}
size = fs.st_size;
if (size < sizeof(Elf64_Ehdr)) {
close(fd);
return 1;
}
data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (data == MAP_FAILED) {
perror("mmap");
close(fd);
return 1;
}
if (is_aarch64(data, size))
rc = aarch64_generate(argv[2], data, size);
else if (is_arm(data, size))
rc = arm_generate(argv[2], data, size);
else
rc = 1;
munmap(data, size);
close(fd);
return rc;
}
@sl4v3k
Copy link

sl4v3k commented Dec 15, 2023

Hi, What is the purpose of this packer? Does this packer format suppose to run and self unpack in memory?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment