6

I'd like to detect from within my app whether the iPhone has been rebooted since last time my app was started. I need to do this because my app uses the timer since last system reboot to clock a user's time and I want to detect reboot so I can invalidate the time.

Is there anywhere I could extract the information from the system console log like reboot , crashes ? The organizer in xcode can access it , maybe I can too.

If not , can you think of other ways to get this information?

Maxm007
  • 1,190
  • 2
  • 13
  • 21

5 Answers5

15

This seems like it would work:

  • get the time since last reboot, and for this example, let's store it in a variable called 'tslr' (duration in milliseconds I guess, BTW, how do you get that?)
  • get the current time, store it in variable 'ct' for example
  • compute the last reboot time (let's call it 'lr'), we have: lr = ct - tslr
  • store 'lr'

Next time your application gets started, load the previous value for 'lr', compute the new one, and if they differ, you have detected a reboot (you'll probably have to tolerate a small difference there... a couple milliseconds perhaps).

I think it would be pretty tough to fool that... the user would have to tamper their phone time very precisely, and they would have to start your application at a very precise moment on top of that, exactly when the new 'lr' would be identical to the previous one... pretty tough to do, the probability of them being able to do that is very close to 0 I think. And you don't need any internet connection to do that...

The new 'lr' would be identical to the previous one in the following cases only:

  • phone was not rebooted, and time was not changed
  • time was tampered with, AND the user managed to start your application at the precise millisecond to fool your algorithm (chances of that happening more than ultraslim)
Zoran Simic
  • 10,293
  • 6
  • 33
  • 35
  • Brilliant. Works perfectly and at the same time detects user changing iphone date. Now I can invalidate any time a user scored that has been tampered with without network connection. Btw: you can get the time since reboot using a mach timer : http://discussions.apple.com/thread.jspa?threadID=1632831 – Maxm007 Sep 19 '09 at 21:37
  • Hmm, this mach timer is strange. Even though my iphone was on all night and didn't reboot, the timer seems to have reset. – Maxm007 Sep 20 '09 at 10:05
  • This method seems like a reliable way to get system uptime without using private APIs or system calls: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/Foundation/Classes/NSProcessInfo_Class/#//apple_ref/occ/instp/NSProcessInfo/systemUptime – Sean Dawson Apr 07 '15 at 04:52
  • Actually [[NSProcessInfo processInfo] systemUptime] doesn't work for this purpose due to: http://stackoverflow.com/questions/6052206/frozen-uptime-on-ios-iphone. CACurrentMediaTime() is a better choice. – Sean Dawson Apr 07 '15 at 06:53
  • But if the 'lr' is not identical to the previous one, how can I tell it's due to Reboot(normal way to use the app) or they tampered the system time(abnormal way to use the app) ? – Code Farmer Sep 29 '17 at 01:34
  • Getting different value for last boot time, even without reboot. Using `sysctlbyname("kern.boottime", &tv, &tvSize, nil, 0)` to get the last reboot time. – Manish Punia Jan 06 '21 at 06:44
2
    // Returns true if device has rebooted since last time
    private func deviceRebootedSinceLastTime() -> Bool {
        let userDefaults = NSUserDefaults.standardUserDefaults()
        let systemUptime = NSProcessInfo.processInfo().systemUptime;
        let timeNow = NSDate().timeIntervalSince1970
        let dateOfLastReboot = NSDate(timeIntervalSince1970: timeNow-systemUptime)

        var didDeviceRebootSinceLastTime = false

        if let storedDateOfLastReboot:NSDate = userDefaults.objectForKey("deviceLastRebootDate") as? NSDate {

            if Int(dateOfLastReboot.timeIntervalSinceDate(storedDateOfLastReboot)) < 1 { //
                print("Reboot time didn't change - date: \(dateOfLastReboot)");
            }
            else {
                print("Reboot time has changed - from: \(storedDateOfLastReboot) to \(dateOfLastReboot)");
                didDeviceRebootSinceLastTime = true

            }
        }
        else {
            print("Reboot time is saved for the first time")
            didDeviceRebootSinceLastTime = true // first time we save value
        }

        userDefaults.setObject(dateOfLastReboot, forKey: "deviceLastRebootDate")
        userDefaults.synchronize() // don't forget this!!!

        return didDeviceRebootSinceLastTime;
    }
Oded Regev
  • 4,065
  • 2
  • 38
  • 50
  • Don't use `.synchronize()`. From [Apple's documentation](https://developer.apple.com/documentation/foundation/nsuserdefaults/1414005-synchronize?language=swift) _"this method is unnecessary and shouldn't be used."_ – Ashley Mills Oct 11 '18 at 14:23
1

Zoran's answer is the right way to go; it's the closest you are going to get without a network connection. (neither the cellular subsystem, nor the syslog are accessible for security reasons)

If you are looking to prevent malicious users from generating fake time data, have some central server (or trusted local server for enterprise deployments) track time-related events for you.

rpetrich
  • 32,196
  • 6
  • 66
  • 89
0

Get and save the time either from the iPhone or from NIST and the current runtime from the BSD uptime function. For NIST time see How can I get the real time in iPhone, not the time set by user in Settings?

When you want to check for a reboot get new values of these, compute the elapsed time for each and compare the elapsed times. Based on the difference you should be able to determine a reboot.

Community
  • 1
  • 1
zaph
  • 111,848
  • 21
  • 189
  • 228
  • In my case that wouldn't work, because I can't depend on internet connection and can't depend on the iPhone time , because that can be tampered with by the user. – Maxm007 Sep 18 '09 at 13:10
  • The user can also tamper with your application. How secure do you need this to be? – Amok Sep 18 '09 at 19:11
  • No the user can't tamper easily with my app, because it is digitally signed. It definitly needs to be more secure than changing the iPhone time in Settings -> General -> Time – Maxm007 Sep 18 '09 at 21:26
-1

Here is one I made. It takes the current time in GMT and the time since last reboot to extrapolate a date for when the device was last restarted. Then it keeps track of this date in memory using NSUserDefaults. Enjoy!

Note: Since you want to check this since last time app was started, you need to make sure you call the method anytime the app is started. The easiest way would be to call the method below in +(void)initialize { and then also whenever you need to check it manually

#define nowInSeconds CFAbsoluteTimeGetCurrent()//since Jan 1 2001 00:00:00 GMT
#define secondsSinceDeviceRestart ((int)round([[NSProcessInfo processInfo] systemUptime]))
#define storage [NSUserDefaults standardUserDefaults]
#define DISTANCE(__valueOne, __valueTwo) ((((__valueOne)-(__valueTwo))>=0)?((__valueOne)-(__valueTwo)):((__valueTwo)-(__valueOne)))

+(BOOL)didDeviceReset {
    static BOOL didDeviceReset;
    static dispatch_once_t onceToken;
    int currentRestartDate = nowInSeconds-secondsSinceDeviceRestart;
    int previousRestartDate = (int)[((NSNumber *)[storage objectForKey:@"previousRestartDate"]) integerValue];
    int dateVarianceThreshold = 10;
    dispatch_once(&onceToken, ^{
        if (!previousRestartDate || DISTANCE(currentRestartDate, previousRestartDate) > dateVarianceThreshold) {
            didDeviceReset = YES;
        } else {
            didDeviceReset = NO;
        }
    });
    [storage setObject:@(currentRestartDate) forKey:@"previousRestartDate"];
    [storage synchronize];
    return didDeviceReset;
}
Albert Renshaw
  • 17,282
  • 18
  • 107
  • 195
  • Don't use `-[NSUserDefaults synchronize]`. From [Apple's documentation](https://developer.apple.com/documentation/foundation/nsuserdefaults/1414005-synchronize?language=objc) _"this method is unnecessary and shouldn't be used."_ – Ashley Mills Oct 11 '18 at 14:25
  • @AshleyMills Our app supports iOS6+ so we need to have synchronize incase user enters background shortly after (attempting to force quit app to bypass device reset detection). Synchronize only became un-necessary after iOS7. Please don't downvote for things like this – Albert Renshaw Oct 11 '18 at 18:07