@@ -771,7 +771,7 @@ def apply_targeted_patches(frida_dir: Path, custom_name: str, frida_major: int):
771771
772772
773773def apply_strict_wx_patch (frida_dir : Path ) -> None :
774- """Disable Gum's persistent anonymous RWX code pools on Android."""
774+ """Disable persistent anonymous RWX mappings owned by Frida on Android."""
775775 allocator_boxed_types = (
776776 "G_DEFINE_BOXED_TYPE (GumCodeSlice, gum_code_slice, gum_code_slice_ref,\n "
777777 " gum_code_slice_unref)\n "
@@ -803,6 +803,24 @@ def apply_strict_wx_patch(frida_dir: Path) -> None:
803803 1 ,
804804 "Android allocator policy scoped" ,
805805 ),
806+ (
807+ Path ("subprojects/frida-gum/gum/gummemory.c" ),
808+ " restored = ((original_protections[i] & GUM_PAGE_WRITE) != 0)\n "
809+ " ? GUM_PAGE_RWX\n "
810+ " : GUM_PAGE_RX;" ,
811+ "#if defined (HAVE_ANDROID)\n "
812+ " restored = ((original_protections[i] & GUM_PAGE_WRITE) != 0 &&\n "
813+ " (original_protections[i] & GUM_PAGE_EXECUTE) != 0)\n "
814+ " ? GUM_PAGE_RWX\n "
815+ " : GUM_PAGE_RX;\n "
816+ "#else\n "
817+ " restored = ((original_protections[i] & GUM_PAGE_WRITE) != 0)\n "
818+ " ? GUM_PAGE_RWX\n "
819+ " : GUM_PAGE_RX;\n "
820+ "#endif" ,
821+ 1 ,
822+ "new Android code pages finish RX" ,
823+ ),
806824 * (
807825 (
808826 Path (relative_path ),
@@ -821,6 +839,272 @@ def apply_strict_wx_patch(frida_dir: Path) -> None:
821839 "subprojects/frida-gum/gum/backend-x86/gumstalker-x86.c" ,
822840 )
823841 ),
842+ (
843+ Path ("subprojects/frida-core/src/linux/frida-helper-backend.vala" ),
844+ "\t \t private static uint64 mmap_offset;\n \t \t private static uint64 munmap_offset;" ,
845+ "\t \t private static uint64 mmap_offset;\n "
846+ "\t \t private static uint64 mprotect_offset;\n "
847+ "\t \t private static uint64 munmap_offset;" ,
848+ 1 ,
849+ "track remote mprotect" ,
850+ ),
851+ (
852+ Path ("subprojects/frida-core/src/linux/frida-helper-backend.vala" ),
853+ '\t \t \t mmap_offset = (uint64) (uintptr) libc.find_export_by_name ("mmap")'
854+ " - local_libc.start;\n "
855+ '\t \t \t munmap_offset = (uint64) (uintptr) libc.find_export_by_name ("munmap")'
856+ " - local_libc.start;" ,
857+ '\t \t \t mmap_offset = (uint64) (uintptr) libc.find_export_by_name ("mmap")'
858+ " - local_libc.start;\n "
859+ "\t \t \t mprotect_offset = (uint64) (uintptr) libc.find_export_by_name "
860+ '("mprotect") - local_libc.start;\n '
861+ '\t \t \t munmap_offset = (uint64) (uintptr) libc.find_export_by_name ("munmap")'
862+ " - local_libc.start;" ,
863+ 1 ,
864+ "resolve remote mprotect" ,
865+ ),
866+ (
867+ Path ("subprojects/frida-core/src/linux/frida-helper-backend.vala" ),
868+ "\t \t \t uint64 loader_base = (uintptr) bres.context.allocation_base;\n "
869+ "\t \t \t GPRegs regs = saved_regs;" ,
870+ "\t \t \t uint64 loader_base = (uintptr) bres.context.allocation_base;\n "
871+ "#if ANDROID\n "
872+ "\t \t \t yield protect_memory ((uintptr) bres.libc.mprotect,\n "
873+ "\t \t \t \t loader_base + loader_layout.ctx_offset,\n "
874+ "\t \t \t \t loader_layout.size - loader_layout.ctx_offset,\n "
875+ "\t \t \t \t Posix.PROT_READ | Posix.PROT_WRITE, cancellable);\n "
876+ "#endif\n "
877+ "\t \t \t GPRegs regs = saved_regs;" ,
878+ 1 ,
879+ "loader context is writable but not executable" ,
880+ ),
881+ (
882+ Path ("subprojects/frida-core/src/linux/frida-helper-backend.vala" ),
883+ "\t \t \t uint64 remote_mmap = 0;\n \t \t \t uint64 remote_munmap = 0;" ,
884+ "\t \t \t uint64 remote_mmap = 0;\n "
885+ "\t \t \t uint64 remote_mprotect = 0;\n "
886+ "\t \t \t uint64 remote_munmap = 0;" ,
887+ 1 ,
888+ "track target mprotect" ,
889+ ),
890+ (
891+ Path ("subprojects/frida-core/src/linux/frida-helper-backend.vala" ),
892+ "\t \t \t \t remote_mmap = remote_libc.start + mmap_offset;\n "
893+ "\t \t \t \t remote_munmap = remote_libc.start + munmap_offset;" ,
894+ "\t \t \t \t remote_mmap = remote_libc.start + mmap_offset;\n "
895+ "\t \t \t \t remote_mprotect = remote_libc.start + mprotect_offset;\n "
896+ "\t \t \t \t remote_munmap = remote_libc.start + munmap_offset;" ,
897+ 1 ,
898+ "locate target mprotect" ,
899+ ),
900+ (
901+ Path ("subprojects/frida-core/src/linux/frida-helper-backend.vala" ),
902+ "\t \t \t if (remote_mmap != 0) {\n "
903+ "\t \t \t \t allocation_base = yield allocate_memory (remote_mmap, allocation_size,\n "
904+ "\t \t \t \t \t Posix.PROT_READ | Posix.PROT_WRITE | Posix.PROT_EXEC, cancellable);\n "
905+ "\t \t \t } else {" ,
906+ "\t \t \t if (remote_mmap != 0) {\n "
907+ "#if ANDROID\n "
908+ "\t \t \t \t allocation_base = yield allocate_memory (remote_mmap, allocation_size,\n "
909+ "\t \t \t \t \t Posix.PROT_READ | Posix.PROT_EXEC, cancellable);\n "
910+ "\t \t \t \t try {\n "
911+ "\t \t \t \t \t yield protect_memory (remote_mprotect,\n "
912+ "\t \t \t \t \t \t allocation_base + allocation_size - stack_size, stack_size,\n "
913+ "\t \t \t \t \t \t Posix.PROT_READ | Posix.PROT_WRITE, cancellable);\n "
914+ "\t \t \t \t } catch (GLib.Error e) {\n "
915+ "\t \t \t \t \t try {\n "
916+ "\t \t \t \t \t \t yield deallocate_memory (remote_munmap, allocation_base,\n "
917+ "\t \t \t \t \t \t \t allocation_size, null);\n "
918+ "\t \t \t \t \t } catch (GLib.Error ignored) {\n "
919+ "\t \t \t \t \t }\n "
920+ "\t \t \t \t \t throw_api_error (e);\n "
921+ "\t \t \t \t }\n "
922+ "#else\n "
923+ "\t \t \t \t allocation_base = yield allocate_memory (remote_mmap, allocation_size,\n "
924+ "\t \t \t \t \t Posix.PROT_READ | Posix.PROT_WRITE | Posix.PROT_EXEC, cancellable);\n "
925+ "#endif\n "
926+ "\t \t \t } else {" ,
927+ 1 ,
928+ "bootstrap code and stack use disjoint permissions" ,
929+ ),
930+ (
931+ Path ("subprojects/frida-core/src/linux/frida-helper-backend.vala" ),
932+ "\t \t \t \t bootstrap_ctx.allocation_size = allocation_size;\n "
933+ "\t \t \t \t write_memory (bootstrap_ctx_location, (uint8[]) &bootstrap_ctx);" ,
934+ "\t \t \t \t bootstrap_ctx.allocation_size = allocation_size;\n "
935+ "\t \t \t \t bootstrap_ctx.stack_size = stack_size;\n "
936+ "\t \t \t \t write_memory (bootstrap_ctx_location, (uint8[]) &bootstrap_ctx);" ,
937+ 1 ,
938+ "pass allocation bootstrap stack size" ,
939+ ),
940+ (
941+ Path ("subprojects/frida-core/src/linux/frida-helper-backend.vala" ),
942+ "\t \t \t \t \t bootstrap_ctx.allocation_size = allocation_size;\n "
943+ "\t \t \t \t \t bootstrap_ctx.page_size = Gum.query_page_size ();" ,
944+ "\t \t \t \t \t bootstrap_ctx.allocation_size = allocation_size;\n "
945+ "\t \t \t \t \t bootstrap_ctx.stack_size = stack_size;\n "
946+ "\t \t \t \t \t bootstrap_ctx.page_size = Gum.query_page_size ();" ,
947+ 1 ,
948+ "pass runtime bootstrap stack size" ,
949+ ),
950+ (
951+ Path ("subprojects/frida-core/src/linux/frida-helper-backend.vala" ),
952+ "\t \t void * allocation_base;\n \t \t size_t allocation_size;\n \n \t \t size_t page_size;" ,
953+ "\t \t void * allocation_base;\n "
954+ "\t \t size_t allocation_size;\n "
955+ "\t \t size_t stack_size;\n \n "
956+ "\t \t size_t page_size;" ,
957+ 1 ,
958+ "bootstrap context records stack size" ,
959+ ),
960+ (
961+ Path ("subprojects/frida-core/src/linux/frida-helper-backend.vala" ),
962+ "\t \t void * mmap;\n \t \t void * munmap;" ,
963+ "\t \t void * mmap;\n \t \t void * mprotect;\n \t \t void * munmap;" ,
964+ 1 ,
965+ "bootstrap resolves mprotect" ,
966+ ),
967+ (
968+ Path ("subprojects/frida-core/src/linux/frida-helper-backend.vala" ),
969+ "\t \t public async void deallocate_memory (uint64 munmap_impl, uint64 address, "
970+ "size_t size, Cancellable? cancellable)" ,
971+ "\t \t public async void protect_memory (uint64 mprotect_impl, uint64 address, "
972+ "size_t size, int prot,\n "
973+ "\t \t \t \t Cancellable? cancellable) throws Error, IOError {\n "
974+ "\t \t \t var builder = new RemoteCallBuilder (mprotect_impl, saved_regs);\n "
975+ "\t \t \t builder\n "
976+ "\t \t \t \t .add_argument (address)\n "
977+ "\t \t \t \t .add_argument (size)\n "
978+ "\t \t \t \t .add_argument (prot);\n "
979+ "\t \t \t RemoteCallResult res = yield builder.build (this).execute (cancellable);\n "
980+ "\t \t \t if (res.status != COMPLETED)\n "
981+ '\t \t \t \t throw new Error.NOT_SUPPORTED ("Unexpected crash while trying to '
982+ 'protect memory");\n '
983+ "\t \t \t if (res.return_value != 0)\n "
984+ '\t \t \t \t throw new Error.NOT_SUPPORTED ("Unexpected failure while trying to '
985+ 'protect memory");\n '
986+ "\t \t }\n \n "
987+ "\t \t public async void deallocate_memory (uint64 munmap_impl, uint64 address, "
988+ "size_t size, Cancellable? cancellable)" ,
989+ 1 ,
990+ "add remote mprotect call" ,
991+ ),
992+ (
993+ Path ("subprojects/frida-core/src/linux/helpers/bootstrapper.c" ),
994+ "static int frida_socketpair (int domain, int type, int protocol, int sv[2]);\n "
995+ "static int frida_prctl (int option, unsigned long arg2, unsigned long arg3, "
996+ "unsigned long arg4, unsigned long arg5);" ,
997+ "static int frida_mprotect (void * address, size_t size, int prot);\n "
998+ "static int frida_socketpair (int domain, int type, int protocol, int sv[2]);\n "
999+ "static int frida_prctl (int option, unsigned long arg2, unsigned long arg3, "
1000+ "unsigned long arg4, unsigned long arg5);" ,
1001+ 1 ,
1002+ "declare raw mprotect helper" ,
1003+ ),
1004+ (
1005+ Path ("subprojects/frida-core/src/linux/helpers/bootstrapper.c" ),
1006+ " if (ctx->allocation_base == NULL)\n "
1007+ " {\n "
1008+ " ctx->allocation_base = mmap (NULL, ctx->allocation_size, "
1009+ "PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n "
1010+ " return (ctx->allocation_base == MAP_FAILED)\n "
1011+ " ? FRIDA_BOOTSTRAP_ALLOCATION_ERROR\n "
1012+ " : FRIDA_BOOTSTRAP_ALLOCATION_SUCCESS;\n "
1013+ " }" ,
1014+ " if (ctx->allocation_base == NULL)\n "
1015+ " {\n "
1016+ "#ifdef __ANDROID__\n "
1017+ " void * stack_base;\n \n "
1018+ " ctx->allocation_base = mmap (NULL, ctx->allocation_size, "
1019+ "PROT_READ | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n "
1020+ " if (ctx->allocation_base == MAP_FAILED)\n "
1021+ " return FRIDA_BOOTSTRAP_ALLOCATION_ERROR;\n \n "
1022+ " stack_base = (uint8_t *) ctx->allocation_base + ctx->allocation_size - "
1023+ "ctx->stack_size;\n "
1024+ " if (frida_mprotect (stack_base, ctx->stack_size, "
1025+ "PROT_READ | PROT_WRITE) != 0)\n "
1026+ " {\n "
1027+ " munmap (ctx->allocation_base, ctx->allocation_size);\n "
1028+ " ctx->allocation_base = MAP_FAILED;\n "
1029+ " return FRIDA_BOOTSTRAP_ALLOCATION_ERROR;\n "
1030+ " }\n \n "
1031+ " return FRIDA_BOOTSTRAP_ALLOCATION_SUCCESS;\n "
1032+ "#else\n "
1033+ " ctx->allocation_base = mmap (NULL, ctx->allocation_size, "
1034+ "PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);\n "
1035+ " return (ctx->allocation_base == MAP_FAILED)\n "
1036+ " ? FRIDA_BOOTSTRAP_ALLOCATION_ERROR\n "
1037+ " : FRIDA_BOOTSTRAP_ALLOCATION_SUCCESS;\n "
1038+ "#endif\n "
1039+ " }" ,
1040+ 1 ,
1041+ "fallback bootstrap splits code and stack" ,
1042+ ),
1043+ (
1044+ Path ("subprojects/frida-core/src/linux/helpers/bootstrapper.c" ),
1045+ " ctx.total_missing = 17;" ,
1046+ " ctx.total_missing = 18;" ,
1047+ 1 ,
1048+ "require mprotect" ,
1049+ ),
1050+ (
1051+ Path ("subprojects/frida-core/src/linux/helpers/bootstrapper.c" ),
1052+ " FRIDA_TRY_COLLECT (mmap)\n FRIDA_TRY_COLLECT (munmap)" ,
1053+ " FRIDA_TRY_COLLECT (mmap)\n "
1054+ " FRIDA_TRY_COLLECT (mprotect)\n "
1055+ " FRIDA_TRY_COLLECT (munmap)" ,
1056+ 1 ,
1057+ "collect mprotect" ,
1058+ ),
1059+ (
1060+ Path ("subprojects/frida-core/src/linux/helpers/bootstrapper.c" ),
1061+ "static int\n frida_socketpair (int domain, int type, int protocol, int sv[2])" ,
1062+ "static int\n "
1063+ "frida_mprotect (void * address, size_t size, int prot)\n "
1064+ "{\n "
1065+ "#ifdef NOLIBC\n "
1066+ " return my_syscall3 (__NR_mprotect, address, size, prot);\n "
1067+ "#else\n "
1068+ " return mprotect (address, size, prot);\n "
1069+ "#endif\n "
1070+ "}\n \n "
1071+ "static int\n "
1072+ "frida_socketpair (int domain, int type, int protocol, int sv[2])" ,
1073+ 1 ,
1074+ "implement raw mprotect helper" ,
1075+ ),
1076+ (
1077+ Path ("subprojects/frida-core/src/linux/helpers/inject-context.h" ),
1078+ " void * allocation_base;\n size_t allocation_size;\n \n size_t page_size;" ,
1079+ " void * allocation_base;\n "
1080+ " size_t allocation_size;\n "
1081+ " size_t stack_size;\n \n "
1082+ " size_t page_size;" ,
1083+ 1 ,
1084+ "align C bootstrap context" ,
1085+ ),
1086+ (
1087+ Path ("subprojects/frida-core/src/linux/helpers/inject-context.h" ),
1088+ " void * (* mmap) (void * addr, size_t length, int prot, int flags, int fd, "
1089+ "off_t offset);\n "
1090+ " int (* munmap) (void * addr, size_t length);" ,
1091+ " void * (* mmap) (void * addr, size_t length, int prot, int flags, int fd, "
1092+ "off_t offset);\n "
1093+ " int (* mprotect) (void * addr, size_t length, int prot);\n "
1094+ " int (* munmap) (void * addr, size_t length);" ,
1095+ 1 ,
1096+ "align C libc API" ,
1097+ ),
1098+ (
1099+ Path ("subprojects/frida-core/src/linux/proc-mem-injector.vala" ),
1100+ '\t \t \t api.table.mmap = resolve_one (remote_maps, "mmap");\n '
1101+ '\t \t \t api.table.munmap = resolve_one (remote_maps, "munmap");' ,
1102+ '\t \t \t api.table.mmap = resolve_one (remote_maps, "mmap");\n '
1103+ '\t \t \t api.table.mprotect = resolve_one (remote_maps, "mprotect");\n '
1104+ '\t \t \t api.table.munmap = resolve_one (remote_maps, "munmap");' ,
1105+ 1 ,
1106+ "align proc-mem libc API" ,
1107+ ),
8241108 )
8251109 for relative_path , old , new , expected_count , description in patches :
8261110 count = replace_in_file (frida_dir / relative_path , old , new )
@@ -1441,7 +1725,7 @@ def main():
14411725 parser .add_argument (
14421726 "--strict-wx" ,
14431727 action = "store_true" ,
1444- help = "Disable persistent anonymous RWX code pools on Android" ,
1728+ help = "Harden Frida-owned persistent anonymous RWX mappings on Android" ,
14451729 )
14461730 parser .add_argument (
14471731 "--work-dir" , "-w" , default = None , help = "Working directory (default: ./build)"
0 commit comments