sigalor / whatsapp-web-reveng

Reverse engineering WhatsApp Web.
MIT License
6.05k stars 803 forks source link

New version of this reveng #303

Closed emustafasahin closed 3 years ago

emustafasahin commented 3 years ago

I write this project owner but never contact me. If you want a Python 3 Version(All options implemented) just mail me. I ask you a riddle and if you find answer i will send you files. Have a good days :)

Screen Shot 2020-10-15 at 20 49 55

arisawali2014 commented 3 years ago

what is your email?

kirya-dev commented 3 years ago

hmm. im doing same.. How you sole sending text messages? Whats app cannot parse my timestamp

emustafasahin commented 3 years ago

hmm. im doing same.. How you sole sending text messages? Whats app cannot parse my timestamp

def sendMessage(self, message, number): messageId = binascii.hexlify(Random.get_random_bytes(16)).upper().decode() messageParams = {"key": {"fromMe": True, "remoteJid": number + "@s.whatsapp.net", "id": messageId}, "messageTimestamp": getTimestamp(), "status": '1', "message": {message['type']: message['message']}} self.message[messageId] = {message['type']: message['message']} msgData = ["action", {"type": "relay", "epoch": str(self.messageSendCount)}, [["message", None, WAWebMessageInfo().encode(messageParams)]]] encryptedMessage = WhatsAppEncrypt(self.settings.encKey, self.settings.macKey, whatsappWriteBinary(msgData)) payload = bytearray(messageId.encode()) + bytearray(','.encode()) + bytearray( [WAMetrics.MESSAGE, WAFlags.IGNORE]) + encryptedMessage self.messageSendCount += 1 self.sender.send(payload, websocket.ABNF.OPCODE_BINARY)

this code works for me

arisawali2014 commented 3 years ago

I've got 401 error message

kirya-dev commented 3 years ago

@19XXS what is whatsapp version you use?

emustafasahin commented 3 years ago

latest version

emustafasahin commented 3 years ago

ıf you have a 401 you wasn't send message send count other request

kirya-dev commented 3 years ago

ıf you have a 401 you wasn't send message send count other request

I have problem with dates. In get very big timestamp value after sending (web version cannot parse so much number, mobile version sometimes crushes), but encoded message is normal. BUT receiver of the message has valid current timestamp.

arisawali2014 commented 3 years ago

But are you sure you have succeeded send the text message?

emustafasahin commented 3 years ago

I send Text,Document,Image,Video,Location and Live Location message

arisawali2014 commented 3 years ago

I have send an email to you when will you send me the riddle i want to see what is wrong with my code. And comparing with your whatsappcore

kirya-dev commented 3 years ago

messageId = binascii.hexlify(Random.get_random_bytes(16)).upper().decode()

Its seams broken. messageid must be starts of 3EB0?

arisawali2014 commented 3 years ago

messageId = binascii.hexlify(Random.get_random_bytes(16)).upper().decode()

Its seams broken. messageid must be starts of 3EB0?

i don't think so.

kirya-dev commented 3 years ago

i don't think so.

Why? Whatsapp always start message id from this string.

I've got 401 error message

Im recive this erro when forget WAWebMessageInfo.encode

arisawali2014 commented 3 years ago

i don't think so.

Why? Whatsapp always start message id from this string.

I've got 401 error message

Im recive this erro when forget WAWebMessageInfo.encode

I have a work ported and it work perfectly without 3EB0 string

kirya-dev commented 3 years ago

