1from xnu import * 2from utils import * 3import sys 4 5current_KDP_mode = "swhosted" 6 7def GetKDPPacketHeaderInt(request=0, is_reply=False, seq=0, length=0, key=0): 8 """ create a 64 bit number that could be saved as pkt_hdr_t 9 params: 10 request:int - 7 bit kdp_req_t request type 11 is_reply:bool - False => request, True => reply 12 seq: int - 8 sequence number within session 13 length: int - 16 bit length of entire pkt including hdr 14 key: int - session key 15 returns: 16 int - 64 bit number to be saved in memory 17 """ 18 retval = request 19 if is_reply: 20 retval = 1<<7 |retval 21 retval = (seq << 8) | retval 22 retval = (length << 16) | retval 23 #retval = (retval << 32) | key 24 retval = (key << 32) | retval 25 return retval 26 27 28def KDPDumpInfo(subcmd, file_name="", dest_ip="", router_ip="", port=0): 29 """ Setup the state for DUMP INFO commands for sending coredump etc 30 """ 31 if "kdp" != GetConnectionProtocol(): 32 print("Target is not connected over kdp. Nothing to do here.") 33 return False 34 input_address = unsigned(addressof(kern.globals.manual_pkt.input)) 35 len_address = unsigned(addressof(kern.globals.manual_pkt.len)) 36 data_address = unsigned(addressof(kern.globals.manual_pkt.data)) 37 if not WriteInt32ToMemoryAddress(0, input_address): 38 return False 39 40 kdp_pkt_size = GetType('kdp_dumpinfo_req_t').GetByteSize() 41 if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address): 42 return False 43 44 data_addr = int(addressof(kern.globals.manual_pkt)) 45 pkt = kern.GetValueFromAddress(data_addr, 'kdp_dumpinfo_req_t *') 46 if len(file_name) > 49: 47 file_name = file_name[:49] 48 if len(dest_ip) > 15: 49 dest_ip = dest_ip[:15] 50 if len(router_ip) > 15: 51 router_ip = router_ip[:15] 52 53 header_value =GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_DUMPINFO'), length=kdp_pkt_size) 54 # 0x1f is same as KDP_DUMPINFO 55 if ( WriteInt64ToMemoryAddress((header_value), int(addressof(pkt.hdr))) and 56 WriteInt32ToMemoryAddress(subcmd, int(addressof(pkt.type))) and 57 WriteStringToMemoryAddress(file_name, int(addressof(pkt.name))) and 58 WriteStringToMemoryAddress(dest_ip, int(addressof(pkt.destip))) and 59 WriteStringToMemoryAddress(router_ip, int(addressof(pkt.routerip))) 60 ): 61 #We have saved important data successfully 62 if port > 0: 63 if not WriteInt32ToMemoryAddress(port, int(addressof(pkt.port))): 64 return False 65 if WriteInt32ToMemoryAddress(1, input_address): 66 return True 67 return False 68 69@lldb_command('sendcore') 70def KDPSendCore(cmd_args=None): 71 """ Configure kernel to send a coredump to the specified IP 72 Syntax: sendcore <IP address> [filename] 73 Configure the kernel to transmit a kernel coredump to a server (kdumpd) 74 at the specified IP address. This is useful when the remote target has 75 not been previously configured to transmit coredumps, and you wish to 76 preserve kernel state for later examination. NOTE: You must issue a "continue" 77 command after using this macro to trigger the kernel coredump. The kernel 78 will resume waiting in the debugger after completion of the coredump. You 79 may disable coredumps by executing the "disablecore" macro. You can 80 optionally specify the filename to be used for the generated core file. 81 82 """ 83 if cmd_args is None or len(cmd_args) == 0: 84 raise ArgumentError() 85 86 ip_address = cmd_args[0] 87 filename="" 88 if len(cmd_args) >=2: 89 filename = cmd_args[1].strip() 90 retval = KDPDumpInfo(GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_CORE'), file_name=filename, dest_ip=ip_address) 91 if retval: 92 print("Remote system has been setup for coredump. Please detach/continue the system. ") 93 return True 94 else: 95 print("Something went wrong. Failed to setup the coredump on the target.") 96 return False 97 98 99@lldb_command('sendsyslog') 100def KDPSendSyslog(cmd_args=None): 101 """ Configure kernel to send a system log to the specified IP 102 Syntax: sendsyslog <IP address> [filename] 103 Configure the kernel to transmit a kernel system log to a server (kdumpd) 104 at the specified IP address. NOTE: You must issue a "continue" 105 command after using this macro to trigger the kernel system log. The kernel 106 will resume waiting in the debugger after completion. You can optionally 107 specify the name to be used for the generated system log. 108 """ 109 if cmd_args is None or len(cmd_args) == 0: 110 raise ArgumentError() 111 112 ip_address = cmd_args[0] 113 filename ="" 114 if len(cmd_args) >=2: 115 filename = cmd_args[1].strip() 116 retval = KDPDumpInfo(GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_SYSTEMLOG'), file_name = filename, dest_ip = ip_address) 117 if retval: 118 print("Remote system has been setup to send system log. please detach/continue the system.") 119 return True 120 else: 121 print("Something went wrong. Failed to setup the systemlog on the target.") 122 return False 123 124@lldb_command('sendpaniclog') 125def KDPSendPaniclog(cmd_args=None): 126 """ Configure kernel to send a panic log to the specified IP 127 Syntax: sendpaniclog <IP address> [filename] 128 Configure the kernel to transmit a kernel paniclog to a server (kdumpd) 129 at the specified IP address. NOTE: You must issue a "continue" 130 command after using this macro to trigger the kernel panic log. The kernel 131 will resume waiting in the debugger after completion. You can optionally 132 specify the name to be used for the generated panic log. 133 """ 134 if cmd_args is None or len(cmd_args) == 0: 135 raise ArgumentError() 136 137 ip_address = cmd_args[0] 138 filename ="" 139 if len(cmd_args) >=2: 140 filename = cmd_args[1].strip() 141 retval = KDPDumpInfo(GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_PANICLOG'), file_name = filename, dest_ip = ip_address) 142 if retval: 143 print("Remote system has been setup to send panic log. please detach/continue the system.") 144 return True 145 else: 146 print("Something went wrong. Failed to setup the paniclog on the target.") 147 return False 148 149 150@lldb_command('disablecore') 151def KDPDisableCore(cmd_args=None): 152 """ Configure the kernel to disable coredump transmission 153 Reconfigures the kernel so that it no longer transmits kernel coredumps. This 154 complements the "sendcore" macro, but it may be used if the kernel has been 155 configured to transmit coredumps through boot-args as well. 156 157 """ 158 retval = KDPDumpInfo(GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_DISABLE')) 159 if retval : 160 print("Disabled coredump functionality on remote system.") 161 else: 162 print("Failed to disable coredump functionality.") 163 return retval 164 165@lldb_command('resume_on') 166def KDPResumeON(cmd_args=None): 167 """ The target system will resume when detaching or exiting from lldb. 168 This is the default behavior. 169 """ 170 subcmd = GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_SETINFO') | GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_RESUME') 171 retval = KDPDumpInfo(subcmd) 172 if retval : 173 print("Target system will resume on detaching from lldb.") 174 else: 175 print("Failed to enable resume functionality.") 176 return retval 177 178@lldb_command('resume_off') 179def KDPResumeOFF(cmd_args=None): 180 """ The target system will not resume when detaching or exiting from lldb. 181 """ 182 subcmd = GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_SETINFO') | GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_NORESUME') 183 retval = KDPDumpInfo(subcmd) 184 if retval : 185 print("Target system will not resume on detaching from lldb.") 186 else: 187 print("Failed to disable resume functionality.") 188 return retval 189 190 191 192@lldb_command('getdumpinfo') 193def KDPGetDumpInfo(cmd_args=None): 194 """ Retrieve the current remote dump settings. 195 """ 196 if not KDPDumpInfo(GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_GETINFO')): 197 print("Failed to get dump settings.") 198 return False 199 dumpinfo = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_dumpinfo_reply_t *') 200 target_dump_type = int(dumpinfo.type) 201 if target_dump_type & GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_REBOOT'): 202 print("System will reboot after kernel info gets dumped.") 203 else: 204 print("System will not reboot after kernel info gets dumped.") 205 if target_dump_type & GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_RESUME'): 206 print("System will allow a re-attach after KDP disconnect.") 207 else: 208 print("System will not allow a re-attach after KDP disconnect.") 209 target_dump_type = target_dump_type & GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_MASK') 210 if target_dump_type == GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_DISABLE'): 211 print("Kernel not setup for remote dumps.") 212 else: 213 kern_dump_type = '' 214 if target_dump_type == GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_CORE'): 215 kern_dump_type = "Core File" 216 elif target_dump_type == GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_PANICLOG'): 217 kern_dump_type = "Panic Log" 218 elif target_dump_type == GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_SYSTEMLOG'): 219 kern_dump_type = "System Log" 220 print("Kernel dump type:" + kern_dump_type) 221 fname = "(autogenerated)" 222 if int(dumpinfo.name[0]) != 0: 223 fname = str(dumpinfo.name) 224 print("Filename: " + fname) 225 print("Network Info: {:s} [{:d}] , Router: {:s}".format(dumpinfo.destip, dumpinfo.port, dumpinfo.routerip)) 226 # end of get dump info 227 228 229@lldb_command('kdp-reenter') 230def KDPReenter(cmd_args=None): 231 """ Schedules reentry into the debugger 232 after <seconds> seconds, and resumes the target. 233 usage: kdp-reenter <seconds> 234 """ 235 if len(cmd_args) < 1: 236 raise ArgumentError("Please provide valid time in seconds") 237 238 if "kdp" != GetConnectionProtocol(): 239 print("Target is not connected over kdp. Nothing to do here.") 240 return False 241 242 num_seconds = ArgumentStringToInt(cmd_args[0]) 243 milliseconds_to_sleep = num_seconds * 1000 244 if WriteInt32ToMemoryAddress(milliseconds_to_sleep, addressof(kern.globals.kdp_reentry_deadline)): 245 lldb.debugger.HandleCommand('process continue') 246 return True 247 print("Failed to setup kdp-reentry.") 248 return False 249 250@lldb_command('kdp-reboot') 251def KDPReboot(cmd_args=None): 252 """ Restart the remote target 253 """ 254 if "kdp" != GetConnectionProtocol(): 255 print("Target is not connected over kdp. Nothing to do here.") 256 return False 257 258 print("Rebooting the remote machine.") 259 lldb.debugger.HandleCommand('process plugin packet send --command 0x13') 260 lldb.debugger.HandleCommand('detach') 261 return True 262 263@lldb_command('setdumpinfo') 264def KDPSetDumpInfo(cmd_args=None): 265 """ Configure the current remote dump settings. 266 Specify "" if you want to use the defaults (filename) or previously configured 267 settings (ip/router). Specify 0 for the port if you wish to 268 use the previously configured/default setting for that. 269 Syntax: setdumpinfo <filename> <ip> <router> <port> 270 """ 271 if cmd_args is None or len(cmd_args) == 0: 272 raise ArgumentError() 273 274 if len(cmd_args) < 4: 275 raise ArgumentError("Not enough arguments.") 276 277 portnum = ArgumentStringToInt(cmd_args[3]) 278 retval = KDPDumpInfo(GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_SETINFO'), cmd_args[0], cmd_args[1], cmd_args[2], portnum) 279 if retval: 280 print("Successfully saved the dumpinfo.") 281 else: 282 print("Failed to save the dumpinfo.") 283 return retval 284 285@lldb_command('kdpmode') 286def KDPMode(cmd_args=None): 287 """ 288 Change KDP mode between software hosted and hardware probe. 289 When lldb is connected to a KDP server backed by a hardware debug tool 290 setting this to 'hwprobe' enables physical memory access. 291 292 swhosted: LLDB is connected to the target using a serial or socket connection. 293 hwprobe: LLDB is connected to the target using a hardware probe. 294 295 usage: kdpmode <mode> 296 mode: 'swhosted' or 'hwprobe' 297 """ 298 global current_KDP_mode 299 300 if cmd_args is None or len(cmd_args) == 0: 301 return current_KDP_mode 302 if len(cmd_args) > 1 or cmd_args[0] not in {'swhosted', 'hwprobe'}: 303 raise ArgumentError() 304 else: 305 current_KDP_mode = cmd_args[0] 306 return 307 308