| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- #!/usr/bin/env python3
- import base64
- import re
- import requests
- from dataclasses import dataclass
- URL = "https://schmid-gartenpflanzen.on3cx.de/MyPhone/MPWebService.asmx"
- SESSION = "b7a74b60-7987-d1ae-9189-49dd1c48803a"
- @dataclass
- class AbandonedCall:
- call_id: int
- number: str | None
- queue: str | None
- raw: bytes
- def read_varint(data, pos):
- value = 0
- shift = 0
- while pos < len(data):
- b = data[pos]
- pos += 1
- value |= (b & 0x7f) << shift
- if not b & 0x80:
- return value, pos
- shift += 7
- raise ValueError("unterminated varint")
- def fields(data):
- pos = 0
- while pos < len(data):
- key, pos = read_varint(data, pos)
- field_no = key >> 3
- wire = key & 7
- if wire == 0:
- value, pos = read_varint(data, pos)
- elif wire == 2:
- length, pos = read_varint(data, pos)
- value = data[pos:pos + length]
- pos += length
- elif wire == 1:
- value = data[pos:pos + 8]
- pos += 8
- elif wire == 5:
- value = data[pos:pos + 4]
- pos += 4
- else:
- raise ValueError(f"unsupported wire type {wire}")
- yield field_no, wire, value
- def strings(data):
- out = []
- for field_no, wire, value in fields(data):
- if wire == 2:
- try:
- text = value.decode("utf-8")
- if text.isprintable() and len(text) >= 2:
- out.append((field_no, text))
- except UnicodeDecodeError:
- pass
- return out
- def find_call_messages(data):
- """
- Command 145 liefert eine Liste verschachtelter Call-Messages.
- Wir suchen rekursiv nach Messages, die eine E.164-Nummer enthalten.
- """
- results = []
- def walk(blob):
- try:
- fs = list(fields(blob))
- except Exception:
- return
- text = strings(blob)
- numbers = [
- value for _, value in text
- if re.fullmatch(r"\+\d{6,20}", value)
- ]
- if numbers:
- # bekannte Struktur: irgendwo in derselben Message
- # liegt die numerische Call-ID als varint.
- varints = [
- value for _, wire, value in fs
- if wire == 0 and isinstance(value, int)
- ]
- # IDs wie 11626/11627 sind hier besonders interessant.
- ids = [v for v in varints if 1000 <= v <= 10000000]
- if ids:
- results.append((ids[0], numbers[0], blob))
- for _, wire, value in fs:
- if wire == 2 and isinstance(value, bytes) and len(value) > 2:
- walk(value)
- walk(data)
- return results
- def main():
- # Command 145, beobachteter Request:
- # 08 91 01 8a 09 06 10 50 18 00 22 00
- payload = bytes.fromhex("08 91 01 8a 09 06 10 50 18 00 22 00")
- r = requests.post(
- URL,
- headers={
- "Accept": "application/octet-stream",
- "Content-Type": "application/octet-stream",
- "MyPhoneSession": SESSION,
- "Origin": "https://schmid-gartenpflanzen.on3cx.de",
- },
- data=payload,
- verify=False,
- timeout=15,
- )
- r.raise_for_status()
- print(f"HTTP: {r.status_code}")
- print(f"Response: {len(r.content)} bytes")
- calls = find_call_messages(r.content)
- seen = set()
- for call_id, number, raw in calls:
- key = (call_id, number)
- if key in seen:
- continue
- seen.add(key)
- print(f"{call_id:>8} {number}")
- if __name__ == "__main__":
- main()
|