You can help me? Im cannot send message((

arisawali2014 commented 3 years ago

Me either 😂 i don't have python ported that work on sending messages

kirya-dev commented 3 years ago

I have a work ported and it work perfectly without 3EB0 string

Seven minutes ago you say that have.

arisawali2014 commented 3 years ago

But I'm not saying python

kirya-dev commented 3 years ago

What ported you are have?)

arisawali2014 commented 3 years ago

As you can see in the readme.md if you looked at the reimplantation section. Go, Typescript, Rust

arisawali2014 commented 3 years ago

https://github.com/adiwajshing/Baileys/blob/5b86dc06bad352b2ad0e0fc6941a84fdd0087b05/src/WAConnection/Utils.ts#L134

kirya-dev commented 3 years ago

You what use python port?

arisawali2014 commented 3 years ago

No i don't have python port

kirya-dev commented 3 years ago

You would like get such port?

arisawali2014 commented 3 years ago

yes, i would like.

arisawali2014 commented 3 years ago

here is my email lilari2310@gmail.com if you would like to send your files

kirya-dev commented 3 years ago

lilari2310@gmail.com

see https://github.com/kirya-dev/whatsapp-web-reveng

emustafasahin commented 3 years ago

import binascii from io import BytesIO import subprocess import curve25519 import requests import websocket from Crypto import Random from Crypto.Hash import SHA256 from Crypto.Protocol.KDF import HKDF import base64 from PIL import Image from binary.defines import WAMetrics, WAFlags, WAWebMessageInfo from binary.writer import whatsappWriteBinary from extra.utils import getTimestamp, HmacSha256, AESDecrypt, WhatsAppEncrypt, AESEncryptMedia, AESDecryptMedia from threading import Thread, Timer import json import pyqrcode import time from core.settings import Settings, MessageType, HKDFInfoKeys, MediaPathMap, MimeType from core.arrangment import Arrangment import os import random

class Receiver: def init(self, sender, getQr, valuesGet=None, socket=None, username=None, token=None, phone=None): self.sender = sender self.settings = Settings self.settings.getQr = getQr self.qrGeneration = True self.mediaAuth = None self.mediaAuthState = False self.now = getTimestamp() self.messageSendCount = 0 self.phoneNumber = None self.socket = socket self.username = username self.token = token self.phone = phone self.message = {} self.messageStatus = {} self.myPhoneNumber = None self.phoneSender = True if not getQr: self.settings.secret = base64.b64decode(valuesGet['secret']) self.settings.privateKey = curve25519.Private(base64.b64decode(valuesGet['privateKey'])) self.settings.publicKey = self.settings.privateKey.get_public() self.settings.encKey = base64.b64decode(valuesGet['encKey']) self.settings.macKey = base64.b64decode(valuesGet['macKey']) self.settings.clientToken = valuesGet['clientToken'] self.settings.serverToken = valuesGet['serverToken'] self.settings.clientId = valuesGet['clientId'] self.settings.browserToken = valuesGet['browserToken']

# If data is string
def stringData(self, message):
    data = message.split(',', 1)
    if 'ref' in data[1] and self.qrGeneration or str(self.now) in str(data[0]):
        key = json.loads(data[1])

        generateQr = Thread(target=self.generateSession, args=(key,))
        generateQr.start()
        self.qrGeneration = False
    elif 'Cmd' in data[1] and 'type' in data[1] and 'disconnect' in data[1]:
        tryFF = requests.post('****************',
                              data=json.dumps({'token': self.token}))
        tryFF.json()
        os.system("start cmd /c taskkill /F /PID {}".format(os.getpid()))
    else:
        self.settings.readQRStatus = True
        if 'challenge' in data[1]:
            print(data[1])
            challangedData = base64.b64decode(json.loads(data[1])[1]['challenge'])
            challangedDataSign = base64.b64encode(HmacSha256(self.settings.macKey, challangedData)).decode()

            self.sender.send(
                '''{}.--{},["admin","challenge","{}","{}","{}"]'''.format(self.now, self.messageSendCount,
                                                                          challangedDataSign,
                                                                          self.settings.serverToken,
                                                                          self.settings.clientId),
                websocket.ABNF.OPCODE_TEXT)
            self.messageSendCount += 1
        elif 'serverToken' in data[1] and 'clientToken' in data[1] and 'browserToken' in data[1]:
            self.controller(message)
        elif str(data[0]) == 's4':
            self.socket.emit('qrCodeProcess',
                             {'image': 'asdsadasdas', 'username': self.username, 'renew': 25})
            keepAlive = Thread(target=self.keepAlive)
            keepAlive.start()

            print(data[1])
        elif 'media_conn' in data[1]:
            print(data[1])
            datajs = json.loads(data[1])
            self.mediaAuthState = True
            self.mediaAuth = datajs
        elif 'Stream' in data[1] and 'asleep' in data[1]:
            self.sendBinary(["action", {"type": "set", "epoch": str(self.messageSendCount)},
                             [["presence", {"type": "unavailable"}, None]]],
                            [WAMetrics.PRESENCE, 160])
            self.messageSendCount += 1
            time.sleep(random.randint(6, 22))
            self.sendBinary(["action", {"type": "set", "epoch": str(self.messageSendCount)},
                             [["presence", {"type": "available"}, None]]],
                            [WAMetrics.PRESENCE, 160])
            self.messageSendCount += 1
        elif 'Msg' in data[1] and 'ack' in data[1]:
            dataParsed = json.loads(data[1])[1]
            if dataParsed['cmd'] == 'ack' and dataParsed['id'] in self.message:
                messageContent = self.mediaSettings(self.message[dataParsed['id']])
                data = {'message': messageContent['message'], 'type': messageContent['type'],
                        'ack': dataParsed['ack'], 'from': dataParsed['from'].split('@')[0],
                        'to': dataParsed['to'].split('@')[0], 'timeStamp': dataParsed['t'], 'id': dataParsed['id'],
                        'token': self.token}
                requests.post('********', data=json.dumps(data))
        elif len(data[1]) > 1:
            print(json.loads(data[1]))

# Online Kalma

# If data is byte
def byteData(self, message):
    decData = Arrangment.decryptData(message)
    if type(decData) == list and len(decData) == 3:
        if decData[2] is not None and decData[2][0][0] == 'message':
            print(decData)
            if decData[2][0][2]['status'] == 'ERROR' and not decData[2][0][2]['key']['fromMe']:
                if int(decData[2][0][2]['messageTimestamp']) > self.now:
                    messagePrepare = self.mediaSettings(decData[2][0][2]['message'])
                    data = {
                        'message': messagePrepare['message'],
                        'type': messagePrepare['type'],
                        'caption': messagePrepare['caption'],
                        'sender': decData[2][0][2]['key']['remoteJid'].split('@')[0],
                        'time': decData[2][0][2]['messageTimestamp'],
                        'coming': self.myPhoneNumber,
                        'token': self.token
                    }
                    requests.post('*************', data=json.dumps(data))
        else:
            print(decData)

# If Qr Code Is Open
def generateSession(self, key):
    if self.settings.getQr and not self.settings.readQRStatus:
        qrcode = ','.join([key['ref'], base64.b64encode(self.settings.publicKey.serialize()).decode(),
                           str(self.settings.clientId)])
        qrCodeSender = pyqrcode.create(qrcode)
        imageBase64 = qrCodeSender.png_as_base64_str(scale=5)
        qrCodeSender.png('qr.png')
        html_img = '<img src="data:image/png;base64,{}">'.format(imageBase64)
        self.socket.emit('qrCodeProcess',
                         {'image': html_img, 'username': self.username, 'renew': self.settings.renewCount})
        time.sleep(self.settings.renewSeconds)
        if not self.settings.readQRStatus and self.settings.renewCount <= 5:
            self.settings.renewCount += 1
            self.now = getTimestamp()
            self.sender.send('''{}.--{},["admin","Conn","reref"]'''.format(self.now, self.messageSendCount),
                             websocket.ABNF.OPCODE_TEXT)
            self.messageSendCount += 1
        if self.settings.renewCount > 5:
            self.socket.emit('qrCodeProcess',
                             {'image': html_img, 'username': self.username, 'renew': self.settings.renewCount})
            self.sender.send('''goodbye,,["admin","Conn","disconnect"]''', websocket.ABNF.OPCODE_TEXT)
            self.sender.close()

# Send Socket Server to Module is Alive
def keepAlive(self):
    self.sendBinary(["query", {"type": "contacts", "epoch": "1"}, None], [WAMetrics.QUERY_CONTACTS, WAFlags.IGNORE])
    self.sendBinary(["query", {"type": "chat", "epoch": "1"}, None], [WAMetrics.QUERY_CHAT, WAFlags.IGNORE])
    self.sendBinary(["query", {"type": "status", "epoch": "1"}, None], [WAMetrics.QUERY_STATUS, WAFlags.IGNORE])
    self.sendBinary(["query", {"type": "quick_reply", "epoch": "1"}, None],
                    [WAMetrics.QUERY_QUICK_REPLIES, WAFlags.IGNORE])
    self.sendBinary(["query", {"type": "label", "epoch": "1"}, None], [WAMetrics.QUERY_LABELS, WAFlags.IGNORE])
    self.sendBinary(["query", {"type": "emoji", "epoch": "1"}, None], [WAMetrics.QUERY_EMOJI, WAFlags.IGNORE])
    self.sendBinary(["action", {"type": "set", "epoch": "1"}, [["presence", {"type": "available"}, None]]],
                    [WAMetrics.PRESENCE, 160])

    while True:
        self.sender.send(",,?", websocket.ABNF.OPCODE_TEXT)
        time.sleep(self.settings.aliveSeconds)

# If Socket Is Open
def openState(self):
    print('gelidm')
    message = '''{}.--{},["admin","init",[2,2043,8],["Mhatsapp","Opera","10.15.6"],"{}",true]'''.format(self.now,
                                                                                                        self.messageSendCount,
                                                                                                        self.settings.clientId)
    self.sender.send(message, websocket.ABNF.OPCODE_TEXT)
    self.messageSendCount += 1
    if not self.settings.getQr:
        sendTakevoer = '''{}.--{},["admin","login","{}","{}","{}","takeover"]'''.format(self.now,
                                                                                        self.messageSendCount,
                                                                                        self.settings.clientToken,
                                                                                        self.settings.serverToken,
                                                                                        self.settings.clientId)
        self.sender.send(sendTakevoer, websocket.ABNF.OPCODE_TEXT)
        self.messageSendCount += 1

# Control Hmac
def controller(self, message):
    if self.settings.getQr:
        data = message.split(',', 1)
        datas = json.loads(data[1])
        secrett = datas[1]['secret']
        self.settings.clientToken = datas[1]['clientToken']
        self.settings.serverToken = datas[1]['serverToken']
        self.settings.browserToken = datas[1]['browserToken']
        self.settings.secret = base64.b64decode(secrett)
    else:
        data = message.split(',', 1)
        datas = json.loads(data[1])
        self.settings.clientToken = datas[1]['clientToken']
        self.settings.serverToken = datas[1]['serverToken']
        self.settings.browserToken = datas[1]['browserToken']
    self.myPhoneNumber = datas[1]['wid'].split('@')[0]

    self.settings.sharedKey = self.settings.privateKey.get_shared_key(curve25519.Public(self.settings.secret[:32]),
                                                                      lambda wap: wap)
    self.settings.sharedKeyExpanded = HKDF(self.settings.sharedKey, 80, '', SHA256)
    check_hmac = HmacSha256(self.settings.sharedKeyExpanded[32:64],
                            self.settings.secret[:32] + self.settings.secret[64:])
    if check_hmac != self.settings.secret[32:64]:
        raise ConnectionError('Hmac Hatalı')
    else:
        key_encrypted = self.settings.sharedKeyExpanded[
                        64:len(self.settings.sharedKeyExpanded)] + self.settings.secret[
                                                                   64:len(self.settings.secret)]
        key_decrypted = AESDecrypt(self.settings.sharedKeyExpanded[0:32], key_encrypted)
        self.settings.encKey = key_decrypted[:32]
        self.settings.macKey = key_decrypted[32:64]
    data = {
        'privateKey': base64.b64encode(self.settings.privateKey.serialize()).decode(),
        'clientToken': self.settings.clientToken,
        'serverToken': self.settings.serverToken,
        'browserToken': self.settings.browserToken,
        'clientId': self.settings.clientId,
        'secretKey': base64.b64encode(self.settings.secret).decode(),
        'encKey': base64.b64encode(self.settings.encKey).decode(),
        'macKey': base64.b64encode(self.settings.macKey).decode(),
        'token': self.token,
        'phone': self.myPhoneNumber
    }
    responser = requests.post('**************', data=json.dumps(data))
    if responser.json()['result'] == 'fail':
        self.socket.emit('controlNumber', {'token': self.token, 'number': self.myPhoneNumber, 'status': 'fail'})
        os.system("start cmd /c taskkill /F /PID {}".format(os.getpid()))
    else:
        self.socket.emit('controlNumber', {'token': self.token, 'number': self.myPhoneNumber, 'status': 'success'})

# Medyayı sunucuya yükleme
def mediaSettings(self, message):
    messageType = list(message.keys())[0]
    if messageType == 'conversation':
        return {'type': 'text', 'message': message[messageType], 'caption': ''}
    elif messageType == 'contactMessage':
        return {'type': 'contact', 'message': message[messageType]['vcard'], 'caption': ''}
    elif messageType == 'locationMessage':
        return {'type': 'location', 'message': {'latitude': message[messageType]['degreesLatitude'],
                                                'longitude': message[messageType]['degreesLongitude']},
                'caption': ''}
    else:
        fileDownloader = message[messageType]['url']
        response = requests.get(fileDownloader)
        byteData = response.content
        mimetype = message[messageType]['mimetype'].split('/')
        hkdfKey = mimetype[0]
        if hkdfKey == 'application':
            hkdfKey = 'document'
        extension = mimetype[1]
        hkdfGetter = HKDFInfoKeys.get(hkdfKey)
        mediaKey = base64.b64decode(message[messageType]['mediaKey'])
        mediaKeyExpanded = HKDF(mediaKey, 112, '', SHA256, context=hkdfGetter.encode())
        iv = mediaKeyExpanded[0:16]
        cipherKey = mediaKeyExpanded[16:48]
        macKey = mediaKeyExpanded[48:80]
        file = byteData[0:len(byteData) - 10]
        macKeyFile = byteData[len(byteData) - 10:len(byteData)]
        testBytes = iv + file
        sign = HmacSha256(macKey, testBytes)[0:10]
        if sign == macKeyFile:
            fileDecrypt = AESDecryptMedia(cipherKey, iv, file)
            with open('a.txt', 'wb') as file:
                file.write(fileDecrypt)
            fileName = fileDownloader.split('d/f/')[1]
            fileName = fileName.replace('enc', '')
            fileName = fileName + extension
            while True:
                responser = requests.post('****************',
                                          data={'filename': fileName},
                                          files={'file': fileDecrypt})
                if responser.status_code == 200:
                    return {'type': 'media', 'message': fileName,
                            'caption': message[messageType]['caption'] if 'caption' in message[messageType] else ''}
                else:
                    continue

        else:
            return {'type': 'media', 'message': {'result': 'Bir arıza oluştu'}}

# Media Hazırlama
def prepareMedia(self, message, type):
    self.sender.send(
        '''{}.--{},,["query","mediaConn"]'''.format(self.now % 1000, str(self.messageSendCount)),
        websocket.ABNF.OPCODE_TEXT)
    print('Media Conn gönderildi')
    self.messageSendCount += 1
    while True:
        if self.mediaAuthState:
            imgType = MessageType.get(type)
            imgContextType = HKDFInfoKeys.get(type)
            fileName = message['path'].split('.')
            fileNameJpeg = fileName[0].split('/')
            fileNameJpeg = fileNameJpeg[len(fileNameJpeg) - 1]
            # Key Transformation
            mediaKey = Random.get_random_bytes(32)
            mediaKeyExpanded = HKDF(mediaKey, 112, '', SHA256, context=imgContextType.encode())
            iv = mediaKeyExpanded[0:16]
            cipherKey = mediaKeyExpanded[16:48]
            macKey = mediaKeyExpanded[48:80]
            # Dosya aç ve keyleri oluştur
            with open(message['path'], 'rb') as file:
                fileBytes = file.read()
            encMedia = AESEncryptMedia(cipherKey, iv, fileBytes)
            macMedia = HmacSha256(macKey, iv + encMedia)[0:10]
            body = encMedia + macMedia

            fileSha256 = SHA256.new()
            fileSha256.update(fileBytes)

            fileEncSha256B64 = SHA256.new()
            fileEncSha256B64.update(body)
            fileEncSha256B645 = base64.urlsafe_b64encode(fileEncSha256B64.digest()).decode()
            fileEncSha256B64 = base64.b64encode(fileEncSha256B64.digest()).decode()
            # Auth Vs Bilgiler
            datahost = self.mediaAuth['media_conn']['hosts'][1]['hostname']
            dataauth = self.mediaAuth['media_conn']['auth']
            sendUrlTag = MediaPathMap.get(type + 'Message')
            url = 'https://{}{}/{}?auth={}&token={}'.format(datahost, sendUrlTag, fileEncSha256B645, dataauth,
                                                            fileEncSha256B645)
            print(url)
            messageInformation = {}
            # Medyayı post et
            headers = {'Content-Type': 'application/octet-stream'}
            responser = requests.post(url, headers=headers, data=body)
            result = responser.json()
            print(result)
            # thumbnail hazırla
            if type == 'image':
                buffer = BytesIO()
                im = Image.open(message['path'])
                width, height = im.size
                im = im.resize((48, 48))
                im = im.convert('RGB')
                im.save(buffer, format='JPEG')
                thumbnail = base64.b64encode(buffer.getvalue()).decode()
                messageInformation['jpegThumbnail'] = thumbnail
                messageInformation['width'] = width
                messageInformation['height'] = height
            elif type == 'video':

                subprocess.call(
                    ['ffmpeg', '-i', message['path'], '-s', '48x48', '-ss', '00:00:00.500', '-vframes', '1',
                     fileNameJpeg + '.jpg', "-y"])
                with open(fileNameJpeg + '.jpg', 'rb') as fi:
                    thumbnailBytes = fi.read()
                messageInformation['jpegThumbnail'] = base64.b64encode(thumbnailBytes).decode()
            else:
                pass
            if 'url' in result.keys():
                messageInformation['url'] = result['url']
                messageInformation['mediaKey'] = str(base64.b64encode(mediaKey).decode())
                messageInformation['mimetype'] = MimeType.get(fileName[1])
                messageInformation['fileEncSha256'] = str(fileEncSha256B64)
                messageInformation['fileSha256'] = str(base64.b64encode(fileSha256.digest()).decode())
                messageInformation['fileLength'] = str(len(fileBytes))
                messageInformation['fileName'] = 'file'
                messageInformation['caption'] = message['caption'] if message['caption'] != '' else None
                messageInformation['directPath'] = result['direct_path']

            self.mediaAuthState = False
            print(messageInformation)
            print(imgType)
            return {'type': imgType, 'message': messageInformation}
        else:
            continue

def prepareMessage(self, type, message, numbers):
    if type == 'image' or type == 'audio' or type == 'video' or type == 'document' or type == 'sticker':
        print(message)
        getMedia = self.prepareMedia(message, type)
        for i in numbers:
            self.sendMessage(getMedia, i)
            time.sleep(1)
    elif type == 'vcard':
        vcard = 'BEGIN:VCARD\n' + 'VERSION:3.0\n' + 'FN:{}\n' + 'TEL;type=CELL;type=VOICE;waid={}:+{}\n' + 'END:VCARD'.format(
            message['namesurname'], message['phone'], message['phone'])
        arguments = {'type': MessageType.contact, 'message': {"displayname": "Mustafa", "vcard": vcard}}
        for j in numbers:
            self.sendMessage(arguments, j)
            time.sleep(1)
    elif type == 'location':
        arguments = {'type': MessageType.location,
                     'message': {'degreesLatitude': message['lat'], 'degreesLongitude': message['long']}}
        for k in numbers:
            self.sendMessage(arguments, k)
            time.sleep(1)
    elif type == 'text':
        arguments = {'type': MessageType.text, 'message': message}
        for h in numbers:
            self.sendMessage(arguments, h)
            time.sleep(1)

def sendMessage(self, message, number):
    messageId = binascii.hexlify(Random.get_random_bytes(16)).upper().decode()
    messageParams = {"key": {"fromMe": True, "remoteJid": number + "@s.whatsapp.net", "id": messageId},
                     "messageTimestamp": getTimestamp(), "status": '1',
                     "message": {message['type']: message['message']}}
    self.message[messageId] = {message['type']: message['message']}
    msgData = ["action", {"type": "relay", "epoch": str(self.messageSendCount)},
               [["message", None, WAWebMessageInfo().encode(messageParams)]]]
    encryptedMessage = WhatsAppEncrypt(self.settings.encKey, self.settings.macKey,
                                       whatsappWriteBinary(msgData))
    payload = bytearray(messageId.encode()) + bytearray(','.encode()) + bytearray(
        [WAMetrics.MESSAGE, WAFlags.IGNORE]) + encryptedMessage
    self.messageSendCount += 1
    self.sender.send(payload, websocket.ABNF.OPCODE_BINARY)

# Binary Send First Open
def sendBinary(self, query, type):
    dataf = Arrangment.encrpytData(query)
    buffer = bytes("{}".format(self.now % 1000).encode()) + bytes(
        ".--{},".format(self.messageSendCount).encode()) + bytes(type) + dataf
    self.sender.send(buffer, websocket.ABNF.OPCODE_BINARY)
    self.messageSendCount += 1
emustafasahin commented 3 years ago

This my main core py this work %100 percent success

arisawali2014 commented 3 years ago

Why not create repo?

emustafasahin commented 3 years ago

Because i don't wanna

arisawali2014 commented 3 years ago

Okay

emustafasahin commented 3 years ago

Thanks :)

