123 lines
3.2 KiB
C++
123 lines
3.2 KiB
C++
/*ref: https://github.com/apernet/OpenGFW/blob/1dce82745d0bc8b3813aea147954ba3a2294ef30/analyzer/tcp/fet.go
|
|
https://www.usenix.org/system/files/usenixsecurity23-wu-mingshi.pdf
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "fet.h"
|
|
|
|
bool isPrintable(unsigned char b) {
|
|
return b >= 0x20 && b <= 0x7e;
|
|
}
|
|
|
|
int popCount(unsigned char b) {
|
|
int count = 0;
|
|
while (b != 0) {
|
|
count += (int)(b & 1);
|
|
b >>= 1;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
float averagePopCount(const unsigned char* bytes, int bytes_len) {
|
|
if (bytes_len == 0) {
|
|
return 0;
|
|
}
|
|
int total = 0;
|
|
for (int i = 0; i < bytes_len; i++) {
|
|
total += popCount(bytes[i]);
|
|
}
|
|
return (float)total / (float)bytes_len;
|
|
}
|
|
|
|
bool isFirstSixPrintable(const unsigned char* bytes, int bytes_len) {
|
|
if (bytes_len < 6) {
|
|
return false;
|
|
}
|
|
for (int i = 0; i < 6; i++) {
|
|
if (!isPrintable(bytes[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
float printablePercentage(const unsigned char* bytes, int bytes_len) {
|
|
if (bytes_len == 0) {
|
|
return 0;
|
|
}
|
|
int count = 0;
|
|
for (int i = 0; i < bytes_len; i++) {
|
|
if (isPrintable(bytes[i])) {
|
|
count++;
|
|
}
|
|
}
|
|
return (float)count / (float)bytes_len;
|
|
}
|
|
|
|
int contiguousPrintable(const unsigned char* bytes, int bytes_len) {
|
|
if (bytes_len == 0) {
|
|
return 0;
|
|
}
|
|
int maxCount = 0;
|
|
int current = 0;
|
|
for (int i = 0; i < bytes_len; i++) {
|
|
if (isPrintable(bytes[i])) {
|
|
current++;
|
|
} else {
|
|
if (current > maxCount) {
|
|
maxCount = current;
|
|
}
|
|
current = 0;
|
|
}
|
|
}
|
|
if (current > maxCount) {
|
|
maxCount = current;
|
|
}
|
|
return maxCount;
|
|
}
|
|
|
|
bool isTLS(const unsigned char* bytes, int bytes_len) {
|
|
if (bytes_len < 3) {
|
|
return false;
|
|
}
|
|
// "We observe that the GFW exempts any connection whose first
|
|
// three bytes match the following regular expression:
|
|
// [\x16-\x17]\x03[\x00-\x09]" - from the paper in Section 4.3
|
|
if (bytes[0] >= 0x16 && bytes[0] <= 0x17 &&
|
|
bytes[1] == 0x03 && bytes[2] <= 0x09) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isHTTP(const unsigned char* bytes, int bytes_len) {
|
|
if (bytes_len < 3) {
|
|
return false;
|
|
}
|
|
|
|
// HTTP request
|
|
bool result = memcmp(bytes, "GET", 3) == 0 || memcmp(bytes, "HEA", 3) == 0 || memcmp(bytes, "POS", 3) == 0 ||
|
|
memcmp(bytes, "PUT", 3) == 0 || memcmp(bytes, "DEL", 3) == 0 || memcmp(bytes, "CON", 3) == 0 ||
|
|
memcmp(bytes, "OPT", 3) == 0 || memcmp(bytes, "TRA", 3) == 0 || memcmp(bytes, "PAT", 3) == 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
int is_data_fet(unsigned char* data, int data_len, struct fet_detail* detail) {
|
|
if (data_len == 0) {
|
|
return 0;
|
|
}
|
|
float ex1 = averagePopCount(data, data_len);
|
|
bool ex2 = isFirstSixPrintable(data, data_len);
|
|
float ex3 = printablePercentage(data, data_len);
|
|
int ex4 = contiguousPrintable(data, data_len);
|
|
bool ex5 = isTLS(data, data_len);
|
|
bool ex6 = isHTTP(data, data_len);
|
|
bool exempt = (ex1 <= 3.4 || ex1 >= 4.6) || ex2 || ex3 > 0.5 || ex4 > 20 || ex5 || ex6;
|
|
|
|
detail->is_tls = ex5;
|
|
|
|
return !exempt;
|
|
} |