Files
workphone-sdk/sdk/agent/hook/scripts/syscall_hook.js

313 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* syscall_hook.js — 系统调用层网络拦截(机擎 SDK v3.0 · H23
*
* 功能:
* 1. 拦截 libc connect/sendto/recvfrom — 监控所有网络连接
* 2. 拦截 SSL_read/SSL_write — HTTPS 明文抓取
* 3. DNS 解析拦截 — 域名映射追踪
* 4. 可选SSL Pinning Bypass
*
* 使用方式:
* 由 FridaManager 单独加载或与 wechat_hook_v2.js 并行
*
* @version 1.0.0
*/
'use strict';
var PLATFORM = 'syscall';
var SYSCALL_CONFIG = {
HOOK_CONNECT: true,
HOOK_SSL: true,
HOOK_DNS: true,
SSL_BYPASS: false,
LOG_LEVEL: 'info',
CAPTURE_PAYLOAD: false,
MAX_PAYLOAD_SIZE: 4096,
MONITORED_DOMAINS: [
'weixin.qq.com', 'wechat.com', 'wx.qq.com',
'long.weixin.qq.com', 'short.weixin.qq.com',
'szlong.weixin.qq.com', 'szshort.weixin.qq.com',
],
MONITORED_PORTS: [80, 443, 8080, 8443],
};
// ============================================================
// 工具
// ============================================================
function log(level, tag, message, extra) {
send({ type: 'log', level: level, tag: 'syscall.' + tag, message: String(message || ''), extra: extra || {}, timestamp: Date.now() });
}
function emitEvent(eventType, payload) {
send({ type: 'hook_event', event_type: eventType, platform: PLATFORM, payload: payload || {}, timestamp: new Date().toISOString() });
}
function ipFromSockaddr(ptr) {
if (ptr.isNull()) return { ip: '0.0.0.0', port: 0, family: 0 };
var family = ptr.readU16();
if (family === 2) {
var port = (ptr.add(2).readU8() << 8) | ptr.add(3).readU8();
var ip = ptr.add(4).readU8() + '.' + ptr.add(5).readU8() + '.' + ptr.add(6).readU8() + '.' + ptr.add(7).readU8();
return { ip: ip, port: port, family: 2 };
}
if (family === 10) {
var port6 = (ptr.add(2).readU8() << 8) | ptr.add(3).readU8();
var bytes = [];
for (var i = 0; i < 16; i++) bytes.push(ptr.add(8 + i).readU8());
if (bytes[10] === 0xff && bytes[11] === 0xff && bytes.slice(0, 10).every(function (b) { return b === 0; })) {
return { ip: bytes[12] + '.' + bytes[13] + '.' + bytes[14] + '.' + bytes[15], port: port6, family: 10 };
}
return { ip: '::ipv6', port: port6, family: 10 };
}
return { ip: 'unknown', port: 0, family: family };
}
// ============================================================
// 统计
// ============================================================
var _stats = {
connections: 0,
ssl_reads: 0,
ssl_writes: 0,
dns_queries: 0,
bytes_sent: 0,
bytes_received: 0,
};
var _connectionMap = {};
// ============================================================
// rpc.exports
// ============================================================
rpc.exports = {
ping: function () { return 'pong from syscall_hook'; },
getStats: function () { return _stats; },
getConnections: function (params) {
var limit = (params && params.limit) || 50;
var keys = Object.keys(_connectionMap).slice(-limit);
return {
success: true,
connections: keys.map(function (k) { return _connectionMap[k]; }),
count: keys.length,
total: Object.keys(_connectionMap).length,
};
},
setConfig: function (params) {
if (params) {
if (params.capture_payload !== undefined) SYSCALL_CONFIG.CAPTURE_PAYLOAD = !!params.capture_payload;
if (params.ssl_bypass !== undefined) SYSCALL_CONFIG.SSL_BYPASS = !!params.ssl_bypass;
if (params.log_level) SYSCALL_CONFIG.LOG_LEVEL = params.log_level;
}
return { success: true, config: SYSCALL_CONFIG };
},
};
// ============================================================
// Hook 1: libc connect — 捕获所有 TCP 连接
// ============================================================
if (SYSCALL_CONFIG.HOOK_CONNECT) {
try {
var connectPtr = Module.findExportByName('libc.so', 'connect');
if (connectPtr) {
Interceptor.attach(connectPtr, {
onEnter: function (args) {
this.fd = args[0].toInt32();
this.addr = args[1];
this.addrlen = args[2].toInt32();
},
onLeave: function (retval) {
if (retval.toInt32() === 0 || retval.toInt32() === -1) {
try {
var info = ipFromSockaddr(this.addr);
if (info.family === 2 || info.family === 10) {
_stats.connections++;
var connId = 'fd_' + this.fd;
_connectionMap[connId] = {
fd: this.fd,
ip: info.ip,
port: info.port,
timestamp: new Date().toISOString(),
pid: Process.id,
tid: Process.getCurrentThreadId(),
};
var isMonitored = SYSCALL_CONFIG.MONITORED_PORTS.indexOf(info.port) !== -1;
if (isMonitored) {
emitEvent('tcp_connect', { fd: this.fd, ip: info.ip, port: info.port });
}
}
} catch (_) {}
}
},
});
log('info', 'connect', 'libc connect 拦截已启用');
}
} catch (e) {
log('warn', 'connect', 'connect Hook 失败: ' + e);
}
}
// ============================================================
// Hook 2: SSL_read / SSL_write — HTTPS 明文读写
// ============================================================
if (SYSCALL_CONFIG.HOOK_SSL) {
var sslLibNames = ['libssl.so', 'libssl.so.1.1', 'libssl.so.3'];
function hookSSL(libName) {
try {
var SSL_read = Module.findExportByName(libName, 'SSL_read');
var SSL_write = Module.findExportByName(libName, 'SSL_write');
if (SSL_read) {
Interceptor.attach(SSL_read, {
onEnter: function (args) {
this.ssl = args[0];
this.buf = args[1];
this.num = args[2].toInt32();
},
onLeave: function (retval) {
var len = retval.toInt32();
if (len > 0) {
_stats.ssl_reads++;
_stats.bytes_received += len;
if (SYSCALL_CONFIG.CAPTURE_PAYLOAD && len <= SYSCALL_CONFIG.MAX_PAYLOAD_SIZE) {
try {
var data = this.buf.readByteArray(Math.min(len, 512));
emitEvent('ssl_read', { length: len, preview: _bytesToHex(data, 64) });
} catch (_) {}
}
}
},
});
}
if (SSL_write) {
Interceptor.attach(SSL_write, {
onEnter: function (args) {
this.ssl = args[0];
this.buf = args[1];
this.num = args[2].toInt32();
_stats.ssl_writes++;
_stats.bytes_sent += this.num;
if (SYSCALL_CONFIG.CAPTURE_PAYLOAD && this.num <= SYSCALL_CONFIG.MAX_PAYLOAD_SIZE) {
try {
var data = this.buf.readByteArray(Math.min(this.num, 512));
emitEvent('ssl_write', { length: this.num, preview: _bytesToHex(data, 64) });
} catch (_) {}
}
},
});
}
if (SSL_read || SSL_write) {
log('info', 'ssl', 'SSL Hook 已启用: ' + libName);
return true;
}
} catch (_) {}
return false;
}
var sslHooked = false;
for (var li = 0; li < sslLibNames.length && !sslHooked; li++) {
sslHooked = hookSSL(sslLibNames[li]);
}
if (!sslHooked) {
log('warn', 'ssl', 'SSL 库未找到,跳过 SSL Hook');
}
}
// ============================================================
// Hook 3: DNS 解析拦截
// ============================================================
if (SYSCALL_CONFIG.HOOK_DNS) {
try {
var getaddrinfoPtr = Module.findExportByName('libc.so', 'getaddrinfo');
if (getaddrinfoPtr) {
Interceptor.attach(getaddrinfoPtr, {
onEnter: function (args) {
this.hostname = args[0].isNull() ? '' : args[0].readCString();
},
onLeave: function (retval) {
if (this.hostname && retval.toInt32() === 0) {
_stats.dns_queries++;
var isMonitored = false;
for (var i = 0; i < SYSCALL_CONFIG.MONITORED_DOMAINS.length; i++) {
if (this.hostname.indexOf(SYSCALL_CONFIG.MONITORED_DOMAINS[i]) !== -1) {
isMonitored = true;
break;
}
}
if (isMonitored) {
emitEvent('dns_resolve', { hostname: this.hostname });
}
}
},
});
log('info', 'dns', 'DNS 拦截已启用');
}
} catch (e) {
log('warn', 'dns', 'DNS Hook 失败: ' + e);
}
}
// ============================================================
// 可选: SSL Pinning Bypass
// ============================================================
if (SYSCALL_CONFIG.SSL_BYPASS) {
Java.perform(function () {
// TrustManager bypass
try {
var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
TrustManagerImpl.verifyChain.implementation = function () {
return arguments[0];
};
log('info', 'bypass', 'TrustManagerImpl bypass 已启用');
} catch (_) {}
// OkHttp CertificatePinner bypass
try {
var CertPinner = Java.use('okhttp3.CertificatePinner');
CertPinner.check.overload('java.lang.String', 'java.util.List').implementation = function () {};
log('info', 'bypass', 'OkHttp CertificatePinner bypass 已启用');
} catch (_) {}
// WebViewClient SSL bypass
try {
var WebViewClient = Java.use('android.webkit.WebViewClient');
WebViewClient.onReceivedSslError.implementation = function (view, handler, error) {
handler.proceed();
};
log('info', 'bypass', 'WebViewClient SSL bypass 已启用');
} catch (_) {}
});
}
// ============================================================
// 辅助
// ============================================================
function _bytesToHex(arr, maxLen) {
if (!arr) return '';
var bytes = new Uint8Array(arr);
var hex = [];
var limit = Math.min(bytes.length, maxLen || 64);
for (var i = 0; i < limit; i++) {
hex.push(('0' + bytes[i].toString(16)).slice(-2));
}
return hex.join(' ') + (bytes.length > limit ? '...' : '');
}
log('info', 'init', 'syscall_hook 初始化完成', _stats);
emitEvent('syscall_hook_initialized', { config: SYSCALL_CONFIG, stats: _stats });