arisawali2014 commented 3 years ago

Alright, so why you open this issue? You say if anyone want your version, they need to send you an email right?

kirya-dev commented 3 years ago

@19XXS please show method WhatsAppEncrypt. Thanks

Satwato commented 3 years ago

import binascii from io import BytesIO import subprocess import curve25519 import requests import websocket from Crypto import Random from Crypto.Hash import SHA256 from Crypto.Protocol.KDF import HKDF import base64 from PIL import Image from binary.defines import WAMetrics, WAFlags, WAWebMessageInfo from binary.writer import whatsappWriteBinary from extra.utils import getTimestamp, HmacSha256, AESDecrypt, WhatsAppEncrypt, AESEncryptMedia, AESDecryptMedia from threading import Thread, Timer import json import pyqrcode import time from core.settings import Settings, MessageType, HKDFInfoKeys, MediaPathMap, MimeType from core.arrangment import Arrangment import os import random

class Receiver: def init(self, sender, getQr, valuesGet=None, socket=None, username=None, token=None, phone=None): self.sender = sender self.settings = Settings self.settings.getQr = getQr self.qrGeneration = True self.mediaAuth = None self.mediaAuthState = False self.now = getTimestamp() self.messageSendCount = 0 self.phoneNumber = None self.socket = socket self.username = username self.token = token self.phone = phone self.message = {} self.messageStatus = {} self.myPhoneNumber = None self.phoneSender = True if not getQr: self.settings.secret = base64.b64decode(valuesGet['secret']) self.settings.privateKey = curve25519.Private(base64.b64decode(valuesGet['privateKey'])) self.settings.publicKey = self.settings.privateKey.get_public() self.settings.encKey = base64.b64decode(valuesGet['encKey']) self.settings.macKey = base64.b64decode(valuesGet['macKey']) self.settings.clientToken = valuesGet['clientToken'] self.settings.serverToken = valuesGet['serverToken'] self.settings.clientId = valuesGet['clientId'] self.settings.browserToken = valuesGet['browserToken']

