Original tooling from the Camhak research project (camera teardown of a
rebranded UBIA / Javiscam IP camera). PyQt6 GUI on top of a curses TUI on
top of a service controller; per-service start/stop, intruder detection,
protocol fingerprinting, OAM HMAC signing, CVE verifiers, OTA bucket
probe, firmware fetcher, fuzzer, packet injection.
Tabs: Dashboard, Live Log, Intruders, Cloud API, Fuzzer, Inject, CVEs,
Config, Help. Real-time per-packet protocol detection, conntrack-based
original-destination lookup, log rotation at 1 GiB.
See SECURITY_PAPER.md for the full writeup, site/index.html for the
public report, README.md for usage. Run with:
sudo /usr/bin/python3 gui.py
Co-authored by Setec Labs.
270 lines
7.7 KiB
C
270 lines
7.7 KiB
C
/*
|
|
* IOTC Bridge — ARM32 process that loads the TUTK library and
|
|
* communicates with the Python TUI over stdin/stdout JSON.
|
|
*
|
|
* Compile: arm-linux-gnueabi-gcc -o iotc_bridge iotc_bridge.c -L../lib -lIOTCAPIs_ALL -lpthread -ldl
|
|
* Run: qemu-arm-static -L /usr/arm-linux-gnueabi ./iotc_bridge
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <pthread.h>
|
|
|
|
/* IOTC API declarations (from IOTCAPIs.h) */
|
|
extern int IOTC_Initialize2(int udp_port);
|
|
extern int IOTC_DeInitialize(void);
|
|
extern int IOTC_Get_SessionID(void);
|
|
extern int IOTC_Connect_ByUID_Parallel(const char *UID, int SID);
|
|
extern int IOTC_Session_Close(int SID);
|
|
extern int IOTC_Session_Check(int SID, void *info);
|
|
extern int IOTC_Lan_Search2(void *result, int max, int timeout_ms);
|
|
|
|
/* AV API declarations (from AVAPIs.h) */
|
|
extern int avInitialize(int max_channels);
|
|
extern int avDeInitialize(void);
|
|
extern int avClientStart(int SID, const char *user, const char *pass,
|
|
int timeout, unsigned int *srvtype, int channel);
|
|
extern void avClientStop(int avIndex);
|
|
extern int avSendIOCtrl(int avIndex, int type, const char *data, int len);
|
|
extern int avRecvIOCtrl(int avIndex, int *type, char *data, int len, int timeout_ms);
|
|
|
|
/* Simple JSON output helpers */
|
|
static void json_ok(const char *key, int value) {
|
|
printf("{\"ok\":true,\"%s\":%d}\n", key, value);
|
|
fflush(stdout);
|
|
}
|
|
|
|
static void json_ok_str(const char *key, const char *value) {
|
|
printf("{\"ok\":true,\"%s\":\"%s\"}\n", key, value);
|
|
fflush(stdout);
|
|
}
|
|
|
|
static void json_err(const char *msg) {
|
|
printf("{\"ok\":false,\"error\":\"%s\"}\n", msg);
|
|
fflush(stdout);
|
|
}
|
|
|
|
static void json_data(const char *cmd, const unsigned char *data, int len) {
|
|
printf("{\"ok\":true,\"cmd\":\"%s\",\"len\":%d,\"hex\":\"", cmd, len);
|
|
for (int i = 0; i < len && i < 4096; i++)
|
|
printf("%02x", data[i]);
|
|
printf("\"}\n");
|
|
fflush(stdout);
|
|
}
|
|
|
|
/* State */
|
|
static int g_sid = -1;
|
|
static int g_av_index = -1;
|
|
static int g_initialized = 0;
|
|
|
|
static int do_init(void) {
|
|
if (g_initialized) {
|
|
json_err("already initialized");
|
|
return -1;
|
|
}
|
|
int ret = IOTC_Initialize2(0);
|
|
if (ret < 0) {
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "IOTC_Initialize2 failed: %d", ret);
|
|
json_err(buf);
|
|
return ret;
|
|
}
|
|
ret = avInitialize(16);
|
|
if (ret < 0) {
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "avInitialize failed: %d", ret);
|
|
json_err(buf);
|
|
return ret;
|
|
}
|
|
g_initialized = 1;
|
|
json_ok("init", 0);
|
|
return 0;
|
|
}
|
|
|
|
static int do_connect(const char *uid) {
|
|
if (!g_initialized) {
|
|
json_err("not initialized");
|
|
return -1;
|
|
}
|
|
int sid = IOTC_Get_SessionID();
|
|
if (sid < 0) {
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "IOTC_Get_SessionID failed: %d", sid);
|
|
json_err(buf);
|
|
return sid;
|
|
}
|
|
g_sid = IOTC_Connect_ByUID_Parallel(uid, sid);
|
|
if (g_sid < 0) {
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "connect failed: %d", g_sid);
|
|
json_err(buf);
|
|
return g_sid;
|
|
}
|
|
json_ok("sid", g_sid);
|
|
return g_sid;
|
|
}
|
|
|
|
static int do_login(const char *user, const char *pass) {
|
|
if (g_sid < 0) {
|
|
json_err("not connected");
|
|
return -1;
|
|
}
|
|
unsigned int srvtype = 0;
|
|
g_av_index = avClientStart(g_sid, user, pass, 20, &srvtype, 0);
|
|
if (g_av_index < 0) {
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "avClientStart failed: %d", g_av_index);
|
|
json_err(buf);
|
|
return g_av_index;
|
|
}
|
|
json_ok("av_index", g_av_index);
|
|
return g_av_index;
|
|
}
|
|
|
|
static int do_ioctrl(int cmd_type, const char *hex_data) {
|
|
if (g_av_index < 0) {
|
|
json_err("not logged in");
|
|
return -1;
|
|
}
|
|
|
|
/* Parse hex data */
|
|
int hex_len = strlen(hex_data);
|
|
int data_len = hex_len / 2;
|
|
unsigned char *data = calloc(1, data_len + 1);
|
|
for (int i = 0; i < data_len; i++) {
|
|
unsigned int byte;
|
|
sscanf(hex_data + i * 2, "%2x", &byte);
|
|
data[i] = (unsigned char)byte;
|
|
}
|
|
|
|
int ret = avSendIOCtrl(g_av_index, cmd_type, (const char *)data, data_len);
|
|
free(data);
|
|
|
|
if (ret < 0) {
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "avSendIOCtrl failed: %d", ret);
|
|
json_err(buf);
|
|
return ret;
|
|
}
|
|
|
|
/* Try to receive response */
|
|
unsigned char resp[8192];
|
|
int resp_type = 0;
|
|
int resp_len = avRecvIOCtrl(g_av_index, &resp_type, (char *)resp, sizeof(resp), 5000);
|
|
if (resp_len > 0) {
|
|
json_data("ioctrl_resp", resp, resp_len);
|
|
} else {
|
|
json_ok("sent", cmd_type);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int do_lan_search(void) {
|
|
if (!g_initialized) {
|
|
json_err("not initialized");
|
|
return -1;
|
|
}
|
|
/* Search result struct: 84 bytes per entry (UID[20] + IP[16] + ...)] */
|
|
unsigned char results[84 * 16];
|
|
memset(results, 0, sizeof(results));
|
|
int count = IOTC_Lan_Search2(results, 16, 3000);
|
|
if (count < 0) {
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "lan_search failed: %d", count);
|
|
json_err(buf);
|
|
return count;
|
|
}
|
|
printf("{\"ok\":true,\"count\":%d,\"devices\":[", count);
|
|
for (int i = 0; i < count; i++) {
|
|
unsigned char *entry = results + (i * 84);
|
|
char uid[21] = {0};
|
|
memcpy(uid, entry, 20);
|
|
if (i > 0) printf(",");
|
|
printf("\"%s\"", uid);
|
|
}
|
|
printf("]}\n");
|
|
fflush(stdout);
|
|
return count;
|
|
}
|
|
|
|
static void do_disconnect(void) {
|
|
if (g_av_index >= 0) {
|
|
avClientStop(g_av_index);
|
|
g_av_index = -1;
|
|
}
|
|
if (g_sid >= 0) {
|
|
IOTC_Session_Close(g_sid);
|
|
g_sid = -1;
|
|
}
|
|
json_ok("disconnected", 1);
|
|
}
|
|
|
|
static void do_deinit(void) {
|
|
do_disconnect();
|
|
if (g_initialized) {
|
|
avDeInitialize();
|
|
IOTC_DeInitialize();
|
|
g_initialized = 0;
|
|
}
|
|
json_ok("deinit", 1);
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
char line[8192];
|
|
|
|
fprintf(stderr, "IOTC Bridge started. Send JSON commands on stdin.\n");
|
|
|
|
while (fgets(line, sizeof(line), stdin)) {
|
|
/* Strip newline */
|
|
line[strcspn(line, "\r\n")] = 0;
|
|
if (strlen(line) == 0) continue;
|
|
|
|
/* Very simple command parsing: cmd arg1 arg2 ... */
|
|
char cmd[64] = {0};
|
|
char arg1[256] = {0};
|
|
char arg2[256] = {0};
|
|
char arg3[8192] = {0};
|
|
sscanf(line, "%63s %255s %255s %8191s", cmd, arg1, arg2, arg3);
|
|
|
|
if (strcmp(cmd, "init") == 0) {
|
|
do_init();
|
|
} else if (strcmp(cmd, "connect") == 0) {
|
|
if (strlen(arg1) == 0) {
|
|
json_err("usage: connect <UID>");
|
|
} else {
|
|
do_connect(arg1);
|
|
}
|
|
} else if (strcmp(cmd, "login") == 0) {
|
|
if (strlen(arg1) == 0 || strlen(arg2) == 0) {
|
|
json_err("usage: login <user> <pass>");
|
|
} else {
|
|
do_login(arg1, arg2);
|
|
}
|
|
} else if (strcmp(cmd, "ioctrl") == 0) {
|
|
if (strlen(arg1) == 0) {
|
|
json_err("usage: ioctrl <cmd_id> [hex_data]");
|
|
} else {
|
|
int cmd_id = atoi(arg1);
|
|
do_ioctrl(cmd_id, strlen(arg2) > 0 ? arg2 : "");
|
|
}
|
|
} else if (strcmp(cmd, "search") == 0) {
|
|
do_lan_search();
|
|
} else if (strcmp(cmd, "disconnect") == 0) {
|
|
do_disconnect();
|
|
} else if (strcmp(cmd, "quit") == 0 || strcmp(cmd, "exit") == 0) {
|
|
do_deinit();
|
|
break;
|
|
} else if (strcmp(cmd, "help") == 0) {
|
|
printf("{\"commands\":[\"init\",\"connect <UID>\",\"login <user> <pass>\",\"ioctrl <cmd_id> [hex]\",\"search\",\"disconnect\",\"quit\"]}\n");
|
|
fflush(stdout);
|
|
} else {
|
|
json_err("unknown command");
|
|
}
|
|
}
|
|
|
|
do_deinit();
|
|
return 0;
|
|
}
|