improvements to commandline scan, VT integration, and welcome window(s)

This commit is contained in:
Patrick Wardle
2025-12-06 11:23:58 -10:00
parent e82dee9740
commit e82cab2f06
14 changed files with 363 additions and 383 deletions
+1 -4
View File
@@ -102,12 +102,9 @@ extern Filter* itemFilter;
//non-UI thread that performs actual scan
@property(nonatomic, strong)NSThread *scannerThread;
//virus total object
//VT object
@property(nonatomic, retain)VirusTotal* virusTotalObj;
//array for all virus total threads
@property(nonatomic, retain)NSMutableArray* vtThreads;
//differences window controller
@property (strong) DiffWindowController* diffWindowController;
+36 -106
View File
@@ -17,7 +17,6 @@
@synthesize friends;
@synthesize plugins;
@synthesize vtThreads;
@synthesize scanButton;
@synthesize isConnected;
@synthesize scannerThread;
@@ -123,9 +122,6 @@ void uncaughtExceptionHandler(NSException* exception) {
//init virus total object
virusTotalObj = [[VirusTotal alloc] init];
//init array for virus total threads
vtThreads = [NSMutableArray array];
//alloc shared item enumerator
sharedItemEnumerator = [[ItemEnumerator alloc] init];
@@ -449,12 +445,22 @@ void uncaughtExceptionHandler(NSException* exception) {
// ->runs in the background to execute each plugin
-(void)scan
{
//flag indicating an active VT thread
BOOL activeThread = NO;
//query VT?
BOOL queryVT = NO;
//set scan flag
self.isConnected = isNetworkConnected();
//load VT API key
NSString* vtAPIKey = loadAPIKeyFromKeychain();
//skip VT scanning if
// not connected, user disabled queries, or no API key
queryVT = (vtAPIKey.length) && self.isConnected && (!self.prefsWindowController.disableVTQueries);
//create dispatch group for VT queries
dispatch_group_t vtGroup = dispatch_group_create();
//iterate over all plugins
// ->invoke's each scan message
for(PluginBase* plugin in self.plugins)
@@ -490,26 +496,32 @@ void uncaughtExceptionHandler(NSException* exception) {
//scan
// will invoke callback as items are found
[plugin scan];
//when 'disable VT' prefs not selected and network is reachable
// ->kick of thread to perform VT query in background
if( (YES != self.prefsWindowController.disableVTQueries) &&
(YES == self.isConnected) )
{
//do query
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self->virusTotalObj checkFiles:plugin];
});
//should query VT?
// if so, do it in background
if(queryVT)
{
//enter group
dispatch_group_enter(vtGroup);
//VT query in BG
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//check all plugin's files
[self->virusTotalObj checkFiles:plugin apiKey:vtAPIKey uiMode:YES completion:^{
//done
dispatch_group_leave(vtGroup);
}];
});
}
}//pool
}
//if VT querying is enabled (default) and network is available
// ->wait till all VT threads are done
if( (YES != self.prefsWindowController.disableVTQueries) &&
(YES == self.isConnected) )
//wait till all VT threads are done
if(queryVT)
{
//update scanner msg
dispatch_async(dispatch_get_main_queue(), ^{
@@ -521,53 +533,10 @@ void uncaughtExceptionHandler(NSException* exception) {
//nap
// ->VT threads take some time to spawn/process
[NSThread sleepForTimeInterval:3.0f];
[NSThread sleepForTimeInterval:1.0f];
//wait for all VT threads to exit
while(YES)
{
//reset flag
activeThread = NO;
//sync
@synchronized(self.vtThreads)
{
//check all threads
for(NSThread* vtThread in self.vtThreads)
{
//check if still running
// ->set flag & break out of loop
if(YES == [vtThread isExecuting])
{
//set flag
activeThread = YES;
//bail
break;
}
}
}//sync
//check flag
if(YES != activeThread)
{
//finally no active threads
// ->bail
break;
}
//exit if scanner (self) thread was cancelled
if(YES == [[NSThread currentThread] isCancelled])
{
//exit
[NSThread exit];
}
//nap
[NSThread sleepForTimeInterval:0.5];
}//active thread
//wait for all VT queries to complete
dispatch_group_wait(vtGroup, DISPATCH_TIME_FOREVER);
}//VT scanning enabled
@@ -588,7 +557,7 @@ void uncaughtExceptionHandler(NSException* exception) {
});
}//scan not stopped by user
}
return;
}
@@ -751,7 +720,6 @@ void uncaughtExceptionHandler(NSException* exception) {
}
//callback when user has updated prefs
// ->reload table, etc
-(void)applyPreferences
{
//currently selected category
@@ -770,26 +738,6 @@ void uncaughtExceptionHandler(NSException* exception) {
//reload item table
[self.itemTableController.itemTableView reloadData];
//(re)check network connectivity
// ->set iVar
self.isConnected = isNetworkConnected();
//if VT query was never done (e.g. scan was started w/ pref disabled) and network is available
// ->kick off VT queries now
if( (0 == self.vtThreads.count) &&
(YES != self.prefsWindowController.disableVTQueries) &&
(YES == self.isConnected) )
{
//iterate over all plugins
// ->do VT query for each
for(PluginBase* plugin in self.plugins)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self->virusTotalObj checkFiles:plugin];
});
}
}
return;
}
@@ -863,24 +811,6 @@ void uncaughtExceptionHandler(NSException* exception) {
[sharedItemEnumerator.enumeratorThread cancel];
}
//sync to cancel all VT threads
@synchronized(self.vtThreads)
{
//tell all VT threads to bail
for(NSThread* vtThread in self.vtThreads)
{
//cancel running threads
if(YES == [vtThread isExecuting])
{
//cancel
[vtThread cancel];
}
}
}
//remove all VT threads
[self.vtThreads removeAllObjects];
//when invoked from the UI (e.g. 'Stop Scan' was clicked)
// ->cancel scanner thread
if([NSThread currentThread] != self.scannerThread)
+150 -138
View File
@@ -28,23 +28,23 @@
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="KnockKnock" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="F0z-JX-Cv5">
<window title="Welcome to KnockKnock" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" frameAutosaveName="" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES"/>
<rect key="contentRect" x="196" y="240" width="700" height="350"/>
<rect key="screenRect" x="0.0" y="0.0" width="1280" height="769"/>
<value key="minSize" type="size" width="700" height="350"/>
<value key="maxSize" type="size" width="700" height="350"/>
<rect key="contentRect" x="196" y="240" width="700" height="400"/>
<rect key="screenRect" x="0.0" y="0.0" width="3440" height="1409"/>
<value key="minSize" type="size" width="700" height="400"/>
<value key="maxSize" type="size" width="700" height="400"/>
<view key="contentView" wantsLayer="YES" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="700" height="350"/>
<rect key="frame" x="0.0" y="0.0" width="700" height="400"/>
<autoresizingMask key="autoresizingMask"/>
</view>
<connections>
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
</connections>
<point key="canvasLocation" x="-39" y="-409"/>
<point key="canvasLocation" x="-42" y="-508"/>
</window>
<customView id="97j-sp-rBE" userLabel="Welcome">
<rect key="frame" x="0.0" y="0.0" width="700" height="350"/>
<rect key="frame" x="0.0" y="0.0" width="700" height="400"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<box fixedFrame="YES" boxType="custom" borderType="none" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="JYh-c0-q0Z">
@@ -57,7 +57,7 @@
<color key="fillColor" red="0.57793885469999995" green="0.75859862570000003" blue="0.2368842065" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</box>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="660" translatesAutoresizingMaskIntoConstraints="NO" id="NEo-Sr-1a0">
<rect key="frame" x="149" y="290" width="338" height="54"/>
<rect key="frame" x="149" y="340" width="338" height="54"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="left" title="Welcome to" id="Qyh-uK-Afv">
<font key="font" size="41" name="AvenirNextCondensed-Regular"/>
@@ -77,17 +77,17 @@
</connections>
</button>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rNc-Xa-Jqh">
<rect key="frame" x="151" y="196" width="371" height="101"/>
<rect key="frame" x="151" y="246" width="371" height="101"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="kkText" id="zfH-MI-2vJ"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="14w-0v-UJ5">
<rect key="frame" x="15" y="217" width="128" height="128"/>
<rect key="frame" x="15" y="267" width="128" height="128"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="kkIcon" id="vEL-Yw-xip"/>
</imageView>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="660" translatesAutoresizingMaskIntoConstraints="NO" id="cpq-67-lQt">
<rect key="frame" x="18" y="86" width="664" height="87"/>
<rect key="frame" x="18" y="171" width="664" height="52"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="center" id="eic-lg-Ln7">
<font key="font" size="19" name="AvenirNextCondensed-Regular"/>
@@ -97,15 +97,24 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="660" translatesAutoresizingMaskIntoConstraints="NO" id="LWe-j3-zJp">
<rect key="frame" x="18" y="90" width="664" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="center" title="Now lets set up how you want KnockKnock to run. " id="JKZ-vA-I78">
<font key="font" size="19" name="AvenirNextCondensed-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<point key="canvasLocation" x="-296" y="504"/>
</customView>
<customView id="R7i-MV-IAu" userLabel="Disk Access">
<rect key="frame" x="0.0" y="0.0" width="966" height="403"/>
<rect key="frame" x="0.0" y="0.0" width="966" height="400"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button tag="1" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Rrc-n2-rAG">
<rect key="frame" x="80" y="272" width="197" height="32"/>
<rect key="frame" x="80" y="273" width="197" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Open 'System Settings'" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="aCh-Th-bAz">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@@ -128,7 +137,7 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="OQh-jg-YAn">
<rect key="frame" x="78" y="255" width="330" height="14"/>
<rect key="frame" x="78" y="254" width="330" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="This is where you manage privacy permissions." id="y5g-ZT-ugG">
<font key="font" size="12" name="Menlo-Regular"/>
@@ -137,7 +146,7 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VM6-h0-eYz">
<rect key="frame" x="78" y="196" width="179" height="21"/>
<rect key="frame" x="78" y="195" width="179" height="21"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" alignment="left" title="Turn on KnockKnock" id="zaJ-jI-pg9">
<font key="font" size="15" name="Menlo-Regular"/>
@@ -164,7 +173,7 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="660" translatesAutoresizingMaskIntoConstraints="NO" id="mRa-dq-CpM">
<rect key="frame" x="38" y="345" width="664" height="54"/>
<rect key="frame" x="38" y="361" width="664" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="left" title="Grant KnockKnock Full Disk Access" id="np6-Pk-2uu">
<font key="font" size="32" name="AvenirNextCondensed-Medium"/>
@@ -173,7 +182,7 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="445" translatesAutoresizingMaskIntoConstraints="NO" id="5bH-jb-blZ">
<rect key="frame" x="41" y="343" width="739" height="14"/>
<rect key="frame" x="41" y="334" width="739" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Full Disk Access lets KnockKnock scan your entire system for potential threats." id="tSA-vA-bDC">
<font key="font" size="12" name="Menlo-Regular"/>
@@ -182,7 +191,7 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</textFieldCell>
</textField>
<box fixedFrame="YES" boxType="custom" borderType="none" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="yDJ-oc-cE9">
<rect key="frame" x="0.0" y="0.0" width="966" height="48"/>
<rect key="frame" x="0.0" y="-3" width="966" height="48"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<view key="contentView" id="SJT-x5-8f3">
<rect key="frame" x="0.0" y="0.0" width="966" height="48"/>
@@ -217,7 +226,7 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
<color key="fillColor" red="0.57793885469999995" green="0.75859862570000003" blue="0.2368842065" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</box>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jZB-fa-4Xa">
<rect key="frame" x="78" y="161" width="330" height="34"/>
<rect key="frame" x="78" y="159" width="330" height="34"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" title="Allow KnockKnock to access all files." id="gDa-9D-yHk">
<font key="font" size="12" name="Menlo-Regular"/>
@@ -226,7 +235,7 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</textFieldCell>
</textField>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="QYO-Jw-YPQ">
<rect key="frame" x="440" y="90" width="494" height="216"/>
<rect key="frame" x="440" y="87" width="494" height="216"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" imageFrameStyle="grayBezel" image="SystemPrefs" id="Bef-Qy-itv"/>
</imageView>
@@ -234,18 +243,18 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
<point key="canvasLocation" x="143" y="-15"/>
</customView>
<customView id="kOW-Us-kgD" userLabel="Configure">
<rect key="frame" x="0.0" y="0.0" width="637" height="302"/>
<rect key="frame" x="0.0" y="0.0" width="700" height="400"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<box fixedFrame="YES" boxType="custom" borderType="none" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="f2W-Nn-YWU">
<rect key="frame" x="0.0" y="0.0" width="637" height="48"/>
<rect key="frame" x="0.0" y="0.0" width="700" height="48"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<view key="contentView" id="jcK-yh-GlV">
<rect key="frame" x="0.0" y="0.0" width="637" height="48"/>
<rect key="frame" x="0.0" y="0.0" width="700" height="48"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button tag="3" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Syw-cO-leg">
<rect key="frame" x="536" y="8" width="81" height="32"/>
<rect key="frame" x="599" y="8" width="81" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Next" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="xbU-XH-hYT">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@@ -260,7 +269,7 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
<color key="fillColor" red="0.57793885469999995" green="0.75859862570000003" blue="0.2368842065" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</box>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="660" translatesAutoresizingMaskIntoConstraints="NO" id="ikd-wz-01Q">
<rect key="frame" x="30" y="263" width="664" height="44"/>
<rect key="frame" x="38" y="361" width="664" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="left" title="Configure KnockKnock:" id="XHD-XH-wR0">
<font key="font" size="32" name="AvenirNextCondensed-Medium"/>
@@ -269,7 +278,7 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="445" translatesAutoresizingMaskIntoConstraints="NO" id="qME-7o-m8l">
<rect key="frame" x="31" y="247" width="433" height="18"/>
<rect key="frame" x="38" y="334" width="433" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Note: These can later be changed via KnockKnock's Settings" id="E65-cF-gfk">
<font key="font" size="12" name="Menlo-Regular"/>
@@ -278,41 +287,24 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="b0q-yl-6sv">
<rect key="frame" x="32" y="182" width="22" height="44"/>
<rect key="frame" x="40" y="275" width="22" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" inset="2" id="6UN-fn-MkZ">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" size="18" name="Menlo-Bold"/>
</buttonCell>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LHE-lc-cEL">
<rect key="frame" x="32" y="114" width="22" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" inset="2" id="aZp-4P-816">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system" size="20"/>
</buttonCell>
</button>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="445" translatesAutoresizingMaskIntoConstraints="NO" id="AhJ-ov-jbx">
<rect key="frame" x="57" y="176" width="459" height="15"/>
<rect key="frame" x="63" y="255" width="602" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Shows persistent macOS and Apple-installed components." id="r1b-Nl-Ii7">
<font key="font" size="12" name="Menlo-Regular"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="445" translatesAutoresizingMaskIntoConstraints="NO" id="KHZ-VJ-JkQ">
<rect key="frame" x="55" y="98" width="482" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Prevents the app from checking for updates automatically." id="3aO-AI-YtY">
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Enable this otion to show persistent macOS and Apple-installed components." id="r1b-Nl-Ii7">
<font key="font" size="12" name="Menlo-Regular"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Udh-qU-3t3">
<rect key="frame" x="55" y="187" width="310" height="28"/>
<rect key="frame" x="63" y="279" width="310" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Include macOS components" id="zo1-a0-tPg">
<font key="font" size="17" name="Menlo-Bold"/>
@@ -320,8 +312,25 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LHE-lc-cEL">
<rect key="frame" x="40" y="192" width="22" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" inset="2" id="aZp-4P-816">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system" size="20"/>
</buttonCell>
</button>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="445" translatesAutoresizingMaskIntoConstraints="NO" id="KHZ-VJ-JkQ">
<rect key="frame" x="63" y="184" width="608" height="14"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Enable this optoion to prevent the app from checking for updates automatically." id="3aO-AI-YtY">
<font key="font" size="12" name="Menlo-Regular"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Gf8-do-3hb">
<rect key="frame" x="55" y="117" width="399" height="28"/>
<rect key="frame" x="63" y="196" width="399" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disable automatic update check" id="ymJ-tB-qiA">
<font key="font" size="17" name="Menlo-Bold"/>
@@ -330,28 +339,30 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</textFieldCell>
</textField>
</subviews>
<point key="canvasLocation" x="431.5" y="502"/>
<point key="canvasLocation" x="431" y="502"/>
</customView>
<customView id="2qR-UH-6qk" userLabel="Support">
<rect key="frame" x="0.0" y="0.0" width="812" height="454"/>
<rect key="frame" x="0.0" y="0.0" width="812" height="400"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0L0-vQ-k5x">
<rect key="frame" x="23" y="245" width="128" height="128"/>
<rect key="frame" x="20" y="227" width="128" height="128"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="Heart" id="NdE-ks-tao"/>
</imageView>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="660" translatesAutoresizingMaskIntoConstraints="NO" id="NuI-gL-I9N">
<rect key="frame" x="153" y="257" width="636" height="110"/>
<rect key="frame" x="150" y="235" width="636" height="110"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="center" title="It's free, open-source, and written by a single (Mac-loving) coder!" id="Uzd-7P-dzG">
<font key="font" size="40" name="AvenirNextCondensed-Regular"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="center" id="Uzd-7P-dzG">
<font key="font" size="32" name="AvenirNextCondensed-Regular"/>
<string key="title">It's free, open-source, and written by a single
(Mac-loving) coder!</string>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<box fixedFrame="YES" boxType="custom" borderType="none" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="3oj-Go-OSg">
<rect key="frame" x="0.0" y="1" width="812" height="48"/>
<rect key="frame" x="0.0" y="0.0" width="812" height="48"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<view key="contentView" id="kck-xf-IsF">
<rect key="frame" x="0.0" y="0.0" width="812" height="48"/>
@@ -383,8 +394,71 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</view>
<color key="fillColor" red="0.57793885469999995" green="0.75859862570000003" blue="0.2368842065" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</box>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.90000000000000002" id="AM3-9J-hwR">
<rect key="frame" x="258" y="60" width="138" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="FriendsMacPaw" id="Ylq-Y7-0yI"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.80000000000000004" id="mxQ-16-QkH">
<rect key="frame" x="573" y="58" width="142" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="FriendsHuntress" id="5gc-uV-gX6"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.80000000000000004" id="KQq-D5-uls">
<rect key="frame" x="100" y="58" width="142" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" image="FriendsMalwarebytes" id="0Du-KM-jmM"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.80000000000000004" id="2M2-4t-B3w">
<rect key="frame" x="724" y="62" width="73" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="FriendsiVerify" id="Qy2-wU-bbf"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.80000000000000004" id="7sb-Zs-q5d">
<rect key="frame" x="234" y="95" width="102" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="FriendsKandji" id="W0a-k0-8a1"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.90000000000000002" id="CsL-Yj-dhy">
<rect key="frame" x="362" y="106" width="89" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" image="FriendsFleet" id="jTn-RV-2Tp"/>
</imageView>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="660" translatesAutoresizingMaskIntoConstraints="NO" id="JBN-MO-km3">
<rect key="frame" x="38" y="361" width="664" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="left" title="Support KnockKnock?" id="qNj-Zh-8FW">
<font key="font" size="32" name="AvenirNextCondensed-Medium"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="660" translatesAutoresizingMaskIntoConstraints="NO" id="NeU-Pi-VDr">
<rect key="frame" x="287" y="179" width="499" height="57"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="right" title="Show some love? " id="XBE-gW-YkK">
<font key="font" size="32" name="AvenirNextCondensed-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.90000000000000002" id="a5u-a9-9Hv">
<rect key="frame" x="13" y="62" width="77" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="FriendsJamf" id="2Ll-Bq-g75"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.80000000000000004" id="wYW-XY-DPT">
<rect key="frame" x="416" y="55" width="148" height="36"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" image="FriendsSophos" id="ZqJ-o4-Joj"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.90000000000000002" id="fcd-Wp-myp">
<rect key="frame" x="476" y="101" width="126" height="29"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="FriendsPANW" id="Dio-uY-kZm"/>
</imageView>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" alphaValue="0.59999999999999998" id="SzT-r7-Ma4">
<rect key="frame" x="290" y="137" width="233" height="29"/>
<rect key="frame" x="299" y="139" width="233" height="29"/>
<autoresizingMask key="autoresizingMask"/>
<textFieldCell key="cell" lineBreakMode="clipping" alignment="center" title="Mahalo to the &quot;Friends Objective-See&quot;" id="7pD-BH-c8i">
<font key="font" size="15" name="AvenirNextCondensed-Regular"/>
@@ -392,85 +466,22 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.90000000000000002" id="AM3-9J-hwR">
<rect key="frame" x="258" y="59" width="138" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="FriendsMacPaw" id="Ylq-Y7-0yI"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.80000000000000004" id="mxQ-16-QkH">
<rect key="frame" x="573" y="57" width="142" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="FriendsHuntress" id="5gc-uV-gX6"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.80000000000000004" id="KQq-D5-uls">
<rect key="frame" x="100" y="57" width="142" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" image="FriendsMalwarebytes" id="0Du-KM-jmM"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.80000000000000004" id="2M2-4t-B3w">
<rect key="frame" x="724" y="61" width="73" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="FriendsiVerify" id="Qy2-wU-bbf"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.80000000000000004" id="7sb-Zs-q5d">
<rect key="frame" x="234" y="94" width="102" height="37"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="FriendsKandji" id="W0a-k0-8a1"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.90000000000000002" id="CsL-Yj-dhy">
<rect key="frame" x="362" y="105" width="89" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" image="FriendsFleet" id="jTn-RV-2Tp"/>
</imageView>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="660" translatesAutoresizingMaskIntoConstraints="NO" id="JBN-MO-km3">
<rect key="frame" x="38" y="380" width="664" height="55"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="left" title="Support KnockKnock?" id="qNj-Zh-8FW">
<font key="font" size="40" name="AvenirNextCondensed-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="660" translatesAutoresizingMaskIntoConstraints="NO" id="NeU-Pi-VDr">
<rect key="frame" x="290" y="192" width="499" height="57"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="right" title="Show some love? " id="XBE-gW-YkK">
<font key="font" size="40" name="AvenirNextCondensed-Regular"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.90000000000000002" id="a5u-a9-9Hv">
<rect key="frame" x="13" y="61" width="77" height="23"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="FriendsJamf" id="2Ll-Bq-g75"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.80000000000000004" id="wYW-XY-DPT">
<rect key="frame" x="416" y="54" width="148" height="36"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" animates="YES" imageScaling="proportionallyDown" image="FriendsSophos" id="ZqJ-o4-Joj"/>
</imageView>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" alphaValue="0.90000000000000002" id="fcd-Wp-myp">
<rect key="frame" x="476" y="100" width="126" height="29"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="FriendsPANW" id="Dio-uY-kZm"/>
</imageView>
</subviews>
<point key="canvasLocation" x="685" y="1355"/>
</customView>
<customView id="afB-QN-4Zp" userLabel="Configure">
<rect key="frame" x="0.0" y="0.0" width="699" height="290"/>
<customView id="afB-QN-4Zp" userLabel="VT Integration">
<rect key="frame" x="0.0" y="0.0" width="700" height="400"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<box fixedFrame="YES" boxType="custom" borderType="none" titlePosition="noTitle" translatesAutoresizingMaskIntoConstraints="NO" id="b9g-lW-n01">
<rect key="frame" x="0.0" y="0.0" width="699" height="48"/>
<rect key="frame" x="0.0" y="0.0" width="700" height="48"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<view key="contentView" id="ofA-lY-dxh">
<rect key="frame" x="0.0" y="0.0" width="699" height="48"/>
<rect key="frame" x="0.0" y="0.0" width="700" height="48"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button tag="4" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ahC-4V-9UW">
<rect key="frame" x="598" y="8" width="81" height="32"/>
<rect key="frame" x="599" y="8" width="81" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Next" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="wgk-Ma-GMi">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
@@ -485,16 +496,16 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
<color key="fillColor" red="0.57793885469999995" green="0.75859862570000003" blue="0.2368842065" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</box>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="660" translatesAutoresizingMaskIntoConstraints="NO" id="Fub-o3-w3r">
<rect key="frame" x="30" y="251" width="664" height="44"/>
<rect key="frame" x="38" y="361" width="664" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="left" title="VirusTotal Integration:" id="1Hq-3Y-qbJ">
<textFieldCell key="cell" sendsActionOnEndEditing="YES" alignment="left" title="(Optional) VirusTotal Integration:" id="1Hq-3Y-qbJ">
<font key="font" size="32" name="AvenirNextCondensed-Medium"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="NOF-lL-dnS">
<rect key="frame" x="30" y="193" width="86" height="20"/>
<rect key="frame" x="38" y="276" width="86" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="API Key:" id="7SG-o8-mRZ">
<font key="font" size="17" name="Menlo-Bold"/>
@@ -503,7 +514,7 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Msu-yL-eqA">
<rect key="frame" x="125" y="188" width="503" height="24"/>
<rect key="frame" x="133" y="273" width="503" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="Your VirusTotal API Key" drawsBackground="YES" id="Og2-Lo-pbi">
<font key="font" size="13" name="Menlo-Regular"/>
@@ -512,7 +523,7 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ucj-y5-V2I">
<rect key="frame" x="31" y="159" width="447" height="21"/>
<rect key="frame" x="39" y="244" width="447" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" selectable="YES" title="Obtain a key (for free!) here:" id="GXa-el-UVP">
<font key="font" size="11" name="Menlo-Regular"/>
@@ -521,7 +532,7 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fi7-v7-obV" customClass="HyperlinkTextField">
<rect key="frame" x="241" y="167" width="389" height="13"/>
<rect key="frame" x="249" y="252" width="389" height="13"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="https://docs.virustotal.com/docs/please-give-me-an-api-key" id="6xX-Qx-lt6">
<font key="font" size="11" name="Menlo-Regular"/>
@@ -530,16 +541,17 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="445" translatesAutoresizingMaskIntoConstraints="NO" id="uQb-VS-DuG">
<rect key="frame" x="30" y="224" width="639" height="28"/>
<rect key="frame" x="38" y="334" width="639" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Check persistent items with VirusTotals threat database to identify known malware." id="7ox-4I-TfC">
<textFieldCell key="cell" sendsActionOnEndEditing="YES" id="7ox-4I-TfC">
<font key="font" size="12" name="Menlo-Regular"/>
<string key="title">Check persistent items with VirusTotals threat database to identify known malware. You can add an API key now or anytime later in KnockKnocks Settings.</string>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GdO-mh-Ufm">
<rect key="frame" x="32" y="100" width="22" height="44"/>
<rect key="frame" x="40" y="183" width="22" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" bezelStyle="regularSquare" imagePosition="left" inset="2" id="5gm-6I-Af8">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
@@ -547,16 +559,16 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</buttonCell>
</button>
<textField focusRingType="none" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" fixedFrame="YES" preferredMaxLayoutWidth="445" translatesAutoresizingMaskIntoConstraints="NO" id="0O9-FJ-fB9">
<rect key="frame" x="55" y="93" width="503" height="14"/>
<rect key="frame" x="63" y="173" width="647" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Prevents the app from querying VirusTotal for known malware." id="TDp-uQ-UJx">
<textFieldCell key="cell" sendsActionOnEndEditing="YES" title="Enable this option to prevent the app from querying VirusTotal for known malware." id="TDp-uQ-UJx">
<font key="font" size="12" name="Menlo-Regular"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="W2v-5l-YaL">
<rect key="frame" x="55" y="103" width="399" height="28"/>
<rect key="frame" x="63" y="186" width="399" height="28"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Disable VirusTotal Integration " id="9lZ-zf-ok2">
<font key="font" size="17" name="Menlo-Bold"/>
@@ -565,7 +577,7 @@ KnockKnock checks “whos there” by scanning all persistent software to rev
</textFieldCell>
</textField>
</subviews>
<point key="canvasLocation" x="309.5" y="881"/>
<point key="canvasLocation" x="309" y="881"/>
</customView>
</objects>
<resources>
-1
View File
@@ -717,7 +717,6 @@ bail:
//dbg msg
//NSLog(@"%s's entropy: %f", ((struct segment_command *)loadCommand)->segname, segmentEntropy);
//TODO: test more!
if(segmentEntropy > 7.2f)
{
//inc total
+2 -1
View File
@@ -378,7 +378,8 @@ bail:
//init json
json = [NSString stringWithFormat:@"\"name\": \"%@\", \"path\": \"%@\", \"plist\": \"%@\", \"hashes\": %@, \"signature(s)\": %@", self.name, self.path, filePlist, fileHashes, fileSigs];
//include VT
//include VT?
// append detection
if(![NSProcessInfo.processInfo.arguments containsObject:@"-skipVT"])
{
//append VT detection
+2 -2
View File
@@ -23,7 +23,7 @@
<window title="KnockKnock Settings" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="F0z-JX-Cv5">
<windowStyleMask key="styleMask" titled="YES" closable="YES" texturedBackground="YES"/>
<rect key="contentRect" x="196" y="240" width="565" height="310"/>
<rect key="screenRect" x="0.0" y="0.0" width="1280" height="769"/>
<rect key="screenRect" x="0.0" y="0.0" width="3440" height="1409"/>
<view key="contentView" id="se5-gp-TjO">
<rect key="frame" x="0.0" y="0.0" width="565" height="310"/>
<autoresizingMask key="autoresizingMask"/>
@@ -126,7 +126,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" selectable="YES" id="Nua-Eb-VTm">
<font key="font" size="11" name="Menlo-Regular"/>
<string key="title">Enter your personal VirusTotal API.
<string key="title">Enter your personal VirusTotal API key.
You can learn more about getting such a key (for free!) here:</string>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
+1 -1
View File
@@ -20,7 +20,7 @@
<window title="Unknown Items" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="pvW-8N-g8T">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<rect key="contentRect" x="403" y="305" width="643" height="339"/>
<rect key="screenRect" x="0.0" y="0.0" width="1280" height="769"/>
<rect key="screenRect" x="0.0" y="0.0" width="3440" height="1409"/>
<value key="minSize" type="size" width="643" height="339"/>
<view key="contentView" id="XBF-k3-Nqu">
<rect key="frame" x="0.0" y="0.0" width="643" height="339"/>
+2 -2
View File
@@ -98,13 +98,13 @@
}
},
"Nua-Eb-VTm.title" : {
"comment" : "Class = \"NSTextFieldCell\"; title = \"Enter your personal VirusTotal API. \\nYou can learn more about getting such a key (for free!) here:\"; ObjectID = \"Nua-Eb-VTm\";",
"comment" : "Class = \"NSTextFieldCell\"; title = \"Enter your personal VirusTotal API key. \\nYou can learn more about getting such a key (for free!) here:\"; ObjectID = \"Nua-Eb-VTm\";",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Enter your personal VirusTotal API. \nYou can learn more about getting such a key (for free!) here:"
"value" : "Enter your personal VirusTotal API key. \nYou can learn more about getting such a key (for free!) here:"
}
}
}
+1
View File
@@ -310,6 +310,7 @@
}
else {
//err msg
NSLog(@"ERROR: %@", result[VT_ERROR]);
//show error msg
+4 -3
View File
@@ -16,9 +16,10 @@
/* METHODS */
-(void)checkFiles:(PluginBase*)plugin;
- (void)submitFile:(NSString *)filePath completion:(void (^)(NSDictionary *result))completion;
//check (all plugin's) files
-(void)checkFiles:(PluginBase*)plugin apiKey:(NSString*)apiKey uiMode:(BOOL)uiMode completion:(void(^)(void))completion;
//submit a file for scanning
-(void)submitFile:(NSString *)filePath completion:(void (^)(NSDictionary *result))completion;
@end
+66 -57
View File
@@ -20,12 +20,16 @@ extern BOOL cmdlineMode;
@implementation VirusTotal
//ask VT about all files for a given plugin
// note: skips Apple binaries
-(void)checkFiles:(PluginBase*)plugin {
// note: always skips Apple binaries, as these won't be malware, and API keys usually rate limited
-(void)checkFiles:(PluginBase*)plugin apiKey:(NSString*)apiKey uiMode:(BOOL)uiMode completion:(void(^)(void))completion {
//load key
NSString* vtAPIKey = loadAPIKeyFromKeychain();
//sanity check
if(!plugin.allItems.count) {
if(completion) completion();
return;
}
//all items in the plugin
for(ItemBase* item in plugin.allItems) {
@@ -49,81 +53,83 @@ extern BOOL cmdlineMode;
continue;
}
//semaphore for synchronous request
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
//build the API URL
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"https://www.virustotal.com/api/v3/files/%@", sha1]];
//create the request
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:@"GET"];
[request setValue:vtAPIKey forHTTPHeaderField:@"x-apikey"];
[request setValue:apiKey forHTTPHeaderField:@"x-apikey"];
//kick off request
NSURLSessionDataTask* task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"VirusTotal error: %@", error.localizedDescription);
NSLog(@"VirusTotal ERROR: %@", error.localizedDescription);
//signal
dispatch_semaphore_signal(sema);
return;
}
//grab HTTP status
NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse *)response;
//401 is API key issue :|
//401 is API key issue
if (httpResponse.statusCode == 401) {
//dbg msg
NSLog(@"VT ERROR: API key %@ is not valid", vtAPIKey);
//show alert
// just once though
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dispatch_async(dispatch_get_main_queue(), ^{
//alert
NSAlert* alert = nil;
//alloc/init alert
alert = [NSAlert alertWithMessageText:@"ERROR: VirusTotal Responded with HTTP 401"
defaultButton:@"OK"
alternateButton:nil
otherButton:nil
informativeTextWithFormat:@"%@", [NSString stringWithFormat:@"API key: '%@', likely invalid.", vtAPIKey]];
//show it
[alert runModal];
NSLog(@"VirusTotal ERROR: API key %@ is not valid", apiKey);
if(uiMode) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dispatch_async(dispatch_get_main_queue(), ^{
NSAlert* alert = [NSAlert alertWithMessageText:@"ERROR: VirusTotal Responded with HTTP 401"
defaultButton:@"OK"
alternateButton:nil
otherButton:nil
informativeTextWithFormat:@"%@", [NSString stringWithFormat:@"API key: '%@', likely invalid.", apiKey]];
[alert runModal];
});
});
});
}
//signal
dispatch_semaphore_signal(sema);
return;
}
//404 is (unknown) file not found
//404 is file not found
if (httpResponse.statusCode == 404) {
//dbg msg
NSLog(@"%@ is unknown to VirusTotal (hash: %@)", file.name, sha1);
//add to unknown items
@synchronized(item.plugin.unknownItems) {
[item.plugin.unknownItems addObject:item];
}
//save
// blank to indicate not found
file.vtInfo = @{};
//notify UI on main thread
dispatch_async(dispatch_get_main_queue(), ^{
[((AppDelegate*)NSApplication.sharedApplication.delegate) itemProcessed:file];
});
if(uiMode) {
dispatch_async(dispatch_get_main_queue(), ^{
[((AppDelegate*)NSApplication.sharedApplication.delegate) itemProcessed:file];
});
}
//signal
dispatch_semaphore_signal(sema);
return;
}
//all other error(s)
if (httpResponse.statusCode != 200) {
NSLog(@"VirusTotal HTTP error: %ld", (long)httpResponse.statusCode);
//signal
dispatch_semaphore_signal(sema);
return;
}
@@ -132,13 +138,15 @@ extern BOOL cmdlineMode;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError];
if (jsonError) {
NSLog(@"VirusTotal JSON error: %@", jsonError.localizedDescription);
//signal
dispatch_semaphore_signal(sema);
return;
}
//extract
NSDictionary* attributes = json[@"data"][@"attributes"];
NSDictionary* stats = attributes[@"last_analysis_stats"];
NSString* itemID = json[@"data"][@"id"];
NSInteger malicious = [stats[@"malicious"] integerValue];
@@ -146,15 +154,11 @@ extern BOOL cmdlineMode;
NSInteger undetected = [stats[@"undetected"] integerValue];
NSInteger harmless = [stats[@"harmless"] integerValue];
//(browser) report
NSString* link = [NSString stringWithFormat:@"https://www.virustotal.com/gui/file/%@", itemID];
//calculate total
//TODO: double check this
NSInteger total = malicious + suspicious + undetected + harmless;
//create result dictionary
NSDictionary *result = @{
//save results
file.vtInfo = @{
VT_RESULTS_POSITIVES : @(malicious),
@"suspicious": @(suspicious),
@"undetected": @(undetected),
@@ -164,29 +168,34 @@ extern BOOL cmdlineMode;
VT_RESULTS_URL: link
};
//save
file.vtInfo = result;
//dbg msg
NSLog(@"File: %@ - Detection ratio: %ld/%ld", file.name, (long)malicious, (long)total);
//malicious?
// add to flagged items
if (malicious > 0) {
@synchronized(item.plugin.flaggedItems) {
[item.plugin.flaggedItems addObject:item];
}
}
//notify UI on main thread
dispatch_async(dispatch_get_main_queue(), ^{
[((AppDelegate*)NSApplication.sharedApplication.delegate) itemProcessed:file];
});
//notify UI
if(uiMode) {
dispatch_async(dispatch_get_main_queue(), ^{
[((AppDelegate*)NSApplication.sharedApplication.delegate) itemProcessed:file];
});
}
//signal
dispatch_semaphore_signal(sema);
}];
//query
[task resume];
//wait for this request to finish
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
//all files done
if(completion) completion();
return;
}
//submit a file to VT
+13 -33
View File
@@ -57,7 +57,7 @@ extern os_log_t logHandle;
//no FDA?
// next view should be 'request FDA'
if(!hasFDA()) {
if(hasFDA()) {
self.nextButton.tag = REQUEST_FDA;
}
//otherwise
@@ -69,24 +69,14 @@ extern os_log_t logHandle;
//show view
[self showView:self.welcomeView firstResponder:self.nextButton];
//center (before showing)
[self.window center];
//make key and front
[self.window makeKeyAndOrderFront:self];
//TODO:
//center
[self.window center];
//activate
if(@available(macOS 14.0, *)) {
[NSApp activate];
}
else
{
[NSApp activateIgnoringOtherApps:YES];
}
//(re)make front
[[NSRunningApplication currentApplication] activateWithOptions:(NSApplicationActivateAllWindows | NSApplicationActivateIgnoringOtherApps)];
[NSApp activateIgnoringOtherApps:YES];
return;
}
@@ -241,12 +231,6 @@ extern os_log_t logHandle;
// note: replaces old view and highlights specified responder
-(void)showView:(NSView*)view firstResponder:(NSView*)firstResponder
{
//x position
CGFloat xPos = 0;
//y position
CGFloat yPos = 0;
//not in dark mode?
// make window white
if(YES != isDarkMode())
@@ -261,15 +245,6 @@ extern os_log_t logHandle;
//update config view
self.window.contentView = view;
//center x
xPos = NSWidth(self.window.screen.frame)/2 - NSWidth(self.window.frame)/2;
//center y
yPos = NSHeight(self.window.screen.frame)/2 - NSHeight(self.window.frame)/2;
//center window
[self.window setFrame:NSMakeRect(xPos, yPos, NSWidth(self.window.frame), NSHeight(self.window.frame)) display:YES];
//make 'next' button first responder
// calling this without a timeout, sometimes fails :/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (100 * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{
@@ -285,10 +260,15 @@ extern os_log_t logHandle;
return;
}
- (IBAction)openSystemSettings:(id)sender {
//open system settings to FDA
-(IBAction)openSystemSettings:(id)sender {
//open `System Preferences`
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:@"x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles"]];
//show FDA view
[NSWorkspace.sharedWorkspace openURL:[NSURL URLWithString:@"x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles"]];
return;
}
@end
+4 -1
View File
@@ -32,6 +32,9 @@ BOOL cmdlineMode = NO;
//cmdline flag
BOOL isVerbose = NO;
//(VT) API key for cmdline scan
NSString* vtAPIKey = nil;
/* FUNCTIONS */
//print usage
@@ -41,7 +44,7 @@ void usage(void);
void version(void);
//perform a cmdline scan
void cmdlineScan(void);
void cmdlineScan(NSArray* args);
//pretty print JSON
void prettyPrintJSON(NSString* output);
+81 -34
View File
@@ -13,11 +13,13 @@ int main(int argc, char *argv[])
//status
int status = -1;
NSArray* args = NSProcessInfo.processInfo.arguments;
@autoreleasepool
{
//handle '-h' or '-help'
if( (YES == [NSProcessInfo.processInfo.arguments containsObject:@"-h"]) ||
(YES == [NSProcessInfo.processInfo.arguments containsObject:@"-help"]) )
if( (YES == [args containsObject:@"-h"]) ||
(YES == [args containsObject:@"-help"]) )
{
//print usage
usage();
@@ -26,7 +28,8 @@ int main(int argc, char *argv[])
goto bail;
}
if(YES == [NSProcessInfo.processInfo.arguments containsObject:@"-version"])
//print version
if(YES == [args containsObject:@"-version"])
{
//print usage
version();
@@ -36,19 +39,54 @@ int main(int argc, char *argv[])
}
//handle '-scan'
// cmdline scan without UI
if(YES == [NSProcessInfo.processInfo.arguments containsObject:@"-whosthere"])
if(YES == [args containsObject:@"-whosthere"])
{
//api key
NSUInteger keyIndex = NSNotFound;
//set flag
cmdlineMode = YES;
//set flag
isVerbose = [NSProcessInfo.processInfo.arguments containsObject:@"-verbose"];
isVerbose = [args containsObject:@"-verbose"];
//extract VT API key
keyIndex = [args indexOfObject:@"-key"];
if(keyIndex != NSNotFound)
{
//sanity check
if(keyIndex + 1 < args.count)
{
//grab
vtAPIKey = args[keyIndex + 1];
}
//validate
if(vtAPIKey.length == 0)
{
//usage
usage();
//done
goto bail;
}
}
//need FDA
if(!hasFDA()) {
//err msg
printf("ERROR: KnockKnock requires Full Disk Access...\n\n");
//done
goto bail;
}
//scan
cmdlineScan();
cmdlineScan(args);
//happy
status = 0;
@@ -83,9 +121,9 @@ void version(void) {
NSDictionary* info = NSBundle.mainBundle.infoDictionary;
NSString* version = info[@"CFBundleVersion"];
if (version) {
printf("%s\n", version.UTF8String);
printf("KnockKnock Version: %s\n", version.UTF8String);
} else {
printf("unknown\n");
printf("KnockKnock Version: unknown\n");
}
return;
@@ -96,19 +134,20 @@ void usage(void)
{
//usage
printf("\nKNOCKNOCK USAGE:\n");
printf(" -h or -help display this usage info\n");
printf(" -whosthere perform command line scan\n");
printf(" -version display current version of\n");
printf(" -verbose display detailed output\n");
printf(" -pretty final output is 'pretty-printed'\n");
printf(" -apple include apple/system items\n");
printf(" -skipVT do not query VirusTotal with item hashes\n\n");
printf(" -h or -help display this usage info\n");
printf(" -whosthere perform command line scan\n");
printf(" -version display current version of\n");
printf(" -verbose display detailed output\n");
printf(" -pretty final output is 'pretty-printed'\n");
printf(" -apple include Apple/System items\n");
printf(" -key <API key> your VirusTotal API key\n");
printf(" -skipVT do not query VirusTotal with item hashes\n\n");
return;
}
//perform a cmdline scan
void cmdlineScan(void)
void cmdlineScan(NSArray* args)
{
//virus total obj
VirusTotal* virusTotal = nil;
@@ -131,6 +170,9 @@ void cmdlineScan(void)
//flag
BOOL prettyPrint = NO;
//flag
BOOL queryVT = NO;
//output
NSMutableString* output = nil;
@@ -143,9 +185,6 @@ void cmdlineScan(void)
//init filter object
itemFilter = [[Filter alloc] init];
//init virus total object
virusTotal = [[VirusTotal alloc] init];
//alloc shared item enumerator
sharedItemEnumerator = [[ItemEnumerator alloc] init];
@@ -156,20 +195,29 @@ void cmdlineScan(void)
if(YES == isVerbose)
{
//msg
printf("starting scan...\n");
printf("Starting KnockKnock scan...\n");
}
//set flag
// include apple items?
includeApple = [NSProcessInfo.processInfo.arguments containsObject:@"-apple"];
includeApple = [args containsObject:@"-apple"];
//set flag
// skip virus total?
skipVirusTotal = [NSProcessInfo.processInfo.arguments containsObject:@"-skipVT"];
skipVirusTotal = [args containsObject:@"-skipVT"];
//set flag
// pretty print json?
prettyPrint = [NSProcessInfo.processInfo.arguments containsObject:@"-pretty"];
prettyPrint = [args containsObject:@"-pretty"];
//skip VT scanning if
// user disabled queries, or no API key
queryVT = (vtAPIKey.length) && !skipVirusTotal;
if(queryVT)
{
//init virus total object
virusTotal = [[VirusTotal alloc] init];
}
//init output string
output = [NSMutableString string];
@@ -215,20 +263,19 @@ void cmdlineScan(void)
printf(" found %lu %s\n", (unsigned long)plugin.allItems.count, plugin.name.UTF8String);
}
//query VT
// unless no items or user explicity says otherwise
if( (YES != skipVirusTotal) &&
(0 != plugin.allItems.count) )
//query VT?
if(queryVT)
{
//dbg msg
if(YES == isVerbose)
{
//msg
printf(" scanning via Virus Total\n");
printf(" querying VirusTotal...\n");
}
//TODO: need API key
// and then scan
//check all plugin's files
[virusTotal checkFiles:plugin apiKey:vtAPIKey uiMode:NO completion:NULL];
}
//append plugin name to output
@@ -281,8 +328,8 @@ void cmdlineScan(void)
int seconds = (int)(timeInterval - (minutes * 60));
//msg
printf("\nscan completed in %02d minutes, %02d seconds\n\n", minutes, seconds);
printf("RESULTS:\n %lu persistent items\n %lu flagged items\n\n", (unsigned long)items, (unsigned long)flaggedItems);
printf("\nScan completed in %02d minutes, %02d seconds\n\n", minutes, seconds);
printf("RESULTS:\n %lu persistent items\n %lu (VT) flagged items\n\n", (unsigned long)items, (unsigned long)flaggedItems);
}
//pretty print?