Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions Windows/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,18 +39,22 @@ Universal proxy client for Windows applications - route any application through
2. Run the installer with Administrator privileges
3. The installer will:
- Install ProxyBridge to `C:\Program Files\ProxyBridge`
- Install and start the `ProxyBridgeSvc` Windows service as `LocalSystem`
- Authorize the installing user to control the engine through the service
- Add the install directory to your system PATH (GUI, CLI, and DLL all accessible)
- Create Start Menu shortcuts for the GUI application
- Include all required dependencies (WinDivert driver)

The installer needs Administrator privileges to register the service and WinDivert driver. After installation, the GUI can be launched normally without elevation; it controls the engine over the service's authenticated named pipes.




## Usage

### GUI Application

Launch `ProxyBridge.exe` with Administrator privileges for an intuitive graphical interface.
Launch `ProxyBridge.exe` normally for the graphical interface. The GUI uses `ProxyBridgeSvc` for packet capture, so it does not need Administrator privileges after installation. The standalone CLI and portable fallback still need elevation to start WinDivert directly.

#### Proxy Settings

Expand Down Expand Up @@ -401,7 +405,7 @@ Case 2: Packet match with proxy rule
### Requirements

- Windows 7 or later (64-bit)
- Administrator privileges (required for WinDivert driver)
- Administrator privileges for the one-time service/WinDivert installation; direct engine or CLI runs also need elevation
- Visual Studio 2019 or later (MSVC - recommended) **or** MinGW-w64 (GCC)
- WinDivert 2.2.2-A or later

Expand All @@ -425,9 +429,11 @@ The script automatically:
# From a Visual Studio Developer Command Prompt
cl /O2 /GL /W4 /D_WIN32_WINNT=0x0601 /DNDEBUG /GS /guard:cf ^
/I"path\to\windivert\include" ^
ProxyBridge.c /LD ^
src\ProxyBridge.c src\pb_util.c src\pb_process.c src\pb_rules.c src\pb_proxy.c ^
src\pb_dns.c src\pb_dns_etw.c src\pb_socks5.c src\pb_http.c src\pb_conntrack.c ^
src\pb_relay.c src\pb_sockmap.c src\pb_sni.c /LD ^
/link /LTCG /OPT:REF /RELEASE /DYNAMICBASE /NXCOMPAT ^
WinDivert.lib ws2_32.lib iphlpapi.lib ^
WinDivert.lib ws2_32.lib iphlpapi.lib tdh.lib shell32.lib advapi32.lib ^
/OUT:ProxyBridgeCore.dll
```

Expand All @@ -436,8 +442,10 @@ cl /O2 /GL /W4 /D_WIN32_WINNT=0x0601 /DNDEBUG /GS /guard:cf ^
```powershell
gcc -O2 -Wall -D_WIN32_WINNT=0x0601 -shared ^
-I"C:\WinDivert-2.2.2-A\include" ^
ProxyBridge.c ^
-L"C:\WinDivert-2.2.2-A\x64" -lWinDivert -lws2_32 -liphlpapi ^
src\ProxyBridge.c src\pb_util.c src\pb_process.c src\pb_rules.c src\pb_proxy.c ^
src\pb_dns.c src\pb_dns_etw.c src\pb_socks5.c src\pb_http.c src\pb_conntrack.c ^
src\pb_relay.c src\pb_sockmap.c src\pb_sni.c ^
-L"C:\WinDivert-2.2.2-A\x64" -lWinDivert -lws2_32 -liphlpapi -ltdh -lshell32 -ladvapi32 ^
-o ProxyBridgeCore.dll
```

Expand Down
6 changes: 3 additions & 3 deletions Windows/compile.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ param(
$WinDivertPath = "C:\WinDivert-2.2.2-A"
$SourcePath = "src"
# Core split across modular translation units (see src\pb_internal.h).
$SourceFile = "ProxyBridge.c pb_util.c pb_process.c pb_rules.c pb_proxy.c pb_dns.c pb_socks5.c pb_http.c pb_conntrack.c pb_relay.c pb_sockmap.c"
$SourceFile = "ProxyBridge.c pb_util.c pb_process.c pb_rules.c pb_proxy.c pb_dns.c pb_dns_etw.c pb_socks5.c pb_http.c pb_conntrack.c pb_relay.c pb_sockmap.c pb_sni.c"
$SourceFiles = ($SourceFile.Split(' ') | ForEach-Object { "$SourcePath\$_" }) -join ' '
$OutputDLL = "ProxyBridgeCore.dll"
$OutputDir = "output"
Expand Down Expand Up @@ -69,7 +69,7 @@ function Compile-MSVC {
"/LD " +
"/link /LTCG /OPT:REF /OPT:ICF /RELEASE /DYNAMICBASE /NXCOMPAT " +
"/LIBPATH:`"$WinDivertPath\$Arch`" " +
"WinDivert.lib ws2_32.lib iphlpapi.lib " +
"WinDivert.lib ws2_32.lib iphlpapi.lib tdh.lib shell32.lib advapi32.lib " +
"/OUT:$OutputDLL"

