feat(integration decoders): http and glimpse_detector

compile pass, todo test
This commit is contained in:
yangwei
2024-08-20 19:01:06 +08:00
committed by lijia
parent 6e46dbf762
commit dafbecd49a
804 changed files with 66904 additions and 4 deletions

View File

@@ -0,0 +1,867 @@
/*
*
* Copyright (c) 2011-2016 The University of Waikato, Hamilton, New Zealand.
* All rights reserved.
*
* This file is part of libprotoident.
*
* This code has been developed by the University of Waikato WAND
* research group. For further information please see http://www.wand.net.nz/
*
* libprotoident is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* libprotoident is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*
*/
#include <string.h>
#ifdef __APPLE__
#include <libkern/OSByteOrder.h>
#define bswap32 OSSwapInt32
#endif
#ifdef __FreeBSD__
#include <sys/endian.h>
#endif
#ifdef __linux__
#include <byteswap.h>
#define bswap32 __bswap_32
#endif
#include "libprotoident.h"
#include "proto_common.h"
bool match_str_either(lpi_data_t *data, const char *string) {
if (MATCHSTR(data->payload[0], string))
return true;
if (MATCHSTR(data->payload[1], string))
return true;
return false;
}
bool match_str_both(lpi_data_t *data, const char *string1,
const char *string2) {
if (MATCHSTR(data->payload[0], string1) &&
MATCHSTR(data->payload[1], string2))
return true;
if (MATCHSTR(data->payload[1], string1) &&
MATCHSTR(data->payload[0], string2))
return true;
return false;
}
bool match_chars_either(lpi_data_t *data, char a, char b, char c,
char d) {
if (MATCH(data->payload[0], a, b, c, d))
return true;
if (MATCH(data->payload[1], a, b, c, d))
return true;
return false;
}
bool match_payload_length(uint32_t payload, uint32_t payload_len) {
uint32_t header = 0;
header = ntohl(payload);
/* See if the length in the (presumed) header matches the
* length of the rest of the packet minus the header itself (4 bytes).
*
* Watch out for the case of a 4 byte packet containing just
* 00 00 00 00! */
if (payload_len > 4 && header == payload_len - 4)
return true;
return false;
}
bool match_ip_address_both(lpi_data_t *data) {
uint8_t matches = 0;
if (data->ips[0] == 0 || data->ips[1] == 0)
return false;
if (data->payload_len[0] == 0)
matches += 1;
else if (data->payload[0] == data->ips[0])
matches += 1;
else if (data->payload[0] == data->ips[1])
matches += 1;
if (data->payload_len[1] == 0)
matches += 1;
else if (data->payload[1] == data->ips[0])
matches += 1;
else if (data->payload[1] == data->ips[1])
matches += 1;
if (matches == 2)
return true;
else
return false;
}
/* Multiple protocols use HTTP-style requests */
bool match_http_request(uint32_t payload, uint32_t len) {
/* HTTP requests - some of these are MS-specific extensions */
if (len == 0)
return true;
if (MATCHSTR(payload, "GET ")) return true;
if (len == 1 && MATCH(payload, 'G', 0x00, 0x00, 0x00))
return true;
if (len == 2 && MATCH(payload, 'G', 'E', 0x00, 0x00))
return true;
if (len == 3 && MATCH(payload, 'G', 'E', 'T', 0x00))
return true;
if (MATCHSTR(payload, "POST")) return true;
if (MATCHSTR(payload, "HEAD")) return true;
if (MATCHSTR(payload, "PUT ")) return true;
if (MATCHSTR(payload, "DELE")) return true;
if (MATCHSTR(payload, "auth")) return true;
/* SVN? */
if (MATCHSTR(payload, "REPO")) return true;
/* Webdav */
if (MATCHSTR(payload, "LOCK")) return true;
if (MATCHSTR(payload, "UNLO")) return true;
if (MATCHSTR(payload, "OPTI")) return true;
if (MATCHSTR(payload, "PROP")) return true;
if (MATCHSTR(payload, "MKCO")) return true;
if (MATCHSTR(payload, "POLL")) return true;
if (MATCHSTR(payload, "SEAR")) return true;
/* Ntrip - some differential GPS system using modified HTTP */
if (MATCHSTR(payload, "SOUR")) return true;
return false;
}
/* File headers are not specific to any particular protocol */
bool match_file_header(uint32_t payload) {
/* RIFF is a meta-format for storing AVI and WAV files */
if (MATCHSTR(payload, "RIFF"))
return true;
/* MZ is a .exe file */
if (MATCH(payload, 'M', 'Z', ANY, 0x00))
return true;
/* Ogg files */
if (MATCHSTR(payload, "OggS"))
return true;
/* ZIP files */
if (MATCH(payload, 'P', 'K', 0x03, 0x04))
return true;
/* MPEG files */
if (MATCH(payload, 0x00, 0x00, 0x01, 0xba))
return true;
/* RAR files */
if (MATCHSTR(payload, "Rar!"))
return true;
/* EBML */
if (MATCH(payload, 0x1a, 0x45, 0xdf, 0xa3))
return true;
/* JPG */
if (MATCH(payload, 0xff, 0xd8, ANY, ANY))
return true;
/* GIF */
if (MATCHSTR(payload, "GIF8"))
return true;
/* I'm also going to include PHP scripts in here */
if (MATCH(payload, 0x3c, 0x3f, 0x70, 0x68))
return true;
/* Unix scripts */
if (MATCH(payload, 0x23, 0x21, 0x2f, 0x62))
return true;
/* PDFs */
if (MATCHSTR(payload, "%PDF"))
return true;
/* PNG */
if (MATCH(payload, 0x89, 'P', 'N', 'G'))
return true;
/* HTML */
if (MATCHSTR(payload, "<htm"))
return true;
if (MATCH(payload, 0x0a, '<', '!', 'D'))
return true;
/* 7zip */
if (MATCH(payload, 0x37, 0x7a, 0xbc, 0xaf))
return true;
/* gzip - may need to replace last two bytes with ANY */
if (MATCH(payload, 0x1f, 0x8b, 0x08, ANY))
return true;
/* XML */
if (MATCHSTR(payload, "<!DO"))
return true;
/* FLAC */
if (MATCHSTR(payload, "fLaC"))
return true;
/* MP3 */
if (MATCH(payload, 'I', 'D', '3', 0x03))
return true;
if (MATCHSTR(payload, "\xff\xfb\x90\xc0"))
return true;
/* RPM */
if (MATCH(payload, 0xed, 0xab, 0xee, 0xdb))
return true;
/* Wz Patch */
if (MATCHSTR(payload, "WzPa"))
return true;
/* Flash Video */
if (MATCH(payload, 'F', 'L', 'V', 0x01))
return true;
/* .BKF (Microsoft Tape Format) */
if (MATCHSTR(payload, "TAPE"))
return true;
/* MS Office Doc file - this is unpleasantly geeky */
if (MATCH(payload, 0xd0, 0xcf, 0x11, 0xe0))
return true;
/* ASP */
if (MATCH(payload, 0x3c, 0x25, 0x40, 0x20))
return true;
/* WMS file */
if (MATCH(payload, 0x3c, 0x21, 0x2d, 0x2d))
return true;
/* ar archive, typically .deb files */
if (MATCHSTR(payload, "!<ar"))
return true;
/* Raw XML */
if (MATCHSTR(payload, "<?xm"))
return true;
if (MATCHSTR(payload, "<iq "))
return true;
/* SPF */
if (MATCHSTR(payload, "SPFI"))
return true;
/* ABIF - Applied Biosystems */
if (MATCHSTR(payload, "ABIF"))
return true;
/* bzip2 - other digits are also possible instead of 9 */
if (MATCH(payload, 'B', 'Z', 'h', '9'))
return true;
/* xz compression format */
if (MATCH(payload, 0xfd, '7', 'z', 'X'))
return true;
/* Microsoft Cabinet Files */
if (MATCH(payload, 'M', 'S', 'C', 'F'))
return true;
/* M4A -- be wary of false positives? */
if (MATCH(payload, 0x00, 0x00, 0x00, 0x20))
return true;
/* TIFF */
if (MATCH(payload, 0x49, 0x49, 0x2a, 0x00))
return true;
/* LZMA */
if (MATCH(payload, 0x5d, 0x00, 0x00, 0x80))
return true;
/* Source engine BSP file */
if (MATCH(payload, 'V', 'B', 'S', 'P'))
return true;
/* TTF */
if (MATCH(payload, 0x00, 0x01, 0x00, 0x00))
return true;
if (MATCH(payload, 'O', 'T', 'T', 'O'))
return true;
/* WOF2 TTCF */
if (MATCH(payload, 't', 't', 'c', 'f'))
return true;
/* RIR delegation files... */
if (MATCH(payload, '2', '.', '3', '|'))
return true;
/* REBASE -- restriction enzyme database
* A bit niche, but might be fairly common at universities? */
if (MATCH(payload, 0x20, 0x0a, 'R', 'E'))
return true;
/* Old coralreef trace files! */
if (MATCHSTR(payload, "\xff\xff\x44\x00"))
return true;
/* I'm pretty sure the following are files of some type or another.
* They crop up pretty often in our test data sets, so I'm going to
* put them in here.
*
* Hopefully one day we will find out what they really are */
if (MATCH(payload, '<', 'c', 'f', ANY))
return true;
if (MATCH(payload, '<', 'C', 'F', ANY))
return true;
if (MATCHSTR(payload, ".tem"))
return true;
if (MATCHSTR(payload, ".ite"))
return true;
if (MATCHSTR(payload, ".lef"))
return true;
return false;
}
bool valid_http_port(lpi_data_t *data) {
/* Must be on a known HTTP port - designed to filter
* out P2P protos that use HTTP.
*
* XXX If this doesn't work well, get rid of it!
*/
if (data->server_port == 80 || data->client_port == 80)
return true;
if (data->server_port == 8080 || data->client_port == 8080)
return true;
if (data->server_port == 8081 || data->client_port == 8081)
return true;
/* If port 443 responds, we want it to be counted as genuine
* HTTP, rather than a bad port scenario */
if (data->server_port == 443 || data->client_port == 443) {
if (data->payload_len[0] > 0 && data->payload_len[1] > 0)
return true;
}
return false;
}
/* 16 03 00 X is an SSLv3 handshake */
static inline bool match_ssl3_handshake(uint32_t payload, uint32_t len) {
if (len == 0)
return true;
if (len == 1 && MATCH(payload, 0x16, 0x00, 0x00, 0x00))
return true;
if (MATCH(payload, 0x16, 0x03, 0x00, ANY))
return true;
return false;
}
/* 16 03 01 X is an TLS handshake */
static inline bool match_tls_handshake(uint32_t payload, uint32_t len) {
if (len == 0)
return true;
if (len == 1 && MATCH(payload, 0x16, 0x00, 0x00, 0x00))
return true;
if (MATCH(payload, 0x16, 0x03, 0x01, ANY))
return true;
if (MATCH(payload, 0x16, 0x03, 0x02, ANY))
return true;
if (MATCH(payload, 0x16, 0x03, 0x03, ANY))
return true;
return false;
}
/* SSLv2 handshake - the ANY byte in the 0x80 payload is actually the length
* of the payload - 2.
*
* XXX This isn't always true - consecutive packets may be merged it seems :(
*/
static inline bool match_ssl2_handshake(uint32_t payload, uint32_t len) {
if (MATCH(payload, 0x80, ANY, 0x01, 0x03))
return true;
if (MATCH(payload, 0x81, ANY, 0x01, 0x03))
return true;
return false;
}
static inline bool match_tls_alert(uint32_t payload, uint32_t len) {
if (MATCH(payload, 0x15, 0x03, 0x01, ANY))
return true;
if (MATCH(payload, 0x15, 0x03, 0x02, ANY))
return true;
if (MATCH(payload, 0x15, 0x03, 0x03, ANY))
return true;
/* Alerts are also possible under SSL 3.0 */
if (MATCH(payload, 0x15, 0x03, 0x00, ANY))
return true;
return false;
}
static inline bool match_tls_change(uint32_t payload, uint32_t len) {
if (MATCH(payload, 0x14, 0x03, 0x01, ANY))
return true;
if (MATCH(payload, 0x14, 0x03, 0x02, ANY))
return true;
if (MATCH(payload, 0x14, 0x03, 0x03, ANY))
return true;
return false;
}
static inline bool match_tls_content(uint32_t payload, uint32_t len) {
if (MATCH(payload, 0x17, 0x03, 0x01, ANY))
return true;
if (MATCH(payload, 0x17, 0x03, 0x02, ANY))
return true;
if (MATCH(payload, 0x17, 0x03, 0x03, ANY))
return true;
return false;
}
bool match_ssl(lpi_data_t *data) {
if (match_ssl3_handshake(data->payload[0], data->payload_len[0]) &&
match_ssl3_handshake(data->payload[1], data->payload_len[1]))
return true;
if (match_tls_handshake(data->payload[0], data->payload_len[0]) &&
match_tls_handshake(data->payload[1], data->payload_len[1]))
return true;
if (match_ssl3_handshake(data->payload[0], data->payload_len[0]) &&
match_tls_handshake(data->payload[1], data->payload_len[1]))
return true;
if (match_tls_handshake(data->payload[0], data->payload_len[0]) &&
match_ssl3_handshake(data->payload[1], data->payload_len[1]))
return true;
/* Seems we can sometimes skip the full handshake and start on the data
* right away (as indicated by 0x17) - for now, I've only done this for TLS */
if (match_tls_handshake(data->payload[0], data->payload_len[0]) &&
match_tls_content(data->payload[1], data->payload_len[1]))
return true;
if (match_tls_handshake(data->payload[1], data->payload_len[1]) &&
match_tls_content(data->payload[0], data->payload_len[0]))
return true;
/* Need to check for TLS alerts (errors) too */
if (match_tls_handshake(data->payload[0], data->payload_len[0]) &&
match_tls_alert(data->payload[1], data->payload_len[1]))
return true;
if (match_tls_handshake(data->payload[1], data->payload_len[1]) &&
match_tls_alert(data->payload[0], data->payload_len[0]))
return true;
if (match_ssl3_handshake(data->payload[0], data->payload_len[0]) &&
match_tls_alert(data->payload[1], data->payload_len[1]))
return true;
if (match_ssl3_handshake(data->payload[1], data->payload_len[1]) &&
match_tls_alert(data->payload[0], data->payload_len[0]))
return true;
/* Need to check for cipher changes too */
if (match_tls_handshake(data->payload[0], data->payload_len[0]) &&
match_tls_change(data->payload[1], data->payload_len[1]))
return true;
if (match_tls_handshake(data->payload[1], data->payload_len[1]) &&
match_tls_change(data->payload[0], data->payload_len[0]))
return true;
/* Some HTTPS servers respond with unencrypted content, presumably
* when somebody invalid attempts a connection */
if (match_tls_handshake(data->payload[0], data->payload_len[0]) &&
MATCHSTR(data->payload[1], "<!DO") &&
data->payload_len[0] != 0)
return true;
if (match_tls_handshake(data->payload[1], data->payload_len[1]) &&
MATCHSTR(data->payload[0], "<!DO") &&
data->payload_len[1] != 0)
return true;
/* Allow TLS content in both directions -- could be multi-path TCP?
* Or some form of picking up where a previous connection left off?
*/
if (match_tls_content(data->payload[0], data->payload_len[0]) &&
match_tls_content(data->payload[1], data->payload_len[1]))
return true;
if (match_tls_content(data->payload[1], data->payload_len[1]) &&
match_tls_content(data->payload[0], data->payload_len[0]))
return true;
if ((match_tls_handshake(data->payload[0], data->payload_len[0]) ||
match_ssl3_handshake(data->payload[0], data->payload_len[0])) &&
match_ssl2_handshake(data->payload[1], data->payload_len[1]))
return true;
if ((match_tls_handshake(data->payload[1], data->payload_len[1]) ||
match_ssl3_handshake(data->payload[1], data->payload_len[1])) &&
match_ssl2_handshake(data->payload[0], data->payload_len[0]))
return true;
if (data->payload_len[0] == 0 && match_ssl2_handshake(data->payload[1], data->payload_len[1]))
return true;
if (data->payload_len[1] == 0 && match_ssl2_handshake(data->payload[0], data->payload_len[0]))
return true;
return false;
}
static bool dns_req(uint32_t payload) {
/* The flags / rcode on requests are usually all zero.
*
* Exceptions: CD and RD may be set
*
* Remember BYTE ORDER!
*/
payload = htonl(payload);
if ((payload & 0x0000ffff) == 0x00000000)
return true;
/* Check for CD */
if ((payload & 0x0000ffff) == 0x00000010)
return true;
/* Check for RD */
if ((payload & 0x0000ffff) == 0x00000100)
return true;
return false;
}
static bool dns_backscatter(uint32_t payload) {
/* Let's see if we can identify unsolicited DNS responses */
/* Last byte seems to be always 0x00 - third is either 0x84 or 0x85 */
payload = htonl(payload);
if ((payload & 0x0000ffff) == 0x00008500)
return true;
if ((payload & 0x0000ffff) == 0x00008580)
return true;
if ((payload & 0x0000ffff) == 0x00008400)
return true;
if ((payload & 0x0000ffff) == 0x00008480)
return true;
if ((payload & 0x0000ffff) == 0x00008483)
return true;
if ((payload & 0x0000ffff) == 0x00008403)
return true;
if ((payload & 0x0000ffff) == 0x00008000)
return true;
return false;
}
bool match_dns(lpi_data_t *data) {
if (data->payload_len[0] == 0 || data->payload_len[1] == 0) {
/* No response, so we have a bit of a hard time - however,
* most requests have a pretty standard set of flags.
*
* We'll also use the port here to help out */
if (data->server_port != 53 && data->client_port != 53)
return false;
if (data->payload_len[0] > 12 && dns_req(data->payload[0]))
return true;
if (data->payload_len[1] > 12 && dns_req(data->payload[1]))
return true;
if (data->payload_len[0] > 12 &&
dns_backscatter(data->payload[0]))
return true;
if (data->payload_len[1] > 12 &&
dns_backscatter(data->payload[1]))
return true;
return false;
}
if (((htonl(data->payload[0])) & 0xffff7800) !=
((htonl(data->payload[1])) & 0xffff7800))
return false;
if ((htonl(data->payload[0]) & 0x00008000) ==
(htonl(data->payload[1]) & 0x00008000))
return false;
return true;
}
bool match_tds_request(uint32_t payload, uint32_t len) {
uint32_t stated_len = 0;
stated_len = (ntohl(payload) & 0xffff);
if (stated_len != len)
return false;
if (MATCH(payload, 0x12, 0x01, ANY, ANY))
return true;
if (MATCH(payload, 0x10, 0x01, ANY, ANY))
return true;
return false;
}
bool match_8000_payload(uint32_t payload, uint32_t len) {
if (len == 0)
return true;
if (MATCH(payload, 0x3b, 0x00, 0x00, 0x00)) {
return true;
}
if (MATCH(payload, 0x3c, 0x00, 0x00, 0x00)) {
return true;
}
if (MATCH(payload, 0x3d, 0x00, 0x00, 0x00)) {
return true;
}
if (MATCH(payload, 0x3e, 0x00, 0x00, 0x00)) {
return true;
}
return false;
}
bool match_emule(lpi_data_t *data) {
/* Check that payload begins with e3 or c5 in both directions before
* classifying as eMule */
/* (I noticed that most emule(probably) flows began with "e3 xx 00 00"
* or "c5 xx 00 00", perhaps is worth looking into... Although I
* couldn't find anything about emule packets) */
if (data->payload_len[0] < 4 && data->payload_len[1] < 4)
return false;
if (MATCH(data->payload[0], 0xe3, ANY, 0x00, 0x00) &&
MATCH(data->payload[1], 0xe3, ANY, 0x00, 0x00))
return true;
if (MATCH(data->payload[0], 0xe3, ANY, 0x00, 0x00) &&
MATCH(data->payload[1], 0xc5, ANY, 0x00, 0x00))
return true;
/* XXX I haven't seen any obviously legit emule that starts with c5
* in both directions */
/*
if (MATCH(data->payload[0], 0xc5, ANY, ANY, ANY) &&
MATCH(data->payload[1], 0xc5, ANY, ANY, ANY))
return true;
*/
if (MATCH(data->payload[0], 0xc5, ANY, 0x00, 0x00) &&
MATCH(data->payload[1], 0xe3, ANY, 0x00, 0x00))
return true;
if (MATCH(data->payload[0], 0xe3, ANY, 0x00, 0x00) &&
data->payload_len[1] == 0)
return true;
if (MATCH(data->payload[1], 0xe3, ANY, 0x00, 0x00) &&
data->payload_len[0] == 0)
return true;
return false;
}
static inline bool match_kaspersky_ke(uint32_t payload, uint32_t len) {
if (len == 0)
return true;
if (MATCH(payload, 'K', 'E', 0x00, 0x00))
return true;
if (MATCH(payload, 'K', 'E', 0x00, 0x02))
return true;
if (MATCH(payload, 'K', 'E', 0x00, 0x07))
return true;
return false;
}
static inline bool match_kaspersky_ks(uint32_t payload, uint32_t len) {
if (len == 0)
return true;
if (MATCH(payload, 'K', 'S', 0x00, 0x00))
return true;
return false;
}
bool match_kaspersky(lpi_data_t *data) {
/* Traffic is either on TCP port 443 or UDP port 2001.
*
* One of the endpoints is always in either a Kaspersky range or
* an old PSInet range */
if (match_str_both(data, "PI\x00\x00", "PI\x00\x00")) {
if (data->payload_len[0] == 2 && data->payload_len[1] == 2)
return true;
}
if (match_kaspersky_ke(data->payload[0], data->payload_len[0])) {
if (match_kaspersky_ke(data->payload[1], data->payload_len[1]))
return true;
}
if (match_kaspersky_ks(data->payload[0], data->payload_len[0])) {
if (match_kaspersky_ks(data->payload[1], data->payload_len[1]))
return true;
}
return false;
}
bool match_youku_payload(uint32_t pload, uint32_t len) {
if (len == 0)
return true;
if (MATCH(pload, 0x4b, 0x55, 0x00, 0x01))
return true;
if (MATCH(pload, 0x4b, 0x55, 0x00, 0x02))
return true;
if (MATCH(pload, 0x4b, 0x55, 0x00, 0x03))
return true;
if (MATCH(pload, 0x4b, 0x55, 0x00, 0x04))
return true;
return false;
}
bool match_tpkt(uint32_t payload, uint32_t len) {
uint32_t stated_len = 0;
/*
* TPKT header is 03 00 + 2 bytes of length (including the TPKT header)
*/
if (!MATCH(payload, 0x03, 0x00, ANY, ANY))
return false;
stated_len = ntohl(payload) & 0xffff;
if (stated_len != len)
return false;
return true;
}
bool match_qqlive_payload(uint32_t payload, uint32_t len) {
uint32_t swap;
/* This appears to have a 3 byte header. First byte is always 0xfe.
* Second and third bytes are the length (minus the 3 byte header).
*/
if (len == 0)
return true;
swap = htonl(payload);
swap = (swap & 0xffff00) >> 8;
if (ntohs(swap) != len - 3)
return false;
/* Interestingly, the third and fourth byte always match */
swap = htonl(payload);
if ((swap & 0xff) != ((swap & 0xff00) >> 8))
return false;
if (MATCH(payload, 0xfe, ANY, ANY, ANY))
return true;
return false;
}
bool match_yy_payload(uint32_t payload, uint32_t len) {
/* The first four bytes are a length field, but using the
* wrong byte order...
*/
if (!MATCH(payload, ANY, ANY, 0x00, 0x00))
return false;
#if BYTE_ORDER == BIG_ENDIAN
if (bswap32(payload) == len)
return true;
#else
if (payload == len)
return true;
#endif
return false;
}
/* Byte swapping functions for various inttypes */
uint64_t byteswap64(uint64_t num)
{
return (byteswap32((num&0xFFFFFFFF00000000ULL)>>32))
|((uint64_t)byteswap32(num&0x00000000FFFFFFFFULL)<<32);
}
uint32_t byteswap32(uint32_t num)
{
return ((num&0x000000FFU)<<24)
| ((num&0x0000FF00U)<<8)
| ((num&0x00FF0000U)>>8)
| ((num&0xFF000000U)>>24);
}
uint16_t byteswap16(uint16_t num)
{
return ((num<<8)&0xFF00)|((num>>8)&0x00FF);
}