Skip to content

Commit 32050d5

Browse files
committed
update certs
1 parent da22f36 commit 32050d5

File tree

3 files changed

+66
-49
lines changed

3 files changed

+66
-49
lines changed

docs/reverse-engineering-internals.md

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,14 @@
2727
#### 1. 根 CA 证书生成 (`CertStore._create_authority`)
2828

2929
```python
30-
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
31-
cert = (
32-
x509.CertificateBuilder()
33-
.add_extension(x509.BasicConstraints(ca=True, path_length=None), critical=True)
34-
.add_extension(x509.KeyUsage(key_cert_sign=True, crl_sign=True, ...), critical=True)
35-
.sign(key, hashes.SHA256())
36-
)
30+
name = x509.Name([
31+
x509.NameAttribute(NameOID.COUNTRY_NAME, self._profile['country']), # 随机国家 (JP/TW/DE/NL/SG)
32+
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, self._profile['state']),
33+
x509.NameAttribute(NameOID.LOCALITY_NAME, self._profile['city']),
34+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, self._profile['org']),
35+
x509.NameAttribute(NameOID.COMMON_NAME, self._profile['cn'])
36+
])
37+
# ... 证书签发逻辑 ...
3738
```
3839

3940
生成的 `ca.crt` 需要被系统/浏览器信任,才能使后续的域名证书被接受。
@@ -105,14 +106,17 @@ elif 'GenerateContent' in path:
105106

106107
### 响应解码流程 (`ResponseHandler.handle_response`)
107108

109+
```python
108110
```python
109111
async def handle_response(self, data, host, path, headers):
112+
# 使用 memoryview 优化内存复制
110113
decoded, completed = self._unchunk(bytes(data))
111114
decoded = self._inflate(decoded)
112115
result = self._extract_content(decoded)
113116
result['done'] = completed
114117
return result
115118
```
119+
```
116120
117121
#### 1. Chunked 解码 (`_unchunk`)
118122
@@ -171,17 +175,24 @@ elif len(payload) > 2:
171175

172176
通过分析包含 Function Call 的响应,发现参数使用递归嵌套数组表示:
173177

178+
```python
174179
```python
175180
def _parse_tool_args(self, args):
181+
extractors = {
182+
1: lambda v: None,
183+
2: lambda v: v[1],
184+
3: lambda v: v[2],
185+
4: lambda v: v[3] == 1,
186+
5: lambda v: self._parse_tool_args(v[4]),
187+
}
188+
176189
for param in params:
177-
name = param[0]
178-
value = param[1]
179-
180-
if len(value) == 1: result[name] = None
181-
elif len(value) == 2: result[name] = value[1]
182-
elif len(value) == 3: result[name] = value[2]
183-
elif len(value) == 4: result[name] = value[3] == 1
184-
elif len(value) == 5: result[name] = self._parse_tool_args(value[4])
190+
name, value = param[0], param[1]
191+
if isinstance(value, list):
192+
extractor = extractors.get(len(value))
193+
if extractor:
194+
result[name] = extractor(value)
195+
```
185196
```
186197
187198
value 数组长度与数据类型的映射关系:

src/proxy/connection.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import datetime
22
import os
33
import ssl
4+
import random
45
from pathlib import Path
56
from typing import Optional, Tuple
67
from urllib.parse import urlparse
@@ -16,13 +17,23 @@
1617
import ssl as ssl_module
1718

1819

20+
CERT_PROFILES = [
21+
{'country': 'JP', 'state': 'Tokyo', 'city': 'Shibuya', 'org': 'Interceptor Ltd', 'cn': 'Local Proxy Root'},
22+
{'country': 'TW', 'state': 'Taiwan', 'city': 'Taipei', 'org': 'Secure Gateway', 'cn': 'Gateway CA'},
23+
{'country': 'DE', 'state': 'Hessen', 'city': 'Frankfurt', 'org': 'Network Services', 'cn': 'Network CA'},
24+
{'country': 'NL', 'state': 'Noord-Holland', 'city': 'Amsterdam', 'org': 'Privacy Tools', 'cn': 'Privacy CA Root'},
25+
{'country': 'SG', 'state': 'Singapore', 'city': 'Singapore', 'org': 'Cloud Proxy', 'cn': 'Cloud CA'},
26+
]
27+
28+
1929
class CertStore:
2030

