Update README.md
[bindip.git] / bindip.c
blobc462919c48b6c263b875893e465105ee7843804a
1 #include "common.h"
2 #include "resource.h"
4 static HKEY registry;
5 static IP_ADAPTER_INFO adapters[256];
6 #if 0
7 static BOOL (*wow64_disable)(void **old);
8 static BOOL (*wow64_enable)(void *old);
9 #endif
11 __declspec(dllimport) BOOL wow64_disable(void **old);
12 __declspec(dllimport) BOOL wow64_enable(void *old);
14 // add unique list item
15 static void adduniq(HWND list, char *s)
17 // dont add duplicate names and empty strings
18 if (!s[0]) return;
19 if (SendMessage(list, LB_FINDSTRINGEXACT, 0, (LPARAM)s)==LB_ERR)
20 SendMessage(list, LB_ADDSTRING, 0, (LPARAM)s);
23 // enumerate running processes and add to the list
24 static void enumproc(HWND list)
26 int i,t;
27 char subname[MAX_PATH];
28 DWORD subsize = sizeof(subname);
29 PROCESSENTRY32 lpe;
30 lpe.dwSize = sizeof(lpe);
31 for (i=0;(t=RegEnumValue(registry, i, subname, &subsize, NULL, NULL, NULL, NULL))==ERROR_SUCCESS;i++,subsize=sizeof(subname))
32 adduniq(list, subname);
33 HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
34 BOOL entry = Process32First(snap, &lpe);
35 while (entry) {
36 if (PathMatchSpec(lpe.szExeFile, "*.exe"))
37 adduniq(list, lpe.szExeFile);
38 lpe.dwSize = sizeof(lpe);
39 entry = Process32Next(snap, &lpe);
41 CloseHandle(snap);
44 // add network interfacess to list
45 static void enumintf(HWND list)
47 IP_ADAPTER_INFO *ada;
48 adduniq(list, "(default route)");
49 for (ada = adapters; ada; ada = ada->Next)
50 adduniq(list, ada->Description);
53 // react to user clicks in exe/intf list boxes
54 // il=1 if clicked on interface list
55 static void process_selections(HWND parent, HWND exe, HWND intf, int il)
57 static int sels[16384];
58 static TCHAR buf[512], buf2[512];
59 IP_ADAPTER_INFO *ada, *curada = NULL;
60 long nsel;
61 int i, j, curif = -1;
62 // Clicked on exelist, go through selected exes
63 nsel = SendMessage(exe, LB_GETSELITEMS, 16384, (LPARAM)sels);
64 if (!il) for (i = 0; i < nsel; i++) {
65 DWORD len = sizeof(buf2);
66 SendMessage(exe, LB_GETTEXT, sels[i], (LPARAM)buf);
67 if (RegQueryValueEx(registry, buf,NULL,NULL, (LPBYTE)buf2, &len)!=ERROR_SUCCESS) {
68 // undefined key -> no adapter, default route, shortcut
69 curif = 0;
70 curada = NULL;
71 break;
73 // and check they have all interfaces in common
74 for (j = 1, ada = adapters; ada; ada = ada->Next, j++)
75 if (!strcmp(ada->AdapterName, buf2)) {
76 // mismatch in selected group -> reset default route
77 if (curif >= 0 && curif != j) {
78 curif = 0;
79 curada = NULL;
80 goto out;
82 // otherwise keep as current interface
83 curif = j;
84 curada = ada;
85 break;
88 out:;
89 if (!il) {
90 // clicked exe list
91 if (curif < 0) curif = 0;
92 SendMessage(intf, LB_SETCURSEL, curif, 0);
93 } else {
94 // clicked interface list
95 curif = SendMessage(intf, LB_GETCURSEL,0,0);
96 // resolve index to name
97 for (i = curif, ada = adapters; ada && i--; ada = ada->Next)
98 curada = ada;
100 // in curif we have corresponding interface index, 0 if default
101 // in curada we have interface, null if default
102 // in sels[] we have exes this aplies to
103 for (i = 0; i < nsel; i++) {
104 SendMessage(exe, LB_GETTEXT, sels[i], (LPARAM)buf);
105 if (curif > 0) { // assign new interface
106 RegSetValueExA(registry, buf, 0, REG_SZ, (LPBYTE)curada->AdapterName, strlen(curada->AdapterName)+1);
107 } else { // no interface, delete it
108 RegDeleteValue(registry, buf);
111 strcpy(buf, "Default interface");
112 strcpy(buf2, "Default source ip");
113 if (curif > 0 && curada) {
114 BYTE *mac = curada->Address;
115 char *pip = curada->IpAddressList.IpAddress.String;
116 wnsprintf(buf, sizeof(buf), "%02x-%02x-%02x-%02x-%02x-%02x",
117 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
118 strcpy(buf2, pip);
120 SendMessage(GetDlgItem(parent, IDC_MAC), WM_SETTEXT, 0, (LPARAM)buf);
121 SendMessage(GetDlgItem(parent, IDC_IP), WM_SETTEXT, 0, (LPARAM)buf2);
124 static int check_system_dlls(char *buf, int len32)
126 return !(GetFileAttributesA(buf) == INVALID_FILE_ATTRIBUTES || GetFileAttributesA(buf+len32) == INVALID_FILE_ATTRIBUTES);
129 // locate dlls in system directory
130 static int get_system_dlls(char *buf)
132 int len32;
133 void *sav=NULL;
134 wow64_disable(&sav);
135 if (!SHGetSpecialFolderPathA(NULL, buf, CSIDL_SYSTEMX86, 0))
136 SHGetSpecialFolderPathA(NULL, buf, CSIDL_SYSTEM, 0);
137 strcat(buf, "\\bindip.dll");
138 len32 = strlen(buf)+1;
139 SHGetSpecialFolderPathA(NULL, buf + len32, CSIDL_SYSTEM, 0);
140 strcat(buf + len32, "\\bindip.dll");
141 wow64_enable(sav);
142 return len32;
145 // locate dlls in all places
146 static int locate_dlls(char *buf, int sys)
148 char tbuf[MAX_PATH];
149 int i,len32 = 0;
150 void *sav=NULL;
151 wow64_disable(&sav);
152 if (sys) {
153 len32 = get_system_dlls(buf);
154 if (check_system_dlls(buf, len32))
155 goto out;
157 for (i = 0; i < 3; i++) {
158 switch(i) {
159 case 2:
160 ExpandEnvironmentStringsA("%PROGRAMFILES(X86)%\\BindIP", tbuf, sizeof(tbuf));
161 break;
162 case 1:
163 ExpandEnvironmentStringsA("%PROGRAMFILES%\\BindIP", tbuf, sizeof(tbuf));
164 break;
165 case 0:
166 GetModuleFileNameA(NULL, tbuf, sizeof(tbuf));
167 PathRemoveFileSpecA(tbuf);
168 break;
170 len32 = wnsprintf(buf, 2048, "%s\\bindip.dll", tbuf)+1;
171 wnsprintf(buf + len32, 2048, "%s\\64\\bindip.dll", tbuf);
172 if (GetFileAttributesA(buf) != INVALID_FILE_ATTRIBUTES || GetFileAttributesA(buf+len32) != INVALID_FILE_ATTRIBUTES)
173 goto out;
175 out:;
176 wow64_enable(sav);
177 return len32;
180 // configure global filter winsock
181 static int dll_config(HWND dlg, int enable)
183 HKEY reg;
184 char buf[MAX_PATH*2], buf2[MAX_PATH*2];
185 static char const orig[] = "%SystemRoot%\\System32\\wshtcpip.dll";
186 DWORD buf2len, buflen = sizeof buf;
187 int e;
188 void *sav=NULL;
189 if ((e=RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Winsock", 0, KEY_ALL_ACCESS, &reg))) {
190 if (dlg) {
191 Button_Enable(dlg, 0);
193 return 0;
195 wow64_disable(&sav);
196 if (RegQueryValueExA(reg, "HelperDllName", 0, NULL, (BYTE*)buf, &buflen)) {
197 enable = 0;
198 goto out;
200 if (!strcmp(buf+buflen-11, "bindip.dll")) {
201 if (enable) {
202 enable = 1;
203 goto out;
205 // request to disable
206 buflen=sizeof(orig);
207 if (RegSetValueExA(reg, "HelperDllName", 0, REG_EXPAND_SZ, (BYTE*)orig, buflen)) {
208 enable = 1; // Failed to disable..
209 } else { // Disabled, delete dlls too
210 buflen = get_system_dlls(buf);
211 DeleteFile(buf);
212 DeleteFile(buf+buflen);
214 } else if (enable == 1) {
215 enable = 0;
216 // request to enable
217 buflen = get_system_dlls(buf);
218 if (!check_system_dlls(buf, buflen)) {
219 buf2len = locate_dlls(buf2, 0);
220 CopyFile(buf2+buf2len, buf+buflen, FALSE);
221 CopyFile(buf2, buf, FALSE);
222 // Re-check that the files are ok now
223 if (!check_system_dlls(buf, buflen))
224 goto out;
226 enable = !RegSetValueExA(reg, "HelperDllName", 0, REG_EXPAND_SZ, (BYTE*)buf+buflen, strlen(buf+buflen)+1);
227 } else enable = 0; // Otherwise disabled
228 out:
229 RegCloseKey(reg);
230 wow64_enable(sav);
231 return enable;
234 // configure shell button registry
235 // -1 - just query status
236 static int shell_config(int enable)
238 HKEY reg, reg2;
239 RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Classes\\exefile\\shell", 0, KEY_ALL_ACCESS, &reg);
240 if (enable == -1) {
241 if (RegQueryValue(reg, "Open with BindIP", NULL, NULL)==ERROR_SUCCESS)
242 enable = 1;
243 else
244 enable = 0;
245 } else if (enable == 1) {
246 char myname[MAX_PATH];
247 char pbuf[MAX_PATH];
248 GetModuleFileName(NULL, myname, sizeof(myname));
249 RegCreateKeyExA(reg, "Open with BindIP\\command", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &reg2, NULL);
250 enable = !RegSetValueExA(reg2, NULL, 0, REG_SZ, (BYTE*)pbuf,wnsprintf(pbuf, sizeof(pbuf), "\"%s\" \"%%1\"", myname)+1);
251 RegCloseKey(reg2);
253 if (enable == 0)
254 SHDeleteKeyA(reg, "Open with BindIP");
255 RegCloseKey(reg);
256 return enable;
260 // dialog handler
261 static INT_PTR CALLBACK dialog_wproc(HWND dlg, UINT msg, WPARAM w, LPARAM l)
263 HWND exe = GetDlgItem(dlg, IDC_EXELIST);
264 HWND intf = GetDlgItem(dlg, IDC_INTLIST);
265 HWND appinit = GetDlgItem(dlg, IDC_APPINIT);
266 HWND shell = GetDlgItem(dlg, IDC_SHELL);
267 int il = 0;
268 switch (msg) {
269 case WM_INITDIALOG:
270 enumproc(exe);
271 enumintf(intf);
272 Button_SetCheck(shell, shell_config(-1));
273 Button_SetCheck(appinit, dll_config(appinit,-1));
274 SetFocus(exe);
275 SendMessage(exe, LB_SETSEL, TRUE, 0);
276 process_selections(dlg,exe,intf, 0);
277 return TRUE;
278 case WM_CLOSE:
279 EndDialog(dlg, 0);
280 return TRUE;
281 case WM_COMMAND: switch (LOWORD(w)) {
282 case IDC_REFRESH:
283 enumproc(exe);
284 return TRUE;
285 case IDC_DONE:
286 EndDialog(dlg, 0);
287 return TRUE;
288 case IDC_INTLIST:
289 il = 1;
290 case IDC_EXELIST:
291 if (HIWORD(w) == LBN_SELCHANGE)
292 process_selections(dlg, exe,intf,il);
293 return TRUE;
294 case IDC_ADD: {
295 char filebuf[MAX_PATH];
296 filebuf[0] = 0;
297 OPENFILENAME ofn = {
298 .lStructSize = sizeof(OPENFILENAME),
299 .hwndOwner = dlg,
300 .lpstrFile = filebuf,
301 .nMaxFile = sizeof(filebuf),
302 .lpstrFilter = "EXE\0*.exe\0COM\0*.com\0SCR\0*.scr\0",
304 if (GetOpenFileName(&ofn)) {
305 PathStripPathA(filebuf);
306 adduniq(exe, filebuf);
307 SendMessage(exe, LB_SETSEL, FALSE, (LPARAM)-1);
308 SendMessage(exe, LB_SETSEL, TRUE, SendMessage(exe, LB_FINDSTRINGEXACT, 0, (LPARAM)filebuf));
309 SetFocus(exe);
310 process_selections(dlg,exe,intf, 0);
312 return TRUE;
314 case IDC_SHELL:
315 Button_SetCheck(shell, shell_config(Button_GetCheck(shell)));
316 return TRUE;
317 case IDC_URL:
318 ShellExecute(NULL, "open", "http://lua.cz/bindip/1", NULL, NULL, SW_SHOWNORMAL);
319 return TRUE;
320 case IDC_APPINIT:
321 Button_SetCheck(appinit, dll_config(appinit, Button_GetCheck(appinit)));
322 return TRUE;
324 } // WM_COMMAND
325 } // msg
326 return FALSE;
329 __declspec(dllimport) char *inject(HANDLE hp, HANDLE ht, char *dll32, int len32);
330 __declspec(dllimport) void cpiw_init();
331 static void run_preloaded(WCHAR *cmd)
333 DWORD ecode = 0;
334 char dlls[MAX_PATH*2];
335 STARTUPINFOW si = {0};
336 PROCESS_INFORMATION pi = {0};
337 int len32 = locate_dlls(dlls, 0);
339 if (len32) {
340 // Triggers our patched CreateProcessInternal
341 dlls[len32-1] = ';';
342 SetEnvironmentVariable("BINDIP_CHAINHOOK", dlls);
343 cpiw_init(); // Patch createprocess
346 si.cb = sizeof(si);
347 // Pass console handles
348 si.dwFlags = STARTF_USESTDHANDLES;
349 si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
350 si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
351 si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
353 // Spawn
354 if (!CreateProcessW(NULL, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) {
355 MessageBoxA(NULL, serr(), "CreateProcess", MB_ICONERROR|MB_OK);
356 goto err;
359 if (WaitForInputIdle(pi.hProcess, INFINITE) == WAIT_FAILED) {
360 // Console process, setup break handler
361 WaitForSingleObject(pi.hProcess, INFINITE);
362 GetExitCodeProcess(pi.hProcess, &ecode);
363 } else {
364 // GUI app, close console
365 FreeConsole();
367 err:;
368 CloseHandle(pi.hThread);
369 CloseHandle(pi.hProcess);
370 ExitProcess(ecode);
374 void winMain()
376 // Skip over exe name
377 WCHAR *cmd = GetCommandLineW();
378 int quot = *cmd++ == L'"';
379 while (*cmd && (quot || (cmd[0]>L' ')))
380 if (*cmd++ == L'"')
381 quot ^= 1;
382 while (*cmd && *cmd<= L' ')
383 cmd++;
385 // Request to launch specified program
386 //static WCHAR fake[MAX_PATH] = L"nc.exe -h";
387 //cmd = fake;
388 if (*cmd) {
389 run_preloaded(cmd);
390 } else {
391 ULONG len = sizeof(adapters);
392 DWORD disp;
393 // Otherwise gui
394 FreeConsole();
395 RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\BindIP\\Mapping", 0, NULL, 0, KEY_ALL_ACCESS, NULL, &registry, &disp);
396 if (disp == REG_CREATED_NEW_KEY) {
397 // Run for the first time - enable shell, delete system dlls if possible
398 shell_config(1);
399 dll_config(NULL, 0);
400 } else if (shell_config(-1)) // Shell enabled, re-enable again to update exe path in registry
401 shell_config(1);
402 GetAdaptersInfo(adapters, &len);
403 // And fire up dialog
404 DialogBox(NULL, MAKEINTRESOURCE(IDD_FORMVIEW), NULL, dialog_wproc);
405 RegCloseKey(registry);
407 ExitProcess(0);