$cmd = "`"$vcvarsPath`" $Arch >nul && cl.exe $clArgs"
Expand Down Expand Up @@ -99,7 +99,7 @@ function Compile-GCC {
"-I`"$WinDivertPath\include`" " +
"$SourcePath\$SourceFile " +
"-L`"$WinDivertPath\$Arch`" " +
"-lWinDivert -lws2_32 -liphlpapi " +
"-lWinDivert -lws2_32 -liphlpapi -ltdh -lshell32 -ladvapi32 " +
"-o $OutputDLL"

Write-Host "Command: $cmd" -ForegroundColor Gray
Expand Down
6 changes: 6 additions & 0 deletions Windows/gui/api/pb_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ typedef void (*PBLogCallback)(const char* message);
typedef void (*PBConnectionCallback)(const char* process_name, DWORD pid,
const char* dest_ip, unsigned short dest_port,
const char* proxy_info);
typedef void (*PBConnectionCallbackEx)(const char* process_name, DWORD pid,
const char* dest_ip, const char* hostname,
unsigned short dest_port, const char* proxy_info);

// exported function pointer typedefs
typedef UINT32 (*PFN_AddProxyConfig)(PBProxyType, const char*, unsigned short, const char*, const char*, BOOL);
Expand All @@ -36,6 +39,7 @@ typedef UINT32 (*PFN_GetRulePosition)(UINT32);
typedef void (*PFN_SetLocalhostViaProxy)(BOOL);
typedef void (*PFN_SetLogCallback)(PBLogCallback);
typedef void (*PFN_SetConnectionCallback)(PBConnectionCallback);
typedef void (*PFN_SetConnectionCallbackEx)(PBConnectionCallbackEx);
typedef void (*PFN_SetTrafficLoggingEnabled)(BOOL);
typedef void (*PFN_ClearConnectionLogs)(void);
typedef BOOL (*PFN_Start)(void);
Expand All @@ -59,6 +63,7 @@ typedef struct {
PFN_SetLocalhostViaProxy SetLocalhostViaProxy;
PFN_SetLogCallback SetLogCallback;
PFN_SetConnectionCallback SetConnectionCallback;
PFN_SetConnectionCallbackEx SetConnectionCallbackEx;
PFN_SetTrafficLoggingEnabled SetTrafficLoggingEnabled;
PFN_ClearConnectionLogs ClearConnectionLogs;
PFN_Start Start;
Expand Down Expand Up @@ -105,6 +110,7 @@ static BOOL PB_LoadDirect(PBApi* api)
PB_BIND(SetLocalhostViaProxy, "SetLocalhostViaProxy");
PB_BIND(SetLogCallback, "SetLogCallback");
PB_BIND(SetConnectionCallback, "SetConnectionCallback");
api->SetConnectionCallbackEx = (PFN_SetConnectionCallbackEx)(void*)GetProcAddress(api->dll, "ProxyBridge_SetConnectionCallbackEx");
PB_BIND(SetTrafficLoggingEnabled, "SetTrafficLoggingEnabled");
PB_BIND(ClearConnectionLogs, "ClearConnectionLogs");
PB_BIND(Start, "Start");
Expand Down
19 changes: 15 additions & 4 deletions Windows/gui/api/pb_ipc.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ static HANDLE g_ipcTestDone = NULL;
static volatile LONG g_ipcTestResult = -1;
static PBLogCallback g_ipcLogCb = NULL;
static PBConnectionCallback g_ipcConnCb = NULL;
static PBConnectionCallbackEx g_ipcConnCbEx = NULL;
static DWORD g_ipcProtoVersion = 1;
static PBTestLogCallback g_ipcTestCb = NULL;
static void* g_ipcTestUser = NULL;

Expand All @@ -39,7 +41,7 @@ static DWORD WINAPI PbIpcReader(LPVOID arg)
if (!ReadFile(g_ipcEvt, buf, sizeof(buf), &n, NULL)) break;
PbR r; PbR_Init(&r, buf, n);
UINT32 type = PbR_U32(&r);
char a[8192], b[8192], c[8192];
char a[8192], b[8192], c[8192], d[8192] = {0};
if (type == PBEVT_LOG)
{
PbR_Str(&r, a, sizeof(a), NULL);
Expand All @@ -50,7 +52,12 @@ static DWORD WINAPI PbIpcReader(LPVOID arg)
PbR_Str(&r, a, sizeof(a), NULL); UINT32 pid = PbR_U32(&r);
PbR_Str(&r, b, sizeof(b), NULL); UINT32 port = PbR_U32(&r);
PbR_Str(&r, c, sizeof(c), NULL);
if (r.ok && g_ipcConnCb) g_ipcConnCb(a, pid, b, (unsigned short)port, c);
if (r.ok && g_ipcProtoVersion >= 2 && r.pos < r.len)
PbR_Str(&r, d, sizeof(d), NULL);
if (r.ok && g_ipcConnCbEx)
g_ipcConnCbEx(a, pid, b, d, (unsigned short)port, c);
else if (r.ok && g_ipcConnCb)
g_ipcConnCb(a, pid, b, (unsigned short)port, c);
}
else if (type == PBEVT_TEST_LINE)
{
Expand Down Expand Up @@ -131,6 +138,7 @@ static void IpcSetTrafficLog(BOOL v) { PBIPC_BEGIN(PBOP_SET_TRAFFIC_LOG); PbW_U
static void IpcClearConnLogs(void) { PBIPC_BEGIN(PBOP_CLEAR_CONN_LOGS); PbIpcCall(_b, _w.len); }
static void IpcSetLogCallback(PBLogCallback cb) { g_ipcLogCb = cb; }
static void IpcSetConnectionCallback(PBConnectionCallback cb) { g_ipcConnCb = cb; }
static void IpcSetConnectionCallbackEx(PBConnectionCallbackEx cb) { g_ipcConnCbEx = cb; }
static BOOL IpcStart(void) { PBIPC_BEGIN(PBOP_START); PBIPC_END(); }
static BOOL IpcStop(void) { PBIPC_BEGIN(PBOP_STOP); PBIPC_END(); }

Expand Down Expand Up @@ -199,13 +207,15 @@ static int PB_LoadService(PBApi* api)

g_ipcCtl = ctl; g_ipcEvt = evt;
{
PBIPC_BEGIN(PBOP_HELLO);
if (PbIpcCall(_b, _w.len) != PB_PROTO_VERSION)
PBIPC_BEGIN(PBOP_HELLO); PbW_U32(&_w, PB_PROTO_VERSION);
UINT32 selected = PbIpcCall(_b, _w.len);
if (selected == 0 || selected > PB_PROTO_VERSION)
{
CloseHandle(g_ipcCtl); g_ipcCtl = INVALID_HANDLE_VALUE;
CloseHandle(g_ipcEvt); g_ipcEvt = INVALID_HANDLE_VALUE;
return PB_IPC_BAD_VERSION;
}
g_ipcProtoVersion = selected;
}
g_ipcReader = CreateThread(NULL, 0, PbIpcReader, NULL, 0, NULL);

Expand All @@ -218,6 +228,7 @@ static int PB_LoadService(PBApi* api)
api->MoveRuleToPosition = IpcMoveRule; api->GetRulePosition = IpcGetRulePos;
api->SetLocalhostViaProxy = IpcSetLocalhost; api->SetLogCallback = IpcSetLogCallback;
api->SetConnectionCallback = IpcSetConnectionCallback;
api->SetConnectionCallbackEx = IpcSetConnectionCallbackEx;
api->SetTrafficLoggingEnabled = IpcSetTrafficLog;
api->ClearConnectionLogs = IpcClearConnLogs; api->Start = IpcStart; api->Stop = IpcStop;
return PB_IPC_OK;
Expand Down
34 changes: 27 additions & 7 deletions Windows/gui/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -194,32 +194,49 @@ static void PBLogCb(const char* message)
free(body);
}

static void PBConnCb(const char* proc, DWORD pid, const char* ip, unsigned short port, const char* info)
static void PBConnCbEx(const char* proc, DWORD pid, const char* ip, const char* hostname,
unsigned short port, const char* info)
{
if (!g_hMain || !g_trafficLog) return;
wchar_t *wp = U2Wdup(proc), *wi = U2Wdup(ip), *wf = U2Wdup(info);
const char *display = hostname && hostname[0] ? hostname : ip;
wchar_t *wp = U2Wdup(proc), *wi = U2Wdup(display), *wf = U2Wdup(info);
// Connection-log filters: derive protocol/action from the proxy_info and drop non-matches.
if (g_fltCount > 0)
{
wchar_t wport[16]; _snwprintf_s(wport, 16, _TRUNCATE, L"%u", port);
wchar_t destination[640];
wchar_t *wip = U2Wdup(ip);
_snwprintf_s(destination, 640, _TRUNCATE, L"%s %s",
wi ? wi : L"", wip ? wip : L"");
const wchar_t* proto = (info && strstr(info, "(UDP)")) ? L"UDP" : L"TCP";
const wchar_t* action = (info && _strnicmp(info, "Direct", 6) == 0) ? L"Direct"
: (info && _strnicmp(info, "Block", 5) == 0) ? L"Blocked" : L"Proxy";
if (!PassesLogFilters(wp ? wp : L"", wi ? wi : L"", wport, proto, action))
{ free(wp); free(wi); free(wf); return; }
BOOL pass = PassesLogFilters(wp ? wp : L"", destination, wport, proto, action);
free(wip);
if (!pass) { free(wp); free(wi); free(wf); return; }
}
wchar_t ts[16]; GetTimePrefix(ts, 16);
size_t n = 64 + (wp ? wcslen(wp) : 0) + (wi ? wcslen(wi) : 0) + (wf ? wcslen(wf) : 0);
wchar_t* line = (wchar_t*)malloc(n * sizeof(wchar_t));
if (line)
{
_snwprintf_s(line, n, _TRUNCATE, L"%s%s (PID:%lu) -> %s:%u via %s\r\n",
ts, wp ? wp : L"?", pid, wi ? wi : L"?", port, wf ? wf : L"?");
if (display && strchr(display, ':'))
_snwprintf_s(line, n, _TRUNCATE, L"%s%s (PID:%lu) -> [%s]:%u via %s\r\n",
ts, wp ? wp : L"?", pid, wi ? wi : L"?", port, wf ? wf : L"?");
else
_snwprintf_s(line, n, _TRUNCATE, L"%s%s (PID:%lu) -> %s:%u via %s\r\n",
ts, wp ? wp : L"?", pid, wi ? wi : L"?", port, wf ? wf : L"?");
LogStoreQueue(&g_connStore, line);
}
free(wp); free(wi); free(wf);
}

static void PBConnCb(const char* proc, DWORD pid, const char* ip,
unsigned short port, const char* info)
{
PBConnCbEx(proc, pid, ip, NULL, port, info);
}

// profile apply / persist
static void SaveActive(void) { PB_ProfileSave(g_activeProfile, &g_profile); }

Expand Down Expand Up @@ -1171,7 +1188,10 @@ int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE hPrev, PWSTR cmd, int show)
g_api.SetLocalhostViaProxy(g_localhost);
g_api.SetTrafficLoggingEnabled(g_trafficLog);
g_api.SetLogCallback(PBLogCb);
g_api.SetConnectionCallback(PBConnCb);
if (g_api.SetConnectionCallbackEx)
g_api.SetConnectionCallbackEx(PBConnCbEx);
else
g_api.SetConnectionCallback(PBConnCb);
ApplyConfigs();
g_started = g_api.Start();
if (g_started) { ApplyRules(); LogStoreAdd(&g_actStore, T(S_STARTED)); }
Expand Down
2 changes: 1 addition & 1 deletion Windows/gui/res/app.rc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// Application icon (reuse the shared logo)
IDI_APPICON ICON "logo.ico"

// Embedded manifest (admin + Common Controls v6 + DPI)
// Embedded manifest (asInvoker + Common Controls v6 + DPI)
1 24 "app.manifest"

// Version info (exe Properties → Details); numbers come from resource.h
Expand Down
Loading