I've tried several techniques to capture a screenshot of an app from within that app. None of the techniques appear to capture the status bar -- it ends up being black.
There apparently was once a way to do this, but that interface is internal and Apple will not let you use it.
Any ideas?
Note: This is an attempt to solve this problem, where I need to determine if airplane mode is on or off (and no, simply knowing if the network is reachable is not sufficient).
However, it would seem that this question is of more general interest, and is distinct from that question.
Your actual issue, determining if a network interface is active, can be resolved with BSD networking functions. BEHOLD.
#include <sys/socket.h>
#include <ifaddrs.h>
#include <net/if.h>
BOOL IsNICTurnedOn(const char *nicName) {
BOOL result = NO;
struct ifaddrs *addrs = NULL;
if (0 == getifaddrs(&addrs)) {
for (struct ifaddrs *addr = addrs; addr != NULL; addr = addr->ifa_next) {
if (0 == strcmp(addr->ifa_name, nicName)) {
result = (0 != (addr->ifa_flags & (IFF_UP | IFF_RUNNING)));
break;
}
}
freeifaddrs(addrs);
}
return result;
}
To use this function:
BOOL isWWANEnabled = IsNICTurnedOn("pdp_ip0");
BOOL isWiFiEnabled = IsNICTurnedOn("en0");
At this point it seems clear that there is no simple way to detect if Airplane Mode is enabled. Although you could probably infer it by looking at low-level network stack info or scraping status bar pixels, either method would be relying on undocumented behavior. It's very possible that on a future release of iOS or a future iOS device, the behavior will change and your code will generate a false positive or false negative.
(Not to mention that, on future devices, the interference may not even be there.)
If I were in your shoes, I would:
File a bug to let Apple know you want this feature.
Work the notice into the app, regardless of whether Airplane Mode is enabled. Yes, it might be kind of annoying to the user if it is enabled, but the overall harm is minimal. I would probably make this an alert that pops up only once (storing a key in NSUserDefaults to indicate whether its already been displayed).
If you want to get super-fancy, analyze the recorded audio and, if the buzz is detected, remind the user again to enable Airplane Mode while recording. You could do this in real time or after the clip has been recorded, whatever makes more sense for your app.
As an alternative solution, perhaps you could detect the connection type, similar to: https://developer.apple.com/library/ios/#samplecode/Reachability/Introduction/Intro.html . With some additional checking for the device type, you could then warn the user only in the case where they need to act.
Kind of a different approach, but you can also link to pages within the Settings application. You could perhaps link to the primary page and tell the user the changes you require.
There appears to be no way to do this.
Related
I have two BluetoothHFP bluetooth devices connected to my iPad(bluetoothA2DP and bluetoothLE) and I need to detect which one is currently getting the audio. Below is the code I am using to detect what bluetooth is available :
let currentRoute = audioSession.currentRoute
for description in currentRoute.outputs {
if convertFromAVAudioSessionPort(description.portType) == convertFromAVAudioSessionPort(AVAudioSession.Port.bluetoothA2DP) {
//Do Something
break
}else if convertFromAVAudioSessionPort(description.portType) == convertFromAVAudioSessionPort(AVAudioSession.Port.bluetoothHFP) {
//Do Something
break
}else if convertFromAVAudioSessionPort(description.portType) == convertFromAVAudioSessionPort(AVAudioSession.Port.bluetoothLE){
//Do Something
break
}
}
What can I use to find out what one of the BluetoothHFP devices is currently getting audio?
You can distinguish between ports of the same type using description.portName and description.uid. Note that the name is not promised to be unique (it comes from the device). The UID is system-assigned and is not promised to be stable. It's only promised to be consistent with the owningPortUID property, and will be unique at any given time.
It happens to be true currently (iOS 13) that the UID is based on the hardware MAC address, and is stable. It's in the format aa:bb:cc:dd:ee:ff-tacl (the MAC address followed by -tacl). This has been true for a long time. I've been using this fact since at least iOS 8, and it's very likely it's been true as long as AVAudioSession has been around. But it's not promised, and is exactly the kind of thing that Apple has been known to change without notice.
"Low Data Mode" is introduced in iOS 13. See "Settings" section Apple's iOS 13 overview:
I couldn't find any developer documentation on this.
Is this something third party app developers can opt into, as suggested by MacRumors? Or is will it just suspend background activity when not connected to Wi-Fi, as suggested by AppleInsider?
To determine if iOS is currently in Low Data mode, you can use the Network library:
import Network // Put this on top of your class
let monitor = NWPathMonitor()
monitor.pathUpdateHandler = { path in
if path.isConstrained {
// Path uses an interface in Low Data Mode.
}
else if path.isExpensive {
// Path uses an interface that is considered expensive, such as Cellular or a Personal Hotspot.
}
}
monitor.start(queue: DispatchQueue.global(qos: .background))
URLSession supports LowData mode in iOS 13.
Steps
Provide two different resources for high resolution and low resolution(low data mode)
If statusCode == 200 (low data mode is disabled in setting).
If error.networkAvailableReason == .constrained (low data mode is enable in settings)
Check out Advances in Networking, Part 1 from WWDC 2019 from 16:00 for a demo and sample code. You can use Combine to make the code simpler, but this is not required.
First of all, you need to configure your URLSession (NSURLSession) to either allow or disallow expensive or constrained network connections to be opened.
You can do that by changing URLSession's respective properties allowsExpensiveNetworkAccess or allowsConstrainedNetworkAccess to false (NO).
If a URLSessionTask results in an error that is either an NSURLErrorNotConnectedToInternet error containing NSURLErrorNetworkUnavailableReasonKey entry in userInfo (Objective-C) or an URLError with a non-nil NetworkUnavailableReason property set (Swift), then you need to act accordingly.
Those reasons can be:
expensive
constrained
cellular
The cellular reason has sort of been there since iOS 7, so it's not new, but the entire reason enumeration is, as Apple streamlined connection type handling a bit this year.
Here is the solution in Xamarin, for those interested:
NWPathMonitor monitor = new NWPathMonitor();
monitor.SetUpdatedSnapshotHandler(path =>
{
if (path.Status == NWPathStatus.Satisfied)
{
if(path.IsConstrained)
{
// Path uses an interface in Low Data Mode.
}
}
});
monitor.SetQueue(CoreFoundation.DispatchQueue.DefaultGlobalQueue);
monitor.Start();
I am building an iOS app that transmits sensitive data to my server, and I'm signing my API requests as an additional measure. I want to make reverse engineering as hard as possible, and having used Cycript to find signing keys of some real-world apps, I know it's not hard to find these keys by attaching to a process. I am absolutely aware that if someone is really skilled and tries hard enough, they eventually will exploit, but I'm trying to make it as hard as possible, while still being convenient for myself and users.
I can check for jailbroken status and take additional measures, or I can do SSL pinning, but both are still easy to bypass by attaching to the process and modifying the memory.
Is there any way to detect if something (whether it be Cycript, gdb, or any similar tool that can be used for cracking the process) is attached to the process, while not being rejected from App Store?
EDIT: This is not a duplicate of Detecting if iOS app is run in debugger. That question is more related to outputting and it checks an output stream to identify if there's an output stream attached to a logger, while my question is not related to that (and that check doesn't cover my condition).
gdb detection is doable via the linked stackoverflow question - it uses the kstat to determine if the process is being debugged. This will detect if a debugger is currently attached to the process.
There is also a piece of code - Using the Macro SEC_IS_BEING_DEBUGGED_RETURN_NIL in iOS app - which allows you to throw in a macro that performs the debugger attached check in a variety of locations in your code (it's C/Objective-C).
As for detecting Cycript, when it is run against a process, it injects a dylib into the process to deal with communications between the cycript command line and the process - the library has part of the name looking like cynject. That name doesn't look similar to any libraries that are present on a typical iOS app. This should be detectable with a little loop like (C):
BOOL hasCynject() {
int max = _dyld_image_count();
for (int i = 0; i < max; i++) {
const char *name = _dyld_get_image_name(i);
if (name != NULL) {
if (strstr(name, "cynject") == 0) return YES;
}
}
}
Again, giving it a better name than this would be advisable, as well as obfuscating the string that you're testing.
These are only approaches that can be taken - unfortunately these would only protect you in some ways at run-time, if someone chooses to point IDA or some other disassembler at it then you would not be protected.
The reason that the check for debugger is implemented as a macro is that you would be placing the code in a variety of places in the code, and as a result someone trying to fix it would have to patch the app in a variety of places.
Based on #petesh's answer, I found the below code achieved what I wanted on a jailbroken phone with Cycript. The existence of printf strings is gold to a reverse engineer, so this code is only suitable for demo / crack-me apps.
#include <stdio.h>
#include <string.h>
#include <mach-o/dyld.h>
int main ()
{
int max = _dyld_image_count();
for (int i = 0; i < max; i++) {
const char *name = _dyld_get_image_name(i);
const char needle[11] = "libcycript";
char *ret;
if ((ret = strstr(name, needle)) != NULL){
printf("%s\nThe substring is: %s\n", name, ret);
}
}
return 0;
}
As far as I know, Cycript process injection is made possible by debug symbols. So, if you strip out debug symbols for the App Store release (the default build setting for the Release configuration), that would help.
Another action you could take, which would have no impact on the usability of the App, would be to use an obfuscator. However, this would render any crash reports useless, since you wouldn't be able to make sense of the symbols, even if the crash report was symbolicated.
I know it is possible to check the traffic usage of wifi and Data through built-in settings in iphone. But is there any solution like a library or function to fetch realtime network usage (upload/download) of the device? (like a real time netstat)
Reachability is one of the things that I found with regard to the mentioned.
http://developer.apple.com/library/ios/#samplecode/Reachability/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007324-Intro-DontLinkElementID_2
this app does something similar:
https://itunes.apple.com/us/app/data-control-manage-data-usage/id390646992?mt=8
Does anyone know what class to call or look for what certain method to access the real time traffic?
I hope that my question is understandable.
There is no high level API to support this, but since iOS is simply a rather pretty unix, you can of course go to the standard APIs.
In this case you would be looking for getifaddrs, which retrieves the in memory structures describing the various interfaces on the device.
You then have to parse these C structures, to get the interface details you want.
The getifaddr interface is documented ( man getifaddr), so you can use this.
I'll add a bit of code, but first a few caveats.
Apple don't really bother maintaining this stuff, so the network counts are 32 bit, so you have to handle 4Gb roll over.
The packet counts are mostly there, I think the tx packet count is always 0, but who is interested in packet counts anyway.
You can probably find better code here by searching for getifaddrs, but here is a short version to get you started.
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <net/if_var.h>
struct ifaddrs* addrs;
int getIFAddrsResult = getifaddrs(&addrs);
if (getIFAddrsResult == 0)
{
struct ifaddrs* addrsPtr = addrs; // need to keep original for freeifaddrs
while (addrsPtr != NULL)
{
NSString *name = [NSString stringWithFormat:#"%s",addrsPtr->ifa_name];
//NSLog(#"ifa_name %s == %#\n", cursor->ifa_name,name);
// names of interfaces: en0 is WiFi ,pdp_ip0 is WWAN
if (addrsPtr->ifa_addr->sa_family == AF_LINK)
{
const struct if_data* stats = (const struct if_data *) addrsPtr->ifa_data;
// We only consider the active data interface, and that will normally be the '0' interface.
if ([name isEqualToString:#"en0"])
{
// Wifi
// look at stats->ifi_obytes & ifi_ibytes
} else if ([name isEqualToString:#"pdp_ip0"])
{
// mobile broad band
} else {
// could be bluetooth, usb, ad hoc wifi, out of band signalling...
}
}
addrsPtr = addrsPtr->ifa_next;
}
freeifaddrs(addrs);
} else {
NSLog(#"getifaddrs failed with error : %s", strerror(getIFAddrsResult));
return nil;
}
P.S. I'm going for my diamond resurrect old questions badge :)
There is no such API in iOS, the App that you mentioned (and all similar apps), rely on an API provided by the carrier (or parsing the carriers website). If you want to implement this kind of App, I'm afraid you have to find something for every possible carrier you are interested in.
I'm working on a little hack sending MIDI messages from an app using RtMidi as a wrapper for CoreMIDI on OS X. I use RtMidiOut::openVirtualPort("MyAwesomePort") so I can select my app as an input source in a DAW.
However, if my program closes and I open it again, my DAW does not recognize the input device as the same port, despite being given the same name.
I was originally using pyrtmidi, so went and verified the behavior writing in C++ directly with RtMidi. "My DAW" in this case is Reaper 4, but I've duplicated the behavior in Pro Tools, Logic, and MuLab.
I know it's possible to retain some uniqueness of a virtual midi port, since MidiKeys behaves just as I'd like my application to behave: my DAWs remember it even if MidiKeys closes and re-opens while my DAW is still running.
So I dug into the RtMidi source, and the CoreMIDI wrapper seemed straightforward enough. All that the MIDISourceCreate asks for is a string. The client parameter is (what I presume after browsing the docs) an identifier for my application, it being a client of the CoreMIDI services.
void RtMidiOut :: openVirtualPort( std::string portName )
{
CoreMidiData *data = static_cast<CoreMidiData *> (apiData_);
if ( data->endpoint ) {
errorString_ = "RtMidiOut::openVirtualPort: a virtual output port already exists!";
error( RtError::WARNING );
return;
}
// Create a virtual MIDI output source.
MIDIEndpointRef endpoint;
OSStatus result = MIDISourceCreate( data->client,
CFStringCreateWithCString( NULL, portName.c_str(), kCFStringEncodingASCII ),
&endpoint );
if ( result != noErr ) {
errorString_ = "RtMidiOut::initialize: error creating OS-X virtual MIDI source.";
error( RtError::DRIVER_ERROR );
}
// Save our api-specific connection information.
data->endpoint = endpoint;
}
So I looked at the MIDISourceCreate documentation, and read this:
After creating a virtual source, it's a good idea to assign it the same unique ID it had the last time your application created it. (Although you should be prepared for this to fail in the unlikely event of a collision.) This will permit other clients to retain persistent references to your virtual source more easily.
This seems like exactly what I'm looking for. Except I have no idea how to assign the source a unique ID. The out parameter for MIDISourceCreate is a MIDIEndpointRef, which according to the docs is just typedef'd to a UInt32 down the line. So I hypothesized that maybe I should keep track of this UInt32, but that seems like a bad idea.
After digging through all of this I feel like I'm hitting a bit of a brick wall. How do I retain the uniqueness of my MIDI port in between runs of my application?
According to the docs,
kMIDIPropertyUniqueID
The system assigns unique ID's to all objects. Creators of virtual endpoints may set this property on their endpoints, though doing so may fail if the chosen ID is not unique.
So maybe something like this:
// Try to set the ID if it's saved.
if (savedUniqueId) {
OSStatus result = MIDIObjectSetIntegerProperty(endpoint, kMIDIPropertyUniqueID, myUniqueId);
if (result == kMIDIIDNotUnique) {
savedUniqueId = 0;
}
}
// If not saved, record the system-assigned ID
if (!savedUniqueId) {
OSStatus result = MIDIObjectGetIntegerProperty(endpoint, kMIDIPropertyUniqueID, &savedUniqueId);
// Handle the error?
}
The unique ID is typedefed to a SInt32. I've made the assumption that 0 is an invalid unique ID, which is at least true for connections (the docs for kMIDIPropertyConnectionUniqueID say it's "non-existant or 0 if there is no connection").
I'm not sure how you maintain long-term uniqueness with only 32 bits, but it'll hopefully be sufficient for relaunches of your app.