/**
 * 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 });
