| ... | ... | @@ -9,9 +9,10 @@ const elf = std.elf; |
| 9 | 9 | const windows = std.os.windows; |
| 10 | 10 | const system = std.os.system; |
| 11 | 11 | const maxInt = std.math.maxInt; |
| 12 | const max = std.math.max; |
| 12 | 13 | |
| 13 | 14 | pub const DynLib = switch (builtin.os) { |
| 14 | | .linux => if (builtin.link_libc) DlDynlib else LinuxDynLib, |
| 15 | .linux => if (builtin.link_libc) DlDynlib else ElfDynLib, |
| 15 | 16 | .windows => WindowsDynLib, |
| 16 | 17 | .macosx, .tvos, .watchos, .ios, .freebsd => DlDynlib, |
| 17 | 18 | else => void, |
| ... | ... | @@ -100,102 +101,127 @@ pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator { |
| 100 | 101 | return LinkMap.Iterator{ .current = link_map_ptr }; |
| 101 | 102 | } |
| 102 | 103 | |
| 103 | | pub const LinuxDynLib = struct { |
| 104 | | pub const Error = ElfLib.Error; |
| 105 | | |
| 106 | | elf_lib: ElfLib, |
| 107 | | fd: i32, |
| 104 | pub const ElfDynLib = struct { |
| 105 | strings: [*:0]u8, |
| 106 | syms: [*]elf.Sym, |
| 107 | hashtab: [*]os.Elf_Symndx, |
| 108 | versym: ?[*]u16, |
| 109 | verdef: ?*elf.Verdef, |
| 108 | 110 | memory: []align(mem.page_size) u8, |
| 109 | 111 | |
| 110 | | /// Trusts the file |
| 111 | | pub fn open(path: []const u8) !LinuxDynLib { |
| 112 | | const fd = try os.open(path, 0, os.O_RDONLY | os.O_CLOEXEC); |
| 113 | | errdefer os.close(fd); |
| 114 | | |
| 115 | | // TODO remove this @intCast |
| 116 | | const size = @intCast(usize, (try os.fstat(fd)).size); |
| 117 | | |
| 118 | | const bytes = try os.mmap( |
| 119 | | null, |
| 120 | | mem.alignForward(size, mem.page_size), |
| 121 | | os.PROT_READ | os.PROT_EXEC, |
| 122 | | os.MAP_PRIVATE, |
| 123 | | fd, |
| 124 | | 0, |
| 125 | | ); |
| 126 | | errdefer os.munmap(bytes); |
| 127 | | |
| 128 | | return LinuxDynLib{ |
| 129 | | .elf_lib = try ElfLib.init(bytes), |
| 130 | | .fd = fd, |
| 131 | | .memory = bytes, |
| 132 | | }; |
| 133 | | } |
| 134 | | |
| 135 | | pub fn openC(path_c: [*:0]const u8) !LinuxDynLib { |
| 136 | | return open(mem.toSlice(u8, path_c)); |
| 137 | | } |
| 138 | | |
| 139 | | pub fn close(self: *LinuxDynLib) void { |
| 140 | | os.munmap(self.memory); |
| 141 | | os.close(self.fd); |
| 142 | | self.* = undefined; |
| 143 | | } |
| 144 | | |
| 145 | | pub fn lookup(self: *LinuxDynLib, comptime T: type, name: [:0]const u8) ?T { |
| 146 | | if (self.elf_lib.lookup("", name)) |symbol| { |
| 147 | | return @intToPtr(T, symbol); |
| 148 | | } else { |
| 149 | | return null; |
| 150 | | } |
| 151 | | } |
| 152 | | }; |
| 153 | | |
| 154 | | pub const ElfLib = struct { |
| 155 | 112 | pub const Error = error{ |
| 156 | 113 | NotElfFile, |
| 157 | 114 | NotDynamicLibrary, |
| 158 | 115 | MissingDynamicLinkingInformation, |
| 159 | | BaseNotFound, |
| 160 | 116 | ElfStringSectionNotFound, |
| 161 | 117 | ElfSymSectionNotFound, |
| 162 | 118 | ElfHashTableNotFound, |
| 163 | 119 | }; |
| 164 | 120 | |
| 165 | | strings: [*:0]u8, |
| 166 | | syms: [*]elf.Sym, |
| 167 | | hashtab: [*]os.Elf_Symndx, |
| 168 | | versym: ?[*]u16, |
| 169 | | verdef: ?*elf.Verdef, |
| 170 | | base: usize, |
| 121 | /// Trusts the file. Malicious file will be able to execute arbitrary code. |
| 122 | pub fn open(path: []const u8) !ElfDynLib { |
| 123 | const fd = try os.open(path, 0, os.O_RDONLY | os.O_CLOEXEC); |
| 124 | defer os.close(fd); |
| 125 | |
| 126 | const stat = try os.fstat(fd); |
| 127 | const size = try std.math.cast(usize, stat.size); |
| 171 | 128 | |
| 172 | | // Trusts the memory |
| 173 | | pub fn init(bytes: []align(@alignOf(elf.Ehdr)) u8) !ElfLib { |
| 174 | | const eh = @ptrCast(*elf.Ehdr, bytes.ptr); |
| 129 | // This one is to read the ELF info. We do more mmapping later |
| 130 | // corresponding to the actual LOAD sections. |
| 131 | const file_bytes = try os.mmap( |
| 132 | null, |
| 133 | mem.alignForward(size, mem.page_size), |
| 134 | os.PROT_READ, |
| 135 | os.MAP_PRIVATE, |
| 136 | fd, |
| 137 | 0, |
| 138 | ); |
| 139 | defer os.munmap(file_bytes); |
| 140 | |
| 141 | const eh = @ptrCast(*elf.Ehdr, file_bytes.ptr); |
| 175 | 142 | if (!mem.eql(u8, eh.e_ident[0..4], "\x7fELF")) return error.NotElfFile; |
| 176 | 143 | if (eh.e_type != elf.ET.DYN) return error.NotDynamicLibrary; |
| 177 | 144 | |
| 178 | | const elf_addr = @ptrToInt(bytes.ptr); |
| 179 | | var ph_addr: usize = elf_addr + eh.e_phoff; |
| 145 | const elf_addr = @ptrToInt(file_bytes.ptr); |
| 180 | 146 | |
| 181 | | var base: usize = maxInt(usize); |
| 147 | // Iterate over the program header entries to find out the |
| 148 | // dynamic vector as well as the total size of the virtual memory. |
| 182 | 149 | var maybe_dynv: ?[*]usize = null; |
| 150 | var virt_addr_end: usize = 0; |
| 183 | 151 | { |
| 184 | 152 | var i: usize = 0; |
| 153 | var ph_addr: usize = elf_addr + eh.e_phoff; |
| 185 | 154 | while (i < eh.e_phnum) : ({ |
| 186 | 155 | i += 1; |
| 187 | 156 | ph_addr += eh.e_phentsize; |
| 188 | 157 | }) { |
| 189 | 158 | const ph = @intToPtr(*elf.Phdr, ph_addr); |
| 190 | 159 | switch (ph.p_type) { |
| 191 | | elf.PT_LOAD => base = elf_addr + ph.p_offset - ph.p_vaddr, |
| 160 | elf.PT_LOAD => virt_addr_end = max(virt_addr_end, ph.p_vaddr + ph.p_memsz), |
| 192 | 161 | elf.PT_DYNAMIC => maybe_dynv = @intToPtr([*]usize, elf_addr + ph.p_offset), |
| 193 | 162 | else => {}, |
| 194 | 163 | } |
| 195 | 164 | } |
| 196 | 165 | } |
| 197 | 166 | const dynv = maybe_dynv orelse return error.MissingDynamicLinkingInformation; |
| 198 | | if (base == maxInt(usize)) return error.BaseNotFound; |
| 167 | |
| 168 | // Reserve the entire range (with no permissions) so that we can do MAP_FIXED below. |
| 169 | const all_loaded_mem = try os.mmap( |
| 170 | null, |
| 171 | virt_addr_end, |
| 172 | os.PROT_NONE, |
| 173 | os.MAP_PRIVATE | os.MAP_ANONYMOUS, |
| 174 | -1, |
| 175 | 0, |
| 176 | ); |
| 177 | errdefer os.munmap(all_loaded_mem); |
| 178 | |
| 179 | const base = @ptrToInt(all_loaded_mem.ptr); |
| 180 | |
| 181 | // Now iterate again and actually load all the program sections. |
| 182 | { |
| 183 | var i: usize = 0; |
| 184 | var ph_addr: usize = elf_addr + eh.e_phoff; |
| 185 | while (i < eh.e_phnum) : ({ |
| 186 | i += 1; |
| 187 | ph_addr += eh.e_phentsize; |
| 188 | }) { |
| 189 | const ph = @intToPtr(*elf.Phdr, ph_addr); |
| 190 | switch (ph.p_type) { |
| 191 | elf.PT_LOAD => { |
| 192 | // The VirtAddr may not be page-aligned; in such case there will be |
| 193 | // extra nonsense mapped before/after the VirtAddr,MemSiz |
| 194 | const aligned_addr = (base + ph.p_vaddr) & ~(@as(usize, mem.page_size) - 1); |
| 195 | const extra_bytes = (base + ph.p_vaddr) - aligned_addr; |
| 196 | const extended_memsz = mem.alignForward(ph.p_memsz + extra_bytes, mem.page_size); |
| 197 | const ptr = @intToPtr([*]align(mem.page_size) u8, aligned_addr); |
| 198 | const prot = elfToMmapProt(ph.p_flags); |
| 199 | if ((ph.p_flags & elf.PF_W) == 0) { |
| 200 | // If it does not need write access, it can be mapped from the fd. |
| 201 | _ = try os.mmap( |
| 202 | ptr, |
| 203 | extended_memsz, |
| 204 | prot, |
| 205 | os.MAP_PRIVATE | os.MAP_FIXED, |
| 206 | fd, |
| 207 | ph.p_offset - extra_bytes, |
| 208 | ); |
| 209 | } else { |
| 210 | const sect_mem = try os.mmap( |
| 211 | ptr, |
| 212 | extended_memsz, |
| 213 | prot, |
| 214 | os.MAP_PRIVATE | os.MAP_FIXED | os.MAP_ANONYMOUS, |
| 215 | -1, |
| 216 | 0, |
| 217 | ); |
| 218 | mem.copy(u8, sect_mem, file_bytes[0..ph.p_filesz]); |
| 219 | } |
| 220 | }, |
| 221 | else => {}, |
| 222 | } |
| 223 | } |
| 224 | } |
| 199 | 225 | |
| 200 | 226 | var maybe_strings: ?[*:0]u8 = null; |
| 201 | 227 | var maybe_syms: ?[*]elf.Sym = null; |
| ... | ... | @@ -218,8 +244,8 @@ pub const ElfLib = struct { |
| 218 | 244 | } |
| 219 | 245 | } |
| 220 | 246 | |
| 221 | | return ElfLib{ |
| 222 | | .base = base, |
| 247 | return ElfDynLib{ |
| 248 | .memory = all_loaded_mem, |
| 223 | 249 | .strings = maybe_strings orelse return error.ElfStringSectionNotFound, |
| 224 | 250 | .syms = maybe_syms orelse return error.ElfSymSectionNotFound, |
| 225 | 251 | .hashtab = maybe_hashtab orelse return error.ElfHashTableNotFound, |
| ... | ... | @@ -228,8 +254,27 @@ pub const ElfLib = struct { |
| 228 | 254 | }; |
| 229 | 255 | } |
| 230 | 256 | |
| 257 | /// Trusts the file. Malicious file will be able to execute arbitrary code. |
| 258 | pub fn openC(path_c: [*:0]const u8) !ElfDynLib { |
| 259 | return open(mem.toSlice(u8, path_c)); |
| 260 | } |
| 261 | |
| 262 | /// Trusts the file |
| 263 | pub fn close(self: *ElfDynLib) void { |
| 264 | os.munmap(self.memory); |
| 265 | self.* = undefined; |
| 266 | } |
| 267 | |
| 268 | pub fn lookup(self: *ElfDynLib, comptime T: type, name: [:0]const u8) ?T { |
| 269 | if (self.lookupAddress("", name)) |symbol| { |
| 270 | return @intToPtr(T, symbol); |
| 271 | } else { |
| 272 | return null; |
| 273 | } |
| 274 | } |
| 275 | |
| 231 | 276 | /// Returns the address of the symbol |
| 232 | | pub fn lookup(self: *const ElfLib, vername: []const u8, name: []const u8) ?usize { |
| 277 | pub fn lookupAddress(self: *const ElfDynLib, vername: []const u8, name: []const u8) ?usize { |
| 233 | 278 | const maybe_versym = if (self.verdef == null) null else self.versym; |
| 234 | 279 | |
| 235 | 280 | const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON); |
| ... | ... | @@ -245,11 +290,19 @@ pub const ElfLib = struct { |
| 245 | 290 | if (!checkver(self.verdef.?, versym[i], vername, self.strings)) |
| 246 | 291 | continue; |
| 247 | 292 | } |
| 248 | | return self.base + self.syms[i].st_value; |
| 293 | return @ptrToInt(self.memory.ptr) + self.syms[i].st_value; |
| 249 | 294 | } |
| 250 | 295 | |
| 251 | 296 | return null; |
| 252 | 297 | } |
| 298 | |
| 299 | fn elfToMmapProt(elf_prot: u64) u32 { |
| 300 | var result: u32 = os.PROT_NONE; |
| 301 | if ((elf_prot & elf.PF_R) != 0) result |= os.PROT_READ; |
| 302 | if ((elf_prot & elf.PF_W) != 0) result |= os.PROT_WRITE; |
| 303 | if ((elf_prot & elf.PF_X) != 0) result |= os.PROT_EXEC; |
| 304 | return result; |
| 305 | } |
| 253 | 306 | }; |
| 254 | 307 | |
| 255 | 308 | fn checkver(def_arg: *elf.Verdef, vsym_arg: i32, vername: []const u8, strings: [*:0]u8) bool { |