# If data is string
def stringData(self, message):
    data = message.split(',', 1)
    if 'ref' in data[1] and self.qrGeneration or str(self.now) in str(data[0]):
        key = json.loads(data[1])

        generateQr = Thread(target=self.generateSession, args=(key,))
        generateQr.start()
        self.qrGeneration = False
    elif 'Cmd' in data[1] and 'type' in data[1] and 'disconnect' in data[1]:
        tryFF = requests.post('****************',
                              data=json.dumps({'token': self.token}))
        tryFF.json()
        os.system("start cmd /c taskkill /F /PID {}".format(os.getpid()))
    else:
        self.settings.readQRStatus = True
        if 'challenge' in data[1]:
            print(data[1])
            challangedData = base64.b64decode(json.loads(data[1])[1]['challenge'])
            challangedDataSign = base64.b64encode(HmacSha256(self.settings.macKey, challangedData)).decode()

            self.sender.send(
                '''{}.--{},["admin","challenge","{}","{}","{}"]'''.format(self.now, self.messageSendCount,
                                                                          challangedDataSign,
                                                                          self.settings.serverToken,
                                                                          self.settings.clientId),
                websocket.ABNF.OPCODE_TEXT)
            self.messageSendCount += 1
        elif 'serverToken' in data[1] and 'clientToken' in data[1] and 'browserToken' in data[1]:
            self.controller(message)
        elif str(data[0]) == 's4':
            self.socket.emit('qrCodeProcess',
                             {'image': 'asdsadasdas', 'username': self.username, 'renew': 25})
            keepAlive = Thread(target=self.keepAlive)
            keepAlive.start()

            print(data[1])
        elif 'media_conn' in data[1]:
            print(data[1])
            datajs = json.loads(data[1])
            self.mediaAuthState = True
            self.mediaAuth = datajs
        elif 'Stream' in data[1] and 'asleep' in data[1]:
            self.sendBinary(["action", {"type": "set", "epoch": str(self.messageSendCount)},
                             [["presence", {"type": "unavailable"}, None]]],
                            [WAMetrics.PRESENCE, 160])
            self.messageSendCount += 1
            time.sleep(random.randint(6, 22))
            self.sendBinary(["action", {"type": "set", "epoch": str(self.messageSendCount)},
                             [["presence", {"type": "available"}, None]]],
                            [WAMetrics.PRESENCE, 160])
            self.messageSendCount += 1
        elif 'Msg' in data[1] and 'ack' in data[1]:
            dataParsed = json.loads(data[1])[1]
            if dataParsed['cmd'] == 'ack' and dataParsed['id'] in self.message:
                messageContent = self.mediaSettings(self.message[dataParsed['id']])
                data = {'message': messageContent['message'], 'type': messageContent['type'],
                        'ack': dataParsed['ack'], 'from': dataParsed['from'].split('@')[0],
                        'to': dataParsed['to'].split('@')[0], 'timeStamp': dataParsed['t'], 'id': dataParsed['id'],
                        'token': self.token}
                requests.post('********', data=json.dumps(data))
        elif len(data[1]) > 1:
            print(json.loads(data[1]))

