gdb_sgx_plugin.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687
  1. #!/usr/bin/env python
  2. #
  3. # Copyright (C) 2011-2018 Intel Corporation. All rights reserved.
  4. #
  5. # Redistribution and use in source and binary forms, with or without
  6. # modification, are permitted provided that the following conditions
  7. # are met:
  8. #
  9. # * Redistributions of source code must retain the above copyright
  10. # notice, this list of conditions and the following disclaimer.
  11. # * Redistributions in binary form must reproduce the above copyright
  12. # notice, this list of conditions and the following disclaimer in
  13. # the documentation and/or other materials provided with the
  14. # distribution.
  15. # * Neither the name of Intel Corporation nor the names of its
  16. # contributors may be used to endorse or promote products derived
  17. # from this software without specific prior written permission.
  18. #
  19. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  22. # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  23. # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  24. # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  25. # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  26. # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  27. # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  28. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. #
  31. #
  32. from __future__ import print_function
  33. import gdb
  34. import struct
  35. import os.path
  36. from ctypes import create_string_buffer
  37. import load_symbol_cmd
  38. import sgx_emmt
  39. import ctypes
  40. # Calculate the bit mode of current debuggee project
  41. SIZE = gdb.parse_and_eval("sizeof(long)")
  42. ET_SIM = 0x1
  43. ET_DEBUG = 0x2
  44. PAGE_SIZE = 0x1000
  45. KB_SIZE = 1024
  46. # The following definitions should strictly align with the structure of
  47. # debug_enclave_info_t in uRTS.
  48. # Here we only care about the first 7 items in the structure.
  49. # pointer: next_enclave_info, start_addr, tcs_list, lpFileName,
  50. # g_peak_heap_used_addr
  51. # int32_t: enclave_type, file_name_size
  52. ENCLAVE_INFO_SIZE = 5 * 8 + 2 * 4
  53. INFO_FMT = 'QQQIIQQ'
  54. ENCLAVES_ADDR = {}
  55. # The following definitions should strictly align with the struct of
  56. # tcs_t
  57. # Here we only care about the first 8 items in the structure
  58. # uint64_t: state, flags, ossa, oentry, aep, ofs_base
  59. # uint32_t: nssa, cssa
  60. ENCLAVE_TCS_INFO_SIZE = 6*8 + 2*4
  61. TCS_INFO_FMT = 'QQQIIQQQ'
  62. def get_inferior():
  63. """Get current inferior"""
  64. try:
  65. if len(gdb.inferiors()) == 0:
  66. print ("No gdb inferior could be found.")
  67. return -1
  68. else:
  69. inferior = gdb.inferiors()[0]
  70. return inferior
  71. except AttributeError:
  72. print ("This gdb's python support is too old, please update first.")
  73. exit()
  74. def read_from_memory(addr, size):
  75. """Read data with specified size from the specified meomory"""
  76. inferior = get_inferior()
  77. # actually we can check the addr more securely
  78. # ( check the address is inside the enclave)
  79. if inferior == -1 or addr == 0:
  80. print ("Error happens in read_from_memory: addr = {0:x}".format(int(addr)))
  81. return None
  82. try:
  83. string = inferior.read_memory(addr, size)
  84. return string
  85. except gdb.MemoryError:
  86. print ("Can't access memory at {0:x}.".format(int(addr)))
  87. return None
  88. def write_to_memory(addr, buf):
  89. """Write a specified buffer to the specified memory"""
  90. inferior = get_inferior()
  91. if inferior == -1 or addr == 0:
  92. print ("Error happens in write_to_memory: addr = {0:x}".format(int(addr)))
  93. return -1
  94. try:
  95. inferior.write_memory(addr, buf)
  96. return 0
  97. except gdb.MemoryError:
  98. print ("Can't access memory at {0:x}.".format(int(addr)))
  99. return -1
  100. def target_path_to_host_path(target_path):
  101. so_name = os.path.basename(target_path)
  102. strpath = gdb.execute("show solib-search-path", False, True)
  103. path = strpath.split()[-1]
  104. strlen = len(path)
  105. if strlen != 1:
  106. path = path[0:strlen-1]
  107. host_path = path + "/" + so_name
  108. #strlen = len(host_path)
  109. #host_path = host_path[0:strlen-7]
  110. return host_path
  111. class enclave_info(object):
  112. """Class to contain the enclave inforation,
  113. such as start address, stack addresses, stack size, etc.
  114. The enclave information is for one enclave."""
  115. def __init__(self, _next_ei, _start_addr, _enclave_type, _stack_addr_list, \
  116. _stack_size, _enclave_path, _heap_addr, _tcs_addr_list):
  117. self.next_ei = _next_ei
  118. self.start_addr = _start_addr
  119. self.enclave_type = _enclave_type
  120. self.stack_addr_list = _stack_addr_list
  121. self.stack_size = _stack_size
  122. self.enclave_path = _enclave_path
  123. self.heap_addr = _heap_addr
  124. self.tcs_addr_list = _tcs_addr_list
  125. def __str__(self):
  126. print ("stack address list = {0:s}".format(self.stack_addr_list))
  127. return "start_addr = %#x, enclave_path = \"%s\", stack_size = %d" \
  128. % (self.start_addr, self.enclave_path, self.stack_size)
  129. def __eq__(self, other):
  130. if other == None:
  131. return False
  132. if self.start_addr == other.start_addr:
  133. return True
  134. else:
  135. return False
  136. def init_enclave_debug(self):
  137. # Only product HW enclave can't be debugged
  138. if (self.enclave_type & ET_SIM) != ET_SIM and (self.enclave_type & ET_DEBUG) != ET_DEBUG:
  139. print ('Warning: {0:s} is a product hardware enclave. It can\'t be debugged and sgx_emmt doesn\'t work'.format(self.enclave_path))
  140. return -1
  141. # set TCS debug flag
  142. for tcs_addr in self.tcs_addr_list:
  143. string = read_from_memory(tcs_addr + 8, 4)
  144. if string == None:
  145. return 0
  146. flag = struct.unpack('I', string)[0]
  147. flag |= 1
  148. gdb_cmd = "set *(unsigned int *)%#x = %#x" %(tcs_addr + 8, flag)
  149. gdb.execute(gdb_cmd, False, True)
  150. #If it is a product enclave, won't reach here.
  151. #load enclave symbol
  152. if os.path.exists(self.enclave_path) == True:
  153. enclave_path = self.enclave_path
  154. else:
  155. enclave_path = target_path_to_host_path(self.enclave_path)
  156. gdb_cmd = load_symbol_cmd.GetLoadSymbolCommand(enclave_path, str(self.start_addr))
  157. if gdb_cmd == -1:
  158. return 0
  159. print (gdb_cmd)
  160. gdb.execute(gdb_cmd, False, True)
  161. global ENCLAVES_ADDR
  162. ENCLAVES_ADDR[self.start_addr] = gdb_cmd.split()[2]
  163. return 0
  164. def get_peak_heap_used(self):
  165. """Get the peak value of the heap used"""
  166. if self.heap_addr == 0:
  167. return -2
  168. # read the peak_heap_used value
  169. string = read_from_memory(self.heap_addr, SIZE)
  170. if string == None:
  171. return -1
  172. if SIZE == 4:
  173. fmt = 'I'
  174. elif SIZE == 8:
  175. fmt = "Q"
  176. peak_heap_used = struct.unpack(fmt, string)[0]
  177. return peak_heap_used
  178. def internal_compare (self, a, b):
  179. return (a > b) - (a < b)
  180. def find_boundary_page_index(self, stack_addr, stack_size):
  181. """Find the unused page index of the boundary for the used and unused pages
  182. with the binary search algorithm"""
  183. page_index = -1 #record the last unused page index
  184. low = 0
  185. high = (stack_size>>12) - 1
  186. mid = 0
  187. # Read the mid page and check if it is used or not
  188. # If the mid page is used, then continue to search [mid+1, high]
  189. while low <= high:
  190. #print "low = %x, high = %x, mid = %x" % (low, high, mid)
  191. mid = (low + high)>>1
  192. string = read_from_memory(stack_addr + mid*PAGE_SIZE + (PAGE_SIZE>>1), PAGE_SIZE>>1)
  193. if string == None:
  194. return -2
  195. dirty_flag = 0
  196. for i in range(0, PAGE_SIZE>>4):
  197. temp = struct.unpack_from("Q", string, ((PAGE_SIZE>>4) - 1 - i)*8)[0]
  198. if (self.internal_compare(temp, 0xcccccccccccccccc)) != 0:
  199. dirty_flag = 1
  200. break
  201. if dirty_flag == 0:
  202. low = mid + 1
  203. page_index = mid
  204. else:
  205. high = mid -1
  206. return page_index
  207. def get_peak_stack_used(self):
  208. """Get the peak value of the stack used"""
  209. peak_stack_used = 0
  210. for tcs_addr in self.tcs_addr_list:
  211. tcs_str = read_from_memory(tcs_addr, ENCLAVE_TCS_INFO_SIZE)
  212. if tcs_str == None:
  213. return -1
  214. tcs_tuple = struct.unpack_from(TCS_INFO_FMT, tcs_str)
  215. offset = tcs_tuple[7]
  216. if SIZE == 4:
  217. td_fmt = '20I'
  218. elif SIZE == 8:
  219. td_fmt = '20Q'
  220. td_str = read_from_memory(self.start_addr+offset, (20*SIZE))
  221. if td_str == None:
  222. return -1
  223. td_tuple = struct.unpack_from(td_fmt, td_str)
  224. stack_commit_addr = td_tuple[19]
  225. stack_base_addr = td_tuple[2]
  226. stack_limit_addr = td_tuple[3]
  227. stack_usage = 0
  228. if stack_commit_addr > stack_limit_addr:
  229. stack_base_addr_page_align = (stack_base_addr + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)
  230. stack_usage = stack_base_addr_page_align - stack_commit_addr
  231. elif stack_limit_addr != 0:
  232. page_index = self.find_boundary_page_index(stack_limit_addr, self.stack_size)
  233. if page_index == (self.stack_size)/PAGE_SIZE - 1:
  234. continue
  235. elif page_index == -2:
  236. return -1
  237. else:
  238. string = read_from_memory(stack_limit_addr + (page_index+1) * PAGE_SIZE, PAGE_SIZE)
  239. if string == None:
  240. return -1
  241. for i in range(0, len(string)):
  242. temp = struct.unpack_from("B", string, i)[0]
  243. if (self.internal_compare(temp, 0xcc)) != 0:
  244. stack_usage = self.stack_size - (page_index+1) * PAGE_SIZE - i
  245. break
  246. if peak_stack_used < stack_usage:
  247. peak_stack_used = stack_usage
  248. return peak_stack_used
  249. def show_emmt(self):
  250. ret = gdb.execute("show sgx_emmt", False, True)
  251. if ret.strip() == "sgx_emmt enabled":
  252. print ("Enclave: \"{0:s}\"".format(self.enclave_path))
  253. peak_stack_used = self.get_peak_stack_used()
  254. if peak_stack_used == -1:
  255. print ("Failed to collect the stack usage information for \"{0:s}\"".format(self.enclave_path))
  256. else:
  257. peak_stack_used_align = (peak_stack_used + KB_SIZE - 1) & ~(KB_SIZE - 1)
  258. print (" [Peak stack used]: {0:d} KB".format(peak_stack_used_align >> 10))
  259. peak_heap_used = self.get_peak_heap_used()
  260. if peak_heap_used == -1:
  261. print ("Failed to collect the heap usage information for \"{0:s}\"".format(self.enclave_path))
  262. elif peak_heap_used == -2:
  263. print (" [Can't get peak heap used]: You may use version script to control symbol export. Please export \'g_peak_heap_used\' in your version script.")
  264. else:
  265. peak_heap_used_align = (peak_heap_used + KB_SIZE - 1) & ~(KB_SIZE - 1)
  266. print (" [Peak heap used]: {0:d} KB".format(peak_heap_used_align >> 10))
  267. def fini_enclave_debug(self):
  268. # If it is HW product enclave, nothing to do
  269. if (self.enclave_type & ET_SIM) != ET_SIM and (self.enclave_type & ET_DEBUG) != ET_DEBUG:
  270. return -2
  271. self.show_emmt()
  272. try:
  273. # clear TCS debug flag
  274. for tcs_addr in self.tcs_addr_list:
  275. string = read_from_memory(tcs_addr + 8, 4)
  276. if string == None:
  277. return -2
  278. flag = struct.unpack('I', string)[0]
  279. flag &= (~1)
  280. gdb_cmd = "set *(unsigned int *)%#x = %#x" %(tcs_addr + 8, flag)
  281. gdb.execute(gdb_cmd, False, True)
  282. #unload symbol
  283. if os.path.exists(self.enclave_path) == True:
  284. enclave_path = self.enclave_path
  285. else:
  286. enclave_path = target_path_to_host_path(self.enclave_path)
  287. gdb_cmd = load_symbol_cmd.GetUnloadSymbolCommand(enclave_path, str(self.start_addr))
  288. if gdb_cmd == -1:
  289. return -1
  290. print (gdb_cmd)
  291. try:
  292. gdb.execute(gdb_cmd, False, True)
  293. global ENCLAVES_ADDR
  294. del ENCLAVES_ADDR[self.start_addr]
  295. except gdb.error:
  296. print ("Old gdb doesn't support remove-file-symbol command")
  297. return 0
  298. ##It is possible enclave has been destroyed, so may raise exception on memory access
  299. except gdb.MemoryError:
  300. return -1
  301. except:
  302. return -1
  303. def append_tcs_list(self, tcs_addr):
  304. for tcs_tmp in self.tcs_addr_list:
  305. if tcs_tmp == tcs_addr:
  306. return 0
  307. self.tcs_addr_list.append(tcs_addr)
  308. return 0
  309. def retrieve_enclave_info(info_addr = 0):
  310. """retrieve one enclave info"""
  311. # Step 1: find the enclave info address
  312. if info_addr == 0:
  313. if SIZE == 4:
  314. info_addr = gdb.parse_and_eval("$eax")
  315. elif SIZE == 8:
  316. info_addr = gdb.parse_and_eval("$rdi")
  317. # Step 2: retrieve the enclave info
  318. info_str = read_from_memory(info_addr, ENCLAVE_INFO_SIZE)
  319. if info_str == None:
  320. return None
  321. info_tuple = struct.unpack_from(INFO_FMT, info_str, 0)
  322. # (next_enclave_info,start_addr,tcs_list,enclave_type,file_name_size,
  323. # lpFileName,g_peak_heap_used_addr)
  324. #print "next_addr: %#x, start_addr: %#x, tcs_list: %#x, enclave_type:%#x, file_name_size: %#x," \
  325. # % (info_tuple[0], info_tuple[1], info_tuple[2], info_tuple[3], info_tuple[4])
  326. #print "name_addr: %#x, peak_heap_used_addr: %#x" \
  327. # % (info_tuple[5], info_tuple[6])
  328. #get enclave path
  329. name_str = read_from_memory(info_tuple[5], info_tuple[4])
  330. if name_str == None:
  331. return None
  332. fmt = str(info_tuple[4]) + 's'
  333. enclave_path = struct.unpack_from(fmt, name_str)[0].decode(encoding='UTF-8')
  334. # get the stack addr list
  335. stack_addr_list = []
  336. tcs_addr_list = []
  337. if SIZE == 4:
  338. fmt = '3I'
  339. elif SIZE == 8:
  340. fmt = '3Q'
  341. tcs_info_addr = info_tuple[2]
  342. if tcs_info_addr == 0:
  343. print ("Error: tcs info address = {0:x}".format(tcs_info_addr))
  344. return None
  345. stacksize = 0;
  346. while tcs_info_addr is not 0:
  347. tcs_info_str = read_from_memory(tcs_info_addr, 3*SIZE)
  348. if tcs_info_str == None:
  349. return None
  350. tcs_info_tuple = struct.unpack_from(fmt, tcs_info_str)
  351. #get tcs struct data
  352. tcs_t_str = read_from_memory(tcs_info_tuple[1], ENCLAVE_TCS_INFO_SIZE)
  353. if tcs_t_str == None:
  354. return None
  355. tcs_t_tuple = struct.unpack_from(TCS_INFO_FMT, tcs_t_str)
  356. if SIZE == 4:
  357. td_fmt = '4I'
  358. elif SIZE == 8:
  359. td_fmt = '4Q'
  360. #get thread_data_t address
  361. td_addr = tcs_t_tuple[7] + info_tuple[1] #thread_data_t = tcs.of_base + debug_enclave_info.start_addr
  362. td_str = read_from_memory(td_addr, (4*SIZE))
  363. if td_str == None:
  364. return None
  365. td_tuple = struct.unpack_from(td_fmt, td_str)
  366. #print ("thread_info:%#x, last_sp:%#x, stack_base_addr:%#x, stack_limit_addr:%#x" \
  367. # % (td_tuple[0], td_tuple[1], td_tuple[2], td_tuple[3]));
  368. if td_tuple[2] != td_tuple[3]:
  369. #stack size = ROUND_TO_PAGE(stack_base_addr - stack_limit_addr) since we have
  370. #a static stack whose size is smaller than PAGE_SIZE
  371. stacksize = (td_tuple[2] - td_tuple[3] + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)
  372. stack_addr_list.append(td_tuple[3]) #use stack limit addr as stack base address
  373. tcs_addr_list.append(tcs_info_tuple[1])
  374. tcs_info_addr = tcs_info_tuple[0]
  375. last_ocall_frame = tcs_info_tuple[2]
  376. last_frame = last_ocall_frame
  377. #print ("last_ocall_frame = {0:x}".format(last_ocall_frame))
  378. while last_ocall_frame is not 0:
  379. if SIZE == 4:
  380. of_fmt = '4I'
  381. elif SIZE == 8:
  382. of_fmt = '4Q'
  383. ocall_frame = read_from_memory(last_ocall_frame, 4*SIZE)
  384. if ocall_frame == None:
  385. return None
  386. ocall_frame_tuple = struct.unpack_from(of_fmt, ocall_frame)
  387. last_frame = ocall_frame_tuple[0]
  388. last_trusted_ocall_frame = td_tuple[1]
  389. #print ("last_trusted_ocall_frame = {0:x}".format(last_trusted_ocall_frame))
  390. #print ("td_tuple[2] = {0:x}".format(td_tuple[2]))
  391. #break
  392. while last_trusted_ocall_frame != td_tuple[2]:
  393. if SIZE == 4:
  394. oc_fmt = '20I'
  395. ret_addr_of_fmt = 'I'
  396. elif SIZE == 8:
  397. oc_fmt = '20Q'
  398. ret_addr_of_fmt = 'Q'
  399. oc_str = read_from_memory(last_trusted_ocall_frame, 20*SIZE)
  400. if oc_str == None:
  401. return None
  402. oc_tuple = struct.unpack_from(oc_fmt, oc_str)
  403. last_trusted_ocall_frame = oc_tuple[6]
  404. #print ("last_trusted_ocall_frame = {0:x}".format(last_trusted_ocall_frame))
  405. #print ("ocall_frame_tuple[1] = {0:x}".format(ocall_frame_tuple[1]))
  406. #print ("oc_tuple[18] = {0:x}".format(oc_tuple[18]))
  407. #break
  408. if ocall_frame_tuple[1] == oc_tuple[18]:
  409. #ocall_frame.pre_last_frame = 0
  410. #ocall_frame.ret = ocall_context.ocall_ret
  411. #ocall_frame.xbp = ocall_context.xbp
  412. xbp = oc_tuple[11]
  413. ret_addr_str = read_from_memory(xbp + SIZE, SIZE)
  414. if ret_addr_str == None:
  415. return None
  416. ret_addr_tuple = struct.unpack_from(ret_addr_of_fmt, ret_addr_str)
  417. gdb_cmd = "set *(uintptr_t *)%#x = 0" %(int(last_ocall_frame))
  418. gdb.execute(gdb_cmd, False, True)
  419. gdb_cmd = "set *(uintptr_t *)%#x = %#x" %(int(last_ocall_frame+(2*SIZE)), xbp)
  420. gdb.execute(gdb_cmd, False, True)
  421. gdb_cmd = "set *(uintptr_t *)%#x = %#x" %(int(last_ocall_frame+(3*SIZE)), ret_addr_tuple[0])
  422. gdb.execute(gdb_cmd, False, True)
  423. break
  424. last_ocall_frame = last_frame
  425. node = enclave_info(info_tuple[0], info_tuple[1], info_tuple[3], stack_addr_list, \
  426. stacksize, enclave_path, info_tuple[6], tcs_addr_list)
  427. return node
  428. def handle_load_event():
  429. """Handle the enclave loading event.
  430. Firstly, retrieve the enclave info node from register
  431. """
  432. node = retrieve_enclave_info()
  433. if node != None:
  434. node.init_enclave_debug()
  435. else:
  436. return
  437. def handle_unload_event():
  438. node = retrieve_enclave_info()
  439. if node != None:
  440. node.fini_enclave_debug()
  441. else:
  442. return
  443. def is_bp_in_urts():
  444. try:
  445. ip = gdb.parse_and_eval("$pc")
  446. solib_name = gdb.solib_name(int(str(ip).split()[0], 16))
  447. if(solib_name.find("libsgx_urts.so") == -1 and solib_name.find("libsgx_urts_sim.so") == -1 and solib_name.find("libsgx_aesm_service.so") == -1):
  448. return False
  449. else:
  450. return True
  451. #If exception happens, just assume it is bp in uRTS.
  452. except:
  453. return True
  454. def init_enclaves_debug():
  455. #execute "set displaced-stepping off" to workaround the gdb 7.11 issue
  456. gdb.execute("set displaced-stepping off", False, True)
  457. enclave_info_addr = gdb.parse_and_eval("*(void**)&g_debug_enclave_info_list")
  458. while enclave_info_addr != 0:
  459. node = retrieve_enclave_info(enclave_info_addr)
  460. if node != None:
  461. node.init_enclave_debug()
  462. else:
  463. return
  464. enclave_info_addr = node.next_ei
  465. return
  466. class detach_enclaves (gdb.Command):
  467. def __init__ (self):
  468. gdb.Command.__init__ (self, "detach_enclaves", gdb.COMMAND_NONE)
  469. def invoke (self, arg, from_tty):
  470. #We reject the command from the input of terminal
  471. if from_tty == True:
  472. return
  473. try:
  474. enclave_info_addr = gdb.parse_and_eval("*(void**)&g_debug_enclave_info_list")
  475. except:
  476. return
  477. while enclave_info_addr != 0:
  478. node = retrieve_enclave_info(enclave_info_addr)
  479. if node != None:
  480. node.fini_enclave_debug()
  481. else:
  482. return
  483. enclave_info_addr = node.next_ei
  484. class UpdateOcallFrame(gdb.Breakpoint):
  485. def __init__(self):
  486. gdb.Breakpoint.__init__ (self, spec="notify_gdb_to_update", internal=1)
  487. def stop(self):
  488. bp_in_urts = is_bp_in_urts()
  489. if bp_in_urts == True:
  490. if SIZE == 4:
  491. base_addr = gdb.parse_and_eval("$eax")
  492. tcs_addr = gdb.parse_and_eval("$edx")
  493. ocall_frame = gdb.parse_and_eval("$ecx")
  494. elif SIZE == 8:
  495. base_addr = gdb.parse_and_eval("$rdi")
  496. tcs_addr = gdb.parse_and_eval("$rsi")
  497. ocall_frame = gdb.parse_and_eval("$rdx")
  498. #print ("base_addr = {0:x}".format(int(base_addr)))
  499. #print ("tcs_addr = {0:x}".format(int(tcs_addr)))
  500. #print ("ocall_frame = {0:x}".format(int(ocall_frame)))
  501. tcs_str = read_from_memory(tcs_addr, ENCLAVE_TCS_INFO_SIZE)
  502. if tcs_str == None:
  503. return False
  504. tcs_tuple = struct.unpack_from(TCS_INFO_FMT, tcs_str)
  505. offset = tcs_tuple[7]
  506. if SIZE == 4:
  507. td_fmt = '4I'
  508. elif SIZE == 8:
  509. td_fmt = '4Q'
  510. td_str = read_from_memory(base_addr+offset, (4*SIZE))
  511. if td_str == None:
  512. return False
  513. td_tuple = struct.unpack_from(td_fmt, td_str)
  514. if SIZE == 4:
  515. trusted_of_fmt = '20I'
  516. ret_addr_of_fmt = 'I'
  517. elif SIZE == 8:
  518. trusted_of_fmt = '20Q'
  519. ret_addr_of_fmt = 'Q'
  520. last_sp = td_tuple[1]
  521. trusted_ocall_frame = read_from_memory(last_sp, (20*SIZE))
  522. if trusted_ocall_frame == None:
  523. return False
  524. trusted_ocall_frame_tuple = struct.unpack_from(trusted_of_fmt, trusted_ocall_frame)
  525. xbp = trusted_ocall_frame_tuple[11]
  526. ret_addr_str = read_from_memory(xbp + SIZE, SIZE)
  527. if ret_addr_str == None:
  528. return False
  529. ret_addr_tuple = struct.unpack_from(ret_addr_of_fmt, ret_addr_str)
  530. gdb_cmd = "set *(uintptr_t *)%#x = 0" %(int(ocall_frame))
  531. gdb.execute(gdb_cmd, False, True)
  532. gdb_cmd = "set *(uintptr_t *)%#x = %#x" %(int(ocall_frame+(2*SIZE)), xbp)
  533. gdb.execute(gdb_cmd, False, True)
  534. gdb_cmd = "set *(uintptr_t *)%#x = %#x" %(int(ocall_frame+(3*SIZE)), ret_addr_tuple[0])
  535. gdb.execute(gdb_cmd, False, True)
  536. return False
  537. class LoadEventBreakpoint(gdb.Breakpoint):
  538. def __init__(self):
  539. gdb.Breakpoint.__init__ (self, spec="sgx_debug_load_state_add_element", internal=1)
  540. def stop(self):
  541. bp_in_urts = is_bp_in_urts()
  542. if bp_in_urts == True:
  543. handle_load_event()
  544. return False
  545. class UnloadEventBreakpoint(gdb.Breakpoint):
  546. def __init__(self):
  547. gdb.Breakpoint.__init__ (self, spec="sgx_debug_unload_state_remove_element", internal=1)
  548. def stop(self):
  549. bp_in_urts = is_bp_in_urts()
  550. if bp_in_urts == True:
  551. handle_unload_event()
  552. return False
  553. class GetTCSBreakpoint(gdb.Breakpoint):
  554. def __init__(self):
  555. gdb.Breakpoint.__init__ (self, spec="urts_add_tcs", internal=1) # sgx_add_tcs should be fastcall
  556. def stop(self):
  557. bp_in_urts = is_bp_in_urts()
  558. if bp_in_urts == True:
  559. if SIZE == 4:
  560. tcs_addr_1 = gdb.parse_and_eval("$eax")
  561. tcs_addr = ctypes.c_uint32(tcs_addr_1).value
  562. elif SIZE == 8:
  563. tcs_addr_1 = gdb.parse_and_eval("$rdi")
  564. tcs_addr = ctypes.c_uint64(tcs_addr_1).value
  565. enclave_info_addr = gdb.parse_and_eval("*(void **)&g_debug_enclave_info_list")
  566. if enclave_info_addr != 0:
  567. node = retrieve_enclave_info(enclave_info_addr)
  568. else:
  569. return False
  570. if node != None:
  571. node.append_tcs_list(tcs_addr)
  572. string = read_from_memory(tcs_addr + 8, 4)
  573. if string == None:
  574. return False
  575. flag = struct.unpack('I', string)[0]
  576. flag |= 1
  577. gdb_cmd = "set *(unsigned int *)%#x = %#x" %(tcs_addr + 8, flag)
  578. gdb.execute(gdb_cmd, False, True)
  579. return False
  580. def sgx_debugger_init():
  581. print ("detect urts is loaded, initializing")
  582. global SIZE
  583. SIZE = gdb.parse_and_eval("sizeof(long)")
  584. inited = 0
  585. bps = gdb.breakpoints()
  586. if None != bps:
  587. for bp in bps:
  588. if bp.location == "sgx_debug_load_state_add_element":
  589. inited = 1
  590. break
  591. if inited == 0:
  592. detach_enclaves()
  593. gdb.execute("source gdb_sgx_cmd", False, True)
  594. UpdateOcallFrame()
  595. LoadEventBreakpoint()
  596. UnloadEventBreakpoint()
  597. GetTCSBreakpoint()
  598. gdb.events.exited.connect(exit_handler)
  599. init_enclaves_debug()
  600. def exit_handler(event):
  601. # When the inferior exited, remove all enclave symbol
  602. for key in list(ENCLAVES_ADDR.keys()):
  603. gdb.execute("remove-symbol-file -a %s" % (ENCLAVES_ADDR[key]), False, True)
  604. ENCLAVES_ADDR.clear()
  605. def newobj_handler(event):
  606. solib_name = os.path.basename(event.new_objfile.filename)
  607. if solib_name == 'libsgx_urts.so' or solib_name == 'libsgx_urts_sim.so' or solib_name == 'libsgx_aesm_service.so':
  608. sgx_debugger_init()
  609. return
  610. if __name__ == "__main__":
  611. gdb.events.new_objfile.connect(newobj_handler)
  612. sgx_emmt.init_emmt()