2131
def __init__(self, storage_path: str = 'certs'):
2232
self.storage_dir = Path(storage_path)
2333
self.storage_dir.mkdir(exist_ok=True)
2434
self.authority_key_file = self.storage_dir / 'ca.key'
2535
self.authority_cert_file = self.storage_dir / 'ca.crt'
36+
self._profile = random.choice(CERT_PROFILES)
2637
if not self.authority_cert_file.exists() or not self.authority_key_file.exists():
2738
self._create_authority()
2839
self._load_authority()
@@ -41,11 +52,11 @@ def _create_authority(self) -> None:
4152
))
4253

4354
name = x509.Name([
44-
x509.NameAttribute(NameOID.COUNTRY_NAME, 'US'),
45-
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, 'California'),
46-
x509.NameAttribute(NameOID.LOCALITY_NAME, 'San Francisco'),
47-
x509.NameAttribute(NameOID.ORGANIZATION_NAME, 'Proxy CA'),
48-
x509.NameAttribute(NameOID.COMMON_NAME, 'Proxy CA Root')
55+
x509.NameAttribute(NameOID.COUNTRY_NAME, self._profile['country']),
56+
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, self._profile['state']),
57+
x509.NameAttribute(NameOID.LOCALITY_NAME, self._profile['city']),
58+
x509.NameAttribute(NameOID.ORGANIZATION_NAME, self._profile['org']),
59+
x509.NameAttribute(NameOID.COMMON_NAME, self._profile['cn'])
4960
])
5061

5162
cert = (

src/proxy/handler.py

Lines changed: 24 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -102,22 +102,20 @@ def _parse_tool_args(self, args: List) -> Dict:
102102
params = args[0]
103103
result = {}
104104

105+
extractors = {
106+
1: lambda v: None,
107+
2: lambda v: v[1],
108+
3: lambda v: v[2],
109+
4: lambda v: v[3] == 1,
110+
5: lambda v: self._parse_tool_args(v[4]),
111+
}
112+
105113
for param in params:
106-
name = param[0]
107-
value = param[1]
108-
114+
name, value = param[0], param[1]
109115
if isinstance(value, list):
110-
length = len(value)
111-
if length == 1:
112-
result[name] = None
113-
elif length == 2:
114-
result[name] = value[1]
115-
elif length == 3:
116-
result[name] = value[2]
117-
elif length == 4:
118-
result[name] = value[3] == 1
119-
elif length == 5:
120-
result[name] = self._parse_tool_args(value[4])
116+
extractor = extractors.get(len(value))
117+
if extractor:
118+
result[name] = extractor(value)
121119

122120
return result
123121
except Exception as e:
@@ -130,35 +128,32 @@ def _inflate(compressed: bytes) -> bytes:
130128

131129
@staticmethod
132130
def _unchunk(body: bytes) -> Tuple[bytes, bool]:
131+
mv = memoryview(body)
133132
result = bytearray()
133+
offset = 0
134+
body_len = len(mv)
134135

135-
while True:
136-
crlf_pos = body.find(b'\r\n')
136+
while offset < body_len:
137+
crlf_pos = body.find(b'\r\n', offset)
137138
if crlf_pos == -1:
138139
break
139140

140-
hex_size = body[:crlf_pos]
141141
try:
142-
chunk_size = int(hex_size, 16)
142+
chunk_size = int(mv[offset:crlf_pos].tobytes(), 16)
143143
except ValueError as e:
144144
logging.error(f'Chunk parse error: {e}')
145145
break
146146

147147
if chunk_size == 0:
148-
end_marker = body.find(b'0\r\n\r\n')
149-
if end_marker != -1:
150-
return bytes(result), True
151-
152-
if chunk_size + 2 > len(body):
153-
break
148+
return bytes(result), True
154149

155-
start = crlf_pos + 2
156-
end = start + chunk_size
157-
result.extend(body[start:end])
150+
data_start = crlf_pos + 2
151+
data_end = data_start + chunk_size
158152

159-
if end + 2 > len(body):
153+
if data_end > body_len:
160154
break
161155

162-
body = body[end + 2:]
156+
result.extend(mv[data_start:data_end])
157+
offset = data_end + 2
163158

164159
return bytes(result), False

0 commit comments

Comments
 (0)