/* FpLang v0.16 (c) 2005 Kostya Kortchinsky Remotely fingerprints Windows localization. It uses the NetShareEnum RPC function (NULL session needed - or credentials of course) to fetch the shares and matches the "Remark" field of IPC$ and C$ shares to identify the language. If we managed to connect, IPC$ should be listed, but there are a few collisions on some of the strings (like Italian & Portuguese). So if C$ is listed, we will prefer this result. It was tested against Windows 2000 platforms, but works fairly well against XP and 2003 ... XP SP2 and 2003 SP1 will not allow the remote call anonymously though. */ #ifndef UNICODE #define UNICODE #endif #pragma comment(lib, "netapi32") #pragma comment(lib, "mpr") #include #include #include typedef struct { UCHAR *Remark; USHORT Size; UCHAR *Language; } SHARE_INFO; SHARE_INFO IpcInfoArray[] = { { "\x52\x00\x65\x00\x6d\x00\x6f\x00\x74\x00\x65\x00\x20\x00\x49\x00\x50\x00\x43\x00", 20, "English, Arabic, Hebrew, Japanese or Korean" }, { "\x52\x00\x65\x00\x6d\x00\x6f\x00\x74\x00\x65\x00\x2d\x00\x49\x00\x50\x00\x43\x00", 20, "German" }, { "\x49\x00\x50\x00\x43\x00\x20\x00\x64\x00\x69\x00\x73\x00\x74\x00\x61\x00\x6e\x00\x74\x00", 22, "French" }, { "\x49\x00\x50\x00\x43\x00\x20\x00\x72\x00\x65\x00\x6d\x00\x6f\x00\x74\x00\x61\x00", 20, "Spanish" }, { "\x23\x04\x34\x04\x30\x04\x3b\x04\x35\x04\x3d\x04\x3d\x04\x4b\x04\x39\x04\x20\x00\x49\x00\x50\x00\x43\x00", 26, "Russian" }, { "\x49\x00\x50\x00\x43\x00\x20\x00\x72\x00\x65\x00\x6d\x00\x6f\x00\x74\x00\x6f\x00", 20, "Italian, Portuguese or Brazilian" }, { "\x5a\x00\x64\x00\x61\x00\x6c\x00\x6e\x00\x65\x00\x20\x00\x49\x00\x50\x00\x43\x00", 20, "Polish" }, { "\x46\x00\x6a\x00\xe4\x00\x72\x00\x72\x00\x2d\x00\x49\x00\x50\x00\x43\x00", 18, "Swedish" }, { "\x45\x00\x78\x00\x74\x00\x65\x00\x72\x00\x6e\x00\x65\x00\x20\x00\x49\x00\x50\x00\x43\x00", 22, "Dutch" }, { "\x45\x00\x6b\x00\x73\x00\x74\x00\x65\x00\x72\x00\x6e\x00\x20\x00\x49\x00\x50\x00\x43\x00", 22, "Norvegian" }, { "\x54\x00\xe1\x00\x76\x00\x6f\x00\x6c\x00\x69\x00\x20\x00\x49\x00\x50\x00\x43\x00", 20, "Hungarian" }, { "\x46\x00\x6a\x00\x65\x00\x72\x00\x6e\x00\x2d\x00\x49\x00\x50\x00\x43\x00", 18, "Danish" }, { "\x45\x00\x74\x00\xe4\x00\x2d\x00\x49\x00\x50\x00\x43\x00", 14, "Finnish" }, { "\xdc\x8f\x0b\x7a\x20\x00\x49\x00\x50\x00\x43\x00", 12, "Simplified Chinese" }, { "\x60\x90\xef\x7a\x20\x00\x49\x00\x50\x00\x43\x00", 12, "Traditional Chinese" }, { "\x91\x03\xc0\x03\xbf\x03\xbc\x03\xb1\x03\xba\x03\xc1\x03\xc5\x03\xc3\x03\xbc\x03\xad\x03\xbd\x03\xbf\x03\x20\x00\x49\x00\x50\x00\x43\x00", 34, "Greek" }, { "\x55\x00\x7a\x00\x61\x00\x6b\x00\x20\x00\x49\x00\x50\x00\x43\x00", 16, "Turkish" }, { "\x56\x00\x7a\x00\x64\x00\xe1\x00\x6c\x00\x65\x00\x6e\x00\xfd\x00\x20\x00\x49\x00\x50\x00\x43\x00", 24, "Czech" } }; SHARE_INFO DiskInfoArray[] = { { "\x44\x00\x65\x00\x66\x00\x61\x00\x75\x00\x6c\x00\x74\x00\x20\x00\x73\x00\x68\x00\x61\x00\x72\x00\x65\x00", 26, "English, Arabic, Hebrew, Japanese or Korean" }, { "\x53\x00\x74\x00\x61\x00\x6e\x00\x64\x00\x61\x00\x72\x00\x64\x00\x66\x00\x72\x00\x65\x00\x69\x00\x67\x00\x61\x00\x62\x00\x65\x00", 32, "German" }, { "\x50\x00\x61\x00\x72\x00\x74\x00\x61\x00\x67\x00\x65\x00\x20\x00\x70\x00\x61\x00\x72\x00\x20\x00\x64\x00\xe9\x00\x66\x00\x61\x00\x75\x00\x74\x00", 36, "French" }, { "\x52\x00\x65\x00\x63\x00\x75\x00\x72\x00\x73\x00\x6f\x00\x20\x00\x70\x00\x72\x00\x65\x00\x64\x00\x65\x00\x74\x00\x65\x00\x72\x00\x6d\x00\x69\x00\x6e\x00\x61\x00\x64\x00\x6f\x00", 44, "Spanish" }, { "\x21\x04\x42\x04\x30\x04\x3d\x04\x34\x04\x30\x04\x40\x04\x42\x04\x3d\x04\x4b\x04\x39\x04\x20\x00\x3e\x04\x31\x04\x49\x04\x38\x04\x39\x04\x20\x00\x40\x04\x35\x04\x41\x04\x43\x04\x40\x04\x41\x04", 48, "Russian" }, { "\x43\x00\x6f\x00\x6e\x00\x64\x00\x69\x00\x76\x00\x69\x00\x73\x00\x69\x00\x6f\x00\x6e\x00\x65\x00\x20\x00\x70\x00\x72\x00\x65\x00\x64\x00\x65\x00\x66\x00\x69\x00\x6e\x00\x69\x00\x74\x00\x61\x00", 48, "Italian" }, { "\x50\x00\x61\x00\x72\x00\x74\x00\x69\x00\x6c\x00\x68\x00\x61\x00\x20\x00\x70\x00\x72\x00\x65\x00\x64\x00\x65\x00\x66\x00\x69\x00\x6e\x00\x69\x00\x64\x00\x61\x00", 40, "Portuguese" }, { "\x44\x00\x6f\x00\x6d\x00\x79\x00\x5b\x01\x6c\x00\x6e\x00\x79\x00\x20\x00\x75\x00\x64\x00\x7a\x00\x69\x00\x61\x00\x42\x01", 30, "Polish" }, { "\x53\x00\x74\x00\x61\x00\x6e\x00\x64\x00\x61\x00\x72\x00\x64\x00\x72\x00\x65\x00\x73\x00\x75\x00\x72\x00\x73\x00", 28, "Swedish" }, { "\x53\x00\x74\x00\x61\x00\x6e\x00\x64\x00\x61\x00\x61\x00\x72\x00\x64\x00\x73\x00\x68\x00\x61\x00\x72\x00\x65\x00", 28, "Dutch" }, { "\x44\x00\x65\x00\x6c\x00\x74\x00\x20\x00\x73\x00\x74\x00\x61\x00\x6e\x00\x64\x00\x61\x00\x72\x00\x64\x00\x72\x00\x65\x00\x73\x00\x73\x00\x75\x00\x72\x00\x73\x00", 40, "Norvegian" }, { "\x41\x00\x6c\x00\x61\x00\x70\x00\xe9\x00\x72\x00\x74\x00\x65\x00\x6c\x00\x6d\x00\x65\x00\x7a\x00\x65\x00\x74\x00\x74\x00\x20\x00\x6d\x00\x65\x00\x67\x00\x6f\x00\x73\x00\x7a\x00\x74\x00\xe1\x00\x73\x00", 50, "Hungarian" }, { "\x53\x00\x74\x00\x61\x00\x6e\x00\x64\x00\x61\x00\x72\x00\x64\x00\x73\x00\x68\x00\x61\x00\x72\x00\x65\x00", 26, "Danish" }, { "\x4f\x00\x6c\x00\x65\x00\x74\x00\x75\x00\x73\x00\x72\x00\x65\x00\x73\x00\x75\x00\x72\x00\x73\x00\x73\x00\x69\x00\x20\x00\x28\x00\x6a\x00\x61\x00\x65\x00\x74\x00\x74\x00\x75\x00\x29\x00", 46, "Finnish" }, { "\xd8\x9e\xa4\x8b\x71\x51\xab\x4e", 8, "Simplified Chinese" }, { "\x10\x98\x2d\x8a\x71\x51\x28\x75", 8, "Traditional Chinese" }, { "\xa0\x03\xc1\x03\xbf\x03\xb5\x03\xc0\x03\xb9\x03\xbb\x03\xb5\x03\xb3\x03\xbc\x03\xad\x03\xbd\x03\xb7\x03\x20\x00\xba\x03\xbf\x03\xb9\x03\xbd\x03\xae\x03\x20\x00\xc7\x03\xc1\x03\xae\x03\xc3\x03\xb7\x03", 50, "Greek" }, { "\x56\x00\x61\x00\x72\x00\x73\x00\x61\x00\x79\x00\x31\x01\x6c\x00\x61\x00\x6e\x00\x20\x00\x64\x00\x65\x00\x1f\x01\x65\x00\x72\x00", 32, "Turkish" }, { "\x56\x00\xfd\x00\x63\x00\x68\x00\x6f\x00\x7a\x00\xed\x00\x20\x00\x73\x00\x64\x00\xed\x00\x6c\x00\x65\x00\x6e\x00\xe1\x00\x20\x00\x70\x00\x6f\x00\x6c\x00\x6f\x00\x7e\x01\x6b\x00\x61\x00", 46, "Czech" }, { "\x52\x00\x65\x00\x63\x00\x75\x00\x72\x00\x73\x00\x6f\x00\x20\x00\x63\x00\x6f\x00\x6d\x00\x70\x00\x61\x00\x72\x00\x74\x00\x69\x00\x6c\x00\x68\x00\x61\x00\x64\x00\x6f\x00\x20\x00\x70\x00\x61\x00\x64\x00\x72\x00\xe3\x00\x6f\x00", 56, "Brazilian" } }; int wmain(int argc, TCHAR *argv[]) { PSHARE_INFO_1 pShareInfoBuf, pTmpBuf; NET_API_STATUS nStatus; LPTSTR pszServerName = NULL; DWORD i, j, dwEntriesRead = 0, dwTotalEntries = 0, dwResumeHandle = 0, dwError = 0; TCHAR pszRemoteName[MAX_PATH]; UCHAR pszLanguage[64]; NETRESOURCE NetResource; memset(pszLanguage, 0, sizeof(pszLanguage)); memset(pszRemoteName, 0, sizeof(pszRemoteName)); strncpy(pszLanguage, "Unknown", sizeof(pszLanguage) - 1); if (argc != 2) { fwprintf(stderr, L"[?] Usage: %s \\\\ServerName\n", argv[0]); return 1; } pszServerName = argv[1]; if (pszServerName[0] != '\\' || pszServerName[1] != '\\') { fprintf(stderr, "[-] Invalid ServerName (must start with \\\\)\n"); return 1; } _snwprintf(pszRemoteName, sizeof(pszRemoteName) - 1, L"%s\\IPC$", pszServerName); memset(&NetResource, 0, sizeof(NetResource)); NetResource.lpLocalName = NULL; NetResource.lpProvider = NULL; NetResource.dwType = RESOURCETYPE_ANY; NetResource.lpRemoteName = pszRemoteName; // Establish a NULL session if ((dwError = WNetAddConnection2(&NetResource, L"", L"", 0)) != NO_ERROR) { fprintf(stderr, "[-] WNetAddConnection2() failed (%d)\n", dwError); return 1; } fprintf(stderr, "[+] WNetAddConnection2() succeeded\n"); // Enumerate the shares do { nStatus = NetShareEnum(pszServerName, 1, (LPBYTE *)&pShareInfoBuf, MAX_PREFERRED_LENGTH, &dwEntriesRead, &dwTotalEntries, &dwResumeHandle); if (nStatus == ERROR_SUCCESS || nStatus == ERROR_MORE_DATA) { fprintf(stderr, "[+] NetShareEnum() succeeded\n"); // Let's first look for the IPC$ share ... pTmpBuf = pShareInfoBuf; for(i = 0; i < dwEntriesRead; i++) { if (wcscmp((LPTSTR)(pTmpBuf->shi1_netname), L"IPC$") == 0) { for (j = 0; j < (sizeof(IpcInfoArray) / sizeof(IpcInfoArray[0])); j++) if ((wcslen((LPTSTR)pTmpBuf->shi1_remark) << 1) >= IpcInfoArray[j].Size && memcmp(pTmpBuf->shi1_remark, IpcInfoArray[j].Remark, IpcInfoArray[j].Size) == 0) { strncpy(pszLanguage, IpcInfoArray[j].Language, sizeof(pszLanguage) - 1); break; } if (j == (sizeof(IpcInfoArray) / sizeof(IpcInfoArray[0]))) { printf("{ \""); for (j = 0; j < wcslen((LPTSTR)pTmpBuf->shi1_remark); j++) printf("\\x%02x\\x%02x", (UCHAR)(((LPTSTR)(pTmpBuf->shi1_remark))[j] & 0xff), (UCHAR)(((LPTSTR)(pTmpBuf->shi1_remark))[j] >> 8)); printf("\", %d, \"Unknown\" },\n", j << 1); wprintf(L"%s\n", (LPTSTR)pTmpBuf->shi1_remark); } break; } pTmpBuf++; } if (i == dwEntriesRead) printf("[!] No IPC$ share\n"); // How would that be possible ? else printf("[!] 1st guess : %s\n", pszLanguage); strncpy(pszLanguage, "Unknown", sizeof(pszLanguage) - 1); // Now the C$ share ... pTmpBuf = pShareInfoBuf; for(i = 0; i < dwEntriesRead; i++) { if (wcscmp((LPTSTR)(pTmpBuf->shi1_netname), L"C$") == 0) { for (j = 0; j < (sizeof(DiskInfoArray) / sizeof(DiskInfoArray[0])); j++) if ((wcslen((LPTSTR)pTmpBuf->shi1_remark) << 1) >= DiskInfoArray[j].Size && memcmp(pTmpBuf->shi1_remark, DiskInfoArray[j].Remark, DiskInfoArray[j].Size) == 0) { strncpy(pszLanguage, DiskInfoArray[j].Language, sizeof(pszLanguage) - 1); break; } if (j == (sizeof(DiskInfoArray) / sizeof(DiskInfoArray[0]))) { printf("{ \""); for (j = 0; j < wcslen((LPTSTR)pTmpBuf->shi1_remark); j++) printf("\\x%02x\\x%02x", (UCHAR)(((LPTSTR)(pTmpBuf->shi1_remark))[j] & 0xff), (UCHAR)(((LPTSTR)(pTmpBuf->shi1_remark))[j] >> 8)); printf("\", %d, \"Unknown\" },\n", j << 1); wprintf(L"%s\n", (LPTSTR)pTmpBuf->shi1_remark); } break; } pTmpBuf++; } if (i == dwEntriesRead) printf("[!] No C$ share\n"); else printf("[!] 2nd guess : %s\n", pszLanguage); NetApiBufferFree(pShareInfoBuf); } else fprintf(stderr, "[-] NetShareEnum() failed (%d)\n", nStatus); } while (nStatus == ERROR_MORE_DATA); // Cancel the NULL session WNetCancelConnection2(pszRemoteName, 0, TRUE); return 0; }