This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision Next revision Both sides next revision | ||
howto:fridahooklibrary [2017/10/23 03:52] czokie [setSSLPinningMode] |
howto:fridahooklibrary [2017/11/06 23:01] czokie Replaced old hooks with generated ones |
||
---|---|---|---|
Line 28: | Line 28: | ||
We'll dig into this more later when/if we need to access parameter data etc. | We'll dig into this more later when/if we need to access parameter data etc. | ||
- | ====== | + | ====== |
- | ===== DJITermsNotificationController - shouldShowTerms | + | As part of this work, I looked at how to " |
+ | |||
+ | Anyway. Naming conventions aside. How does it work? There is a shell script that parses a CSV file that contains configuration information for a list of hooks that will be created in the output. | ||
+ | |||
+ | ===== PrettyWoman.sh | ||
+ | <code bash PrettyWoman.sh> | ||
+ | # | ||
+ | |||
+ | function iosheader { | ||
+ | cat << | ||
+ | // | ||
+ | // DJI GO 4 - Frida Tweaks | ||
+ | // | ||
- | <code javascript DJITermsNotificationController.shouldShowTerms.js> | ||
- | 'use strict'; | ||
if (ObjC.available) { | if (ObjC.available) { | ||
- | var DJITermsNotificationController = ObjC.classes.DJITermsNotificationController; | + | EOF |
+ | } | ||
- | | + | function iosfooter { |
- | var shouldShowTermsImpl | + | cat << |
- | | + | } |
- | | + | EOF |
- | console.log(' | + | } |
- | | + | |
+ | function functionheader { | ||
+ | cat << | ||
+ | |||
+ | // | ||
+ | // ${1} | ||
+ | // | ||
+ | |||
+ | EOF | ||
+ | } | ||
+ | function jsobject { | ||
+ | |||
+ | cat << | ||
+ | | ||
+ | var ${3} = ${1}['${2} ${3}:']; | ||
+ | var ${3}Impl | ||
+ | | ||
+ | | ||
+ | | ||
+ | if ( ${7} == 0 || ${CLASS}${METHOD}SeenReplace == 0 ) { | ||
+ | | ||
+ | ${CLASS}${METHOD}SeenReplace = 1; | ||
+ | } | ||
+ | | ||
+ | if ( ${7} == 0 || ${CLASS}${METHOD}SeenHit == 0 ) { | ||
+ | console.log(" | ||
+ | ${CLASS}${METHOD}SeenHit = 1; | ||
+ | } | ||
+ | } | ||
}); | }); | ||
+ | EOF | ||
} | } | ||
- | </ | + | |
- | ===== DJIAppSettings - sdr_force_fcc ===== | + | function jsfunction { |
- | <code javascript DJIAppSettings.sdr_force_fcc.js> | + | cat <<EOF |
- | if (ObjC.available) { | + | var ${1} = ObjC.classes.${1}; |
- | var DJIAppSettings | + | var ${3} = ${1}['${2} ${3}']; |
- | | + | var ${3}Impl |
- | var sdr_force_fcc | + | |
- | var sdr_force_fccImpl | + | var originalResult = ${3}Impl(handle, selector); |
- | | + | |
- | var originalResult = sdr_force_fccImpl(handle, selector); | + | if ( ${7} == 0 || ${CLASS}${METHOD}SeenReplace == 0 ) { |
- | console.log(' | + | |
- | return | + | ${CLASS}${METHOD}SeenReplace = 1; |
+ | } | ||
+ | } else { | ||
+ | if ( ${7} == 0 || ${CLASS}${METHOD}SeenHit == 0 ) { | ||
+ | console.log(" | ||
+ | ${CLASS}${METHOD}SeenHit = 1; | ||
+ | } | ||
+ | } | ||
+ | return | ||
}); | }); | ||
+ | EOF | ||
} | } | ||
+ | |||
+ | |||
+ | function jsfunctioniffcc { | ||
+ | cat <<EOF | ||
+ | var ${1} = ObjC.classes.${1}; | ||
+ | var ${3} = ${1}[' | ||
+ | var ${3}Impl = ${3}.implementation; | ||
+ | ${3}.implementation = ObjC.implement(${3}, | ||
+ | var originalResult = ${3}Impl(handle, | ||
+ | if ( originalResult != ${4} ) { | ||
+ | if ( ${7} == 0 || ${CLASS}${METHOD}SeenReplace == 0 ) { | ||
+ | console.log(" | ||
+ | ${CLASS}${METHOD}SeenReplace = 1; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | if( DJIAppSettingssdr_force_fccSeenReplace == 0 ) { | ||
+ | return ${4}; //mavic | ||
+ | } else { | ||
+ | return originalResult; | ||
+ | } | ||
+ | }); | ||
+ | EOF | ||
+ | } | ||
+ | |||
+ | |||
+ | function defines { | ||
+ | cat <<EOF | ||
+ | // | ||
+ | // Create variables that we can use for "one shot" rules | ||
+ | // | ||
+ | EOF | ||
+ | |||
+ | while read X | ||
+ | do | ||
+ | # | ||
+ | CLASS=`echo $X | cut -d "," | ||
+ | METHOD=`echo $X | cut -d "," | ||
+ | PLUSMINUS=`echo $X | cut -d "," | ||
+ | # | ||
+ | # | ||
+ | # | ||
+ | # | ||
+ | |||
+ | echo " | ||
+ | echo " | ||
+ | |||
+ | done < PrettyWoman.csv | ||
+ | } | ||
+ | |||
+ | function mainloop { | ||
+ | while read X | ||
+ | do | ||
+ | TEMPLATE=`echo $X | cut -d "," | ||
+ | CLASS=`echo $X | cut -d "," | ||
+ | METHOD=`echo $X | cut -d "," | ||
+ | PLUSMINUS=`echo $X | cut -d "," | ||
+ | REPLACE=`echo $X | cut -d "," | ||
+ | LOGHIT=`echo $X | cut -d "," | ||
+ | LOGCHANGED=`echo $X | cut -d "," | ||
+ | ONESHOT=`echo $X | cut -d "," | ||
+ | |||
+ | functionheader " | ||
+ | ${TEMPLATE} ${CLASS} ${PLUSMINUS} ${METHOD} ${REPLACE} " | ||
+ | done < PrettyWoman.csv | ||
+ | } | ||
+ | |||
+ | iosheader | ||
+ | defines | ||
+ | mainloop | ||
+ | iosfooter | ||
</ | </ | ||
- | ===== canUseIllegalChannels | + | ===== PrettyWoman.csv |
- | < | + | Below is a CSV config file for PrettyWoman.sh. This file has the following fields: |
+ | |||
+ | ^Template|The name of a template, as listed in PrettyWoman.sh| | ||
+ | ^Class|A class name from the DJI Application source code| | ||
+ | ^Method|The name of a method or function within the class| | ||
+ | ^PlusMinus|A plus or a minus sign (as defined in the DJI reversed source code)| | ||
+ | ^OurValue|What should we replace the original function return value with?| | ||
+ | ^LogMessageTweaked|What to display in debug when we successfully bypass DJI functions etc| | ||
+ | ^LogSeen|What to display in debug when we are called, but we don't change anything| | ||
+ | ^OneShot|Set this to 1 if you want to see a log message for this once only| | ||
+ | |||
+ | < | ||
+ | jsobject, | ||
+ | jsfunction, | ||
+ | jsfunction, | ||
+ | jsfunction, | ||
+ | jsfunction, | ||
+ | jsfunction, | ||
+ | jsfunction, | ||
+ | jsfunction, | ||
+ | jsfunctioniffcc, | ||
+ | </ | ||
+ | |||
+ | ===== Tweak.js ===== | ||
+ | |||
+ | This code was generated by PrettyWoman.sh - using the CSV file above. Feel free to just use this set of hooks for your tweaking pleasure. | ||
+ | |||
+ | <code java Tweak.js> | ||
+ | // | ||
+ | // DJI GO 4 - Frida Tweaks | ||
+ | // | ||
if (ObjC.available) { | if (ObjC.available) { | ||
+ | |||
+ | // | ||
+ | // Create variables that we can use for "one shot" rules | ||
+ | // | ||
+ | AFSecurityPolicysetSSLPinningModeSeenHit=0; | ||
+ | AFSecurityPolicysetSSLPinningModeSeenReplace=0; | ||
+ | DJIAccountManagercheckIsAdminUserSeenHit=0; | ||
+ | DJIAccountManagercheckIsAdminUserSeenReplace=0; | ||
+ | DJIAppSettingscanUseIllegalChannelsSeenHit=0; | ||
+ | DJIAppSettingscanUseIllegalChannelsSeenReplace=0; | ||
+ | DJIRadioLogiccanUseIllegalChannelsSeenHit=0; | ||
+ | DJIRadioLogiccanUseIllegalChannelsSeenReplace=0; | ||
+ | DJIAppSettingssdr_force_fccSeenHit=0; | ||
+ | DJIAppSettingssdr_force_fccSeenReplace=0; | ||
+ | DJITermsNotificationControllershouldShowTermsSeenHit=0; | ||
+ | DJITermsNotificationControllershouldShowTermsSeenReplace=0; | ||
+ | DJIAppForceUpdateManagerhasCheckedSeenHit=0; | ||
+ | DJIAppForceUpdateManagerhasCheckedSeenReplace=0; | ||
+ | DJIUpgradeNotifyViewModelnotifyHiddenSeenHit=0; | ||
+ | DJIUpgradeNotifyViewModelnotifyHiddenSeenReplace=0; | ||
+ | DJIProductManagercurrentProductCodeSeenHit=0; | ||
+ | DJIProductManagercurrentProductCodeSeenReplace=0; | ||
+ | |||
+ | // | ||
+ | // AFSecurityPolicy setSSLPinningMode jsobject | ||
+ | // | ||
+ | |||
+ | var AFSecurityPolicy = ObjC.classes.AFSecurityPolicy; | ||
+ | var setSSLPinningMode = AFSecurityPolicy[' | ||
+ | var setSSLPinningModeImpl = setSSLPinningMode.implementation; | ||
+ | setSSLPinningMode.implementation = ObjC.implement(setSSLPinningMode, | ||
+ | setSSLPinningModeImpl(handle, | ||
+ | if ( originalResult != 0 ) { | ||
+ | if ( 00 == 0 || AFSecurityPolicysetSSLPinningModeSeenReplace == 0 ) { | ||
+ | console.log(" | ||
+ | AFSecurityPolicysetSSLPinningModeSeenReplace = 1; | ||
+ | } | ||
+ | } else { | ||
+ | if ( 00 == 0 || AFSecurityPolicysetSSLPinningModeSeenHit == 0 ) { | ||
+ | console.log(" | ||
+ | AFSecurityPolicysetSSLPinningModeSeenHit = 1; | ||
+ | } | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | // | ||
+ | // DJIAccountManager checkIsAdminUser jsfunction | ||
+ | // | ||
+ | |||
+ | var DJIAccountManager = ObjC.classes.DJIAccountManager; | ||
+ | var checkIsAdminUser = DJIAccountManager[' | ||
+ | var checkIsAdminUserImpl = checkIsAdminUser.implementation; | ||
+ | checkIsAdminUser.implementation = ObjC.implement(checkIsAdminUser, | ||
+ | var originalResult = checkIsAdminUserImpl(handle, | ||
+ | if ( originalResult != 1 ) { | ||
+ | if ( 00 == 0 || DJIAccountManagercheckIsAdminUserSeenReplace == 0 ) { | ||
+ | console.log(" | ||
+ | DJIAccountManagercheckIsAdminUserSeenReplace = 1; | ||
+ | } | ||
+ | } else { | ||
+ | if ( 00 == 0 || DJIAccountManagercheckIsAdminUserSeenHit == 0 ) { | ||
+ | console.log(" | ||
+ | DJIAccountManagercheckIsAdminUserSeenHit = 1; | ||
+ | } | ||
+ | } | ||
+ | return 1; | ||
+ | }); | ||
+ | |||
+ | // | ||
+ | // DJIAppSettings canUseIllegalChannels jsfunction | ||
+ | // | ||
+ | |||
var DJIAppSettings = ObjC.classes.DJIAppSettings; | var DJIAppSettings = ObjC.classes.DJIAppSettings; | ||
- | | ||
var canUseIllegalChannels = DJIAppSettings[' | var canUseIllegalChannels = DJIAppSettings[' | ||
var canUseIllegalChannelsImpl = canUseIllegalChannels.implementation; | var canUseIllegalChannelsImpl = canUseIllegalChannels.implementation; | ||
canUseIllegalChannels.implementation = ObjC.implement(canUseIllegalChannels, | canUseIllegalChannels.implementation = ObjC.implement(canUseIllegalChannels, | ||
var originalResult = canUseIllegalChannelsImpl(handle, | var originalResult = canUseIllegalChannelsImpl(handle, | ||
- | console.log(' | + | |
+ | if ( 00 == 0 || DJIAppSettingscanUseIllegalChannelsSeenReplace == 0 ) { | ||
+ | | ||
+ | DJIAppSettingscanUseIllegalChannelsSeenReplace = 1; | ||
+ | } | ||
+ | } else { | ||
+ | if ( 00 == 0 || DJIAppSettingscanUseIllegalChannelsSeenHit == 0 ) { | ||
+ | console.log(" | ||
+ | DJIAppSettingscanUseIllegalChannelsSeenHit = 1; | ||
+ | } | ||
+ | } | ||
return 1; | return 1; | ||
}); | }); | ||
- | | + | |
+ | // | ||
+ | // DJIRadioLogic canUseIllegalChannels jsfunction | ||
+ | // | ||
var DJIRadioLogic = ObjC.classes.DJIRadioLogic; | var DJIRadioLogic = ObjC.classes.DJIRadioLogic; | ||
- | | ||
var canUseIllegalChannels = DJIRadioLogic[' | var canUseIllegalChannels = DJIRadioLogic[' | ||
var canUseIllegalChannelsImpl = canUseIllegalChannels.implementation; | var canUseIllegalChannelsImpl = canUseIllegalChannels.implementation; | ||
canUseIllegalChannels.implementation = ObjC.implement(canUseIllegalChannels, | canUseIllegalChannels.implementation = ObjC.implement(canUseIllegalChannels, | ||
var originalResult = canUseIllegalChannelsImpl(handle, | var originalResult = canUseIllegalChannelsImpl(handle, | ||
- | console.log(' | + | |
+ | if ( 00 == 0 || DJIRadioLogiccanUseIllegalChannelsSeenReplace == 0 ) { | ||
+ | | ||
+ | DJIRadioLogiccanUseIllegalChannelsSeenReplace = 1; | ||
+ | } | ||
+ | } else { | ||
+ | if ( 00 == 0 || DJIRadioLogiccanUseIllegalChannelsSeenHit == 0 ) { | ||
+ | console.log(" | ||
+ | DJIRadioLogiccanUseIllegalChannelsSeenHit = 1; | ||
+ | } | ||
+ | } | ||
return 1; | return 1; | ||
}); | }); | ||
- | } | ||
- | </ | ||
- | ===== checkIsAdminUser ===== | + | // |
- | <code javascript DJIAccountManager.checkIsAdminUser.js> | + | // DJIAppSettings sdr_force_fcc jsfunction |
- | if (ObjC.available) { | + | // |
- | var DJIAccountManager | + | var DJIAppSettings |
+ | var sdr_force_fcc = DJIAppSettings[' | ||
+ | var sdr_force_fccImpl = sdr_force_fcc.implementation; | ||
+ | sdr_force_fcc.implementation = ObjC.implement(sdr_force_fcc, | ||
+ | var originalResult = sdr_force_fccImpl(handle, | ||
+ | if ( originalResult != 1 ) { | ||
+ | if ( 00 == 0 || DJIAppSettingssdr_force_fccSeenReplace == 0 ) { | ||
+ | console.log(" | ||
+ | DJIAppSettingssdr_force_fccSeenReplace = 1; | ||
+ | } | ||
+ | } else { | ||
+ | if ( 00 == 0 || DJIAppSettingssdr_force_fccSeenHit == 0 ) { | ||
+ | console.log(" | ||
+ | DJIAppSettingssdr_force_fccSeenHit = 1; | ||
+ | } | ||
+ | } | ||
+ | return 1; | ||
+ | }); | ||
- | var checkIsAdminUser | + | |
- | var checkIsAdminUserImpl | + | // DJITermsNotificationController shouldShowTerms jsfunction |
- | | + | // |
- | var originalResult = checkIsAdminUserImpl(handle, selector); | + | |
- | console.log(' | + | var DJITermsNotificationController = ObjC.classes.DJITermsNotificationController; |
+ | | ||
+ | var shouldShowTermsImpl | ||
+ | | ||
+ | var originalResult = shouldShowTermsImpl(handle, selector); | ||
+ | | ||
+ | if ( 00 == 0 || DJITermsNotificationControllershouldShowTermsSeenReplace == 0 ) { | ||
+ | | ||
+ | DJITermsNotificationControllershouldShowTermsSeenReplace = 1; | ||
+ | } | ||
+ | } else { | ||
+ | if ( 00 == 0 || DJITermsNotificationControllershouldShowTermsSeenHit == 0 ) { | ||
+ | console.log(" | ||
+ | DJITermsNotificationControllershouldShowTermsSeenHit = 1; | ||
+ | } | ||
+ | } | ||
+ | return 0; | ||
+ | }); | ||
+ | |||
+ | // | ||
+ | // DJIAppForceUpdateManager hasChecked jsfunction | ||
+ | // | ||
+ | |||
+ | var DJIAppForceUpdateManager = ObjC.classes.DJIAppForceUpdateManager; | ||
+ | var hasChecked = DJIAppForceUpdateManager['- hasChecked' | ||
+ | var hasCheckedImpl = hasChecked.implementation; | ||
+ | hasChecked.implementation = ObjC.implement(hasChecked, function (handle, selector) { | ||
+ | var originalResult | ||
+ | if ( originalResult != 1 ) { | ||
+ | if ( 00 == 0 || DJIAppForceUpdateManagerhasCheckedSeenReplace == 0 ) { | ||
+ | console.log(" | ||
+ | DJIAppForceUpdateManagerhasCheckedSeenReplace = 1; | ||
+ | } | ||
+ | } else { | ||
+ | if ( 00 == 0 || DJIAppForceUpdateManagerhasCheckedSeenHit == 0 ) { | ||
+ | console.log(" | ||
+ | DJIAppForceUpdateManagerhasCheckedSeenHit = 1; | ||
+ | } | ||
+ | } | ||
return 1; | return 1; | ||
}); | }); | ||
- | } | ||
- | </ | ||
- | ===== setSSLPinningMode | + | // |
- | <code javascript AFSecurityPolicy.setSSLPinningMode.js> | + | // DJIUpgradeNotifyViewModel notifyHidden jsfunction |
- | if (ObjC.available) { | + | // |
- | + | ||
- | var AFSecurityPolicy | + | var DJIUpgradeNotifyViewModel |
- | + | var notifyHidden | |
- | var setSSLPinningMode | + | var notifyHiddenImpl |
- | var setSSLPinningModeImpl | + | notifyHidden.implementation |
- | | + | var originalResult |
- | | + | if ( originalResult != 1 ) { |
- | console.log(' | + | if ( 00 == 0 || DJIUpgradeNotifyViewModelnotifyHiddenSeenReplace |
+ | | ||
+ | | ||
+ | } | ||
+ | } else { | ||
+ | | ||
+ | console.log(" | ||
+ | DJIUpgradeNotifyViewModelnotifyHiddenSeenHit = 1; | ||
+ | } | ||
+ | } | ||
+ | return 1; | ||
+ | }); | ||
+ | |||
+ | // | ||
+ | // DJIProductManager currentProductCode jsfunctioniffcc | ||
+ | // | ||
+ | |||
+ | var DJIProductManager | ||
+ | var currentProductCode | ||
+ | var currentProductCodeImpl | ||
+ | | ||
+ | | ||
+ | | ||
+ | if ( 01 == 0 || DJIProductManagercurrentProductCodeSeenReplace == 0 ) { | ||
+ | | ||
+ | DJIProductManagercurrentProductCodeSeenReplace = 1; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | if( DJIAppSettingssdr_force_fccSeenReplace == 0 ) { | ||
+ | return 13; //mavic | ||
+ | } else { | ||
+ | return originalResult; | ||
+ | } | ||
}); | }); | ||
} | } | ||
</ | </ | ||
+ |