/*ref: https://github.com/apernet/OpenGFW/blob/1dce82745d0bc8b3813aea147954ba3a2294ef30/analyzer/tcp/fet.go https://www.usenix.org/system/files/usenixsecurity23-wu-mingshi.pdf */ #include #include #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; }