当前位置:   article > 正文

[frida]拦截SSL_read/SSL_write函数获得HTTPS请求和响应_frida截获网络数据

frida截获网络数据

此方法网上已经有很多文章介绍,可以绕过证书校验。这里只放出关键代码,hook.js:

  1. function hook() {
  2. if (!Java.available) {
  3. console.error('Java API not available');
  4. return;
  5. }
  6. Java.perform(function () {
  7. console.log('hooked');
  8. var SSL_write, SSL_read;
  9. const apiResolver = new ApiResolver('module');
  10. apiResolver.enumerateMatches('exports:*libssl*!SSL_*').forEach(function (v) {
  11. if (v.name.indexOf('SSL_write') > 0) {
  12. SSL_write = v.address;
  13. } else if (v.name.indexOf('SSL_read') > 0) {
  14. SSL_read = v.address;
  15. }
  16. });
  17. if (SSL_write) {
  18. Interceptor.attach(SSL_write, {
  19. onEnter: function (args) {
  20. this.ssl = args[0].toString();
  21. this.buf = ptr(args[1]);
  22. },
  23. onLeave: function (retval) {
  24. const len = retval.toInt32();
  25. if (len > 0) {
  26. // console.log('SSL_write\n', this.buf.readByteArray(len), '\n', '*'.repeat(120));
  27. send({
  28. code: 100,
  29. ssl: this.ssl
  30. }, this.buf.readByteArray(len));
  31. // send({
  32. // code: 100,
  33. // ssl: this.ssl
  34. // }, Memory.readByteArray(this.buf, len));
  35. }
  36. }
  37. });
  38. }
  39. if (SSL_read) {
  40. Interceptor.attach(SSL_read, {
  41. onEnter: function (args) {
  42. this.ssl = args[0].toString();
  43. this.buf = ptr(args[1]);
  44. },
  45. onLeave: function (retval) {
  46. const len = retval.toInt32();
  47. if (len > 0) {
  48. // console.log('SSL_read\n', this.buf.readByteArray(len), '\n', '*'.repeat(120));
  49. send({
  50. code: 200,
  51. ssl: this.ssl
  52. }, this.buf.readByteArray(len));
  53. // send({
  54. // code: 200,
  55. // ssl: this.ssl
  56. // }, Memory.readByteArray(this.buf, len));
  57. }
  58. }
  59. });
  60. }
  61. });
  62. }
  63. hook();

host.py:

  1. import time
  2. import frida
  3. import zlib
  4. device = frida.get_usb_device()
  5. # 直接启动对应的 APP
  6. # pid = device.spawn("com.snail.android.lucky")
  7. # device.resume(pid)
  8. # 使用 frida-ps -Uia 获取 APP 的 PID
  9. pid = 5341
  10. print(pid)
  11. # time.sleep(1)
  12. session = device.attach(pid)
  13. with open("hook.js") as f:
  14. script = session.create_script(f.read())
  15. def show_data(headers, payload):
  16. try:
  17. _headers = headers.decode('utf-8')
  18. headers = _headers
  19. except:
  20. pass
  21. try:
  22. _payload = payload.decode('utf-8')
  23. payload = _payload
  24. except:
  25. pass
  26. print(headers, '\n\n', payload)
  27. # “请求-响应”对,使用 SSL 句柄作为 KEY
  28. pairs = dict()
  29. def on_message(message, payload):
  30. # print(message, payload)
  31. if message['type'] != 'send':
  32. return
  33. ssl = message['payload']['ssl']
  34. if message['payload']['code'] == 100: # SSL_write
  35. pairs[ssl] = dict()
  36. pairs[ssl]['w'] = payload
  37. elif message['payload']['code'] == 200: # SSL_read
  38. # print(ssl)
  39. if ssl not in pairs:
  40. return
  41. # 响应分段合并
  42. if payload.find(b'HTTP') == 0: # 首段
  43. pairs[ssl]['r'] = bytearray(payload)
  44. else: # 后面的分段
  45. if 'r' not in pairs[ssl]:
  46. del pairs[ssl]
  47. return
  48. pairs[ssl]['r'].extend(payload)
  49. #########################################################################################################################################################
  50. r_headers = bytes()
  51. r_payload = bytes()
  52. _parts = pairs[ssl]['r'].split(b'\r\n\r\n', 1)
  53. if len(_parts) != 2:
  54. del pairs[ssl]
  55. print('read parts length err =', len(_parts))
  56. return
  57. # 尝试解压已接收到的响应数据
  58. if _parts[0].find(b'Content-Encoding: gzip') >= 0:
  59. _data = bytearray()
  60. _gzip = _parts[1].split(b'\r\n')
  61. for i in range(1, len(_gzip), 2): # 跳过长度
  62. _data.extend(_gzip[i])
  63. try:
  64. _data = zlib.decompress(_data, 16 + zlib.MAX_WBITS)
  65. except: # 可能是响应还未接收完
  66. return
  67. r_headers = _parts[0]
  68. r_payload = _data
  69. else: # 无需解压
  70. r_headers = _parts[0]
  71. r_payload = _parts[1]
  72. #########################################################################################################################################################
  73. w_headers = bytes()
  74. w_payload = bytes()
  75. _parts = pairs[ssl]['w'].split(b'\r\n\r\n', 1)
  76. if len(_parts) != 2:
  77. del pairs[ssl]
  78. print('write parts length err =', len(_parts))
  79. return
  80. # 尝试解压已接收到的请求数据
  81. if _parts[0].find(b'Content-Encoding: gzip') >= 0:
  82. _data = bytearray()
  83. try:
  84. _data = zlib.decompress(_parts[1], 16 + zlib.MAX_WBITS)
  85. except:
  86. del pairs[ssl]
  87. print('write payload decompress err =', _parts[1])
  88. return
  89. w_headers = _parts[0]
  90. w_payload = _data
  91. else: # 无需解压
  92. w_headers = _parts[0]
  93. w_payload = _parts[1]
  94. print('*' * 120)
  95. show_data(w_headers, w_payload)
  96. print('\n')
  97. show_data(r_headers, r_payload)
  98. print('*' * 120)
  99. del pairs[ssl]
  100. script.on("message", on_message)
  101. script.load()
  102. input() #等待输入
  103. session.detach()

效果图:

简化版本,直接保存为capture_ssl.js,然后frida -p pid -l capture_ssl.js

  1. (new ApiResolver('module'))
  2. .enumerateMatches('exports:*!SSL_*')
  3. .filter(x => ['SSL_read', 'SSL_write'].some(y => x.name.indexOf(y) >= 0))
  4. .forEach(x => {
  5. Interceptor.attach(x.address, {
  6. onEnter: function (args) {
  7. // int SSL_read(SSL *ssl, void *buf, int num);
  8. // int SSL_write(SSL *ssl, const void *buf, int num);
  9. const buf = ptr(args[1]);
  10. const num = args[2].toInt32();
  11. console.log(x.name, buf, num);
  12. // const data = buf.readByteArray(num);
  13. // const data = buf.readCString(num
  14. // const data = buf.readUtf8String(num);
  15. // const data = buf.readUtf16String(num);
  16. const data = buf.readAnsiString(num);
  17. console.log(data);
  18. },
  19. onLeave: function (retval) {
  20. console.log(x.name, retval);
  21. }
  22. })
  23. });

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号