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) < 1: 84 print(KDPSendCore.__doc__) 85 return False 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) < 1: 110 print(KDPSendSyslog.__doc__) 111 return False 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) < 1: 135 print(KDPSendPaniclog.__doc__) 136 return False 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 print("Please provide valid time in seconds") 237 print(KDPReenter.__doc__) 238 return False 239 240 if "kdp" != GetConnectionProtocol(): 241 print("Target is not connected over kdp. Nothing to do here.") 242 return False 243 244 num_seconds = ArgumentStringToInt(cmd_args[0]) 245 milliseconds_to_sleep = num_seconds * 1000 246 if WriteInt32ToMemoryAddress(milliseconds_to_sleep, addressof(kern.globals.kdp_reentry_deadline)): 247 lldb.debugger.HandleCommand('process continue') 248 return True 249 print("Failed to setup kdp-reentry.") 250 return False 251 252@lldb_command('kdp-reboot') 253def KDPReboot(cmd_args=None): 254 """ Restart the remote target 255 """ 256 if "kdp" != GetConnectionProtocol(): 257 print("Target is not connected over kdp. Nothing to do here.") 258 return False 259 260 print("Rebooting the remote machine.") 261 lldb.debugger.HandleCommand('process plugin packet send --command 0x13') 262 lldb.debugger.HandleCommand('detach') 263 return True 264 265@lldb_command('setdumpinfo') 266def KDPSetDumpInfo(cmd_args=None): 267 """ Configure the current remote dump settings. 268 Specify "" if you want to use the defaults (filename) or previously configured 269 settings (ip/router). Specify 0 for the port if you wish to 270 use the previously configured/default setting for that. 271 Syntax: setdumpinfo <filename> <ip> <router> <port> 272 """ 273 if not cmd_args: 274 print(KDPSetDumpInfo.__doc__) 275 return False 276 if len(cmd_args) < 4: 277 print("Not enough arguments.") 278 print(KDPSetDumpInfo.__doc__) 279 return False 280 portnum = ArgumentStringToInt(cmd_args[3]) 281 retval = KDPDumpInfo(GetEnumValue('kdp_dumpinfo_t::KDP_DUMPINFO_SETINFO'), cmd_args[0], cmd_args[1], cmd_args[2], portnum) 282 if retval: 283 print("Successfully saved the dumpinfo.") 284 else: 285 print("Failed to save the dumpinfo.") 286 return retval 287 288@lldb_command('kdpmode') 289def KDPMode(cmd_args=None): 290 """ 291 Change KDP mode between software hosted and hardware probe. 292 When lldb is connected to a KDP server backed by a hardware debug tool 293 setting this to 'hwprobe' enables physical memory access. 294 295 swhosted: LLDB is connected to the target using a serial or socket connection. 296 hwprobe: LLDB is connected to the target using a hardware probe. 297 298 usage: kdpmode <mode> 299 mode: 'swhosted' or 'hwprobe' 300 """ 301 global current_KDP_mode 302 303 if cmd_args is None or len(cmd_args) == 0: 304 return current_KDP_mode 305 if len(cmd_args) > 1 or cmd_args[0] not in {'swhosted', 'hwprobe'}: 306 print("Invalid Arguments", KDPMode.__doc__) 307 else: 308 current_KDP_mode = cmd_args[0] 309 return 310 311