From c47ca004ba979d23a86211393c9e35deadd66c46 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Sat, 24 Jan 2015 16:35:14 +0000 Subject: adaptation of WhatsApp for protocol version 1.5/2.0 git-svn-id: http://svn.miranda-ng.org/main/trunk@11898 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/WhatsApp/src/WASocketConnection.cpp | 49 ++-- protocols/WhatsApp/src/WASocketConnection.h | 7 +- .../WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp | 289 ++++++++++++--------- .../WhatsApp/src/WhatsAPI++/BinTreeNodeReader.h | 1 - .../WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.cpp | 111 ++++---- .../WhatsApp/src/WhatsAPI++/ISocketConnection.h | 7 +- protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp | 263 ++----------------- protocols/WhatsApp/src/WhatsAPI++/WAConnection.h | 3 +- protocols/WhatsApp/src/WhatsAPI++/WALogin.cpp | 180 +++++-------- protocols/WhatsApp/src/WhatsAPI++/WALogin.h | 11 +- protocols/WhatsApp/src/WhatsAPI++/WARegister.cpp | 37 +-- protocols/WhatsApp/src/constants.h | 4 +- protocols/WhatsApp/src/version.h | 4 +- 13 files changed, 382 insertions(+), 584 deletions(-) (limited to 'protocols') diff --git a/protocols/WhatsApp/src/WASocketConnection.cpp b/protocols/WhatsApp/src/WASocketConnection.cpp index b5d1b0235c..41d0b78663 100644 --- a/protocols/WhatsApp/src/WASocketConnection.cpp +++ b/protocols/WhatsApp/src/WASocketConnection.cpp @@ -101,36 +101,38 @@ void WASocketConnection::write(const std::vector& bytes, int leng unsigned char WASocketConnection::read() { - char c; - SetLastError(0); - int result; - //do { - result = Netlib_Recv(this->hConn, &c, 1, 0 /*MSG_NOHTTPGATEWAYWRAP | MSG_NODUMP*/); - //} while (WSAGetLastError() == EINTR); - if (result <= 0) { + + char c; + int result = Netlib_Recv(this->hConn, &c, 1, 0); + if (result <= 0) throw WAException(getLastErrorMsg(), WAException::SOCKET_EX, WAException::SOCKET_EX_RECV); - } + return c; } +int WASocketConnection::read(unsigned char *buf, int length) +{ + int result = Netlib_Recv(this->hConn, (char*)buf, length, 0); + if (result <= 0) + throw WAException(getLastErrorMsg(), WAException::SOCKET_EX, WAException::SOCKET_EX_RECV); + + return result; +} + int WASocketConnection::read(std::vector& b, int off, int length) { - if (off < 0 || length < 0) { + if (off < 0 || length < 0) throw new WAException("Out of bounds", WAException::SOCKET_EX, WAException::SOCKET_EX_RECV); - } - char* buffer = new char[length]; - int result = Netlib_Recv(this->hConn, buffer, length, MSG_NOHTTPGATEWAYWRAP | MSG_NODUMP); - if (result <= 0) { + char* buffer = (char*)_alloca(length); + int result = Netlib_Recv(this->hConn, buffer, length, MSG_NOHTTPGATEWAYWRAP | MSG_NODUMP); + if (result <= 0) throw WAException(getLastErrorMsg(), WAException::SOCKET_EX, WAException::SOCKET_EX_RECV); - } for (int i = 0; i < result; i++) b[off + i] = buffer[i]; - delete[] buffer; - return result; } @@ -139,6 +141,21 @@ void WASocketConnection::forceShutdown() Netlib_Shutdown(this->hConn); } +void WASocketConnection::dump(const void *pData, int length) +{ + BYTE *pBuf = (BYTE*)pData; + while (length > 0) { + int portion = (length < 16) ? length : 16; + + char str[100]; + for (int i = 0; i < portion; i++) + sprintf(str + i * 3, "%02X ", *pBuf++); + Netlib_Logf(WASocketConnection::hNetlibUser, "DATA: %s", str); + + length -= portion; + } +} + WASocketConnection::~WASocketConnection() { this->forceShutdown(); diff --git a/protocols/WhatsApp/src/WASocketConnection.h b/protocols/WhatsApp/src/WASocketConnection.h index 48c64f2a1a..c585e98b3d 100644 --- a/protocols/WhatsApp/src/WASocketConnection.h +++ b/protocols/WhatsApp/src/WASocketConnection.h @@ -22,17 +22,20 @@ private: public: WASocketConnection(const std::string& dir, int port) throw (WAException); + virtual ~WASocketConnection(); + void write(int i); unsigned char read(); + int read(std::vector& b, int off, int length); + int read(unsigned char*, int length); void flush(); void write(const std::vector& b, int length); void write(const std::vector& bytes, int offset, int length); - int read(std::vector& b, int off, int length); void makeNonBlock(); int waitForRead(); void forceShutdown(); + void dump(const void *buf, int length); - virtual ~WASocketConnection(); static void initNetwork(HANDLE hNetlibUser) throw (WAException); static void quitNetwork(); }; diff --git a/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp index 388bf10e4a..573afb4680 100644 --- a/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp +++ b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.cpp @@ -11,7 +11,32 @@ #include "ProtocolTreeNode.h" #include "utilities.h" -BinTreeNodeReader::BinTreeNodeReader(WAConnection* conn, ISocketConnection* connection, const char** dictionary, const int dictionarysize) { +static const char *secondary_dict[] = { + "mpeg4", "wmv", "audio/3gpp", "audio/aac", "audio/amr", "audio/mp4", "audio/mpeg", "audio/ogg", "audio/qcelp", "audio/wav", + "audio/webm", "audio/x-caf", "audio/x-ms-wma", "image/gif", "image/jpeg", "image/png", "video/3gpp", "video/avi", "video/mp4", + "video/mpeg", "video/quicktime", "video/x-flv", "video/x-ms-asf", "302", "400", "401", "402", "403", "404", "405", "406", "407", + "409", "410", "500", "501", "503", "504", "abitrate", "acodec", "app_uptime", "asampfmt", "asampfreq", "audio", "clear", "conflict", + "conn_no_nna", "cost", "currency", "duration", "extend", "file", "fps", "g_notify", "g_sound", "gcm", "gone", "google_play", "hash", + "height", "invalid", "jid-malformed", "latitude", "lc", "lg", "live", "location", "log", "longitude", "max_groups", "max_participants", + "max_subject", "mimetype", "mode", "napi_version", "normalize", "orighash", "origin", "passive", "password", "played", + "policy-violation", "pop_mean_time", "pop_plus_minus", "price", "pricing", "redeem", "Replaced by new connection", "resume", + "signature", "size", "sound", "source", "system-shutdown", "username", "vbitrate", "vcard", "vcodec", "video", "width", + "xml-not-well-formed", "checkmarks", "image_max_edge", "image_max_kbytes", "image_quality", "ka", "ka_grow", "ka_shrink", "newmedia", + "library", "caption", "forward", "c0", "c1", "c2", "c3", "clock_skew", "cts", "k0", "k1", "login_rtt", "m_id", "nna_msg_rtt", + "nna_no_off_count", "nna_offline_ratio", "nna_push_rtt", "no_nna_con_count", "off_msg_rtt", "on_msg_rtt", "stat_name", "sts", + "suspect_conn", "lists", "self", "qr", "web", "w:b", "recipient", "w:stats", "forbidden", "aurora.m4r", "bamboo.m4r", "chord.m4r", + "circles.m4r", "complete.m4r", "hello.m4r", "input.m4r", "keys.m4r", "note.m4r", "popcorn.m4r", "pulse.m4r", "synth.m4r", "filehash", + "max_list_recipients", "en-AU", "en-GB", "es-MX", "pt-PT", "zh-Hans", "zh-Hant", "relayelection", "relaylatency", "interruption", + "Apex.m4r", "Beacon.m4r", "Bulletin.m4r", "By The Seaside.m4r", "Chimes.m4r", "Circuit.m4r", "Constellation.m4r", "Cosmic.m4r", + "Crystals.m4r", "Hillside.m4r", "Illuminate.m4r", "Night Owl.m4r", "Opening.m4r", "Playtime.m4r", "Presto.m4r", "Radar.m4r", + "Radiate.m4r", "Ripples.m4r", "Sencha.m4r", "Signal.m4r", "Silk.m4r", "Slow Rise.m4r", "Stargaze.m4r", "Summit.m4r", "Twinkle.m4r", + "Uplift.m4r", "Waves.m4r", "voip", "eligible", "upgrade", "planned", "current", "future", "disable", "expire", "start", "stop", + "accuracy", "speed", "bearing", "recording", "encrypt", "key", "identity", "w:gp2", "admin", "locked", "unlocked", "new", "battery", + "archive", "adm", "plaintext_size", "compressed_size", "delivered", "msg", "pkmsg", "everyone", "v", "transport", "call-id" +}; + +BinTreeNodeReader::BinTreeNodeReader(WAConnection* conn, ISocketConnection* connection, const char** dictionary, const int dictionarysize) +{ this->conn = conn; this->rawIn = connection; this->tokenMap = dictionary; @@ -21,14 +46,16 @@ BinTreeNodeReader::BinTreeNodeReader(WAConnection* conn, ISocketConnection* conn this->buf = new std::vector(BUFFER_SIZE); } -BinTreeNodeReader::~BinTreeNodeReader() { +BinTreeNodeReader::~BinTreeNodeReader() +{ if (this->buf != NULL) delete this->buf; if (this->in != NULL) delete this->in; } -ProtocolTreeNode* BinTreeNodeReader::nextTreeInternal() { +ProtocolTreeNode* BinTreeNodeReader::nextTreeInternal() +{ int b = this->in->read(); int size = readListSize(b); b = this->in->read(); @@ -40,7 +67,7 @@ ProtocolTreeNode* BinTreeNodeReader::nextTreeInternal() { if ((size == 0) || (tag == NULL)) throw WAException("nextTree sees 0 list or null tag", WAException::CORRUPT_STREAM_EX, -1); int attribCount = (size - 2 + size % 2) / 2; - std::map* attribs = readAttributes(attribCount); + std::map* attribs = readAttributes(attribCount); if (size % 2 == 1) { ProtocolTreeNode* ret = new ProtocolTreeNode(*tag, attribs); delete tag; @@ -59,7 +86,8 @@ ProtocolTreeNode* BinTreeNodeReader::nextTreeInternal() { std::string* s = (std::string*) obj->data; data = new std::vector(s->begin(), s->end()); delete s; - } else { + } + else { data = (std::vector*) obj->data; } @@ -69,25 +97,31 @@ ProtocolTreeNode* BinTreeNodeReader::nextTreeInternal() { return ret; } -bool BinTreeNodeReader::isListTag(int b) { +bool BinTreeNodeReader::isListTag(int b) +{ return (b == 248) || (b == 0) || (b == 249); } -void BinTreeNodeReader::decodeStream(int flags, int offset, int length) { +void BinTreeNodeReader::decodeStream(int flags, int offset, int length) +{ if ((flags & 8) != 0) { - if (length < 4) { - throw WAException("invalid length" + length, WAException::CORRUPT_STREAM_EX, 0); - } - offset += 4; + if (length < 4) + throw WAException("invalid length" + length, WAException::CORRUPT_STREAM_EX, 0); + length -= 4; - this->conn->inputKey->decodeMessage(&(*this->buf)[0], offset - 4, offset, length); + + unsigned char *pData = (unsigned char*)&(*this->buf)[0]; + this->conn->inputKey->decodeMessage(pData, offset + length, 0, length); + this->rawIn->dump(pData + offset, length); } + if (this->in != NULL) delete this->in; this->in = new ByteArrayInputStream(this->buf, offset, length); } -std::map* BinTreeNodeReader::readAttributes(int attribCount) { +std::map* BinTreeNodeReader::readAttributes(int attribCount) +{ std::map* attribs = new std::map(); for (int i = 0; i < attribCount; i++) { std::string* key = readStringAsString(); @@ -99,91 +133,106 @@ std::map* BinTreeNodeReader::readAttributes(int attribCount) { return attribs; } -std::vector* BinTreeNodeReader::readList(int token) { +std::vector* BinTreeNodeReader::readList(int token) +{ int size = readListSize(token); std::vector* list = new std::vector(size); - for (int i = 0; i < size; i++) { + for (int i = 0; i < size; i++) (*list)[i] = nextTreeInternal(); - } return list; } -int BinTreeNodeReader::readListSize(int token) { - int size; - if (token == 0) { - size = 0; +int BinTreeNodeReader::readListSize(int token) +{ + switch (token) { + case 0: return 0; + case 248: return readInt8(this->in); + case 249: return readInt16(this->in); + default: + throw new WAException("invalid list size in readListSize: token " + token, WAException::CORRUPT_STREAM_EX, 0); } - else { - size = 0; - if (token == 248) { - size = readInt8(this->in); - } - else - { - size = 0; - if (token == 249) - size = readInt16(this->in); - else - throw new WAException("invalid list size in readListSize: token " + token, WAException::CORRUPT_STREAM_EX, 0); - } - } - - return size; + return 0; } -std::vector* BinTreeNodeReader::readList() { +std::vector* BinTreeNodeReader::readList() +{ return readList(this->in->read()); } -ReadData* BinTreeNodeReader::readString() { +ReadData* BinTreeNodeReader::readString() +{ return readString(this->in->read()); } -ReadData* BinTreeNodeReader::readString(int token) { - if (token == -1) { +ReadData* BinTreeNodeReader::readString(int token) +{ + if (token == -1) throw WAException("-1 token in readString", WAException::CORRUPT_STREAM_EX, -1); - } + int bSize; ReadData* ret = new ReadData(); - if ((token > 4) && (token < 245)) { + if (token > 2 && token <= this->tokenmapsize) { + if (token != this->tokenmapsize) + ret->data = new std::string(this->tokenMap[token]); + else { + token = readInt8(this->in); + if (token >= 0 && token < _countof(secondary_dict)) + ret->data = new std::string(secondary_dict[token]); + else + throw WAException("invalid token/length in getToken", WAException::CORRUPT_STREAM_EX, 0); + } + ret->type = STRING; - ret->data = new std::string(getToken(token)); return ret; } - switch(token) { + switch (token) { case 0: return NULL; - case 252: { - int size8 = readInt8(this->in); - std::vector* buf8 = new std::vector(size8); - fillArray(*buf8, size8, this->in); - // std::string* ret = new std::string(buf8->begin(), buf8->end()); - // delete buf8; - ret->type = ARRAY; - ret->data = buf8; - return ret; - } - case 253: { - int size24 = readInt24(this->in); - std::vector* buf24 = new std::vector(size24); - fillArray(*buf24, size24, this->in); - // std::string* ret = new std::string(buf24->begin(), buf24->end()); - // delete buf24; - ret->type = ARRAY; - ret->data = buf24; + case 252: + bSize = readInt8(this->in); + { + std::vector* buf8 = new std::vector(bSize); + fillArray(*buf8, bSize, this->in); + ret->type = ARRAY; + ret->data = buf8; + } return ret; - } - case 254: { - token = (unsigned char) this->in->read(); - ret->type = STRING; - ret->data = new std::string(getToken(245 + token)); + + case 253: + bSize = readInt24(this->in); + { + std::vector* buf24 = new std::vector(bSize); + fillArray(*buf24, bSize, this->in); + ret->type = ARRAY; + ret->data = buf24; + } return ret; - } - case 250: { + + case 255: + bSize = readInt8(this->in); + { + int size = bSize & 0x7f; + int numnibbles = size * 2 - ((bSize & 0x80) ? 1 : 0); + + std::vector tmp(size); + fillArray(tmp, size, this->in); + std::string s; + for (int i = 0; i < numnibbles; i++) { + char c = (tmp[i / 2] >> (4 - ((i & 1) << 2))) & 0xF; + if (c < 10) s += (c + '0'); + else s += (c - 10 + '-'); + } + + ret->type = STRING; + ret->data = new std::string(s); + } + return ret; + + case 250: std::string* user = readStringAsString(); std::string* server = readStringAsString(); if ((user != NULL) && (server != NULL)) { @@ -201,14 +250,13 @@ ReadData* BinTreeNodeReader::readString(int token) { } throw WAException("readString couldn't reconstruct jid", WAException::CORRUPT_STREAM_EX, -1); } - } - throw WAException("readString couldn't match token" + (int) token, WAException::CORRUPT_STREAM_EX, -1); + throw WAException("readString couldn't match token" + (int)token, WAException::CORRUPT_STREAM_EX, -1); } -std::string* BinTreeNodeReader::objectAsString(ReadData* o) { - if (o->type == STRING) { +std::string* BinTreeNodeReader::objectAsString(ReadData* o) +{ + if (o->type == STRING) return (std::string*) o->data; - } if (o->type == ARRAY) { std::vector* v = (std::vector*) o->data; @@ -220,61 +268,44 @@ std::string* BinTreeNodeReader::objectAsString(ReadData* o) { return NULL; } -std::string* BinTreeNodeReader::readStringAsString() { +std::string* BinTreeNodeReader::readStringAsString() +{ ReadData* o = this->readString(); std::string* ret = this->objectAsString(o); delete o; return ret; } -std::string* BinTreeNodeReader::readStringAsString(int token) { +std::string* BinTreeNodeReader::readStringAsString(int token) +{ ReadData* o = this->readString(token); std::string* ret = this->objectAsString(o); delete o; return ret; } - -void BinTreeNodeReader::fillArray(std::vector& buff, int len, ByteArrayInputStream* in) { +void BinTreeNodeReader::fillArray(std::vector& buff, int len, ByteArrayInputStream* in) +{ int count = 0; - while (count < len) { + while (count < len) count += in->read(buff, count, len - count); - } } -void BinTreeNodeReader::fillArray(std::vector& buff, int len, ISocketConnection* in) { +void BinTreeNodeReader::fillArray(std::vector& buff, int len, ISocketConnection* in) +{ int count = 0; - while (count < len) { + while (count < len) count += in->read(buff, count, len - count); - } } +void BinTreeNodeReader::getTopLevelStream() +{ + int stanzaSize = readInt24(this->rawIn); + int flags = (stanzaSize >> 20); + stanzaSize &= 0x0FFFFF; -std::string BinTreeNodeReader::getToken(int token) { - std::string ret; - - if ((token >= 0) && (token < this->tokenmapsize)) - ret = std::string(this->tokenMap[token]); - if (ret.empty()) { - throw WAException("invalid token/length in getToken", WAException::CORRUPT_STREAM_EX, 0); - } - return ret; -} - - -void BinTreeNodeReader::getTopLevelStream() { - int stanzaSize; - int flags; - int byte = readInt8(this->rawIn); - flags = byte >> 4; - int size0 = byte & 15; - int size1 = readInt8(this->rawIn); - int size2 = readInt8(this->rawIn); - - stanzaSize = (size0 << 16) + (size1 << 8) + size2; - - if (this->buf->size() < (size_t) stanzaSize) { - int newsize = max((int) (this->buf->size() * 3 / 2), stanzaSize); + if (this->buf->size() < (size_t)stanzaSize) { + int newsize = max((int)(this->buf->size() * 3 / 2), stanzaSize); delete this->buf; this->buf = new std::vector(newsize); } @@ -283,18 +314,21 @@ void BinTreeNodeReader::getTopLevelStream() { this->decodeStream(flags, 0, stanzaSize); } -int BinTreeNodeReader::readInt8(ByteArrayInputStream* in) { +int BinTreeNodeReader::readInt8(ByteArrayInputStream* in) +{ return in->read(); } -int BinTreeNodeReader::readInt16(ByteArrayInputStream* in) { +int BinTreeNodeReader::readInt16(ByteArrayInputStream* in) +{ int intTop = in->read(); int intBot = in->read(); int value = (intTop << 8) + intBot; return value; } -int BinTreeNodeReader::readInt24(ByteArrayInputStream* in) { +int BinTreeNodeReader::readInt24(ByteArrayInputStream* in) +{ int int1 = in->read(); int int2 = in->read(); int int3 = in->read(); @@ -303,12 +337,14 @@ int BinTreeNodeReader::readInt24(ByteArrayInputStream* in) { return value; } -ProtocolTreeNode* BinTreeNodeReader::nextTree() { +ProtocolTreeNode* BinTreeNodeReader::nextTree() +{ this->getTopLevelStream(); return nextTreeInternal(); } -void BinTreeNodeReader::streamStart() { +void BinTreeNodeReader::streamStart() +{ this->getTopLevelStream(); int tag = this->in->read(); @@ -319,26 +355,25 @@ void BinTreeNodeReader::streamStart() { } int attribCount = (size - 2 + size % 2) / 2; - std::map* attributes = readAttributes(attribCount); + std::map* attributes = readAttributes(attribCount); delete attributes; } -int BinTreeNodeReader::readInt8(ISocketConnection* in) { +int BinTreeNodeReader::readInt8(ISocketConnection* in) +{ return in->read(); } -int BinTreeNodeReader::readInt16(ISocketConnection* in) { - int intTop = in->read(); - int intBot = in->read(); - int value = (intTop << 8) + intBot; - return value; +int BinTreeNodeReader::readInt16(ISocketConnection* in) +{ + unsigned char data[2]; + in->read(data, 2); + return (int(data[0]) << 8) + int(data[1]); } -int BinTreeNodeReader::readInt24(ISocketConnection* in) { - int int1 = in->read(); - int int2 = in->read(); - int int3 = in->read(); - int value = (int1 << 16) + (int2 << 8) + int3; - - return value; +int BinTreeNodeReader::readInt24(ISocketConnection* in) +{ + unsigned char data[3]; + in->read(data, 3); + return (int(data[0]) << 16) + (int(data[1]) << 8) + int(data[2]); } diff --git a/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.h b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.h index 61b70b218b..4288c671f3 100644 --- a/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.h +++ b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeReader.h @@ -54,7 +54,6 @@ private: std::string* objectAsString(ReadData* o); std::string* readStringAsString(); std::string* readStringAsString(int token); - std::string getToken(int token); void getTopLevelStream(); static int readInt8(ByteArrayInputStream* in); static int readInt8(ISocketConnection* in); diff --git a/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.cpp b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.cpp index 6936de9a8b..b79bdcfd09 100644 --- a/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.cpp +++ b/protocols/WhatsApp/src/WhatsAPI++/BinTreeNodeWriter.cpp @@ -10,8 +10,9 @@ #include "utilities.h" BinTreeNodeWriter::BinTreeNodeWriter(WAConnection* conn, ISocketConnection* connection, - const char** dictionary, const int dictionarysize, IMutex* mutex) { - this->mutex = mutex; + const char** dictionary, const int dictionarysize, IMutex* mutex) +{ + this->mutex = mutex; this->conn = conn; this->out = new ByteArrayOutputStream(2048); this->realOut = connection; @@ -37,22 +38,19 @@ void BinTreeNodeWriter::processBuffer() { bool flag = this->conn->outputKey != NULL; unsigned int num = 0u; - if (flag) - { + if (flag) { long num2 = (long)this->out->getLength() + 4L; this->out->setLength(num2); this->out->setPosition(num2); num |= 1u; } long num3 = (long)this->out->getLength() - 3L - (long) this->dataBegin; - if (num3 >= 1048576L) - { + if (num3 >= 1048576L) { throw WAException("Buffer too large: " + num3, WAException::CORRUPT_STREAM_EX, 0); } std::vector* buffer = this->out->getBuffer(); - if (flag) - { + if (flag) { int num4 = (int)num3 - 4; this->conn->outputKey->encodeMessage(buffer->data(), this->dataBegin + 3 + num4, this->dataBegin + 3, num4); } @@ -61,15 +59,16 @@ void BinTreeNodeWriter::processBuffer() (*buffer)[this->dataBegin + 2] = (unsigned char)(num3 & 255L); } -void BinTreeNodeWriter::streamStart(std::string domain, std::string resource) { +void BinTreeNodeWriter::streamStart(std::string domain, std::string resource) +{ this->mutex->lock(); try { this->out->setPosition(0); this->out->setLength(0); - this->out->write(87); - this->out->write(65); + this->out->write('W'); + this->out->write('A'); this->out->write(1); - this->out->write(2); + this->out->write(5); std::map attributes; attributes["to"] = domain; @@ -80,44 +79,53 @@ void BinTreeNodeWriter::streamStart(std::string domain, std::string resource) { this->writeAttributes(&attributes); this->processBuffer(); this->flushBuffer(true, 0); - } catch (exception& ex) { + } + catch (exception& ex) { this->mutex->unlock(); throw ex; } this->mutex->unlock(); } -void BinTreeNodeWriter::writeListStart(int i) { +void BinTreeNodeWriter::writeListStart(int i) +{ if (i == 0) { this->out->write(0); - } else if (i < 256) { + } + else if (i < 256) { this->out->write(248); writeInt8(i); - } else { + } + else { this->out->write(249); writeInt16(i); } } -void BinTreeNodeWriter::writeInt8(int v) { +void BinTreeNodeWriter::writeInt8(int v) +{ this->out->write(v & 0xFF); } -void BinTreeNodeWriter::writeInt16(int v, ISocketConnection* o) { +void BinTreeNodeWriter::writeInt16(int v, ISocketConnection* o) +{ o->write((v & 0xFF00) >> 8); o->write((v & 0xFF) >> 0); } -void BinTreeNodeWriter::writeInt16(int v) { +void BinTreeNodeWriter::writeInt16(int v) +{ writeInt16(v, this->out); } -void BinTreeNodeWriter::writeInt16(int v, ByteArrayOutputStream* o) { +void BinTreeNodeWriter::writeInt16(int v, ByteArrayOutputStream* o) +{ o->write((v & 0xFF00) >> 8); o->write((v & 0xFF) >> 0); } -void BinTreeNodeWriter::writeAttributes(std::map* attributes) { +void BinTreeNodeWriter::writeAttributes(std::map* attributes) +{ if (attributes != NULL) { std::map::iterator ii; for (ii = attributes->begin(); ii != attributes->end(); ii++) { @@ -127,14 +135,15 @@ void BinTreeNodeWriter::writeAttributes(std::map* attributes) { } } -void BinTreeNodeWriter::writeString(const std::string& tag) { +void BinTreeNodeWriter::writeString(const std::string& tag) +{ std::map::iterator it = this->tokenMap.find(tag); if (it != this->tokenMap.end()) writeToken(it->second); else { size_t atIndex = tag.find('@'); if (atIndex == 0 || atIndex == string::npos) - writeBytes((unsigned char*) tag.data(), (int)tag.length()); + writeBytes((unsigned char*)tag.data(), (int)tag.length()); else { std::string server = tag.substr(atIndex + 1); std::string user = tag.substr(0, atIndex); @@ -143,18 +152,21 @@ void BinTreeNodeWriter::writeString(const std::string& tag) { } } -void BinTreeNodeWriter::writeJid(std::string* user, const std::string& server) { +void BinTreeNodeWriter::writeJid(std::string* user, const std::string& server) +{ this->out->write(250); if (user != NULL && !user->empty()) { writeString(*user); - } else { + } + else { writeToken(0); } writeString(server); } -void BinTreeNodeWriter::writeToken(int intValue) { +void BinTreeNodeWriter::writeToken(int intValue) +{ if (intValue < 245) this->out->write(intValue); else if (intValue <= 500) { @@ -163,32 +175,36 @@ void BinTreeNodeWriter::writeToken(int intValue) { } } -void BinTreeNodeWriter::writeBytes(unsigned char* bytes, int length) { +void BinTreeNodeWriter::writeBytes(unsigned char* bytes, int length) +{ if (length >= 256) { this->out->write(253); writeInt24(length); - } else { + } + else { this->out->write(252); writeInt8(length); } this->out->write(bytes, length); } -void BinTreeNodeWriter::writeInt24(int v) { +void BinTreeNodeWriter::writeInt24(int v) +{ this->out->write((v & 0xFF0000) >> 16); this->out->write((v & 0xFF00) >> 8); this->out->write(v & 0xFF); } -void BinTreeNodeWriter::writeInternal(ProtocolTreeNode* node) { +void BinTreeNodeWriter::writeInternal(ProtocolTreeNode* node) +{ writeListStart( - 1 + (node->attributes == NULL ? 0 : (int)node->attributes->size() * 2) - + (node->children == NULL ? 0 : 1) - + (node->data == NULL ? 0 : 1)); + 1 + (node->attributes == NULL ? 0 : (int)node->attributes->size() * 2) + + (node->children == NULL ? 0 : 1) + + (node->data == NULL ? 0 : 1)); writeString(node->tag); writeAttributes(node->attributes); if (node->data != NULL) { - writeBytes((unsigned char*) node->data->data(), (int)node->data->size()); + writeBytes((unsigned char*)node->data->data(), (int)node->data->size()); } if (node->children != NULL && !node->children->empty()) { writeListStart((int)node->children->size()); @@ -203,10 +219,12 @@ void BinTreeNodeWriter::flushBuffer(bool flushNetwork) this->flushBuffer(flushNetwork, this->dataBegin); } -void BinTreeNodeWriter::flushBuffer(bool flushNetwork, int startingOffset) { +void BinTreeNodeWriter::flushBuffer(bool flushNetwork, int startingOffset) +{ try { this->processBuffer(); - } catch (WAException& ex) { + } + catch (WAException& ex) { this->out->setPosition(0); this->out->setLength(0); throw ex; @@ -216,8 +234,7 @@ void BinTreeNodeWriter::flushBuffer(bool flushNetwork, int startingOffset) { std::vector buffer(this->out->getBuffer()->begin(), this->out->getBuffer()->end()); int num = (int)(this->out->getLength() - (long)startingOffset); - if (flushNetwork && ((long)this->out->getCapacity() - this->out->getLength() < 3L || this->out->getLength() > 4096L)) - { + if (flushNetwork && ((long)this->out->getCapacity() - this->out->getLength() < 3L || this->out->getLength() > 4096L)) { delete this->out; this->out = new ByteArrayOutputStream(4096); } @@ -226,24 +243,28 @@ void BinTreeNodeWriter::flushBuffer(bool flushNetwork, int startingOffset) { this->realOut->write(buffer, startingOffset, num); } -void BinTreeNodeWriter::streamEnd() { +void BinTreeNodeWriter::streamEnd() +{ this->mutex->lock(); try { writeListStart(1); this->out->write(2); flushBuffer(true); - } catch (exception& ex) { + } + catch (exception& ex) { this->mutex->unlock(); throw ex; } this->mutex->unlock(); } -void BinTreeNodeWriter::write(ProtocolTreeNode* node) { +void BinTreeNodeWriter::write(ProtocolTreeNode* node) +{ write(node, true); } -void BinTreeNodeWriter::write(ProtocolTreeNode* node, bool needsFlush) { +void BinTreeNodeWriter::write(ProtocolTreeNode* node, bool needsFlush) +{ this->mutex->lock(); try { this->writeDummyHeader(); @@ -253,14 +274,16 @@ void BinTreeNodeWriter::write(ProtocolTreeNode* node, bool needsFlush) { writeInternal(node); } flushBuffer(needsFlush); - } catch (exception& ex) { + } + catch (exception& ex) { this->mutex->unlock(); throw WAException(ex.what()); } this->mutex->unlock(); } -BinTreeNodeWriter::~BinTreeNodeWriter() { +BinTreeNodeWriter::~BinTreeNodeWriter() +{ if (this->out != NULL) delete this->out; } diff --git a/protocols/WhatsApp/src/WhatsAPI++/ISocketConnection.h b/protocols/WhatsApp/src/WhatsAPI++/ISocketConnection.h index a2d82efc64..b9fa3d5faa 100644 --- a/protocols/WhatsApp/src/WhatsAPI++/ISocketConnection.h +++ b/protocols/WhatsApp/src/WhatsAPI++/ISocketConnection.h @@ -7,19 +7,20 @@ class ISocketConnection { public: ISocketConnection() {} + virtual ~ISocketConnection() {} + virtual void write(int i) = 0; virtual unsigned char read() = 0; virtual void flush() = 0; virtual void write(const std::vector& b, int length) = 0; virtual void write(const std::vector& bytes, int offset, int length) = 0; + virtual int read(unsigned char*, int length) = 0; virtual int read(std::vector& b, int off, int length) = 0; virtual void makeNonBlock() = 0; virtual int waitForRead() = 0; virtual void forceShutdown() = 0; - virtual ~ISocketConnection() {} - //static void initNetwork(); - //static void quitNetwork(); + virtual void dump(const void *buf, int length) = 0; }; #endif /* ISOCKETCONNECTION_H_ */ diff --git a/protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp b/protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp index 8fb24b443b..352d49b07d 100644 --- a/protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp +++ b/protocols/WhatsApp/src/WhatsAPI++/WAConnection.cpp @@ -12,245 +12,31 @@ #include "utilities.h" const char* WAConnection::dictionary[] = { - "", - "", - "", - "", - "", - "account", - "ack", - "action", - "active", - "add", - "after", - "ib", - "all", - "allow", - "apple", - "audio", - "auth", - "author", - "available", - "bad-protocol", - "bad-request", - "before", - "Bell.caf", - "body", - "Boing.caf", - "cancel", - "category", - "challenge", - "chat", - "clean", - "code", - "composing", - "config", - "conflict", - "contacts", - "count", - "create", - "creation", - "default", - "delay", - "delete", - "delivered", - "deny", - "digest", - "DIGEST-MD5-1", - "DIGEST-MD5-2", - "dirty", - "elapsed", - "broadcast", - "enable", - "encoding", - "duplicate", - "error", - "event", - "expiration", - "expired", - "fail", - "failure", - "false", - "favorites", - "feature", - "features", - "field", - "first", - "free", - "from", - "g.us", - "get", - "Glass.caf", - "google", - "group", - "groups", - "g_notify", - "g_sound", - "Harp.caf", - "http://etherx.jabber.org/streams", - "http://jabber.org/protocol/chatstates", - "id", - "image", - "img", - "inactive", - "index", - "internal-server-error", - "invalid-mechanism", - "ip", - "iq", - "item", - "item-not-found", - "user-not-found", - "jabber:iq:last", - "jabber:iq:privacy", - "jabber:x:delay", - "jabber:x:event", - "jid", - "jid-malformed", - "kind", - "last", - "latitude", - "lc", - "leave", - "leave-all", - "lg", - "list", - "location", - "longitude", - "max", - "max_groups", - "max_participants", - "max_subject", - "mechanism", - "media", - "message", - "message_acks", - "method", - "microsoft", - "missing", - "modify", - "mute", - "name", - "nokia", - "none", - "not-acceptable", - "not-allowed", - "not-authorized", - "notification", - "notify", - "off", - "offline", - "order", - "owner", - "owning", - "paid", - "participant", - "participants", - "participating", - "password", - "paused", - "picture", - "pin", - "ping", - "platform", - "pop_mean_time", - "pop_plus_minus", - "port", - "presence", - "preview", - "probe", - "proceed", - "prop", - "props", - "p_o", - "p_t", - "query", - "raw", - "reason", - "receipt", - "receipt_acks", - "received", - "registration", - "relay", - "remote-server-timeout", - "remove", - "Replaced by new connection", - "request", - "required", - "resource", - "resource-constraint", - "response", - "result", - "retry", - "rim", - "s.whatsapp.net", - "s.us", - "seconds", - "server", - "server-error", - "service-unavailable", - "set", - "show", - "sid", - "silent", - "sound", - "stamp", - "unsubscribe", - "stat", - "status", - "stream:error", - "stream:features", - "subject", - "subscribe", - "success", - "sync", - "system-shutdown", - "s_o", - "s_t", - "t", - "text", - "timeout", - "TimePassing.caf", - "timestamp", - "to", - "Tri-tone.caf", - "true", - "type", - "unavailable", - "uri", - "url", - "urn:ietf:params:xml:ns:xmpp-sasl", - "urn:ietf:params:xml:ns:xmpp-stanzas", - "urn:ietf:params:xml:ns:xmpp-streams", - "urn:xmpp:delay", - "urn:xmpp:ping", - "urn:xmpp:receipts", - "urn:xmpp:whatsapp", - "urn:xmpp:whatsapp:account", - "urn:xmpp:whatsapp:dirty", - "urn:xmpp:whatsapp:mms", - "urn:xmpp:whatsapp:push", - "user", - "username", - "value", - "vcard", - "version", - "video", - "w", - "w:g", - "w:p", - "w:p:r", - "w:profile:picture", - "wait", - "x", - "xml-not-well-formed", - "xmlns", - "xmlns:stream", - "Xylophone.caf", - "1", - "WAUTH-1" + "", "", "", "account", "ack", "action", "active", "add", "after", "all", "allow", "apple", "auth", "author", "available", + "bad-protocol", "bad-request", "before", "body", "broadcast", "cancel", "category", "challenge", "chat", "clean", "code", + "composing", "config", "contacts", "count", "create", "creation", "debug", "default", "delete", "delivery", "delta", "deny", + "digest", "dirty", "duplicate", "elapsed", "enable", "encoding", "error", "event", "expiration", "expired", "fail", "failure", + "false", "favorites", "feature", "features", "feature-not-implemented", "field", "first", "free", "from", "g.us", "get", "google", + "group", "groups", "groups_v2", "http://etherx.jabber.org/streams", "http://jabber.org/protocol/chatstates", "ib", "id", "image", + "img", "index", "internal-server-error", "ip", "iq", "item-not-found", "item", "jabber:iq:last", "jabber:iq:privacy", "jabber:x:event", + "jid", "kind", "last", "leave", "list", "max", "mechanism", "media", "message_acks", "message", "method", "microsoft", "missing", + "modify", "mute", "name", "nokia", "none", "not-acceptable", "not-allowed", "not-authorized", "notification", "notify", "off", + "offline", "order", "owner", "owning", "p_o", "p_t", "paid", "participant", "participants", "participating", "paused", "picture", + "pin", "ping", "platform", "port", "presence", "preview", "probe", "prop", "props", "query", "raw", "read", "readreceipts", "reason", + "receipt", "relay", "remote-server-timeout", "remove", "request", "required", "resource-constraint", "resource", "response", "result", + "retry", "rim", "s_o", "s_t", "s.us", "s.whatsapp.net", "seconds", "server-error", "server", "service-unavailable", "set", "show", "silent", + "stat", "status", "stream:error", "stream:features", "subject", "subscribe", "success", "sync", "t", "text", "timeout", "timestamp", "to", + "true", "type", "unavailable", "unsubscribe", "uri", "url", "urn:ietf:params:xml:ns:xmpp-sasl", "urn:ietf:params:xml:ns:xmpp-stanzas", + "urn:ietf:params:xml:ns:xmpp-streams", "urn:xmpp:ping", "urn:xmpp:whatsapp:account", "urn:xmpp:whatsapp:dirty", "urn:xmpp:whatsapp:mms", + "urn:xmpp:whatsapp:push", "urn:xmpp:whatsapp", "user", "user-not-found", "value", "version", "w:g", "w:p:r", "w:p", "w:profile:picture", + "w", "wait", "WAUTH-2", "xmlns:stream", "xmlns", "1", "chatstate", "crypto", "phash", "enc", "class", "off_cnt", "w:g2", "promote", + "demote", "creator", "Bell.caf", "Boing.caf", "Glass.caf", "Harp.caf", "TimePassing.caf", "Tri-tone.caf", "Xylophone.caf", "background", + "backoff", "chunked", "context", "full", "in", "interactive", "out", "registration", "sid", "urn:xmpp:whatsapp:sync", "flt", "s16", "u8", + "adpcm", "amrnb", "amrwb", "mp3", "pcm", "qcelp", "wma", "h263", "h264", "jpeg" }; +const int WAConnection::DICTIONARY_LEN = _countof(WAConnection::dictionary); + WAConnection::WAConnection(IMutex* mutex, WAListener* event_handler, WAGroupListener* group_event_handler) { this->init(event_handler, group_event_handler, mutex); @@ -263,9 +49,8 @@ WAConnection::~WAConnection() delete this->in; delete this->out; std::map::iterator it; - for (it = this->pending_server_requests.begin(); it != this->pending_server_requests.end(); it++) { + for (it = this->pending_server_requests.begin(); it != this->pending_server_requests.end(); it++) delete it->second; - } } void WAConnection::init(WAListener* event_handler, WAGroupListener* group_event_handler, IMutex* mutex) diff --git a/protocols/WhatsApp/src/WhatsAPI++/WAConnection.h b/protocols/WhatsApp/src/WhatsAPI++/WAConnection.h index 9fbfde8484..92a8b82e4c 100644 --- a/protocols/WhatsApp/src/WhatsAPI++/WAConnection.h +++ b/protocols/WhatsApp/src/WhatsAPI++/WAConnection.h @@ -393,13 +393,12 @@ class WAConnection { time_t expire_date; int account_kind; time_t lastTreeRead; - static const int DICTIONARY_LEN = 237; + static const int DICTIONARY_LEN; static const char* dictionary[]; static MessageStore* message_store; KeyStream* inputKey; KeyStream* outputKey; - static std::string removeResourceFromJid(const std::string& jid); WALogin* getLogin(); diff --git a/protocols/WhatsApp/src/WhatsAPI++/WALogin.cpp b/protocols/WhatsApp/src/WhatsAPI++/WALogin.cpp index 311edc70a5..459cc7efe9 100644 --- a/protocols/WhatsApp/src/WhatsAPI++/WALogin.cpp +++ b/protocols/WhatsApp/src/WhatsAPI++/WALogin.cpp @@ -7,7 +7,6 @@ #include "WALogin.h" #include "ByteArray.h" -//#include "ApplicationData.h" #include "ProtocolTreeNode.h" #include "WAException.h" #include @@ -56,86 +55,26 @@ std::vector* WALogin::login(const std::vector& aut return this->readFeaturesUntilChallengeOrSuccess(); } -BinTreeNodeReader* WALogin::getTreeNodeReader() { +BinTreeNodeReader* WALogin::getTreeNodeReader() +{ return this->inn; } -BinTreeNodeWriter* WALogin::getTreeNodeWriter() { +BinTreeNodeWriter* WALogin::getTreeNodeWriter() +{ return this->out; } -std::string WALogin::getResponse(const std::string& challenge) { - unsigned char md5_buffer[16]; - - size_t i = challenge.find(WALogin::NONCE_KEY); - i += WALogin::NONCE_KEY.length(); - - size_t j = challenge.find('"', i); - std::string nonce = challenge.substr(i,j-i); - - std::string cnonce = str(absLong(randLong()), 36); - _LOGDATA("cnonce = %s", cnonce.c_str()); - std::string nc = "00000001"; - std::string cinfo(this->user + ":" + this->domain + ":" + this->password); - - _LOGDATA("cinfo = %s", cinfo.c_str()); - - ByteArrayOutputStream bos; - bos.write(utils::md5string(cinfo, md5_buffer), SIZEOF(md5_buffer)); - bos.write(58); - bos.write(nonce); - bos.write(58); - bos.write(cnonce); - // bos.print(); - - std::string digest_uri = "xmpp/" + this->domain; - std::vector* A1 = bos.toByteArray(); - std::string A2 = "AUTHENTICATE:" + digest_uri; - std::string KD = bytesToHex(utils::md5string(&A1->front(), (int)A1->size(), md5_buffer), SIZEOF(md5_buffer)); - KD += + ":" + nonce + ":" + nc + ":" + cnonce + ":auth:" + bytesToHex(utils::md5string(A2, md5_buffer), SIZEOF(md5_buffer)); - - _LOGDATA("KD = %s", KD.c_str()); - - std::string response = bytesToHex(utils::md5string(KD, md5_buffer), SIZEOF(md5_buffer)); - - _LOGDATA("response = %s", response.c_str()); - - std::string bigger_response; - bigger_response.append("realm=\""); - bigger_response.append(this->domain); - bigger_response.append("\",response="); - bigger_response.append(response); - bigger_response.append(",nonce=\""); - bigger_response.append(nonce); - bigger_response.append("\",digest-uri=\""); - bigger_response.append(digest_uri); - bigger_response.append("\",cnonce=\""); - bigger_response.append(cnonce); - bigger_response.append("\",qop=auth"); - bigger_response.append(",username=\""); - bigger_response.append(this->user); - bigger_response.append("\",nc="); - bigger_response.append(nc); - - _LOGDATA("biggerresponse = %s", bigger_response.c_str()); - - delete A1; - - return bigger_response; -} - void WALogin::sendResponse(const std::vector& challengeData) { std::vector* authBlob = this->getAuthBlob(challengeData); - // std::string response = this->getResponse(challengeData); - std::map *attributes = new std::map(); - (*attributes)["xmlns"] = "urn:ietf:params:xml:ns:xmpp-sasl"; - ProtocolTreeNode node("response", attributes, authBlob); + ProtocolTreeNode node("response", NULL, authBlob); this->out->write(&node); } -void WALogin::sendFeatures() { +void WALogin::sendFeatures() +{ ProtocolTreeNode* child = new ProtocolTreeNode("receipt_acks", NULL); std::vector* children = new std::vector(); children->push_back(child); @@ -151,47 +90,47 @@ void WALogin::sendFeatures() { this->out->write(&node, true); } -void WALogin::sendAuth(const std::vector& existingChallenge) { +void WALogin::sendAuth(const std::vector& existingChallenge) +{ std::vector* data = NULL; if (!existingChallenge.empty()) { data = this->getAuthBlob(existingChallenge); } std::map* attributes = new std::map(); - (*attributes)["xmlns"] = "urn:ietf:params:xml:ns:xmpp-sasl"; - (*attributes)["mechanism"] = "WAUTH-1"; + (*attributes)["mechanism"] = "WAUTH-2"; (*attributes)["user"] = this->user; ProtocolTreeNode node("auth", attributes, data, NULL); this->out->write(&node, true); } -std::vector* WALogin::getAuthBlob(const std::vector& nonce) { - unsigned char out[20]; +std::vector* WALogin::getAuthBlob(const std::vector& nonce) +{ + unsigned char out[4*20]; KeyStream::keyFromPasswordAndNonce(this->password, nonce, out); if (this->connection->inputKey != NULL) delete this->connection->inputKey; - this->connection->inputKey = new KeyStream(out, 20); + this->connection->inputKey = new KeyStream(out + 40, out + 60); if (this->outputKey != NULL) delete this->outputKey; + this->outputKey = new KeyStream(out, out + 20); - this->outputKey = new KeyStream(out, 20); std::vector* list = new std::vector(0); for (int i = 0; i < 4; i++) list->push_back(0); list->insert(list->end(), this->user.begin(), this->user.end()); list->insert(list->end(), nonce.begin(), nonce.end()); - std::string strTime = Utilities::intToStr( time(NULL)); - list->insert(list->end(), strTime.begin(), strTime.end()); this->outputKey->encodeMessage(&((*list)[0]), 0, 4, (int)list->size() - 4); return list; } -std::vector* WALogin::readFeaturesUntilChallengeOrSuccess() { +std::vector* WALogin::readFeaturesUntilChallengeOrSuccess() +{ ProtocolTreeNode* root; while ((root = this->inn->nextTree()) != NULL) { if (ProtocolTreeNode::tagEquals(root, "stream:features")) { @@ -221,7 +160,8 @@ std::vector* WALogin::readFeaturesUntilChallengeOrSuccess() { throw WAException("fell out of loop in readFeaturesAndChallenge", WAException::CORRUPT_STREAM_EX, 0); } -void WALogin::parseSuccessNode(ProtocolTreeNode* node) { +void WALogin::parseSuccessNode(ProtocolTreeNode* node) +{ const string &expiration = node->getAttributeValue("expiration"); if (!expiration.empty()) { this->expire_date = atol(expiration.c_str()); @@ -242,7 +182,8 @@ void WALogin::parseSuccessNode(ProtocolTreeNode* node) { this->connection->outputKey = this->outputKey; } -std::vector WALogin::readSuccess() { +std::vector WALogin::readSuccess() +{ ProtocolTreeNode* node = this->inn->nextTree(); if (ProtocolTreeNode::tagEquals(node, "failure")) { @@ -271,58 +212,79 @@ std::vector WALogin::readSuccess() { return data; } -WALogin::~WALogin() { -} +WALogin::~WALogin() +{} -KeyStream::KeyStream(unsigned char* key, size_t keyLength) { - unsigned char drop[256]; +KeyStream::KeyStream(unsigned char* _key, unsigned char* _keyMac) : + seq(0) +{ + memcpy(key, _key, 20); + memcpy(keyMac, _keyMac, 20); - this->key = new unsigned char[keyLength]; - memcpy(this->key, key, keyLength); - this->keyLength = (int)keyLength; + RC4_set_key(&this->rc4, 20, this->key); - RC4_set_key(&this->rc4, this->keyLength, this->key); - RC4(&this->rc4, 256, drop, drop); - HMAC_CTX_init(&this->hmac); + unsigned char drop[768]; + RC4(&this->rc4, sizeof(drop), drop, drop); + + HMAC_CTX_init(&hmac); } -KeyStream::~KeyStream() { - delete [] this->key; - HMAC_CTX_cleanup(&this->hmac); +KeyStream::~KeyStream() +{ + HMAC_CTX_cleanup(&hmac); } -void KeyStream::keyFromPasswordAndNonce(const std::string& pass, const std::vector& nonce, unsigned char *out) { - PKCS5_PBKDF2_HMAC_SHA1(pass.data(), (int)pass.size(), nonce.data(), (int)nonce.size(), 16, 20, out); +void KeyStream::keyFromPasswordAndNonce(const std::string& pass, const std::vector& nonce, unsigned char *out) +{ + size_t cbSize = nonce.size(); + + uint8_t *pNonce = (uint8_t*)_alloca(cbSize + 1); + memcpy(pNonce, nonce.data(), cbSize); + + for (int i = 0; i < 4; i++) { + pNonce[cbSize] = i + 1; + PKCS5_PBKDF2_HMAC_SHA1(pass.data(), (int)pass.size(), pNonce, (int)cbSize+1, 2, 20, out + i*20); + } } -void KeyStream::decodeMessage(unsigned char* buffer, int macOffset, int offset, const int length) { +void KeyStream::decodeMessage(unsigned char* buffer, int macOffset, int offset, const int length) +{ unsigned char digest[20]; this->hmacsha1(buffer + offset, length, digest); - for (int i = 0; i < 4; i++) { - if (buffer[macOffset + i] != digest[i]) { - throw WAException("invalid MAC", WAException::CORRUPT_STREAM_EX, 0); - } - } - unsigned char* out = new unsigned char[length]; + if (memcmp(&buffer[macOffset], digest, 4)) + throw WAException("invalid MAC", WAException::CORRUPT_STREAM_EX, 0); + + unsigned char* out = (unsigned char*)_alloca(length); RC4(&this->rc4, length, buffer + offset, out); memcpy(buffer + offset, out, length); - delete[] out; } -void KeyStream::encodeMessage(unsigned char* buffer, int macOffset, int offset, const int length) { - unsigned char* out = new unsigned char[length]; +void KeyStream::encodeMessage(unsigned char* buffer, int macOffset, int offset, const int length) +{ + unsigned char* out = (unsigned char*)_alloca(length); RC4(&this->rc4, length, buffer + offset, out); memcpy(buffer + offset, out, length); - delete[] out; unsigned char digest[20]; this->hmacsha1(buffer + offset, length, digest); - memcpy(buffer + macOffset, digest, 4); } -void KeyStream::hmacsha1(unsigned char* text, int textLength, unsigned char *out) { +void KeyStream::hmacsha1(unsigned char* text, int textLength, unsigned char *out) +{ + HMAC_Init(&hmac, this->keyMac, 20, EVP_sha1()); + HMAC_Update(&hmac, text, textLength); + + unsigned char hmacInt[4]; + hmacInt[0] = (this->seq >> 24); + hmacInt[1] = (this->seq >> 16); + hmacInt[2] = (this->seq >> 8); + hmacInt[3] = (this->seq); + HMAC_Update(&hmac, hmacInt, sizeof(hmacInt)); + unsigned int mdLength; - HMAC(EVP_sha1(), this->key, this->keyLength, text, textLength, out, &mdLength); + HMAC_Final(&hmac, out, &mdLength); + + this->seq++; } diff --git a/protocols/WhatsApp/src/WhatsAPI++/WALogin.h b/protocols/WhatsApp/src/WhatsAPI++/WALogin.h index 868e806880..2949213e53 100644 --- a/protocols/WhatsApp/src/WhatsAPI++/WALogin.h +++ b/protocols/WhatsApp/src/WhatsAPI++/WALogin.h @@ -23,15 +23,15 @@ class BinTreeNodeWriter; class KeyStream { private: RC4_KEY rc4; - HMAC_CTX hmac; - unsigned char* key; - int keyLength; + unsigned char key[20], keyMac[20]; + int seq; + HMAC_CTX hmac; void hmacsha1(unsigned char* text, int textLength, unsigned char *out); public: - KeyStream(unsigned char* key, size_t keyLegnth); - virtual ~KeyStream(); + KeyStream(unsigned char* _key, unsigned char* _keyMac); + ~KeyStream(); static void keyFromPasswordAndNonce(const std::string& pass, const std::vector& nonce, unsigned char *out); void decodeMessage(unsigned char* buffer, int macOffset, int offset, const int length); @@ -54,7 +54,6 @@ private: std::vector* readFeaturesUntilChallengeOrSuccess(); void parseSuccessNode(ProtocolTreeNode* node); std::vector readSuccess(); - std::string getResponse(const std::string& challenge); public: std::string user; diff --git a/protocols/WhatsApp/src/WhatsAPI++/WARegister.cpp b/protocols/WhatsApp/src/WhatsAPI++/WARegister.cpp index a27c3c42eb..d2ff14411a 100644 --- a/protocols/WhatsApp/src/WhatsAPI++/WARegister.cpp +++ b/protocols/WhatsApp/src/WhatsAPI++/WARegister.cpp @@ -13,41 +13,16 @@ using namespace Utilities; ///////////////////////////////////////////////////////////////////////////////////////// // Token generation -static char WaKey[] = "/UIGKU1FVQa+ATM2A0za7G2KI9S/CwPYjgAbc67v7ep42eO/WeTLx1lb1cHwxpsEgF4+PmYpLd2YpGUdX/A2JQitsHzDwgcdBpUf7psX1BU="; -static char WaSignature[] = "MIIDMjCCAvCgAwIBAgIETCU2pDALBgcqhkjOOAQDBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENsYXJhMRYwFAYDVQQKEw1XaGF0c0FwcCBJbmMuMRQwEgYDVQQLEwtFbmdpbmVlcmluZzEUMBIGA1UEAxMLQnJpYW4gQWN0b24wHhcNMTAwNjI1MjMwNzE2WhcNNDQwMjE1MjMwNzE2WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEUMBIGA1UEBxMLU2FudGEgQ2xhcmExFjAUBgNVBAoTDVdoYXRzQXBwIEluYy4xFDASBgNVBAsTC0VuZ2luZWVyaW5nMRQwEgYDVQQDEwtCcmlhbiBBY3RvbjCCAbgwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OBHXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUPBPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvMspK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCjrh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtVJWQBTDv+z0kqA4GFAAKBgQDRGYtLgWh7zyRtQainJfCpiaUbzjJuhMgo4fVWZIvXHaSHBU1t5w//S0lDK2hiqkj8KpMWGywVov9eZxZy37V26dEqr/c2m5qZ0E+ynSu7sqUD7kGx/zeIcGT0H+KAVgkGNQCo5Uc0koLRWYHNtYoIvt5R3X6YZylbPftF/8ayWTALBgcqhkjOOAQDBQADLwAwLAIUAKYCp0d6z4QQdyN74JDfQ2WCyi8CFDUM4CaNB+ceVXdKtOrNTQcc0e+t"; -static char WaClassesMd5[] = "xOyKd7AoN0uoos7Fkeup5w=="; // 2.11.407 +static char WaToken[] = "PdA2DJyKoUrwLw1Bg6EIhzh502dF9noR9uFCllGk1418865329241"; std::string WAToken::GenerateToken(const string &number) { - unsigned int keyLen, dataLen, classesLen; - mir_ptr key((BYTE*)mir_base64_decode(WaKey, &keyLen)); - mir_ptr data((BYTE*)mir_base64_decode(WaSignature, &dataLen)); - mir_ptr classes((BYTE*)mir_base64_decode(WaClassesMd5, &classesLen)); - - BYTE opad[64], ipad[64]; - memset(opad, 0x5C, sizeof(opad)); - memset(ipad, 0x36, sizeof(ipad)); - for (int i = 0; i < sizeof(opad); i++) { - opad[i] = (BYTE)(opad[i] ^ key[i]); - ipad[i] = (BYTE)(ipad[i] ^ key[i]); - } + uint8_t digest[16]; + md5_string(WaToken + number, digest); - BYTE hash1[MIR_SHA1_HASH_SIZE], hash2[MIR_SHA1_HASH_SIZE]; - mir_sha1_ctx ctx; - mir_sha1_init(&ctx); - mir_sha1_append(&ctx, ipad, sizeof(ipad)); - mir_sha1_append(&ctx, data, dataLen); - mir_sha1_append(&ctx, classes, classesLen); - mir_sha1_append(&ctx, (PBYTE)number.c_str(), (int)number.size()); - mir_sha1_finish(&ctx, hash1); - - mir_sha1_init(&ctx); - mir_sha1_append(&ctx, opad, sizeof(opad)); - mir_sha1_append(&ctx, hash1, sizeof(hash1)); - mir_sha1_finish(&ctx, hash2); - - ptrA result(mir_urlEncode(ptrA(mir_base64_encode(hash2, sizeof(hash2))))); - return std::string(result); + char dest[33]; + bin2hex(digest, sizeof(digest), dest); + return dest; } ///////////////////////////////////////////////////////////////////////////////////////// diff --git a/protocols/WhatsApp/src/constants.h b/protocols/WhatsApp/src/constants.h index 6b292eb218..ccb9c9a332 100644 --- a/protocols/WhatsApp/src/constants.h +++ b/protocols/WhatsApp/src/constants.h @@ -33,7 +33,7 @@ // WhatsApp #define WHATSAPP_LOGIN_SERVER "c.whatsapp.net" -#define ACCOUNT_USER_AGENT "WhatsApp/2.11.407 Android/4.0.4 Device/GalaxyS3" +#define ACCOUNT_USER_AGENT "WhatsApp/2.12.60 S40Version/14.26 Device/Nokia302" #define ACCOUNT_URL_CODEREQUEST "https://r.whatsapp.net/v1/code.php" #define ACCOUNT_URL_CODEREQUESTV2 "https://v.whatsapp.net/v2/code" #define ACCOUNT_URL_REGISTERREQUEST "https://r.whatsapp.net/v1/register.php" @@ -42,7 +42,7 @@ #define ACCOUNT_URL_EXISTSV2 "https://v.whatsapp.net/v2/exist" // WhatsApp Nokia 302 S40 -#define ACCOUNT_RESOURCE "S40-2.12.49" +#define ACCOUNT_RESOURCE "S40-2.12.60" #define WHATSAPP_RECV_MESSAGE 1 #define WHATSAPP_SEND_MESSAGE 2 diff --git a/protocols/WhatsApp/src/version.h b/protocols/WhatsApp/src/version.h index 684deffc8a..0a73307133 100644 --- a/protocols/WhatsApp/src/version.h +++ b/protocols/WhatsApp/src/version.h @@ -1,7 +1,7 @@ #define __MAJOR_VERSION 0 #define __MINOR_VERSION 1 -#define __RELEASE_NUM 0 -#define __BUILD_NUM 5 +#define __RELEASE_NUM 1 +#define __BUILD_NUM 0 #include -- cgit v1.2.3