├── BlackvueCloudStream.cpp ├── HTTPGET_URLs ├── LICENSE ├── README.md └── simpledump.sh /BlackvueCloudStream.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "winhttp.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "sha256.h" // http://www.zedwood.com/article/cpp-sha256-function 9 | 10 | std::string get_from_reg(const char * name) 11 | { 12 | std::string res; 13 | HKEY hKey = 0; 14 | if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\PittaSoft\\BlackVue", &hKey) == ERROR_SUCCESS) 15 | { 16 | DWORD dwType = REG_SZ; 17 | DWORD len = 0; 18 | RegQueryValueEx(hKey, name, NULL, &dwType, NULL, &len); 19 | if (len > 1) 20 | { 21 | char * user_token = (char *)malloc(len+1); 22 | if (user_token == NULL) 23 | { 24 | printf( "Cannot allocate %u bytes\n", len+1); 25 | } 26 | else 27 | { 28 | RegQueryValueEx(hKey, name, NULL, &dwType, (LPBYTE)user_token, &len); 29 | res.assign(user_token); 30 | free(user_token); 31 | } 32 | } 33 | RegCloseKey(hKey); 34 | } 35 | return res; 36 | } 37 | 38 | std::string get_user_token() 39 | { 40 | return get_from_reg("user_token"); 41 | } 42 | 43 | void set_user_token(const char * user_token) 44 | { 45 | HKEY hKey = 0; 46 | if (RegOpenKeyA(HKEY_CURRENT_USER, "Software\\PittaSoft\\BlackVue", &hKey) == ERROR_SUCCESS) 47 | { 48 | RegSetKeyValueA(hKey, NULL, "user_token", REG_SZ, user_token, strlen(user_token)+1); 49 | RegCloseKey(hKey); 50 | } 51 | } 52 | 53 | int hash_hex(char *hex, int hex_len, const char * str, int str_len) 54 | { 55 | unsigned char digest[SHA256::DIGEST_SIZE]; 56 | memset(digest, 0, SHA256::DIGEST_SIZE); 57 | 58 | SHA256 ctx = SHA256(); 59 | ctx.init(); 60 | ctx.update((const unsigned char *)str, str_len); 61 | ctx.final(digest); 62 | 63 | int offset = 0; 64 | for (int i=0; i < SHA256::DIGEST_SIZE; ++i) 65 | { 66 | offset += sprintf_s(&hex[offset], hex_len - offset, "%02X", digest[i]); 67 | } 68 | return offset; 69 | } 70 | 71 | std::string request(const char * server, const char * url, const char * body, bool bIsJson, int * status) 72 | { 73 | std::string res; 74 | *status = 0; 75 | 76 | if (server == NULL) 77 | { 78 | server = "api.blackvuecloud.com"; 79 | } 80 | 81 | printf( "Request: %s%s\n", server, url); 82 | if (body != NULL) 83 | { 84 | printf( "%s\n", body); 85 | } 86 | 87 | char bcsDate[128]; 88 | { 89 | __time64_t t = _time64(NULL); 90 | struct tm _tm; 91 | _localtime64_s(&_tm, &t); 92 | strftime(bcsDate, 128, "%Y%m%dT%H%M%SZ", &_tm); 93 | } 94 | 95 | char bcsToken[] = "hH751PfkmHdktlkNUmS8qDaCGZrdXxMbw8qT2oy78dB3jhebz0n6IvnA4C788Cts"; 96 | 97 | char bcsSignature[8192]; 98 | 99 | int bcsSignature_len = sprintf_s(bcsSignature, sizeof(bcsSignature), "%s&%s&%s&%s&%s&%s", 100 | (body != NULL) ? "POST" : "GET", 101 | server, 102 | url, 103 | bcsToken, 104 | bcsDate, 105 | "1077a134d84ab47c9ed82e7f4874f4802340a289cd0401ba27a6ebf74bceb8a6"); 106 | 107 | char bcsSignatureHEX[65]; 108 | hash_hex(bcsSignatureHEX, sizeof(bcsSignatureHEX), bcsSignature, bcsSignature_len); 109 | 110 | char headers[8192]; 111 | sprintf_s(headers, sizeof(headers), "Content-Type: application/%s\r\nbcsToken: %s\r\nbcsSignature: %s\r\nbcsDate: %s\n", 112 | bIsJson ? "json" : "x-www-form-urlencoded", 113 | bcsToken, 114 | bcsSignatureHEX, 115 | bcsDate); 116 | 117 | HINTERNET hSession = WinHttpOpen(L"BlackVue C win32", WINHTTP_ACCESS_TYPE_NO_PROXY, WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0); 118 | if (hSession == NULL) 119 | { 120 | printf( "WinHttpOpen failed, error %d\n", GetLastError()); 121 | } 122 | else 123 | { 124 | WinHttpSetTimeouts(hSession, 20000, 20000, 20000, 20000); 125 | 126 | bool bSecure = true; 127 | char _server[256]; 128 | wchar_t w_server[256]; 129 | if (strncmp(server, "https://", 8) == 0) 130 | { 131 | strcpy_s(_server, &server[8]); 132 | } 133 | else if (strncmp(server, "http://", 7) == 0) 134 | { 135 | strcpy_s(_server, &server[7]); 136 | bSecure = false; 137 | } 138 | else 139 | { 140 | strcpy_s(_server, server); 141 | } 142 | 143 | int port = 443; 144 | char * p = strchr(_server, ':'); 145 | if (p != NULL) 146 | { 147 | *p = '\0'; 148 | port = atoi(&p[1]); 149 | } 150 | 151 | MultiByteToWideChar(CP_ACP, 0, _server, -1, w_server, _countof(w_server)); 152 | 153 | 154 | HINTERNET hConnect = WinHttpConnect(hSession, w_server, port, 0); 155 | if (hConnect == NULL) 156 | { 157 | printf( "WinHttpConnect failed, error %d\n", GetLastError()); 158 | } 159 | else 160 | { 161 | wchar_t w_url[8192]; 162 | MultiByteToWideChar(CP_ACP, 0, url, -1, w_url, _countof(w_url)); 163 | 164 | HINTERNET hRequest = WinHttpOpenRequest(hConnect, (body != NULL) ? L"POST" : L"GET", w_url, NULL, WINHTTP_NO_REFERER, 165 | WINHTTP_DEFAULT_ACCEPT_TYPES, 166 | (bSecure ? WINHTTP_FLAG_SECURE /* 0xFF800000 */ : 0) | WINHTTP_FLAG_BYPASS_PROXY_CACHE ); 167 | 168 | if (hRequest == NULL) 169 | { 170 | printf( "WinHttpOpenRequest failed, error %d\n", GetLastError()); 171 | } 172 | else 173 | { 174 | DWORD body_len = ((body == NULL) ? 0 : strlen(body)); 175 | wchar_t w_headers[8192]; 176 | MultiByteToWideChar(CP_ACP, 0, headers, -1, w_headers, _countof(w_headers)); 177 | if (!WinHttpSendRequest(hRequest, w_headers, -1, (LPVOID)body, body_len, body_len, NULL)) 178 | { 179 | printf( "WinHttpSendRequest failed, error %d\n", GetLastError()); 180 | } 181 | else 182 | { 183 | if (!WinHttpReceiveResponse(hRequest, NULL)) 184 | { 185 | printf( "WinHttpReceiveResponse failed, error %d\n", GetLastError()); 186 | } 187 | else 188 | { 189 | DWORD dwStatus = 0; 190 | DWORD dwContentLenght = 0; 191 | DWORD dwSize = 4; 192 | WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_FLAG_NUMBER | WINHTTP_QUERY_STATUS_CODE, NULL, &dwStatus, &dwSize, 0); 193 | printf( "Response (%u):\n", dwStatus); 194 | 195 | *status = dwStatus; 196 | 197 | // dwSize = 4; 198 | // WinHttpQueryHeaders(hRequest, WINHTTP_QUERY_FLAG_NUMBER | WINHTTP_QUERY_CONTENT_LENGTH, NULL, &dwContentLenght, &dwSize, 0); 199 | 200 | if (!WinHttpQueryDataAvailable(hRequest, &dwSize)) 201 | { 202 | printf( "WinHttpQueryDataAvailable failed, error %d\n", GetLastError()); 203 | } 204 | else 205 | { 206 | char * buffer = (char *)malloc(dwSize+1); 207 | if (buffer == NULL) 208 | { 209 | printf( "Cannot allocate %u bytes\n", dwSize); 210 | } 211 | else 212 | { 213 | DWORD dwRead = 0; 214 | while(dwRead < dwSize) 215 | { 216 | DWORD _dwRead = 0; 217 | if (!WinHttpReadData(hRequest, &buffer[dwRead], dwSize - dwRead, &_dwRead)) 218 | { 219 | printf( "WinHttpReadData failed, error %d\n", GetLastError()); 220 | break; 221 | } 222 | else 223 | { 224 | dwRead += _dwRead; 225 | } 226 | } 227 | buffer[dwRead] = '\0'; 228 | 229 | printf( "%s\n\n", buffer); 230 | res.assign(buffer); 231 | free(buffer); 232 | } 233 | } 234 | } 235 | } 236 | 237 | WinHttpCloseHandle(hRequest); 238 | } 239 | WinHttpCloseHandle(hConnect); 240 | } 241 | WinHttpCloseHandle(hSession); 242 | } 243 | 244 | return res; 245 | } 246 | 247 | std::string get_json_value(std::string json, const char * name) 248 | { 249 | std::string res; 250 | char substr[256]; 251 | sprintf_s(substr, sizeof(substr), "\"%s\":\"", name); 252 | const char * s1 = strstr(json.c_str(), substr); 253 | if (s1 != NULL) 254 | { 255 | s1 += strlen(substr); 256 | const char * s2 = strchr(s1, '\"'); 257 | if (s2 != NULL) 258 | { 259 | int len = (int)(s2 - s1); 260 | res.assign(s1, len); 261 | } 262 | } 263 | return res; 264 | } 265 | 266 | std::string login(const char * email, const char * passwd) 267 | { 268 | std::string user_token; 269 | 270 | char mobile_uuid[13] = {0}; 271 | { 272 | // GetAdaptersInfo() // MAC address 273 | strcpy_s(mobile_uuid, sizeof(mobile_uuid), "ffffffffffff"); 274 | } 275 | 276 | char mobile_name[16] = {0}; 277 | { 278 | DWORD len = 15; 279 | GetComputerNameA(mobile_name, &len); 280 | if (mobile_name[0] == '\0') 281 | { 282 | GetComputerNameExA(ComputerNameDnsHostname, mobile_name, &len); 283 | if (mobile_name[0] == '\0') 284 | { 285 | strcpy_s(mobile_name, sizeof(mobile_name), "WIN_BLACKBUE"); 286 | } 287 | } 288 | } 289 | 290 | int time_interval = 24 * 60 * 60; 291 | 292 | char * server = NULL; // "https://pitta.blackvuecloud.com:443"; 293 | char * url = "/BCS/user_login.php"; // "/app/user_login.php"; 294 | 295 | char body_login[8192]; 296 | int offset = sprintf_s(body_login, sizeof(body_login), "email=%s&passwd=", email); 297 | offset += hash_hex(&body_login[offset], sizeof(body_login) - offset, passwd, strlen(passwd)); 298 | offset += sprintf_s(&body_login[offset], sizeof(body_login) - offset, "&mobile_uuid=%s&mobile_name=%s&mobile_os_type=%s&app_ver=%s&time_interval=%d", 299 | mobile_uuid, 300 | mobile_name, 301 | "win32_blackvue", 302 | "1.00", 303 | time_interval / 60); 304 | 305 | int status; 306 | printf("Login...\n"); 307 | std::string res = request(server, url, body_login, false, &status); 308 | 309 | std::string resultcode = get_json_value(res, "resultcode"); 310 | 311 | if (status == 406 || (status == 200 && strcmp(resultcode.c_str(), "BC_ERR_DUPLICATED") == 0)) 312 | { 313 | std::string id = get_json_value(res, "id"); 314 | 315 | printf("Logout...\n"); 316 | char body_logout[8192]; 317 | sprintf_s(body_logout, sizeof(body_logout), "%s&logout_id=%s", body_login, id.c_str()); 318 | res = request(server, url, body_logout, false, &status); 319 | if (status == 200) 320 | { 321 | printf("Login again...\n"); 322 | res = request(server, url, body_login, false, &status); 323 | } 324 | } 325 | if (status == 200) 326 | { 327 | user_token = get_json_value(res, "user_token"); 328 | if (!user_token.empty()) 329 | { 330 | set_user_token(user_token.c_str()); 331 | } 332 | } 333 | return user_token; 334 | } 335 | 336 | int main(int argc, char * argv[]) 337 | { 338 | std::string email = get_from_reg("email"); 339 | std::string passwd = get_from_reg("passwd"); 340 | 341 | if (argc < 2) 342 | { 343 | printf("Usage: BlackvueCloudStream.exe [email=...] [password=...] [ch=1|2] [my|bookmark|shared]\n\n"); 344 | } 345 | 346 | int list_index = 0; 347 | int channel = 1; 348 | 349 | for (int i=1; i