Skip to content

Commit 1767ae2

Browse files
committed
fix: injection fail of libzygisk.so
This commit fixes the issue where Remote CSOLoader would fail to inject "libzygisk.so" due to high kernel version needed for setting name in anon VMAs. Switched to file backed to ensure maximum compatibility.
1 parent 1c07641 commit 1767ae2

1 file changed

Lines changed: 164 additions & 109 deletions

File tree

loader/src/ptracer/remote_csoloader.c

Lines changed: 164 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
#include <stdlib.h>
1111
#include <string.h>
1212
#include <sys/mman.h>
13-
#include <sys/prctl.h>
1413
#include <unistd.h>
1514

1615
#include <elf.h>
@@ -414,57 +413,6 @@ static bool find_dynsym_value(int fd, const struct elf_dyn_info *info, const cha
414413
return false;
415414
}
416415

417-
/* INFO: Apply VMA names to anonymous mappings in a region via remote prctl. */
418-
static bool apply_vma_names(int pid, struct user_regs_struct *regs, uintptr_t prctl_addr,
419-
uintptr_t libc_return_addr, uintptr_t region_start, size_t region_len,
420-
uintptr_t name_ptr) {
421-
char pid_maps[64];
422-
snprintf(pid_maps, sizeof(pid_maps), "/proc/%d/maps", pid);
423-
424-
struct maps *current_maps = parse_maps(pid_maps);
425-
if (!current_maps) {
426-
LOGE("Failed to parse remote process maps");
427-
428-
return false;
429-
}
430-
431-
uintptr_t region_end = region_start + region_len;
432-
struct user_regs_struct call_regs;
433-
434-
for (size_t i = 0; i < current_maps->size; i++) {
435-
struct map *m = &current_maps->maps[i];
436-
437-
/* INFO: Skip VMAs outside our region */
438-
if (m->end <= region_start || m->start >= region_end) continue;
439-
440-
/* INFO: Skip non-anonymous mappings (backed by files) */
441-
if (m->dev != 0 || m->inode != 0) continue;
442-
443-
size_t vma_len = m->end - m->start;
444-
445-
call_regs = *regs;
446-
long prctl_args[5];
447-
prctl_args[0] = PR_SET_VMA;
448-
prctl_args[1] = PR_SET_VMA_ANON_NAME;
449-
prctl_args[2] = (long)m->start;
450-
prctl_args[3] = (long)vma_len;
451-
prctl_args[4] = (long)name_ptr;
452-
453-
uintptr_t rc = remote_call(pid, &call_regs, prctl_addr, libc_return_addr, prctl_args, 5);
454-
if (rc != 0) {
455-
LOGE("Failed to set VMA name for region 0x%" PRIxPTR "-0x%" PRIxPTR ": prctl returned %p", (uintptr_t)m->start, (uintptr_t)m->end, (void *)rc);
456-
457-
free_maps(current_maps);
458-
459-
return false;
460-
}
461-
}
462-
463-
free_maps(current_maps);
464-
465-
return true;
466-
}
467-
468416
#ifdef __LP64__
469417
#define ELF_R_TYPE ELF64_R_TYPE
470418
#define ELF_R_SYM ELF64_R_SYM
@@ -759,29 +707,91 @@ bool remote_csoloader_load_and_resolve_entry(int pid, struct user_regs_struct *r
759707
return false;
760708
}
761709

