/// <summary> /// This internal method handles executing a host file I/O request. All requests use the /// DBGKD_FILEIO structure; using a structure with a different length will greatly confuse /// the kernel debugger, the kernel, or both. /// </summary> /// /// <param name="Request">The request to send to the debugger.</param> /// <param name="RequestDataBuffer">A buffer containing additional data, if any, to send with the request.</param> /// <param name="RequestDataLength">The length of the additional data to send, or zero if there is none.</param> /// <param name="Response">On return, contains the response message.</param> /// <param name="ResponseDataBuffer">A buffer which receives any returned with the response.</param> /// <param name="ResponseDataMaximumLength">The maximum length of the data to store in the response buffer.</param> /// <param name="ResponseDataActualLength">The actual length of the data to store in the response buffer.</param> /// <returns></returns> static unsafe bool SendFileIoRequestWaitResponse( HostFileIoRequest Request, byte *RequestDataBuffer, int RequestDataLength, out HostFileIoResponse Response, byte *ResponseDataBuffer, int ResponseDataMaximumLength, out int ResponseDataActualLength) { HostFileIoResponse local_response = new HostFileIoResponse(); Kd.Lock(); // Do NOT use Dbg() or DebugStub.WriteLine() while Kd.Lock() is in effect. bool result = Kd.SendRequestWaitResponse( KdPacketType.FileIo, (byte *)&Request, sizeof(HostFileIoRequest), RequestDataBuffer, RequestDataLength, (byte *)&local_response, sizeof(HostFileIoResponse), ResponseDataBuffer, ResponseDataMaximumLength, out ResponseDataActualLength); Kd.Unlock(); Response = local_response; return(result); }
/// <summary> /// Closes a file handle that was opened using CreateHostFile. /// </summary> /// <param name="FileHandle">The file handle to close.</param> public unsafe static void CloseHostFile(long FileHandle) { if (!Kd.IsDebuggerPresent()) { return; } Dbg("Closing file handle 0x{0:x}", FileHandle); HostFileIoRequest request = new HostFileIoRequest(); request.ApiNumber = (uint)KdApi.CloseFile; request.CloseFile.FileHandle = FileHandle; HostFileIoResponse response; int response_data_length; bool succeeded = SendFileIoRequestWaitResponse( request, null, 0, out response, null, 0, out response_data_length); if (succeeded) { if (response.Status == 0) { Dbg("Successfully closed host file handle."); } else { Dbg("Received response to DBGKD_CLOSE_FILE, but it indicates error. NTSTATUS: 0x{0:x8}", response.Status); } } else { Dbg("Failed to receive packet for response to DBGKD_CLOSE_FILE"); } }
/// <summary> /// This method opens a file on the host. This file handle can be used in calls to ReadHostFile, /// WriteHostFile (depending on access requested), and CloseHostFile. /// </summary> /// <param name="FileHandle">Returns the file handle. This handle has meaning only to the host.</param> /// <param name="FileLength">Returns the length of the file.</param> /// /// <param name="FileName"> /// The file to open. Note that KD will not allow the debug target to open arbitrary files; this method /// can only be used to open files that have been added to the .kdfiles set by the developer controlling KD. /// For example, a .kdfiles file (say, kdfiles.txt) contains map entries, in this form: /// /// map [from] [to] /// /// This string identifies the "from" part. The "to" part identifies some file in the namespace of /// the machine running KD. If the file is opened successfully, then the debug target can read/write /// the file. /// </param> /// /// <param name="DesiredAccess"> /// The access mask requested, using standard Windows access masks. /// However, note that KD will only look for the FILE_READ_DATA and FILE_WRITE_DATA bits, which it /// maps to GENERIC_READ and GENERIC_WRITE. So don't bother specifying any bits outside of those; /// they will be ignored. /// </param> /// /// <param name="FileAttributes">Initial file attributes, if creating a file. Most callers should pass 0.</param> /// /// <param name="ShareAccess"> /// The shared access mask, if any. If opening a file for read access, then pass FILE_SHARE_READ. /// </param> /// /// <param name="CreateDisposition">Specifies what to do if the file already exists, or doesn't. /// Use FILE_OPEN, FILE_CREATE, etc. /// </param> /// /// <returns>True if the file was successfully opened.</returns> public static unsafe bool CreateHostFile( out long FileHandle, out long FileLength, string FileName, uint DesiredAccess, uint FileAttributes, uint ShareAccess, uint CreateDisposition) { FileHandle = 0; FileLength = 0; if (!Kd.IsDebuggerPresent()) { // Think about how silly it would be to call DebugStub.WriteLine here. return(false); } int request_data_size = sizeof(char) * (FileName.Length + 1); if (request_data_size + sizeof(HostFileIoRequest) > Kd.MaxPacketSize) { Dbg("CreateHostFile: Filename is too long."); return(false); } // The request data for "create file" consists of the filename, // in little-endian UTF-16, with a Unicode NUL terminator character. byte[] request_data = new byte[request_data_size]; for (int i = 0; i < FileName.Length; i++) { char c = FileName[i]; request_data[i * 2] = (byte)(c & 0xff); request_data[i * 2 + 1] = (byte)((c >> 8) & 0xff); } request_data[FileName.Length * 2] = 0; request_data[FileName.Length * 2 + 1] = 0; // // Send packet to the kernel debugger on the host machine. // HostFileIoRequest request = new HostFileIoRequest(); request.ApiNumber = (uint)KdApi.CreateFile; request.CreateFile.DesiredAccess = DesiredAccess; request.CreateFile.FileAttributes = FileAttributes; request.CreateFile.ShareAccess = ShareAccess; request.CreateFile.CreateDisposition = CreateDisposition; request.CreateFile.CreateOptions = 0; HostFileIoResponse response; bool succeeded; int response_data_length; fixed(byte *request_data_pinned = &request_data[0]) { succeeded = SendFileIoRequestWaitResponse( request, request_data_pinned, request_data_size, out response, null, 0, out response_data_length); } if (succeeded) { if (response.Status == 0) { Dbg("Successfully opened KD file '{0}', handle 0x{1:x} length 0x{2:x}", FileName, response.CreateFile.FileHandle, response.CreateFile.FileLength); FileHandle = response.CreateFile.FileHandle; FileLength = response.CreateFile.FileLength; return(true); } else { Dbg("Failed to open KD file '{0}'. NTSTATUS = 0x{1:x}.", FileName, response.Status); return(false); } } else { Dbg("Failed to open KD file '{0}'.", FileName); return(false); } }
/// <summary> /// Writes data to a file open on the host. /// </summary> /// /// <param name="FileHandle">The host file handle, acquired using CreateHostFile.</param> /// <param name="FileOffset"> /// The offset within the file to begin writing. /// (TODO: Does this support NT-style always-append semantics? Probably.) /// </param> /// <param name="Buffer">The buffer containing the data to write.</param> /// <param name="Length">The number of bytes to write.</param> /// <param name="BytesTransferred">The actual number of bytes transferred.</param> /// <returns>True if the transfer succeeded.</returns> public static unsafe bool WriteHostFile( long FileHandle, long FileOffset, void *Buffer, int Length, out int BytesTransferred) { BytesTransferred = 0; if (!Kd.IsDebuggerPresent()) { return(false); } if (Length < 0) { throw new ArgumentOutOfRangeException("Length"); } int transfer_size = Math.Min(Length, Kd.MaxPacketSize - sizeof(HostFileIoRequest)); HostFileIoRequest request = new HostFileIoRequest(); request.ApiNumber = (uint)KdApi.WriteFile; request.WriteFile.FileHandle = FileHandle; request.WriteFile.FileOffset = FileOffset; request.WriteFile.Length = (uint)transfer_size; HostFileIoResponse response; // // Send packet to the kernel debugger on the host machine. // int response_data_length; bool succeeded = SendFileIoRequestWaitResponse( request, (byte *)Buffer, Length, out response, null, 0, out response_data_length ); if (succeeded) { if (response.Status == 0) { Dbg("Successfully wrote {0} bytes to host file.", response.WriteFile.BytesTransferred); return(true); } else { Dbg("Received response to 'write file' request, but it indicates failure."); return(false); } } else { Dbg("Received response to 'write file' request, but it indicates failure."); return(false); } }
/// <summary> /// Reads data from a host file that has already been opened using CreateHostFile. /// </summary> /// <param name="RemoteHandle">The file handle.</param> /// <param name="FileOffset">Offset within the file of the chunk to read.</param> /// <param name="Buffer">The buffer to store data into.</param> /// <param name="Length">The number of bytes to transfer.</param> /// <param name="BytesTransferred">On return, the number of bytes actually transferred.</param> /// <returns>True if the transfer succeeded.</returns> public static unsafe bool ReadHostFile( long RemoteHandle, long FileOffset, void *Buffer, int Length, out int BytesTransferred) { BytesTransferred = 0; if (!Kd.IsDebuggerPresent()) { return(false); } Dbg("ReadHostFile: Handle {0:x8} FileOffset {1:x8} Buffer {2:x8} Length {3:x8}", RemoteHandle, FileOffset, (UIntPtr)Buffer, Length); if (Length < 0) { throw new ArgumentOutOfRangeException("Length"); } int transfer_size = Length; int MaxTransferSize = Kd.MaxPacketSize - 0x200; // sizeof(HostFileIoRequest); if (transfer_size > MaxTransferSize) { Dbg("ReadHostFile: Length exceeds max transfer size; truncating to 0x{0:x}", MaxTransferSize); transfer_size = MaxTransferSize; } HostFileIoRequest request = new HostFileIoRequest(); request.ApiNumber = (uint)KdApi.ReadFile; request.ReadFile.FileHandle = RemoteHandle; request.ReadFile.FileOffset = FileOffset; request.ReadFile.Length = (uint)transfer_size; HostFileIoResponse response; int response_data_length; bool succeeded; succeeded = SendFileIoRequestWaitResponse( request, null, 0, out response, (byte *)Buffer, transfer_size, out response_data_length); if (succeeded) { if (response.Status == 0) { Dbg("Received DBGKD_READ_FILE. Successfully transfered {0} bytes.", response.ReadFile.BytesTransferred); BytesTransferred = (int)response.ReadFile.BytesTransferred; return(true); } else { Dbg("Received DBGKD_READ_FILE, Status indicates failure. Status = 0x{0:x8}", (uint)response.Status); DebugStub.Break(); BytesTransferred = 0; return(false); } } else { Dbg("Failed to receive response to DBGKD_READ_FILE."); BytesTransferred = 0; return(false); } }