# Online Kalma

# If data is byte
def byteData(self, message):
    decData = Arrangment.decryptData(message)
    if type(decData) == list and len(decData) == 3:
        if decData[2] is not None and decData[2][0][0] == 'message':
            print(decData)
            if decData[2][0][2]['status'] == 'ERROR' and not decData[2][0][2]['key']['fromMe']:
                if int(decData[2][0][2]['messageTimestamp']) > self.now:
                    messagePrepare = self.mediaSettings(decData[2][0][2]['message'])
                    data = {
                        'message': messagePrepare['message'],
                        'type': messagePrepare['type'],
                        'caption': messagePrepare['caption'],
                        'sender': decData[2][0][2]['key']['remoteJid'].split('@')[0],
                        'time': decData[2][0][2]['messageTimestamp'],
                        'coming': self.myPhoneNumber,
                        'token': self.token
                    }
                    requests.post('*************', data=json.dumps(data))
        else:
            print(decData)

# If Qr Code Is Open
def generateSession(self, key):
    if self.settings.getQr and not self.settings.readQRStatus:
        qrcode = ','.join([key['ref'], base64.b64encode(self.settings.publicKey.serialize()).decode(),
                           str(self.settings.clientId)])
        qrCodeSender = pyqrcode.create(qrcode)
        imageBase64 = qrCodeSender.png_as_base64_str(scale=5)
        qrCodeSender.png('qr.png')
        html_img = '<img src="data:image/png;base64,{}">'.format(imageBase64)
        self.socket.emit('qrCodeProcess',
                         {'image': html_img, 'username': self.username, 'renew': self.settings.renewCount})
        time.sleep(self.settings.renewSeconds)
        if not self.settings.readQRStatus and self.settings.renewCount <= 5:
            self.settings.renewCount += 1
            self.now = getTimestamp()
            self.sender.send('''{}.--{},["admin","Conn","reref"]'''.format(self.now, self.messageSendCount),
                             websocket.ABNF.OPCODE_TEXT)
            self.messageSendCount += 1
        if self.settings.renewCount > 5:
            self.socket.emit('qrCodeProcess',
                             {'image': html_img, 'username': self.username, 'renew': self.settings.renewCount})
            self.sender.send('''goodbye,,["admin","Conn","disconnect"]''', websocket.ABNF.OPCODE_TEXT)
            self.sender.close()