762-
void *prctl_addr = find_func_addr(local_map, remote_map, libc_path, "prctl");
763-
if (!prctl_addr) {
764-
LOGE("Failed to resolve remote prctl");
710+
void *open_addr = find_func_addr(local_map, remote_map, libc_path, "open");
711+
if (!open_addr) {
712+
LOGE("Failed to resolve remote open");
713+
714+
free(phdr);
715+
close(fd);
716+
717+
return false;
718+
}
719+
720+
void *close_addr = find_func_addr(local_map, remote_map, libc_path, "close");
721+
if (!close_addr) {
722+
LOGE("Failed to resolve remote close");
765723

766724
free(phdr);
767725
close(fd);
768726

769727
return false;
770728
}
771729

772-
/* INFO: Reserve address space with PROT_NONE */
773730
long args[6];
731+
732+
/* INFO: Copy library path into remote memory for file-backed mappings */
733+
size_t path_len = strlen(lib_path) + 1;
734+
args[0] = 0;
735+
args[1] = (long)path_len;
736+
args[2] = PROT_READ | PROT_WRITE;
737+
args[3] = MAP_PRIVATE | MAP_ANONYMOUS;
738+
args[4] = -1;
739+
args[5] = 0;
740+
741+
struct user_regs_struct call_regs = regs_saved;
742+
uintptr_t remote_path = remote_call(pid, &call_regs, (uintptr_t)mmap_addr, libc_return_addr, args, 6);
743+
if (!remote_path || remote_path == (uintptr_t)MAP_FAILED) {
744+
LOGE("remote mmap for path failed: %p", (void *)remote_path);
745+
746+
free(phdr);
747+
close(fd);
748+
749+
return false;
750+
}
751+
752+
if (write_proc(pid, remote_path, lib_path, path_len) != (ssize_t)path_len) {
753+
LOGE("Failed to write remote path string");
754+
755+
free(phdr);
756+
close(fd);
757+
758+
return false;
759+
}
760+
761+
args[0] = (long)remote_path;
762+
args[1] = O_RDONLY | O_CLOEXEC;
763+
args[2] = 0;
764+
765+
call_regs = regs_saved;
766+
long remote_fd = (long)remote_call(pid, &call_regs, (uintptr_t)open_addr, libc_return_addr, args, 3);
767+
if (remote_fd < 0) {
768+
LOGE("Failed to open remote file: %s", lib_path);
769+
770+
free(phdr);
771+
close(fd);
772+
773+
return false;
774+
}
775+
776+
/* INFO: Reserve address space with PROT_NONE */
774777
args[0] = 0;
775778
args[1] = (long)map_size;
776779
args[2] = PROT_NONE;
777780
args[3] = MAP_PRIVATE | MAP_ANONYMOUS;
778781
args[4] = -1;
779782
args[5] = 0;
780783

781-
struct user_regs_struct call_regs = regs_saved;
784+
call_regs = regs_saved;
782785
uintptr_t remote_base = remote_call(pid, &call_regs, (uintptr_t)mmap_addr, libc_return_addr, args, 6);
783786
if (!remote_base || remote_base == (uintptr_t)MAP_FAILED) {
784787
LOGE("remote mmap reserve failed: %p", (void *)remote_base);
788+
789+
call_regs = regs_saved;
790+
791+
args[0] = remote_fd;
792+
793+
remote_call(pid, &call_regs, (uintptr_t)close_addr, libc_return_addr, args, 1);
794+
785795
free(phdr);
786796
close(fd);
787797

@@ -799,7 +809,7 @@ bool remote_csoloader_load_and_resolve_entry(int pid, struct user_regs_struct *r
799809

800810
size_t segs_count = 0;
801811

802-
/* INFO: Map each PT_LOAD as anonymous RW, copy file bytes, record final permissions */
812+
/* INFO: Map non-writable PT_LOAD from file */
803813
for (int i = 0; i < eh.e_phnum; i++) {
804814
if (phdr[i].p_type != PT_LOAD) continue;
805815

@@ -809,39 +819,106 @@ bool remote_csoloader_load_and_resolve_entry(int pid, struct user_regs_struct *r
809819
uintptr_t seg_page_end = page_end(seg_end, page_size);
810820
size_t seg_page_len = (size_t)(seg_page_end - seg_page);
811821

812-
args[0] = (long)seg_page;
813-
args[1] = (long)seg_page_len;
814-
args[2] = PROT_READ | PROT_WRITE;
815-
args[3] = MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS;
816-
args[4] = -1;
817-
args[5] = 0;
822+
bool is_writable = (phdr[i].p_flags & PF_W) != 0;
818823

819-
call_regs = regs_saved;
820-
uintptr_t seg_map = remote_call(pid, &call_regs, (uintptr_t)mmap_addr, libc_return_addr, args, 6);
821-
if (!seg_map || seg_map == (uintptr_t)MAP_FAILED) {
822-
LOGE("remote mmap segment failed for phdr %d", i);
823-
free(phdr);
824-
close(fd);
824+
if (is_writable) {
825+
args[0] = (long)seg_page;
826+
args[1] = (long)seg_page_len;
827+
args[2] = PROT_READ | PROT_WRITE;
828+
args[3] = MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS;
829+
args[4] = -1;
830+
args[5] = 0;
825831

826-
return false;
827-
}
832+
call_regs = regs_saved;
833+
uintptr_t seg_map = remote_call(pid, &call_regs, (uintptr_t)mmap_addr, libc_return_addr, args, 6);
834+
if (!seg_map || seg_map == (uintptr_t)MAP_FAILED) {
835+
LOGE("remote mmap writable segment failed for phdr %d", i);
828836

829-
/* INFO: Copy segment file content to remote in one shot. BSS (p_memsz > p_filesz)
830-
is already zeroed by kernel since we used MAP_ANONYMOUS. */
831-
size_t filesz = (size_t)phdr[i].p_filesz;
832-
if (filesz > 0) {
833-
char *buf = (char *)malloc(filesz);
834-
if (!buf || !read_loop_offset(fd, buf, filesz, (off_t)phdr[i].p_offset) || write_proc(pid, seg_start, buf, filesz) != (ssize_t)filesz) {
835-
LOGE("Failed to copy segment data for phdr %d", i);
837+
call_regs = regs_saved;
836838

837-
free(buf);
839+
args[0] = remote_fd;
840+
841+
remote_call(pid, &call_regs, (uintptr_t)close_addr, libc_return_addr, args, 1);
838842
free(phdr);
839843
close(fd);
840844

841845
return false;
842846
}
843847

844-
free(buf);
848+
size_t filesz = (size_t)phdr[i].p_filesz;
849+
if (filesz > 0) {
850+
char *buf = (char *)malloc(filesz);
851+
if (!buf || !read_loop_offset(fd, buf, filesz, (off_t)phdr[i].p_offset) || write_proc(pid, seg_start, buf, filesz) != (ssize_t)filesz) {
852+
LOGE("Failed to copy segment data for phdr %d", i);
853+
854+
free(buf);
855+
free(phdr);
856+
close(fd);
857+
858+
return false;
859+
}
860+
861+
free(buf);
862+
}
863+
} else {
864+
off_t seg_offset = (off_t)phdr[i].p_offset;
865+
off_t file_page_offset = (off_t)page_start((uintptr_t)seg_offset, page_size);
866+
uintptr_t file_end = (uintptr_t)phdr[i].p_vaddr + (uintptr_t)phdr[i].p_filesz + load_bias;
867+
uintptr_t file_page_end = page_end(file_end, page_size);
868+
869+
if (phdr[i].p_filesz > 0) {
870+
call_regs = regs_saved;
871+
872+
size_t file_map_len = (size_t)(file_page_end - seg_page);
873+
args[0] = (long)seg_page;
874+
args[1] = (long)file_map_len;
875+
args[2] = PROT_READ | PROT_WRITE;
876+
args[3] = MAP_FIXED | MAP_PRIVATE;
877+
args[4] = remote_fd;
878+
args[5] = (long)file_page_offset;
879+
880+
uintptr_t seg_map = remote_call(pid, &call_regs, (uintptr_t)mmap_addr, libc_return_addr, args, 6);
881+
if (!seg_map || seg_map == (uintptr_t)MAP_FAILED) {
882+
LOGE("remote mmap file-backed segment failed for phdr %d", i);
883+
884+
call_regs = regs_saved;
885+
886+
args[0] = remote_fd;
887+
888+
remote_call(pid, &call_regs, (uintptr_t)close_addr, libc_return_addr, args, 1);
889+
free(phdr);
890+
close(fd);
891+
892+
return false;
893+
}
894+
}
895+
896+
if (seg_page_end > file_page_end) {
897+
call_regs = regs_saved;
898+
899+
args[0] = (long)file_page_end;
900+
args[1] = (long)(seg_page_end - file_page_end);
901+
args[2] = PROT_READ | PROT_WRITE;
902+
args[3] = MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS;
903+
args[4] = -1;
904+
args[5] = 0;
905+
906+
uintptr_t bss_map = remote_call(pid, &call_regs, (uintptr_t)mmap_addr, libc_return_addr, args, 6);
907+
if (!bss_map || bss_map == (uintptr_t)MAP_FAILED) {
908+
LOGE("remote mmap bss segment failed for phdr %d", i);
909+
910+
call_regs = regs_saved;
911+
912+
args[0] = remote_fd;
913+
914+
call_regs = regs_saved;
915+
remote_call(pid, &call_regs, (uintptr_t)close_addr, libc_return_addr, args, 1);
916+
free(phdr);
917+
close(fd);
918+
919+
return false;
920+
}
921+
}
845922
}
846923

847924
/* INFO: Record segment info for later protection finalization */
@@ -859,6 +936,12 @@ bool remote_csoloader_load_and_resolve_entry(int pid, struct user_regs_struct *r
859936
}
860937
}
861938

939+
call_regs = regs_saved;
940+
941+
args[0] = remote_fd;
942+
943+
remote_call(pid, &call_regs, (uintptr_t)close_addr, libc_return_addr, args, 1);
944+
862945
struct elf_dyn_info dinfo;
863946
if (!elf_load_dyn_info(fd, &eh, phdr, &dinfo)) {
864947
LOGE("Failed to load ELF dynamic info");
@@ -914,34 +997,6 @@ bool remote_csoloader_load_and_resolve_entry(int pid, struct user_regs_struct *r
914997
remote_call(pid, &call_regs, (uintptr_t)mprotect_addr, libc_return_addr, args, 3);
915998
}
916999

917-
/* INFO: Some kernels drop names if string pointer invalidates.
918-
We already have the ELF parsed, so look up symbol inline and use the helper. */
919-
ElfW(Addr) name_sym_value = 0;
920-
if (!find_dynsym_value(fd, &dinfo, "k_library_name", &name_sym_value)) {
921-
LOGE("Failed to resolve k_library_name from ELF dynsym");
922-
923-
free((void *)needed_paths);
924-
elf_dyn_info_destroy(&dinfo);
925-
free(phdr);
926-
close(fd);
927-
928-
return false;
929-
}
930-
931-
call_regs = regs_saved;
932-
933-
uintptr_t name_ptr = (uintptr_t)load_bias + (uintptr_t)name_sym_value;
934-
if (!apply_vma_names(pid, &call_regs, (uintptr_t)prctl_addr, libc_return_addr, remote_base, map_size, name_ptr)) {
935-
LOGE("Failed to apply VMA names");
936-
937-
free((void *)needed_paths);
938-
elf_dyn_info_destroy(&dinfo);
939-
free(phdr);
940-
close(fd);
941-
942-
return false;
943-
}
944-
9451000
ElfW(Addr) entry_value = 0;
9461001
if (!find_dynsym_value(fd, &dinfo, "entry", &entry_value)) {
9471002
LOGE("Failed to resolve entry from ELF dynsym");

0 commit comments

Comments
 (0)