authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-30 17:04:50+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-03-30 21:08:31+02:00
log423b9f11144cc7f7f027db9a091174e9c752c511
treed982e96c0df8c8e347d010e778b5801e79130990
parentd2f013085523436b51023f7d28d52a859b70a4b3

coff: reimplement Read/WriteProcessMemory using our own ntdll wrappers


1 files changed, 53 insertions(+), 64 deletions(-)

src/link/Coff.zig+53-64
...@@ -793,28 +793,60 @@ fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []u8) !void {...@@ -793,28 +793,60 @@ fn writeAtom(self: *Coff, atom_index: Atom.Index, code: []u8) !void {
793 log.debug("page not mapped for write access; re-mapping...", .{});793 log.debug("page not mapped for write access; re-mapping...", .{});
794 try writeMemProtected(handle, vaddr, code);794 try writeMemProtected(handle, vaddr, code);
795 } else {795 } else {
796 try writeMem(handle, vaddr, code);796 if (WriteProcessMemory(handle, vaddr, code)) |amt| {
797 if (amt != code.len) return error.InputOutput;
798 } else |err| {
799 log.warn("writing to process memory failed with error: {s}", .{@errorName(err)});
800 }
797 }801 }
798 }802 }
799803
800 try self.base.file.?.pwriteAll(code, file_offset);804 try self.base.file.?.pwriteAll(code, file_offset);
801}805}
802806
803extern "kernel32" fn ReadProcessMemory(807extern "ntdll" fn NtReadVirtualMemory(
804 hProcess: std.os.windows.HANDLE,808 ProcessHandle: std.os.windows.HANDLE,
805 lpBaseAddress: std.os.windows.LPCVOID,809 BaseAddress: std.os.windows.PVOID,
806 lpBuffer: std.os.windows.LPVOID,810 Buffer: std.os.windows.LPVOID,
807 nSize: std.os.windows.SIZE_T,811 NumberOfBytesToRead: std.os.windows.SIZE_T,
808 lpNumberOfBytesRead: *std.os.windows.SIZE_T,812 NumberOfBytesRead: ?*std.os.windows.SIZE_T,
809) std.os.windows.BOOL;813) std.os.windows.NTSTATUS;
810814
811extern "kernel32" fn WriteProcessMemory(815extern "ntdll" fn NtWriteVirtualMemory(
812 hProcess: std.os.windows.HANDLE,816 ProcessHandle: std.os.windows.HANDLE,
813 lpBaseAddress: std.os.windows.LPVOID,817 BaseAddress: std.os.windows.PVOID,
814 lpBuffer: std.os.windows.LPCVOID,818 Buffer: std.os.windows.LPCVOID,
815 nSize: std.os.windows.SIZE_T,819 NumberOfBytesToWrite: std.os.windows.SIZE_T,
816 lpNumberOfBytesWritten: *std.os.windows.SIZE_T,820 NumberOfBytesWritten: ?*std.os.windows.SIZE_T,
817) std.os.windows.BOOL;821) std.os.windows.NTSTATUS;
822
823fn ReadProcessMemory(handle: std.os.windows.HANDLE, base_addr: usize, buffer: []u8) ![]u8 {
824 var nread: usize = 0;
825 switch (NtReadVirtualMemory(
826 handle,
827 @intToPtr(*anyopaque, base_addr),
828 buffer.ptr,
829 buffer.len,
830 &nread,
831 )) {
832 .SUCCESS => return buffer[0..nread],
833 else => |rc| return std.os.windows.unexpectedStatus(rc),
834 }
835}
836
837fn WriteProcessMemory(handle: std.os.windows.HANDLE, base_addr: usize, buffer: []const u8) !usize {
838 var nwritten: usize = 0;
839 switch (NtWriteVirtualMemory(
840 handle,
841 @intToPtr(*anyopaque, base_addr),
842 @ptrCast(*const anyopaque, buffer.ptr),
843 buffer.len,
844 &nwritten,
845 )) {
846 .SUCCESS => return nwritten,
847 else => |rc| return std.os.windows.unexpectedStatus(rc),
848 }
849}
818850
819extern "kernel32" fn VirtualProtectEx(851extern "kernel32" fn VirtualProtectEx(
820 hProcess: std.os.windows.HANDLE,852 hProcess: std.os.windows.HANDLE,
...@@ -849,43 +881,16 @@ fn getProcessBaseAddress(handle: std.ChildProcess.Id) !u64 {...@@ -849,43 +881,16 @@ fn getProcessBaseAddress(handle: std.ChildProcess.Id) !u64 {
849 }881 }
850882
851 var peb_buf: [@sizeOf(std.os.windows.PEB)]u8 align(@alignOf(std.os.windows.PEB)) = undefined;883 var peb_buf: [@sizeOf(std.os.windows.PEB)]u8 align(@alignOf(std.os.windows.PEB)) = undefined;
852 var peb_nread: usize = 0;884 const pebout = try ReadProcessMemory(handle, @ptrToInt(info.PebBaseAddress), &peb_buf);
853 if (ReadProcessMemory(885 const peb = @ptrCast(*const std.os.windows.PEB, @alignCast(@alignOf(std.os.windows.PEB), pebout.ptr));
854 handle,
855 info.PebBaseAddress,
856 &peb_buf,
857 @sizeOf(std.os.windows.PEB),
858 &peb_nread,
859 ) == 0) {
860 const err = std.os.windows.kernel32.GetLastError();
861 log.warn("reading from process memory failed with err: {s}({x})", .{ @tagName(err), @enumToInt(err) });
862 return error.FailedToReadPebForProcess;
863 }
864 if (peb_nread != @sizeOf(std.os.windows.PEB)) return error.InputOutput;
865
866 const peb = @ptrCast(*const std.os.windows.PEB, &peb_buf);
867 return @ptrToInt(peb.ImageBaseAddress);886 return @ptrToInt(peb.ImageBaseAddress);
868}887}
869888
870fn debugMem(allocator: Allocator, handle: std.ChildProcess.Id, vaddr: u64, code: []const u8) !void {889fn debugMem(allocator: Allocator, handle: std.ChildProcess.Id, vaddr: u64, code: []const u8) !void {
871 var buffer = try allocator.alloc(u8, code.len);890 var buffer = try allocator.alloc(u8, code.len);
872 defer allocator.free(buffer);891 defer allocator.free(buffer);
873 var nread: usize = 0;892 const memread = try ReadProcessMemory(handle, vaddr, buffer);
874 if (ReadProcessMemory(893 log.debug("in memory: {x}", .{std.fmt.fmtSliceHexLower(memread)});
875 handle,
876 @intToPtr(*anyopaque, vaddr),
877 buffer.ptr,
878 code.len,
879 &nread,
880 ) == 0) {
881 const err = std.os.windows.kernel32.GetLastError();
882 log.warn("reading from process memory failed with err: {s}({x})", .{ @tagName(err), @enumToInt(err) });
883 }
884 if (nread != code.len) {
885 log.warn("reading from process memory InputOutput error: read != requested: {x} != {x}", .{ nread, code.len });
886 }
887
888 log.debug("in memory: {x}", .{std.fmt.fmtSliceHexLower(buffer)});
889 log.debug("to write: {x}", .{std.fmt.fmtSliceHexLower(code)});894 log.debug("to write: {x}", .{std.fmt.fmtSliceHexLower(code)});
890}895}
891896
...@@ -898,7 +903,8 @@ fn writeMemProtected(handle: std.ChildProcess.Id, vaddr: u64, code: []const u8)...@@ -898,7 +903,8 @@ fn writeMemProtected(handle: std.ChildProcess.Id, vaddr: u64, code: []const u8)
898 log.warn("making page(s) writeable failed with error: {s}({x})", .{ @tagName(err), @enumToInt(err) });903 log.warn("making page(s) writeable failed with error: {s}({x})", .{ @tagName(err), @enumToInt(err) });
899 return;904 return;
900 }905 }
901 try writeMem(handle, vaddr, code);906 const amt = try WriteProcessMemory(handle, vaddr, code);
907 if (amt != code.len) return error.InputOutput;
902 // TODO: We can probably just set the pages writeable and leave it at that without having to restore the attributes.908 // TODO: We can probably just set the pages writeable and leave it at that without having to restore the attributes.
903 // For that though, we want to track which page has already been modified.909 // For that though, we want to track which page has already been modified.
904 if (VirtualProtectEx(handle, pvaddr, code.len, old_prot, &new_prot) == 0) {910 if (VirtualProtectEx(handle, pvaddr, code.len, old_prot, &new_prot) == 0) {
...@@ -907,23 +913,6 @@ fn writeMemProtected(handle: std.ChildProcess.Id, vaddr: u64, code: []const u8)...@@ -907,23 +913,6 @@ fn writeMemProtected(handle: std.ChildProcess.Id, vaddr: u64, code: []const u8)
907 }913 }
908}914}
909915
910fn writeMem(handle: std.ChildProcess.Id, vaddr: u64, code: []const u8) !void {
911 var nwritten: usize = 0;
912 if (WriteProcessMemory(
913 handle,
914 @intToPtr(*anyopaque, vaddr),
915 code.ptr,
916 code.len,
917 &nwritten,
918 ) == 0) {
919 const err = std.os.windows.kernel32.GetLastError();
920 log.warn("writing to process memory failed with err: {s}({x})", .{ @tagName(err), @enumToInt(err) });
921 }
922 if (nwritten != code.len) {
923 log.warn("writing to process memory InputOutput error: written != requested: {x} != {x}", .{ nwritten, code.len });
924 }
925}
926
927fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void {916fn writePtrWidthAtom(self: *Coff, atom_index: Atom.Index) !void {
928 switch (self.ptr_width) {917 switch (self.ptr_width) {
929 .p32 => {918 .p32 => {