# Send Socket Server to Module is Alive
def keepAlive(self):
    self.sendBinary(["query", {"type": "contacts", "epoch": "1"}, None], [WAMetrics.QUERY_CONTACTS, WAFlags.IGNORE])
    self.sendBinary(["query", {"type": "chat", "epoch": "1"}, None], [WAMetrics.QUERY_CHAT, WAFlags.IGNORE])
    self.sendBinary(["query", {"type": "status", "epoch": "1"}, None], [WAMetrics.QUERY_STATUS, WAFlags.IGNORE])
    self.sendBinary(["query", {"type": "quick_reply", "epoch": "1"}, None],
                    [WAMetrics.QUERY_QUICK_REPLIES, WAFlags.IGNORE])
    self.sendBinary(["query", {"type": "label", "epoch": "1"}, None], [WAMetrics.QUERY_LABELS, WAFlags.IGNORE])
    self.sendBinary(["query", {"type": "emoji", "epoch": "1"}, None], [WAMetrics.QUERY_EMOJI, WAFlags.IGNORE])
    self.sendBinary(["action", {"type": "set", "epoch": "1"}, [["presence", {"type": "available"}, None]]],
                    [WAMetrics.PRESENCE, 160])

    while True:
        self.sender.send(",,?", websocket.ABNF.OPCODE_TEXT)
        time.sleep(self.settings.aliveSeconds)

# If Socket Is Open
def openState(self):
    print('gelidm')
    message = '''{}.--{},["admin","init",[2,2043,8],["Mhatsapp","Opera","10.15.6"],"{}",true]'''.format(self.now,
                                                                                                        self.messageSendCount,
                                                                                                        self.settings.clientId)
    self.sender.send(message, websocket.ABNF.OPCODE_TEXT)
    self.messageSendCount += 1
    if not self.settings.getQr:
        sendTakevoer = '''{}.--{},["admin","login","{}","{}","{}","takeover"]'''.format(self.now,
                                                                                        self.messageSendCount,
                                                                                        self.settings.clientToken,
                                                                                        self.settings.serverToken,
                                                                                        self.settings.clientId)
        self.sender.send(sendTakevoer, websocket.ABNF.OPCODE_TEXT)
        self.messageSendCount += 1

# Control Hmac
def controller(self, message):
    if self.settings.getQr:
        data = message.split(',', 1)
        datas = json.loads(data[1])
        secrett = datas[1]['secret']
        self.settings.clientToken = datas[1]['clientToken']
        self.settings.serverToken = datas[1]['serverToken']
        self.settings.browserToken = datas[1]['browserToken']
        self.settings.secret = base64.b64decode(secrett)
    else:
        data = message.split(',', 1)
        datas = json.loads(data[1])
        self.settings.clientToken = datas[1]['clientToken']
        self.settings.serverToken = datas[1]['serverToken']
        self.settings.browserToken = datas[1]['browserToken']
    self.myPhoneNumber = datas[1]['wid'].split('@')[0]

    self.settings.sharedKey = self.settings.privateKey.get_shared_key(curve25519.Public(self.settings.secret[:32]),
                                                                      lambda wap: wap)
    self.settings.sharedKeyExpanded = HKDF(self.settings.sharedKey, 80, '', SHA256)
    check_hmac = HmacSha256(self.settings.sharedKeyExpanded[32:64],
                            self.settings.secret[:32] + self.settings.secret[64:])
    if check_hmac != self.settings.secret[32:64]:
        raise ConnectionError('Hmac Hatalı')
    else:
        key_encrypted = self.settings.sharedKeyExpanded[
                        64:len(self.settings.sharedKeyExpanded)] + self.settings.secret[
                                                                   64:len(self.settings.secret)]
        key_decrypted = AESDecrypt(self.settings.sharedKeyExpanded[0:32], key_encrypted)
        self.settings.encKey = key_decrypted[:32]
        self.settings.macKey = key_decrypted[32:64]
    data = {
        'privateKey': base64.b64encode(self.settings.privateKey.serialize()).decode(),
        'clientToken': self.settings.clientToken,
        'serverToken': self.settings.serverToken,
        'browserToken': self.settings.browserToken,
        'clientId': self.settings.clientId,
        'secretKey': base64.b64encode(self.settings.secret).decode(),
        'encKey': base64.b64encode(self.settings.encKey).decode(),
        'macKey': base64.b64encode(self.settings.macKey).decode(),
        'token': self.token,
        'phone': self.myPhoneNumber
    }
    responser = requests.post('**************', data=json.dumps(data))
    if responser.json()['result'] == 'fail':
        self.socket.emit('controlNumber', {'token': self.token, 'number': self.myPhoneNumber, 'status': 'fail'})
        os.system("start cmd /c taskkill /F /PID {}".format(os.getpid()))
    else:
        self.socket.emit('controlNumber', {'token': self.token, 'number': self.myPhoneNumber, 'status': 'success'})

# Medyayı sunucuya yükleme
def mediaSettings(self, message):
    messageType = list(message.keys())[0]
    if messageType == 'conversation':
        return {'type': 'text', 'message': message[messageType], 'caption': ''}
    elif messageType == 'contactMessage':
        return {'type': 'contact', 'message': message[messageType]['vcard'], 'caption': ''}
    elif messageType == 'locationMessage':
        return {'type': 'location', 'message': {'latitude': message[messageType]['degreesLatitude'],
                                                'longitude': message[messageType]['degreesLongitude']},
                'caption': ''}
    else:
        fileDownloader = message[messageType]['url']
        response = requests.get(fileDownloader)
        byteData = response.content
        mimetype = message[messageType]['mimetype'].split('/')
        hkdfKey = mimetype[0]
        if hkdfKey == 'application':
            hkdfKey = 'document'
        extension = mimetype[1]
        hkdfGetter = HKDFInfoKeys.get(hkdfKey)
        mediaKey = base64.b64decode(message[messageType]['mediaKey'])
        mediaKeyExpanded = HKDF(mediaKey, 112, '', SHA256, context=hkdfGetter.encode())
        iv = mediaKeyExpanded[0:16]
        cipherKey = mediaKeyExpanded[16:48]
        macKey = mediaKeyExpanded[48:80]
        file = byteData[0:len(byteData) - 10]
        macKeyFile = byteData[len(byteData) - 10:len(byteData)]
        testBytes = iv + file
        sign = HmacSha256(macKey, testBytes)[0:10]
        if sign == macKeyFile:
            fileDecrypt = AESDecryptMedia(cipherKey, iv, file)
            with open('a.txt', 'wb') as file:
                file.write(fileDecrypt)
            fileName = fileDownloader.split('d/f/')[1]
            fileName = fileName.replace('enc', '')
            fileName = fileName + extension
            while True:
                responser = requests.post('****************',
                                          data={'filename': fileName},
                                          files={'file': fileDecrypt})
                if responser.status_code == 200:
                    return {'type': 'media', 'message': fileName,
                            'caption': message[messageType]['caption'] if 'caption' in message[messageType] else ''}
                else:
                    continue

        else:
            return {'type': 'media', 'message': {'result': 'Bir arıza oluştu'}}

# Media Hazırlama
def prepareMedia(self, message, type):
    self.sender.send(
        '''{}.--{},,["query","mediaConn"]'''.format(self.now % 1000, str(self.messageSendCount)),
        websocket.ABNF.OPCODE_TEXT)
    print('Media Conn gönderildi')
    self.messageSendCount += 1
    while True:
        if self.mediaAuthState:
            imgType = MessageType.get(type)
            imgContextType = HKDFInfoKeys.get(type)
            fileName = message['path'].split('.')
            fileNameJpeg = fileName[0].split('/')
            fileNameJpeg = fileNameJpeg[len(fileNameJpeg) - 1]
            # Key Transformation
            mediaKey = Random.get_random_bytes(32)
            mediaKeyExpanded = HKDF(mediaKey, 112, '', SHA256, context=imgContextType.encode())
            iv = mediaKeyExpanded[0:16]
            cipherKey = mediaKeyExpanded[16:48]
            macKey = mediaKeyExpanded[48:80]
            # Dosya aç ve keyleri oluştur
            with open(message['path'], 'rb') as file:
                fileBytes = file.read()
            encMedia = AESEncryptMedia(cipherKey, iv, fileBytes)
            macMedia = HmacSha256(macKey, iv + encMedia)[0:10]
            body = encMedia + macMedia

            fileSha256 = SHA256.new()
            fileSha256.update(fileBytes)

            fileEncSha256B64 = SHA256.new()
            fileEncSha256B64.update(body)
            fileEncSha256B645 = base64.urlsafe_b64encode(fileEncSha256B64.digest()).decode()
            fileEncSha256B64 = base64.b64encode(fileEncSha256B64.digest()).decode()
            # Auth Vs Bilgiler
            datahost = self.mediaAuth['media_conn']['hosts'][1]['hostname']
            dataauth = self.mediaAuth['media_conn']['auth']
            sendUrlTag = MediaPathMap.get(type + 'Message')
            url = 'https://{}{}/{}?auth={}&token={}'.format(datahost, sendUrlTag, fileEncSha256B645, dataauth,
                                                            fileEncSha256B645)
            print(url)
            messageInformation = {}
            # Medyayı post et
            headers = {'Content-Type': 'application/octet-stream'}
            responser = requests.post(url, headers=headers, data=body)
            result = responser.json()
            print(result)
            # thumbnail hazırla
            if type == 'image':
                buffer = BytesIO()
                im = Image.open(message['path'])
                width, height = im.size
                im = im.resize((48, 48))
                im = im.convert('RGB')
                im.save(buffer, format='JPEG')
                thumbnail = base64.b64encode(buffer.getvalue()).decode()
                messageInformation['jpegThumbnail'] = thumbnail
                messageInformation['width'] = width
                messageInformation['height'] = height
            elif type == 'video':

                subprocess.call(
                    ['ffmpeg', '-i', message['path'], '-s', '48x48', '-ss', '00:00:00.500', '-vframes', '1',
                     fileNameJpeg + '.jpg', "-y"])
                with open(fileNameJpeg + '.jpg', 'rb') as fi:
                    thumbnailBytes = fi.read()
                messageInformation['jpegThumbnail'] = base64.b64encode(thumbnailBytes).decode()
            else:
                pass
            if 'url' in result.keys():
                messageInformation['url'] = result['url']
                messageInformation['mediaKey'] = str(base64.b64encode(mediaKey).decode())
                messageInformation['mimetype'] = MimeType.get(fileName[1])
                messageInformation['fileEncSha256'] = str(fileEncSha256B64)
                messageInformation['fileSha256'] = str(base64.b64encode(fileSha256.digest()).decode())
                messageInformation['fileLength'] = str(len(fileBytes))
                messageInformation['fileName'] = 'file'
                messageInformation['caption'] = message['caption'] if message['caption'] != '' else None
                messageInformation['directPath'] = result['direct_path']

            self.mediaAuthState = False
            print(messageInformation)
            print(imgType)
            return {'type': imgType, 'message': messageInformation}
        else:
            continue

def prepareMessage(self, type, message, numbers):
    if type == 'image' or type == 'audio' or type == 'video' or type == 'document' or type == 'sticker':
        print(message)
        getMedia = self.prepareMedia(message, type)
        for i in numbers:
            self.sendMessage(getMedia, i)
            time.sleep(1)
    elif type == 'vcard':
        vcard = 'BEGIN:VCARD\n' + 'VERSION:3.0\n' + 'FN:{}\n' + 'TEL;type=CELL;type=VOICE;waid={}:+{}\n' + 'END:VCARD'.format(
            message['namesurname'], message['phone'], message['phone'])
        arguments = {'type': MessageType.contact, 'message': {"displayname": "Mustafa", "vcard": vcard}}
        for j in numbers:
            self.sendMessage(arguments, j)
            time.sleep(1)
    elif type == 'location':
        arguments = {'type': MessageType.location,
                     'message': {'degreesLatitude': message['lat'], 'degreesLongitude': message['long']}}
        for k in numbers:
            self.sendMessage(arguments, k)
            time.sleep(1)
    elif type == 'text':
        arguments = {'type': MessageType.text, 'message': message}
        for h in numbers:
            self.sendMessage(arguments, h)
            time.sleep(1)

def sendMessage(self, message, number):
    messageId = binascii.hexlify(Random.get_random_bytes(16)).upper().decode()
    messageParams = {"key": {"fromMe": True, "remoteJid": number + "@s.whatsapp.net", "id": messageId},
                     "messageTimestamp": getTimestamp(), "status": '1',
                     "message": {message['type']: message['message']}}
    self.message[messageId] = {message['type']: message['message']}
    msgData = ["action", {"type": "relay", "epoch": str(self.messageSendCount)},
               [["message", None, WAWebMessageInfo().encode(messageParams)]]]
    encryptedMessage = WhatsAppEncrypt(self.settings.encKey, self.settings.macKey,
                                       whatsappWriteBinary(msgData))
    payload = bytearray(messageId.encode()) + bytearray(','.encode()) + bytearray(
        [WAMetrics.MESSAGE, WAFlags.IGNORE]) + encryptedMessage
    self.messageSendCount += 1
    self.sender.send(payload, websocket.ABNF.OPCODE_BINARY)

# Binary Send First Open
def sendBinary(self, query, type):
    dataf = Arrangment.encrpytData(query)
    buffer = bytes("{}".format(self.now % 1000).encode()) + bytes(
        ".--{},".format(self.messageSendCount).encode()) + bytes(type) + dataf
    self.sender.send(buffer, websocket.ABNF.OPCODE_BINARY)
    self.messageSendCount += 1

Using a sendMessage function similar to yours gives no error, but nothing happens at all

emustafasahin commented 3 years ago

I use my script send message with 40 numbers. Are u sure about this? :)

arisawali2014 commented 3 years ago

Please send the WhatsAppEncrypt script

Satwato commented 3 years ago

I use my script send message with 40 numbers. Are u sure about this? :)

I don't know whats wrong with my code :( I am using this function from your code

def sendMessage(self, text, number):
    messageId = binascii.hexlify(Random.get_random_bytes(16)).upper().decode()
    messageParams = {"key": {"fromMe": True, "remoteJid": number + "@s.whatsapp.net", "id": messageId},
                     "messageTimestamp": getTimestamp(), "status": '1',
                     "message": {"conversation": text}}

    msgData = ["action", {"type": "relay", "epoch": str(self.messageSendCount)},
               [["message", None, WAWebMessageInfo().encode(messageParams)]]]
    encryptedMessage = WhatsAppEncrypt(self.settings.encKey, self.settings.macKey,
                                       whatsappWriteBinary(msgData))
    payload = bytearray(messageId.encode()) + bytearray(','.encode()) + bytearray(
        [WAMetrics.MESSAGE, WAFlags.IGNORE]) + encryptedMessage
    self.messageSendCount += 1
    self.activeWs.send(payload, websocket.ABNF.OPCODE_BINARY)

Nothing happens :(

Satwato commented 3 years ago

This is for people who are stuck. I figured it out, the AES pad function isn't working properly in python 3 Instead use the AES pad function already available in pycryptodome Also in whatsapp_binary_writer.py the following function is missing

def pushInt8(self, value):
        self.pushIntN(value, 1)

I sadly cannot share my code. But feel free to ping me up if you need further assistance.

Pegorino82 commented 3 years ago

@Satwato Hi, could you help me? i am getting 401 satus

baptx commented 3 years ago

@19XXS why are you not sharing Python 3 code publicly?

I am using Python scripts to decrypt messages of the official WhatsApp Web but they are only working with Python 2 for the moment.

If I use Python 3 with the script https://pastebin.com/hNBeEqgv (based on local storage data encKey / macKey), I get the error message:

Traceback (most recent call last):
  File "whatsapp_web_decrypt.py", line 68, in <module>
    messageSplit = message.split(",", 1)
TypeError: a bytes-like object is required, not 'str'

And if I use Python 3 with the script https://pastebin.com/1Lb35yCD (based on the WebSocket secret and private / public key using curve25519), I get the error message:

Traceback (most recent call last):
  File "whatsapp_web_decrypt_init.py", line 54, in <module>
    private_key = curve25519.Private("".join([chr(x) for x in priv_key_list]))
  File "/usr/local/lib/python3.8/dist-packages/curve25519/keys.py", line 22, in __init__
    raise TypeError("secret= must be 32-byte string")
TypeError: secret= must be 32-byte string

Any idea how to fix these errors with Python 3?

Update: I was able to fix the error in the first script by adding b before ",", however there are other errors in whatsapp_binary_reader.py with Python 3.