‘fejlesztés’ kategória cikkei

Milyen egy jó App Store-os alkalmazás?

csütörtök, április 4th, 2013

Vendég blogger rovatunkban @krasznai kolléga meséli el, hogy szerinte milyen egy jó App Store-os alkalmazás.

images

1., Csinálj egy jó alkalmazást
Ez elég triviálisnak tűnhet, de a tapasztalatok alapján mégsem az. Az éppen aktuálisan elérhető alkalmazások egy jelentős hányada katasztrofális minőségben elkészített fércmű. Egyrészt még mindig sokan gondolják azt, hogy elég egy bármilyen minőségben elkészített vacakot feltenni a Store-ba és máris milliomosok lesznek, másrészt sok fejlesztőnek a képességei hiányoznak a minőségi munkához. Ha találomra kiválasztunk egy kategóriát és letöltünk belőle 100 db alkalmazást, a tesztelést követően elég lesújtó véleménnyel leszünk az átlagosan elérhető minőséget illetően.

2., Legyen a design szenzációs
Ez majdnem megegyezik az előző ponttal, de azért másról van itt szó. Ha megnézzük az újonnan kijövő, legmenőbb alkalmazásokat, azt látjuk, hogy nem elsősorban tartalmában hoz újítást (persze van olyan is), hanem sokszor a design irányából tartalmaz valami olyan pluszt, amiért végül sikert arat a felhasználók körében. Számtalan egyszerű példát lehet találni, amitől szenzációsan fog kinézni az alkalmazásunk.

3., Ikon, ikon, ikon
Az emberek nem nagyon szeretnek olvasni. Sokszor impulzusok, felületes benyomások alapján hoznak döntéseket és ez az alkalmazásletöltésre fokozottan igaz. A felhasználó beír valamit a keresőbe, végigpörget 10-20-50 találatot és nem bíbelődik a leírásokkal. A képernyőmentések (lásd előző pont) illetve sokszor mindössze az ikonok alapján próbálják ki az egyes alkalmazásokat. És hogy milyen egy jó ikon? Például itt, egy helyen lehet a legjobbak közül válogatni.

4., Tudd értelmezni az értékesítési számaidat
Az Apple-től napi, heti, havi illetve éves bontásban kapott sales adatok első ránézésre nem tartalmaznak túl sok hasznos információt, ugyanakkor ha az exceles varázslásokból egyet továbblépünk és profi sales analitikai eszközökkel vizsgáljuk a rendelkezésünkre álló adatokat, akkor meglepően színes kép tárulhat elénk. Az elmúlt években sokat fejlődött ez a piac (is). Ezek a szolgáltatások ma már a legkülönbözőbb bontásban és kontextusban teszik láthatóvá saját adatainkat. Egy-két példa az eszközök közül: App Annie, Mopapp, Apptrace.

5., Ismerd meg a saját felhasználóidat
Mindenképpen érdemes az értékesítési adatokon túlmenően minél többet megtudnunk a saját felhasználóinkról. A felhasználóink (kor és/vagy nem szerinti) megoszlása, a készülékek összetétele, az operációs rendszer szegmentációja, a lokáció adatok, a hálózati elérhetőségek, a képernyő méretek/felbontások illetve a userek alkalmazáson belüli viselkedése mind-mind elérhető adatok számunkra. Használjuk őket az alkalmazásunk továbbfejlesztése és finomhangolása érdekében. Részletesebben a témáról itt írtam.

És végül, de nem utolsó sorban:

6., Maximalizáld a kulcsszavakban rejlő lehetőségeket
Sokan csak erre az utolsó pontra gondolnak, illetve koncentrálnak az optimalizálás folyamatának keretében. Ez előző öt pont figyelembevétele nélkül nincs igazán értelme a kulcsszavakkal bíbelődni. Az App Store keresőjében sajnos nem a relevancia az elsődleges rendező elv, hanem a letöltési számok. A kulcsszavak gondos kiválasztásával illetve optimalizálásával mindössze azon tudunk segíteni, hogy a meglévő értékesítési számaink alapján minél többször kerüljünk a felhasználók látóterébe. Az, hogy ebből aztán letöltés, majd tényleges, érdemleges alkalmazáshasználat is legyen, az összes pontot komolyan kell vennünk.

Ha a bejegyzés felkeltette az érdeklődésedet, további írások az App Store SEO oldalon olvashatók. A cikk szerzőjének elérhetősége: @krasznai.

Kapcsolódó cikkek:

Miért fontos és hogyan lehet optimalizálni az iOS App Store megjelenésünket?

kedd, március 19th, 2013

Vendég blogger rovatunkban most @krasznai kolléga meséli el, hogy mennyire fontos az App Store SEO.

images

Az Apple hivatalos jelentése szerint is már több mint 775.000 alkalmazás érhető el az App Store-ban. Ilyen mennyiségű alkalmazás esetén már meglehetősen nehéz kitűnni a több mint 100.000 fejlesztő munkája közül. Ez már nem a 2008-2009-es aranyláz, mikor egy egyszerű fingós alkalmazással lehetett jelentős pénzt csinálni. Minőségi alkalmazásra és átgondolt stratégiára van szükség a termék bevezetésekor.

Természetesen a különböző optimalizálási stratégiák akkor kerülnek jobban előtérbe, mikor a marketing büdzsé nem éri el azt a kritikus szintet, amivel hatást lehet gyakorolni a piacra.

Kutatások szerint a felhasználók 63%-ban a kereső használatával találnak rá az általuk letöltött alkalmazásokra így a keresőben rejlő lehetőségek maximális kiaknázásával jelentősen növelhető a letöltések száma illetve az árbevétel.

appstore-search

Az Apple mobil alkalmazásboltja egy zárt rendszer. Kereséskor az eredmények sorrendjét nem különböző egymásra hivatkozások, hanem alapvetően a letöltések mennyisége alapján határozzák meg. Amelyik alkalmazást többen töltik le, az a keresőben előrébb kerül. Ez a rendező elv némiképp változott a Chomp 2012-es akvizícióját követően, de a tényleges szempontrendszer nem nyilvános.

Az Apple a kereséseknél a fejlesztő nevét, az alkalmazás elnevezését, az alkalmazáshoz megadott kulcsszavakat illetve az In-App Purchase elnevezéseket veszi figyelembe. Tekintettel arra, hogy meglehetősen limitált karakterről van szó, így nagy körültekintéssel kell kiválasztanunk a megfelelő értékeket. Ezeket az értékeket érdemes hát úgy megadnunk, hogy azok tartalmilag relevánsak legyenek az alkalmazásunkkal, azokra vélhetően sokan keressenek majd és végül de nem utolsó sorban melyekre kevés találatot ad az App Store.

itunes-seo-1-595x310

A különböző optimalizálást segítő eszközök mindegyike a fenti szempontrendszer minél hatékonyabb megvalósításában segítenek.

Az egyik ilyen optimalizálást segítő eszköz az App Store Rankings, ahol a regisztrációt követően, relatív normális árazás mellett tudjuk kiszűrni a teljesen felesleges kulcsszavakat, ellenőrizni a már felhasznált kulcsszavak értékét, monitorozni a versenytársak kulcsszavait, különböző szempontok alapján generált javaslatokat kapni a jövőben használatos kulcsszavakról, illetve közvetlen annak felhasználása előtt optimalizálni a kulcsszavainkat.

Az optimalizálási folyamatnak a lényegi része abban rejlik, hogy a változtatások után újra megy újra átvizsgáljuk az eredményeket illetve a jövőbeni lehetőségeket egyaránt. Ennél a szolgáltatásná erre is lehetőségünk nyílik.

Ha a bejegyzés felkeltette az érdeklődésedet, további írások az App Store SEO oldalon olvashatók. A cikk szerzőjének elérhetősége: @krasznai.

Te honnan szoktál értesülni az új alkalmazásokról?

Eredmények megtekintése

Loading ... Loading ...

Kapcsolódó cikkek:

Hasznos git repók iOS alkalmazás fejlesztőknek

hétfő, március 18th, 2013

A többek között magyar fejlesztésekkel foglalkozó Canecom oldalán találtam ezt a leírást. Főleg magyar fejlesztőknek jöhet jól az alábbi felsorolás. :)

devpost

A fejlesztő számára mindig nagy kérdés az, hogy egy meglévő megoldást használjon-e munkája során, vagy inkább nulláról írjon egy sajátot. Általában igaz az, hogy a saját fejlesztés jobb, hiszen azt ismerni fogja a későbbiekben és nem kell bajlódni egy meglévő rendszer betanulásával, azonban mégis vannak olyan esetek amikor egy kiforrott open source lib használata kifizetődőbb. Hogy mégis miért? Mert ezzel időt takarít meg a programozó, és több energiát fektethet egyéb problémák megoldásába. Felmerül akkor a kérdés, mégis mi az amit érdemes saját kezűleg megvalósítani, és mi az amire érdemes keretrendszert, forráskódot, csomagot (bármilyen meglévő eszközt) keresni. Ehhez a döntéshez próbál néhány hasznos github repót adni ez a cikk.

1. https://github.com/rs/SDWebImage

Amennyiben mobil alkalmazásunk online adatforrásokat használ, jelen esetben képeket tölt le, akkor érdemes megfontolni az SDWebImage használatát. Nagyon egyszerűen képes UIImageView-ban online képeket megjeleníteni. Gyors, aszinkron, nem blokkolja a fő szálat, a legújabb technológiákkal készült (GCD, ARC), használata pedig igazán egyszerű. Integrálása gyorsan elvégezhető ezek után pedig roppant kényelmes megoldást nyújt a fejlesztés során.

2. https://github.com/shiki/STableViewController

Manapság az iOS alkalmazásokban népszerű megvalósítás, hogy ha egy táblázatot megfogunk és lefele húzunk akkor annak tartalma automatikusan frissül. A másik pedig az úgy nevezetett infinite scrolling technika. Ennek az osztálynak a használatával mindkettő gyerekjáték.

3. https://github.com/romaonthego/REComposeViewController

Aki iOS5-re fejleszt és szeretne közösségi megosztást az alkalmazásba integrálni, azoknak lehet nagyszerű döntés ez a forrás. Egyszerűen testre szabható blokkos megoldást biztosít ez a kód. Néhány alap beállítás után már teljesen egyedi felbukkanó ablakokat hozhatunk létre, és postolhatjuk kedvenc közösségi fiókunkra az app tartalmát.

4. https://github.com/nicklockwood/StandardPaths

Végtelenül egyszerű és hasznos kis osztályról van itt szó. Egyszerű elérési útvonalakat ad vissza, nem kell bajlódni a rendszerben található könyvtárakkal többé.

5. https://github.com/honcheng/iOSPlot

Aki ismeri a google által hostingolt core-plot http://code.google.com/p/core-plot/ keretrendszert az már tudja is miről van szó. Ez egy diagram rajzoló minimál keretrendszer. Akinek nincs szüksége a core-plot által nyújtott robosztus megoldásokra, annak ez a csomag igazán hasznos tud lenni. Használata egyszerű, támogatja az ARC-ot, iOS5 alatt fut. Egy apró megjegyzés, hogy még nem frissítették benne az iOS6 alatt elavultá vált állandó neveket. (pl. UITextAlignmentCenter)

6. https://github.com/edgecase/ECSlidingViewController

Szintén divatos megoldás a jobbra balra swipe-olással elérhető tartalom. Ennek a megvalósítását segíti elő ez a forrás.

7. https://github.com/domness/DWTagList

Egyszerű megoldás címkék rajzolásához. Könnyen integrálható UIView kiegészítés. Két fájl az egész, jelenleg nem találtam ennél jobbat, vagy hasznosabbat. Érdemes kipróbálni.

8. https://github.com/baztokyo/foursquare-ios-api

A foursquare közösségi hálózat nem foglalkozott azzal, hogy saját iOS keretrendszert adjon ki, helyette ez a kis repó jelent meg. Nagyszerűen integrálva van minden fő funkció. Könnyedén tudunk bejelentkeztetni, “becsekkolni”, képet megosztani, helyszíneket lekérni. Annyi szépséghibája van, hogy nem ARC-ra készült, illetve iOS5 alatt is működik feltéve ha egy JSON keretrendszert is használunk mellette.

9. https://github.com/tonymillion/Reachability

Szeretnéd tudni, hogy van-e Wi-Fi / Bluetooth / hálózati kapcsolat az iOS eszközödön? Mi sem egyszerűbb. Ez az osztály a legtöbb ilyen jellegű kérdésre gond nélkül megadja a válszt.

10. https://github.com/tomaz/appledoc

Végül pedig, de nem utolsó sorban, a dokumentáció gyártás fontosságára alapozva itt egy könyvtár ami automatikusan képes generálni a meglévő kódból apple stílusú dokumentációt. Használata roppant egyszerű, és ha fordítunk rá némi energiát akkor a project befejezésekor rögtön egy teljes fejlesztői dokumentáció is a rendelkezésünkre áll.

A Canecom csapat magyar fejlesztései:

Olimpia TV Olimpia TV
Ár: 0 EUR
Méret: 6.01 MB
Verzió: 1.1
Értékelés: (236 Ratings)
show in iTunes

Sziget 2012 Sziget 2012
Ár: 0 EUR
Méret: 5.53 MB
Verzió: 2.0.1
Értékelés: (24 Ratings)
show in iTunes

Balaton Sound 2012 Balaton Sound 2012
Ár: 0 EUR
Méret: 6.26 MB
Verzió: 2.0
Értékelés: (24 Ratings)
show in iTunes

Jogtár Mobil Jogtár Mobil
Ár: 0 EUR
Méret: 4.59 MB
Verzió: 2.0
Értékelés: (44 Ratings)
show in iTunes

Kapcsolódó cikkek:

iOS-fejlesztés másképp, avagy hogyan készítsünk cydiás tweakeket – 3. rész: reverse engineering alapok

szerda, február 20th, 2013

Tweakfejlesztésről szóló sorozatunk harmadik részében abba vezetjük be a nyájas Olvasót, hogyan is lehet belenézni az operációs rendszer vagy egy alkalmazás belsejébe, hogyan lehet kikövetkeztetni a működését, illetve hogyan tudjuk módosítani azt.

Screen Shot 2013-02-19 at 7.05.15 PM

Aki már olvasott valamilyen IT-biztonsággal foglalkozó cikket, az bizonyosan belefutott a „reverse engineering” kifejezésbe. Bizonyosan olyan is van köztünk, akit pont az ilyen szép szavak tántorítottak el attól, hogy az ilyesmibe belevágjon, netán utánanézzen, mit is jelent ez. :D

Mi is tehát a reverse engineering valójában?

Nos, ez egy folyamat, melynek során valamilyen számítógépes programot, kódot, kommunikációs protokollt, titkosítási (vagy bármilyen más) algoritmust visszafejtünk, azaz megkeressük: mit, hogyan és miért csinál. Legvégül egy átfogó, részletes képet kaphatunk arról, hogy mi a célja a szóban forgó programnak, és azt milyen módon éri el. A reverse engineering egyik legfontosabb tulajdonsága, hogy általában akkor van szükségünk ennek a technikának a használatára, ha a visszafejtendő, megismerni kívánt program vagy bármilyen más komponens eredeti „forrása” nem ismert (itt programok esetén a forráskódról van szó, az előbb említett hálózati protokollok esetében ez a továbbított adatok formátuma, de egy szimmetrikus kulcsú kódolás esetén akár egészen egyszerűen két nagy prímszámot is jelenthet).

A reverse engineering felhasználási területe, amint azt a fentiekből is láthatjuk, igen széles. Szoftverek feltörésétől, crackelésétől kezdve érzékeny adatok megszerzésén át egészen a MobileSubstrate-kiegészítők fejlesztéséig.

Miért jó ez, vagy miért van rá szükségünk?

A válasz egyszerű, de annál kiábrándítóbb (mint oly sok minden az életben): az iOS legnagyobb része zárt forrású, tehát például a SpringBoard vagy a rendszeralkalmazások kódja nem áll a külsős fejlesztők rendelkezésére. Ha tehát mondjuk egy olyan kiegészítőt szeretnénk készíteni, ami valamilyen plusz funkcióval ruházza fel a Fényképező alkalmazás exponálógombját, akkor első ránézésre fogalmunk sincsen arról, hogy konkrétan a kódban melyik osztály melyik metódusa hívódik meg annak megnyomásakor, hiszen nem ismerjük az alkalmazás forráskódját. Ezért tehát szükség van valamire (egy eszközre, módszerre), aminek segítségével ezt ki tudjuk küszöbölni, és az eredeti forráskód nélkül is megtudjuk, mi történik futásidőben. Ez pedig egy kevés nagyon alapszintű reverse engineering.

Konkrétumok

A reverse engineering egy program esetén elég nehéz feladat lehet: általános esetben a folyamat a bináris (végrehajtható) fájl assemblyvé való visszakonvertálása (disassembly) után nem automatizálható. A visszakapott assemblyből a fejlesztőnek/reverse engineernek kell kikövetkeztetni, hogy mit csinál a program. Ehhez a legáltalánosabban használt eszköz egy interaktív („intelligens”) disassembler, az IDA Pro. Ez a szoftver támogatja az iOS-alkalmazások formátumát és architektúráját (Mach-O és ARM) is.

Az iOS-es tweakek fejlesztése során viszont szerencsére általában nem szükséges, köszönhetően az Objective-C nyelv dinamikus jellegének. A dinamizmus megvalósításához a compilernek ugyanis rengeteg kiegészítő információt kell tárolnia a programfájlban, amik más nyelvek (például C, C++) esetén gyakran teljesen elvesznek a gépi kódra való fordítás során. Ilyenek az objektumok és osztályok típusa, a meghívott metódusok neve, stb. Ezeknek az adatoknak a formátuma és a Mach-O fájlokban való tárolási módja jól dokumentált. Ennek eredményeképp lehetségessé válik egy olyan eszköz megírása, ami egy végrehajtható fájlból kinyeri az összes Objective-C-vel kapcsolatos információt, és készít egy listát a fájlban lévő osztályokról, (többé-kevésbé) helyes deklarációk, header fájlok formájában. Ez a program pedig a „class-dump”.

Hogyan használjuk a class-dumpot?

Pontosabban a „class-dump-z”-t – ez az eredeti class-dump programnak egy átírt, javított és kibővített változata. Letöltése után (fut OS X-en, iOS-en, Linuxon és Windowson is) argumentumok nélkül elindítva kapunk egy kis segítséget, amelyből a következő fontosabb paramétereket kell kiemelni:

class-dump-z -z -p -R -b -H -o <DIR> <FILE>

A „-H” kapcsoló a „-o”-val együtt azt mondja meg a class-dumpnek, hogy minden egyes visszafejtett osztály deklarációját külön fájlba írja. Ez egy általánosan elfogadott konvenció objektumorientált programozási nyelvek esetén, hiszen megkönnyíti a program struktúrájának áttekintését.

A „-p” a property, azaz tulajdonság szóra utal: az Objective-C nevezéktanának megfelelő nevű párban álló metódusokat –foo és –setFoo: alakban való megadás helyett az Objective-C 2.0 verziójában bevezetett @propertszintaxissal deklarálja.

Az „-R” és a „-b” paraméter csak egy kicsit olvashatóbbá teszi a kódot: előbbi a pointer qualifier néven ismert csillag karakter helyét határozza meg (a szemantikának jobban megfelelő TYPE *ptr; formát használva a talán kevésbé szép TYPE* ptr; helyett), utóbbi pedig egy szóközt illeszt a metódusneveket megelőző + és – jelek után. Természetesen ezeken kívül számtalan hasznos opció van még, amit használhatunk, ezekkel érdemes a program helpje alapján kísérletezni egy kicsit, mindenki másokat tarthat szükségesnek.

Játék és nómenklatúra tíz percben

Amint azt említettem, a class-dump program nem generál teljes implementációs fájlokat, kizárólag típusdefiníciókat és osztálydeklarációkat kapunk. Éppen ezért fontos ismernünk a Cocoa Touch frameworkök nevezéktani konvencióit, hogy magába a végrehajtott kódba való belepillantás nélkül, pusztán az osztályok és metódusok nevei alapján rekonstruálni tudjuk a kód logikáját. Álljon tehát itt a nevezéktan néhány legfontosabb alappillére:

1. Tulajdonságok (Properties): a tulajdonságok esetén a getter metódusok neve a mögöttes változóval egyező nevű (kivéve, ha a változó neve aláhúzásjellel kezdődik, mert akkor a metódusnév nem tartalmazza), a setter pedig a „set” prefixummal kezdődik, ezt camelCaps formátumban követi a változó neve. Tehát például egy _date nevű példányváltozóhoz a – date és – setDate: nevű metódusok tartoznak.

2. A többesszám: az olyan metódusok, amelyeknek nevében többesszám van, általában NSArray típusú objektumot adnak vissza vagy várnak argumentumként (ez az esetek 99%-ában igaz, a maradék 1%-ban pedig kísérletezhetünk, hogy NSIndexSet, NSDictionary, vagy milyen más, úgynevezett collection class esetén nem kapunk hibát, esetleg ki is írathatjuk a runtime-mal az osztály nevét – de erről később. Ezt azért fontos tudni, mert a class-dump bővebb információ hiányában csak annyit tud a metódusdeklarációkba írni, hogy visszatérési értékük id, azaz egy általános Objective-C objektum – a pontos osztálynév nem kerül tárolásra.) Ennek megfelelően például a (id)itemsForType:(int)arg; metódus feltehetőleg egy NSArray értékkel tér vissza, a (void)addObjects:(id)arg; metódus pedig szintén NSArray-t vár.

3. Block típusú lambda-kifejezések: az Objective-C az iOS 4 óta támogatja névtelen függvények használatát. Az érdekesség az, hogy ezek a névtelen függvények szabályszerű Objective-C-objektumok, tehát a class-dump id-ként fogja őket kezelni. Hogy rájöjjünk, melyik (id) típusú argumentum vagy visszatérési érték lehet ilyen block, jegyezzük meg, hogy általában (funkciótól függően) a „block”, „callback” vagy „handler” szavak egyike benne van az ominózus metódus nevében, tehát például a következő helyen álló id-k gyanúsak:

- (id)blockForObject:(id)arg; // block visszatérési érték

- (void)invokeHandler:(id)arg; // block típusú argumentum

- (void)sendRequest:(id)req successCallback:(id)succ errorHandler:(id)errhndl; // „succ” és „errhndlr” block lesznek!

4. Osztálynevek és az MVC-architektúra: az OS X-en és iOS-en futó alkalmazások az úgynevezett MVC (Model-View-Controller, azaz modell-nézet-vezérlő) mintát használják a kód struktúrálása érdekében. Ennek a megvalósulása az osztályok elnevezésében is megnyilvánul: a három réteghez értelemszerűen a FooDelegate vagy FooDataSource, a FooView illetve a FooController vagy FooViewController jellegű nevekkel rendelkező osztályok tartoznak. Ha tehát a kód logikáján szeretnénk változtatni, érdemes a modell vagy a vezérlő osztályaiban megfelelő, „gyanús” nevű metódusok után kutatni, míg egy grafikai tuning tervezésekor valószínűleg a nézet vagy ugyancsak a vezérlő rétegeiben találhatunk hasznos deklarációkra.

Egy példa: módosítsuk a már emlegetett exponálógomb működését!

Az alábbi leírást én egy iOS 6.1-et futtató iPaden való kutatás eredményeképpen készítettem el, úgyhogy lehetséges, hogy más verziójú operációs rendszeren vagy más készüléken módosítani kell a működés érdekében.

Vágjunk is bele! A Fényképező alkalmazás a rendszeralkalmazások szokott helyén, a /Applications/Camera.app könyvtárban található, a bináris neve is Camera. A class-dump-z programot lefuttatva rajta kaptam egy jó pár osztályt, a két legfontosabb név szerint:

CameraApplication

PhotosApplication (a Fényképező és a Fotók alkalmazás valójában ugyanaz az app, csak egy trükkel észleli, hogy melyik ikon segítségével indították el)

Ezek közül minket nyilván a CameraApplication osztály érdekel. Belenézve megtaláljuk az instance variable-ök listáját:

@interface CameraApplication : PhotosApplication {

    PLCameraPageController *_cameraPageController;

}

A PLCameraPageController osztályhoz a class-dump nem generált headerfájlt, tehát ez egy külső, importált osztály (egyébként a PhotoLibrary framework része). Egy kis guglizás után megtaláltam ennek is a deklarációját GitHubon:

@interface PLCameraPageController : UIPageController <PLApplicationCameraViewControllerDelegate, PLAlbumChangeObserver, PLCameraPreviewWellImageChangeObserver, UIScrollViewDelegate, UIPageControllerDelegate> {

    struct NSObject { Class x1; } *_cameraAlbum;

    PLApplicationCameraViewController *_cameraViewController;

    UIViewController *_cameraAlbumNavigationController;

    UIViewController *_presentedCameraAlbumNavigationController;

    UIPanGestureRecognizer *_cameraPagePanGestureRecognizer;

    double _sessionStartTime;

    BOOL _supportsVideos;

    BOOL _usesSessionAlbum;

    BOOL _shouldShowCameraAlbum;

    BOOL _delayLoadingPhotoLibrary;

    BOOL _previouslyDidntChangeStatusBar;

    PLKeepDaemonAliveAssertion *_keepDaemonAliveAssertion;

}

Ami a név alapján egy különösen érdekes osztály lehet, az a PLApplicationCameraViewController. Ezt is megtaláltam ugyanabban a GitHub-repóban, és bizony ennek már volt is egy ígéretes nevű (void)takePicture; metódusa (az igazsághoz hozzátartozik, hogy már az előző osztálynak is volt, de mint utóbb kiderült, az egyáltalán nem működött úgy, ahogy azt szerettem volna…).

Először tehát megpróbáltam ezt a metódust megpatkolni a MobileSubstrate segítségével, ám csak félsikerrel jártam: a módosítás telepítése után elég bugos lett az alkalmazás felülete. Kifagyogatott, néha más képet mutatott, mint amit éppen kellett volna, stb. Mindez arra mutatott, hogy ez a metódus még valamit csinál az exponáláson kívül, és én ezt a valamit nem nagyon kellene, hogy piszkáljam. Úgyhogy tovább kutakodtam, és itt mát magát a MobileSubstrate-et hívtam segítségül a nyomozáshoz.

A Cocoa Touch nevezéktanának megfelelően ez az osztály egy view controller (ha a deklarációkon keresztül követjük az öröklési láncot, láthatjuk is, hogy az UIViewControllerből származik le), és természetesen így rendelkezik egy (void)loadView; metódussal is. Írtam tehát egy olyan függvényt, ami az adott PLApplicationCameraViewController-példány view propertyjén meghívja a recursiveDescription metódust. Ez egy privát metódusa az UIView osztálynak, ami a UIView összes gyermekét (subview) kilistázza. Meg is találtam, hogy melyik subview-ról van szó: a PLCameraView osztály egy példánya, ennek a deklarációjában pedig szerepel egy cameraShutterClicked: metódus. A következő kódot írtam tehát:

#import <substrate.h>

#import <UIKit/UIKit.h>

static IMP _orig;

void _mod(id self, SEL _cmd, id button)

{

    [[[[UIAlertView alloc]

        initWithTitle:@”Hello”

        message:nil

        delegate:nil

        cancelButtonTitle:@”OK”

        otherButtonTitles:nil]

    autorelease] show];

}

__attribute__((constructor))

void init()

{

    MSHookMessageEx(

        objc_getClass(“PLCameraView”),

        @selector(cameraShutterClicked:),

        (IMP)_mod,

        &_orig

    );

}

És lám, az exponálógomb megnyomásakor fényképezés helyett kaptam egy felugró ablakot „Hello” üzenettel, mindenféle bug és hiba nélkül! :)

Kitekintés

Láttuk tehát, hogy hogyan használhatjuk a class-dump programot és magát a MobileSubstrate API-t is akalmazások működésének kiismerésére. Ha már úgyis a reverse engineering témakörénél tartunk, ezek alapján érdemes még átgondolni a választ néhány praktikus kérdésre.

1. Ha egyszer készítek egy zseniális, nélkülözhetetlen tweaket, és 40 dollárért fogom árulni, akkor hogyan kerülhetem el, hogy feltörjék, illetve azt, hogy a crackerek használják?

- Először is, ne kérj negyven dollárt egy tweakért. :) A tizede is elég borsos árnak számít a 89 eurocentes alkalmazások világában. Szoftveres védelmet vagy anticrack-mechanizmust természetesen érdemes viszont beépíteni a munkánkba. Erre többféle metódus létezik, viszont ha igazán hatékony anticracket akarunk készíteni, ahhoz a cracker, a reverse engineer szemszögéből érdemes nézni a helyzetet. Mert mi a legnehezebb egy crackernek, amikor meg akar törni egy tweaket? Kitalálni, hogy hol van a fizetést ellenőrző kód. És mi segít neki ezt megtalálni (a disassembleren és saját logikáján kívül)? Természetesen az értelmes függvény-, metódus- és osztálynevek. Éppen ezért fogadjuk meg a következő tanácsokat, ha fizetős tweaket készítünk:

- A fizetésellenőrzés, patch protection, stb. logikáját lehetőleg C-ben írjuk, ne Objective-C-ben. A C kódot van annyival nehezebb visszafejteni, hogy legalább a kezdő crackereket ez elbátortalanítsa. Egy tanulságos kísérlet, amit már több, mint egy éve végeztem, csupán a hecc kedvéből: letöltöttem egy alkalmazást (nem árulom el, melyiket) az akkor még működő AppTrackr-ről, amiről írták, hogy detektálja, ha meg lett crackelve. Elindítottam, ki is írta, hogy „Sajnos nem fizettél”, és az OK gombra való kattintással ki is lépett. Gyorsan lefuttattam rajta a class-dumpot, és találtam is egy CopyProtectionManager vagy hasonló nevű osztályt, aminek volt egy (BOOL)isCraked; metódusa. MobileSubstrate-tel átírva a visszatérési értékét NO-ra, rögtön kiválóan működött. Persze csak addig, amíg nagyjából egy percnyi használat után le nem töröltem – a kalózkodás nem a kenyerem, de ez mindenképp egy érdekes eset volt.

- „Strippeljük” (a hasonló nevű „strip” program segítségével) a binárist, mielőtt kiadjuk a tweaket. Ez a program minden lehetséges C és C++ függvénynevet eltávolít a fájlból, csak a memóriacímeket hagyja meg, tehát első ránézésre a crackernek fogalma sem lesz arról, mi mit csinál. Egy hasonló megoldás lehet az, ha értelmetlen neveket adunk a függvényeinknek, pl. hívjuk őket aeorisvzib() vagy scpouaerhqy() néven. Ezeknek a neveknek a használatát még kódolás közben is kényelmessé tehetjük például a C preprocesszor segítségével:

#define IS_CRACKED() aeorisvzib()

- Használjunk minél kevesebb külső library-t az ellenőrzéshez! A külső, importált függvényneveket nem lehet strippelni a binárisból, hiszen a program indulásakor a dinamikus linkernek szüksége van rájuk, hogy a library-kban lévő nevekkel és memóriacímekkel össze tudja őket párosítani. Éppen ezért, ha például SHA512-t használunk valamilyen hashelésre, akkor kivételesen ne használjuk a CommonCrypto API SHA512 függvényeit, hanem keressünk az Interneten egy SHA512-implementációt, fordítsuk egybe a programunkkal, majd futtassuk a „strip” toolt.

- Amennyiben szerveroldali ellenőrzést is használunk (ugye használunk!?), titkosítsuk, rejtsük el az ellenőrzéshez használt URL-t reprezentáló stringet (illetve lehetőleg minél több stringet)! Ha a cracker lefuttatja a strings <bináris> parancsot a programunkon, és meglátja benne ezt:

https://h2co3.com/MyAwesomeTweak/VerifyLicense.php

…akkor már tudja is, mi a teendője: IDA Pro-ban megkeresi azt a függvényt, ami hivatkozik erre a stringre, átírja a visszatérési értékét TRUE-ra, és bumm, oda a licenszvédelem.

A stringek elrejtésére egyébként jó, ha valamilyen egyszerű, ám nem túl ismert vagy elterjedt (esetleg teljesen saját magunk által kreált) kétirányú kódolási algoritmust használunk. Én például egyik munkámban (nem mondom meg, melyikben, aki nagyon ki akarja találni, az úgyis kitalálja) Base-85-be kódoltam a stringeket, és URL-ek helyett a binárisban csak valami olyasmi látszott, hogy „69cb87′”ckdsbghvÜ),cs”

2. Úgy hallottam, hogy a jailbreakelés nagyon veszélyes, mert az embernek minden személyes adatát kilophatják az alkalmazásból, illetve könnyebbé válik a játékokban a csalás. Mint fejlesztő, mit tudok tenni ez ellen?

Egyvalamit biztosan nem: „Ez az alkalmazás sajnos csak eredeti iOS-szel rendelkező készülékeken fut.” – ez a legrosszabb, ahogyan egy fejlesztő reagálhat a jailbreakelésre; ezzel gyakorlatilag eleve tolvajnak és/vagy csalónak tekinti az összes jailbreakelő felhasználót, noha a jailbreak eleve nem a csalásról vagy warezról szól. Arról nem is beszélve, hogy Cydiába készülő tweakek esetén pedig egyszerűen nonszensz. A megfelelő módszer: tegyük az alkalmazásunkat és annak adatait biztonságossá – tegyük meg a pici többleterőfeszítést, ami ezt megkívánja. Ne tároljuk a játékok állását és a felhasználó jelszavát property list fájlokban, hiszen az plaintext (ez kizárja az NSUserDefaults ilyen célú felhasználását is, de természetesen az NSUserDefaults osztályra egyébként sem szabad semmi ilyesmit rábízni). Tudni kell azt is, hogy jailbreakelt készüléken még az egyébként biztonságosnak feltételezett keychain sem igazán biztonságos: annak a tartalmát a Keychain-Dumper nevű kis programocskával teljes egészében ki lehet íratni, a program némi módosításával pedig a benne lévő adatok átírása is lehetséges, jailbreakelt eszközökön tehát ez sem a legjobb hely az érzékenyebb információk elmentésére.

Nos, mára ennyit a reverse engineeringről, használatáról, előnyeiről és veszélyeiről. A következő részben már egy teljes, működő tweak elkészítésén megyünk végig lépésről lépésre, az eddigiek alapján.

Kapcsolódó cikkek:

iOS-fejlesztés másképp, avagy hogyan készítsünk cydiás tweakeket – 2. rész: „csak dinamikusan!”

vasárnap, február 10th, 2013

Cydiás iOS-fejlesztéssel kapcsolatos sorozatunk e részében a rendszer és az alkalmazások módosításához elengedhetetlenül szükséges alapismereteket tesszük közzé.

project

Az Objective-C runtime library használata

Amint azt már az előző részben is említettem: az Apple nagy szívességet tett a jövőbeli cydiás fejlesztőknek (valószínűleg az első és utolsó szívesség, amit a fejlesztők az Apple-től kaptak…), amikor az Objective-C nyelvet választotta az alkalmazások „anyanyelvéül”. Ez a programozási nyelv ugyanis beépített támogatást nyújt az egyes osztályok és objektumok tulajdonságának futásidejű, „önreflexív” módosítására a runtime library, azaz a libobjc API-jainak segítségével.

Na de mire is jó mindez?

Először is, ha akár csak egy hivatalos App Store-ba készülő alkalmazást fejlesztünk, akkor is jól jöhet a futásidejű „introspekció”, ahogy az angolszász szakirodalom szereti emílteni ezt a funkcionalitást. Tegyük fel például, hogy alkalmazásunk egy JSON-alapú webservice-szel kommunikál, ami a különböző lekérdezésekre különböző típusú adatokkal válaszol (ez ténylegesen egy valós probléma). Tekintsük például a következő esetet: bizonyos esetekben (például: „Keressük a legfinomabb gyümölcsöt”) elég egyetlen objektum visszaadása:

{

    „fruit”: „apple”,

    „tastiest”: true,

    „origin”: „California”

}

Másokban viszont egy listával vagy tömbbel kell visszatérni (például: „Keressük a finom gyümölcsöket”), tehát valami ilyesmi:

{

    {

        „fruit”: „apple”,

        „tastiest”: true,

        „origin”: „California”

    },

    {

        „fruit”: „orange”,

        „tastiest”: false,

        „origin”: „Paris”

    }

]

A jól képzett iOS-fejlesztő ekkor gyorsan belenyúl virtuális zsebébe, előhúzza az NSJSONSerialization osztályt, és már kap is egy frissen sült narancsos-almás objektumot:

id <NSObject> obj = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];

Igen ám, csakhogy nem tudjuk konkrétan, hogy milyen objektumot kaptunk vissza: lehet az NSArray vagy NSDictionary, a választól függően. Ekkor nyúlhatunk az Objective-C runtime-hoz, és megkérdezhetjük az objektumtól, hogy milyen osztályba is tartozik:

if ([obj isKindOfClass:[NSArray class]]) {

    // egy egész raklapnyi gyümölcs (tömb)

} else if ([obj isKindOfClass:[NSDictionary class]]) {

    // egyetlen szem alma

} else {

    // romlott gyümölcs (ha egyik sem: hiba történt)

}

Ez már valóban egy olyan lehetőség, ami a dinamizmusnak köszönhető. Statikusan típusos programozási nyelvekkel (mint például a C) hasonló trükköt, tudniillik egy érték típusának a futásidejű meghatározását, nem lehet megvalósítani.

Egy másik fontos művelet lehet egy metódushoz tartozó függvény (ugye tudjátok, hogy minden Objective-C-metódus valójában egy sima C függényként kerül implementálásra?) lekérése, a metódusnév („selector”) alapján. Még itt sem igazán látszik, hogy az Objective-C runtime-mal dolgoznánk, hiszen, ahogyan az előző példában is, csupán az NSObject osztályt fogjuk használni. A magyarázat teljesen egyszerű: az NSObject sok olyan „beépített” metódust tartalmaz, amik csupán az Objective-C runtime körüli, kényelmi célokat szolgáló úgynevezett wrapperek. Álljon itt tehát, hogyan nyerhetjük ki egy selector segítségével egy adott metódus tényleges implementációját:

// Itt kapunk egy pointert az implementációra

IMP imp = [Osztaly instanceMethodForSelector:@selector(foo)];

Osztaly *obj = [[Osztaly alloc] init];

imp(obj, @selector(foo)); // itt pedig meghívjuk egy konkrét példányon

[obj release];

Az IMP egy „függvénymutató” (angolul jobban hangzik: function pointer) típus. Ha esetleg a C nyelvben való jártasságunk idáig már nem terjed, akkor gyorsan tanuljuk meg használni őket – a tweakfejlesztésben kikerülhetetlen szerepet játszanak.

Hogy végre a „csupasz” Objective-C runtime is látható legyen, íme az előzővel ekvivalens kód, közvetlenül a libobjc API felhasználásával:

Class oszt = objc_getClass(„Osztaly”);

// vagy ez:

Method met = class_getInstanceMethod(oszt, @selector(foo));

IMP imp = method_getImplementation(met);

// vagy rövidebben:

IMP imp = class_getMethodImplementation(oszt, @selector(foo));

// innentől kezdve pedig minden ugyanaz

Ha még ennél is tovább akarunk menni, akkor akár a felhasználótól is bekérhetünk egy tetszőleges stringet, és megpróbálhatjuk végrehajtani azt egy objektumon (a „textField” változó itt egy érvényes, inicializált UITextField példány kell, hogy legyen, amibe a felhasználó ír):

NSString *str = textField.text;

SEL sel = NSSelectorFromString(str); // selector (metódusnév) a felhaszánló által beírt szövegből

Osztaly *obj = [[Osztaly alloc] init];

if ([obj respondsToSelector:sel]) {

    [obj performSelector:sel];

}

[obj release];

Természetesen a valós életben, élesben ne tegyünk ilyet, mert ez komoly biztonsági kockázatot jelentene. Itt ez csak a teljesség és az érdekesség kedvéért áll.

Majdnem ugyanezt a csíntalanságot elkövethetjük az „instance variable” megnevezésű, példányokhoz kötött változók esetén is. Ez az én egyik személyes kedvencem is egyébként, már csak a már-már perverz módon kihasznált pointeraritmetika miatt is:

@interface Osztaly: NSObject {

    MasikOsztaly *peld_valtozo;

    CGRect valami_negyzet;

}

@end

// …

// Itt a függvényünk, ami a lényegi munkát végzi:

void *object_getIvarPointer(id obj, const char *name)

{

    Class cls = object_getClass(obj);

    Ivar ivar = class_getInstanceVariable(cls, name); // kiolvassuk az ivart reprezentáló Ivar struktúrát

    char *base_addr = (char *)obj;

    return base_addr + ivar_getOffset(ivar);

    // majd visszatérünk az objektum címéből és az Ivar struktúrából számolt abszolút címmel

}

// Itt pedig az, hogyan kell használni:

Osztaly *obj = [[Osztaly alloc] init];

void *ivarAddr;

MasikOsztaly *stolenIvar;

ivarAddr = object_getIvarPointer(obj, „peld_valtozo”

stolenIvar = *(MasikOsztaly **)ivarAddr;

CGRect rect;

ivarAddr = object_getIvarPointer(obj, „valami_negyzet”);

rect = *(CGRect *)ivarAddr;

Fontos megjegyezni, hogy ugyan az Objective-C runtime tartalmaz beépített függvényeket a „példányváltozók” olvasására és írására, ezek a föggvények mind a generikus „id” típussal dolgoznak, ami – mint tudjuk – egy pointertípus. Ez azt jelenti, hogy a pointernél nagyobb méretű értékek esetében (iOS-en ez a 32 bites architektúra miatt a 64 bites egészeket, a szintén 64 bites „double” típust, valamint természetesen a megfelelő méretű „struct” struktúrákat jelenti) nem használhatóak, míg a fenti függvény univerzális.

A következő lépésben még tovább megyünk típusok terén. Természetesen nem csak a változóknak, objektumoknak van típusa, hanem a metódusoknak és a függvényeknek is. Ezekről is kaphatunk információt a runtime segítségével (a releváns StackOverflow-kérdést és -választ is érdemes elolvasni):

Method m = class_getInstanceMethod([Osztaly class], @selector(foo:bar:));

char type[128];

method_getReturnType(m, type, sizeof(type));

Mi is történik itt tulajdonképpen? Az első sorban a metódust leíró Method struktúrát érjük el. A második sorban egy 128 karakternyi tömböt foglalunk le, majd a harmadik sor a Method struktúrából kinyeri a típusra vonatkozó információt.

(Fontos megjegyezni, hogy az eddigi néhány példában csak példánymetódusokra vonatkozó példákat hoztam, osztálymetódusokról nem volt szó. Ennek pusztán az az oka, hogy Objective-C-ben az osztálymetódusok is példánymetódusok, miután az osztályok is objektumok: a saját metaosztályuk példányai. Az „NSObject” osztály „+alloc” nevű metódusa tehát gyakorlatilag felfogható az „NSObject” metaosztály „-alloc” metódusaként is. Az összes fenti példában tehát az osztályokat a metaosztályukra kicserélve dolgozhatunk osztálymetódusokon is.

De mi is lesz mindezek után a „type” bufferben? Az Objective-C runtime egy, az összes (teljes) C és Objective-C típust reprezentálni képes típusleíró „nyelvet” is tartalmaz (teljes dokumentáció) (az „incomplete”, avagy „forward-declared” típusokra ez a típusleírás nem alklamazható). A lényeg, hogy minden egyes primitív típus egy-egy betűnek felel meg, az „összetett” típusok (pl. tömbök, struktúrák, objektumok) leírésa pedig ezek kombinációjával, néhány kiegészítő jelölés segítségével történik. Példák:

i → int

* → char *

f → float

: → SEL

@ → id

^{StructName=ci} → struct StructName { char ch; int i; } *

[128^i] → int[128]

Ennek a tudásnak a segítségével már továbbléphetünk egy még érdekesebb kérdéskörre: ne csak olvassuk a program adatait, hanem módosítsuk is azokat! Itt elsősorban az osztályok és metódusok viszonyáról van szó. Az egyik fontos manifesztációja a runtime ilyen célú felhasználásának az osztályokhoz való metódusok dinamikus hozzáadása. Miért lenne ilyesmire egyáltalán szükség, tehetjük föl a jogosnak látszó kérdést, mikor kategóriákat és „class extension”-öket is írhatunk? Nos, a válasz: a kategóriák és class extensionök készítése csak akkor lehetséges, ha az Objective-C compiler (pontosabban a linker) hozzáfér az ily módon kiterjeszteni kívánt osztály definíciójához (azaz vagy mi írtuk az eredeti osztályt is, és megvan a forráskódja, vagy egy library vagy framework exportálja, mint nyilvános, linkelhető szimbólumot). Ez pedig tweakek írása esetén általában nem igaz. Ha például egy olyan osztályt szeretnénk kiegészíteni, ami a SpringBoard részét képezi, akkor ahhoz a linker semmilyen formában nem tud hozzáférni. Így tehát nem marad más, mint a metódus teljesen dinamikus hozzáadása (ezt a trükköt kellett egyébként alkalmaznom a libipodimport szerveroldali részének megírásakor, pontosan ebből az okból kifolyólag):

const char *_SBUIController_$_foo_bar_(id self, SEL _cmd, id arg1, int arg2)

{

    NSString *str = [NSString stringWithFormat:@”%@ %d”, arg1, arg2];

    return strdup(str.UTF8String);

}

class_addMethod(

    objc_getClass(„SBUIController”),

    @selector(foo:bar:),

    (IMP)_SBUIController_$_foo_bar_,

    „*@:@i”

);

Magyarázat: a _SBUIController_$_foo_bar_ függvény adja a létrehozandó metódus implementációját, azaz a tényleges kódot, ami a metódushíváskor végrehajtódik. Ez a függvény pusztán annyit tesz, hogy megfelelően megformázva egy C stringben visszaadja két argumentumának emberi olvasásra is alkalmas leírását.

Az érdekes rész a „class_addMethod()” függvény hívása. Ennek az első argumentuma az osztály, amihez a metódust szeretnénk adni. Figyeljük meg, hogy itt nem használhatjuk az [SBUIController class] formát, hiszen az SBUIController osztály nem érhető el a linker számára, tekintve, hogy a SpringBoard egyik osztálya – a fordítás során tehát linker errort kapnánk (a híres-hírhedt „Undefined reference to symbol _$_OBJC_CLASS_$_SBUIController” hibaüzenetet).

A második argumentum egyszerűen a selector, a metódus neve, a harmadik pedig a függvénypointer, ami az implementációt tartalmazza. Az előző részben leírt típusleírásnak pedig a negyedik argumentum megírása során vesszük hasznát: a runtime itt a metódus típusát („function signature”) leíró sztringet vár. Ennek megkonstruálására a szabály: a legelső karakter a visszatérési értéket jelenti, a többi pedig az argumentumokét sorban. Ennek az is a következménye, hogy a class_addMethod() használata során a típussztring 2. és 3. karaktere mindig „@” és „:” lesz, hiszen ezt a két első implicit argumentumot minden Objective-C metódusként használt függvény megkapja.

A függvény meghívása után ugyanúgy használhatjuk a metódust, mintha azt kézzel implementálták volna:

char *str = [[SBUIController sharedInstance] foo:nil bar:1337];

A legutolsó műveletet nem véletlenül hagytam a sor végére. Ez a legösszetettebb dolog, amit praktikusan csinálni fogunk a runtime segítségével, ez igényli a legtöbb figyelmet, valamint ezt fogjuk a legtöbbször használni, habár nem is pontosan ilyen formában. Amiről most szó lesz, az metódusimplementációk cseréje. A szakirodalomban „hooking”, „method swizzling” és hasonló félelmetes neveken említik. A koncepció első ránézésre ennek ellenére meglehetősen egyszerű: vegyünk egy függvényt, és cseréljük ki erre a függvényre egy már meglévő metódus implementációját. Ez azt jelenti, hogy innentől az a metódus mást fog csinálni, amikor meghívják, mint amire eredetileg tervezték. Ugye ez már egészen hasonlít a tweakek működésére, nem igaz? :)

Csináljunk egy rossz heccet (persze csak gondolatban, vagy abban az esetben, ha ellopták a készülékünket): tegyük lehetetlenné a készüléken lévő alkalmazások megnyitását.

Nehezítés: valósítsuk meg mindezt kalapács és tömény salétromsav használata nélkül.

A nehezítés miatt a készülék fizikai tönkretételét jó közelítéssel kizártuk, marad tehát a bonyolultabb, ám elegánsabb megoldás: az ikonokat az Objective-C runtime segítségével rávesszük arra, hogy ne csináljanak semmit, ha megnyomják őket.

Ha közelebbről megnézzük a SpringBoard belső felépítését (azt, hogy ezt hogyan lehet viszonylag egyszerűen megtenni, a következő részben meg fogjátok tudni), találunk egy SBApplicationIcon nevű osztályt. Ez az osztály pontosan arra való, amire a neve utal: a SpringBoardon egy alkalmazás ikonját testesíti meg. Rendelkezik ez az osztály egy „-launch” nevű metódussal, ami szintén logikus nevet kapott: ez indítja az alkalmazást az ikonra való tapizáskor. Ha tehát ezt a metódust kicseréljük egy NOP-ra, akkor nem fognak indulni az alkalmazások. Első körben valami ilyesmiről lehet szó:

Class _$SBApplicationIcon = objc_getClass(„SBApplicationIcon”);

static IMP _original_$_SBApplicationIcon_$_launch;

void _modified_$_SBApplicationIcon_$_launch(id self, SEL _cmd)

{

    // nem csinálunk semmit

    // ha mégis meg akarnánk nyitni az alkalmazást,

    // akkor a régi, lecserélt implementációt meghívhatnánk:

    // _original_$_SBApplicationIcon_$_launch(self, _cmd);

}

Method m = class_getInstanceMethod(_$SBApplicationIcon, @selector(launch));

_original_$_SBApplicationIcon_$_launch = method_setImplementation(m, (IMP)_modified_$_SBApplicationIcon_$_launch);

Az első két sor ismét magáért beszél: a megfelelő osztályt kinyerjük a runtime segítségével, majd deklarálunk egy globális változót (ejnye, nem szép dolog…) az eredeti implementáció tárolására. A következő pár sor csak az üres függvény definiálására szolgál, majd a szokásos class_getInstanceMethod() után végül a lényeg: a method_setImplementation() függvény. Ez a függvény nem tesz mást, mint az első argumentumaként átadott metódus implementációját a második argumentumban átadott függvénypointerre változtatja, majd visszatér az eredeti implementációra mutató pointerrel, így az eredeti implementáció sem veszik el. Ezt meghívhatjuk (és általában meg is kell hívnunk) a módosított, „modified” függvényből.

A MobileSubstrate pontosan ezt a módszert alkalmazza, csak rengeteg, itt ki nem fejtendő biztonsági lépést tesz annak érdekében, hogy a tweakek ne „akadjanak össze” egymással és a rendszerrel,. Ez az, amiért általában érdemes a MobileSubstrate-hez fordulni az Objective-C runtime közvetlen meghívása helyett. Azt pedig, hogy pontosan hogyan használhatjuk a MobileSubstrate API-jait, megtudjátok két rész múlva, amikor is elkészítjük első, ténylegesen működő, komplett tweakünket!

Kapcsolódó cikkek:

iOS-fejlesztés másképp, avagy hogyan készítsünk cydiás tweakeket – 1. rész: Bevezetés

szombat, február 2nd, 2013

Többen is érdeklődtök a jailbreakes fejlesztés iránt, ezért úgy döntöttünk, indítunk egy cikksorozatot egy egyszerű tweak elkészítéséről, ahol lépésről-lépésre végigvezetünk a kiegészítő születésén.

Habár az App Store nemrég érte el az alkalmazások számának tekintetében álomhatárnak mondható egymilliót, és a felhasználók láthatóan lelkesen használják az általuk letöltött mintegy negyvenmilliárd appot, azért mindig van némi üröm az örömben. Amint azt tudjuk, az Apple mobil operációs rendszere igen sok – valós vagy vélt biztonsági, vagy egyszerűen gazdasági okkal rendelkező – korlátozással rendelkezik, ennek pedig része a fejlesztők kezének megkötése is. Minden valamire való iOS-fejlesztő tudja (hiszen a saját bőrén tapasztalja), hogy az iOS által nyújtott programozási interfészek (API-k) közül több hasznos funkciót nyújtó tiltott, privát (nincsen dokumentálva a léte sem), vagy más okokból nem használható egy App Store-ba készülő alkalmazásban.

Természetesen (majdnem) minden, az ilyen korlátozások feloldására, a készülék testreszabására valamint teljesértékű számítástechnikai eszközként való használatára irányuló vágy megvalósulhat az iPhone-ok és iPadek jailbreakelésével, majd olyan szoftverek készítésével, amelyek az immár az Apple által nagy testvérként meg nem figyelt és funkcionalitásaikban meg nem kötött módon segítik kedvenc kütyüjeink hasznosabbá és egyedivé tételét.

Mindenki használja manapság a „tweak” kifejezést, de a szomorú tapasztalat az, hogy nemcsak sok felhasználó, hanem egyes „fejlesztők” (igen, idézőjelben) sincsenek tisztában a szó iOS-re vonatkozó jelentésével.

A tweakek olyan kiegészítő programok, amelyek a hagyományos (App Store-os és cydiás) alkalmazásokkal ellentétben nem önállóan futnak, hanem az operációs rendszer vagy valamely más alkalmazás processzébe beépülve annak a viselkedését módosítják. Az ok, amiért ilyen programok írása különösen könnyű iOS-en (és egyébként ugyanilyen könnyű lenne az OS X esetében, ha szükség lenne rá), az a legtöbb alkalmazás és az operációs rendszer magasabb szintű rétegei által is használt programozási nyelvnek, az Objective-C-nek köszönhető. A jelen sorokat olvasó fejlesztőknek illik, hogy már be is ugorjon, miért: a nyelv dinamikus. A program futása közben használt osztályokról, objektumokról, metódusokról, típusokról az Objective-C runtime segítségével információkat kérdezhetünk le, és azokat – egy bizonyos mértékig – módosíthatjuk is.

A nyelv ezen előnyös tulajdonságát kihasználva írta meg még a jailbreakelés hőskorában az iOS-hackerek egyik legnagyobbika, Saurik, a MobileSubstrate nevű library-t. Ez a „könyvtár” gyakorlatilag egy egyszerű, biztonságos keretet ad az Objective-C runtime library köré, és segítségével könnyen belenyúlhatunk csaknem minden rendszer- és felhasználói alkalmazásba.

Jelen cikksorozat elkövetkezendő részeiben egy teljes cydiás tweak elkészítését fogom bemutatni, a nulláról indulva egészen a kész, Cydiában közzé tehető Debian csomag felépítéséig. Természetesen van néhány feltétele annak, hogy ezt a leírást követni tudd.

Első és legfontosabb feltétel: legalább alap- de inkább „középszintű” iOS-programozói ismeretek. Ezek nélkül inkább bele se kezdj – a tweakek készítése érdekes, ám néha nem kockázatmentes dolog. Ne feledjétek: a tweakek sokszor az operációs rendszer alapvető funkcióit hackelik meg! A különböző alapvető koncepciók ismerete nélkül könnyen megtörténhet, hogy szoftverünk „felfalja” a memóriát, vagy összeomlasztja a rendszert. Bár az ilyen hibák nem halálosak, és általában a MobileSubstrate maga kapja el az esetleg hibásan működő beépülők grabancát, legvégső esetben akár restore is szükséges lehet, ami pedig potenciális adatvesztéssel járhat (ha nem szinkronizálunk rendszeresen az iTunes-szal). Tudjuk tehát perfektül a C és az Objective-C nyelvet, ismerjük és tudjuk gördülékenyen használni a Cocoa Touch API komponenseit, osztályait és szabályait.

original

Másodszor: egy működőképes cross-toolchain iOS-re. Aki az első pontban leírt dolgokat teljesítette, annak már bizonyosan van ilyen. :) Természetesen legyen az hivatalos, az Xcode-dal együtt feltelepülő OS X-en futó toolchain, vagy egy linuxos (horribile dictu, windowsos), cross llvm-gcc alapú, akár egy magán a készüléken futó, natív eszköztár (innen közvetlenül letölthető): mind megfelel a célnak. Egy kisebb változtatást mindhárom esetben meg kell tennünk azonban: a MobileSubstrate használatához annak Cydiából való telepítése (van, akinek nincs telepítve egyébként is?) után a fejlesztéshez szükséges részeit a toolchain megfelelő részébe kell másolnunk. Nevezetesen a készüléken megtalálható fájlok közül a /usr/lib/libsubstrate.dylib fájlt a <SYSROOT>/usr/lib/libsubstrate.dylib, a /usr/include/substrate.h fájlt pedig a <SYSROOT>/usr/include/substrate.h helyre kell másolnunk, hogy a tweakünk fordítása során a compiler és a linker megtalálja őket (a <SYSROOT> helyére természetesen azt a könyvtárat helyettesítsük be, ahol a toolchain által használt library-k és header-ek találhatóak). Hasonló átmásolgatásokat a későbbiek során is lehet, hogy meg kell ejtenetek majd – a Cydiában számtalan hasznos library található, amelyek közül több kifejezetten tweakekhez való (ilyen például az Activator, a libstatusbar vagy a libipodimport).

IMG_0159

Egyvalamit még említésre méltónak tartok még így a legelején megemlíteni. Létezik több olyan keretrendszer, framework is, amely specifikusan az iOS-tweakek fejlesztésének megkönnyítését tűzte ki célul (ilyenek a Theos és az iOSOpenDev). Mi azonban nem fogjuk ezeket használni, és ennek kettős oka van. Az egyik az, hogy „történeti okokból” én nem használom ezeket. A másik az, hogy így megtanul(hat)játok kezelni a fontosabb parancssoros fejlesztői eszközöket, hogy ne legyetek semmilyen kiegészítőre, segédprogramra szorulva – sajnálatos tapasztalatom az is, hogy valaki úgy próbált meg az iOS-tweakek írásához hozzáfogni, hogy egyáltalán nem is tudta, hogyan kell a compiler-t kézzel meghívni (szomorú, bár annyira nem meglepő dolog a mai IDE-k világában).

Nos, mára ennyit bevezetőnek. Kövessetek minket figyelemmel, hamarosan folytatása következik! ;-)

Kapcsolódó cikkek:

A hét legjobb cydiás szoftverei: október 1–7.

kedd, október 9th, 2012

A ModMyi szerkesztősége összeszedte az általuk a múlt héten a legjobbnak ítélt jailbreakes kiegészítőket és alkalmazásokat. Álljon itt egy rövid összefoglaló róluk.

1. BTC Mouse & Trackpad

Ez a tweak Matthias Ringwald “szerzeménye”. Funkciója, hogy iOS-készülékeinken a bluetooth-os mutatóeszközöket, azaz egereket és touchpadeket teszi támogatottá. A desktopon már megszokott módon számos beállítást módosíthatunk, többek között az egérmutató (kurzor) gyorsulásának mértékét, vagy Keynote-prezentáció előadásakor a diák közötti léptetés módját. A tweak többek között olyan, iOS-specifikus képességekkel is felruházza eszközünket, mint a bal gombbal való kattintás érintésként és a jobb kattintásnak a Home gombra történő nyomásként való érzékelése, vagy a kerék használata nagyításra illetve a főképernyőn belüli navigációra. Matthias Ringwald egyébként ismert a BTstack nevű nyílt forráskódú úgynevezett “Bluetooth-stack” elkészítéséről is – amit azonban, nyilvánvaló okokból, nem ruházott fel a fájlmegosztás képességével (pontosabban az OBEX protokoll támogatásával) – azt csak külön, egyes fizetős alkalmazások (pl. az iBlueNova) vele kapcsolatban álló készítői számára tette elérhetővé.

A tweak egyébként meglehetősen drága, majdnem 5 dollárba kerül a BigBoss repóban.

2. iWidgets

Az “eldwin” álnéven működő iOS-fejlesztő készítette ezt az ügyes kis kiegészítőt, amelynek segítségével “élő” HTML-widgeteket varázsolhatunk a SpringBoardra. A csomag alapértelmezetten két widgetet tartalmaz, de egy pluginrendszer segítségével külső fejlesztők is készíthetnek saját kiegészítőket (ehhez nem muszáj, hogy az illető iOS-programozó legyen – bárki, aki ismeri a HTML, CSS és JavaScript nyelveket, egyszerűen készíthet újabb widgeteket – ehhez álljon itt a készítő eredeti leírása). Ezekből is megjelent már egy pár, például a “3DaysForecastS”, a “Clocky”, a “FlipClockS” vagy az “Epiq”. Tekintve, hogy a widgetek HTML-ből épülnek fel, szinte bármit meg lehet csinálni velük! Természetesen a widgetek mozgathatók a képernyőn és bármikor eltávolíthatóak róla vagy újra hozzáadhatóak.

Mivel a tweak ingyenes, ezért jó szívvel tudom ajánlani mindenkinek a letöltését a ModMyi repóból.

3. Music2iPod (a. k. a. Fridge)

Nagy örömömre szolgált, hogy a hét három legjobb cydiás programja közé legfrissebb fejlesztésem, a Music2iPod is bekerülhetett. A név, annak ellenére, hogy nem túl fantáziadús, informatív: az alkalmazás – a múlt héten már megismert libipodimport csomagomat használva – mindenki számára könnyen és egyszerűen elérhetővé teszi zenék, videók, podcastek és egyéb audiovizuális médiának az iPod illetve a “Zene” és a “Videók” alkalmazásba történő hozzáadását, immár egy szép, grafikus felületen, rendes iOS-alkalmazás formájában.

Az appot megnyitva gyakorlatilag egy fájlböngészővel találja szembe magát a kedves Felhasználó. A fájlrendszer egészéből, bárhonnan választhatunk egy fájlt, legyen az MP3, M4A/M4R, AIFF, FLAC, MP4, 3GPP vagy MOV formátumú. A rajta történő “tappintás” után felnyílik egy ablak, amelyben a fájl metaadatait szerkeszthetjük, és kiválaszhatjuk, hogy milyen módon (zene, videó vagy podcast formájában) szeretnénk elhelyezni a médiakönyvtárban. Ez a választás egyébként fájlkiterjesztés alapján automatikusan is megtörténik. A “Done” (“Kész”) gombra kattintva a hozzáadás sikerességéről értesülhetünk.

Sokak által kért és éppen ezért mindenképp a közeljövőben a fejlesztés tárgyát képező funkciók lesznek például a borítókép hozzáadásának lehetősége, valamint egy megadott URL-ről a fájl letöltése és “importálása”. Ezeket szeretném pár napon belül elkészíteni.

Az alkalmazás természetesen ingyenesen letölthető a BigBoss repóból – sikerét mutatja, hogy mára (megjelenése után három nappal) már túllépte a 15 000 letöltést. A szoftver nyílt forráskódú is – ezt megtalálhatjátok a GitHubon.

(Egy kicsit a névről: a Fridge név az azonos funkciójú, de fizetős Bridge alkalmazás nevéből és a “free”, azaz “ingyenes” szóból tevődik össze, tehát nem a hűtőszekrény az elsődleges jelentése! :D )

Kapcsolódó cikkek:

Végre lehetséges: zenék hozzáadása az iPodhoz, közvetlenül a készüléken!

péntek, szeptember 28th, 2012

Régóta foglalkozatja a jailbreakes fejlesztői közösséget (és persze a felhasználókat is…) annak a kérdése, hogy milyen módon lehetne az iPod (mostanában inkább “Zene”) alkalmazáshoz az iTunes és egyáltalán számítógép használata nélkül zenéket és videókat adni. Ez az egyik központi, és a közösség számára sokáig megoldatlan kérdés volt.

Természetesen mint mindenre, erre is voltak egészen jónak tűnő megoldások – voltak olyan leleményes fejleszők, akik rájöttek a dolog nyitjára, de ők kivétel nélkül fizetőssé tették az elkészített alkalmazásukat, lásd a MewSeek, valamint a PwnTunes alkalmazásokat, az ismert YourTube és Safari Download Manager nevű tweakek ilyen célú funkcionalitását biztosító “Gremlin frameworköt”, valamint a pár napja megjelent Bridge alkalmazást. Az még hagyján is, hogy ezek az (egyébként a véleményem szerint alapfunkciót biztosító) alkalmazások fizetősek voltak, a fejlesztőik nemhogy ingyen, de még pénzért sem árulták el a titkot, nem készítettek más fejlesztők által használható API-t ehhez a feature-höz.

Ez utóbbi alkalmazás megjelenése inspirált arra, hogy én is elkezdjek turkálni az iOS-ben. Ez eleinte teljesen reménytelennek tűnt, ám pár fejlesztő hasznos tanácsainak köszönhetően végül nekem is sikerült visszafejtenem az iOS-nek ezt a funckióját, és készítettem belőle egy ingyenes, nyílt forráskódú úgynevezett libraryt, amit a fejlesztők használhatnak arra, hogy ezt a funkciót saját alkalmazásaikba integrálják. Ez lett a libipodimport. Immár az 1.2-es verziónál tart, és a forráskódja elérhető GitHubról, valamint a Debian csomag letölthető Cydiából, a BigBoss repóból. A csak fejlesztőknek hasznos library mellett a csomag egy parancssoros programot (ennek a parancsnak a neve ‘addtoipod’) is tartalmaz, amivel programozói tudás nélkül is azonnal adhatunk különböző típusú médiát az iPodhoz.

Szép és jó, de mit is jelent mindez? Nos, ez azt jelenti, hogy hamarosan várhatjátok a különböző médialetöltő, -konvertáló, -kezelő és egyéb alkalkmazások frissítését, amelyekben jelen lesz ez a képesség. Készül továbbá egy olyan alkalmazás is, ami kifejezetten az “Add to iPod” célt fogja szolgálni, tehát körülbelül a MewSeek, PwnTunes, Bridge stb. alkalmazások funckionalitását teszi elérhetővé, csupán ingyen. Egyébként ennek az alkalmazásnak a fejlesztője (@bensge) is azok között van, akik segítségemre voltak a reverse engineering folyamata során.

Egy valamit még érdemes megjegyeznem: a német fejlesztő, Albert Schultz, a YouTubeToMP3 nevű YouTube-letöltőalkalmazás szerzője már ki is használta a lehetőséget, és alkalmazásának néhány perce megjelent, 7.5-ös verziószámú frissítése már tartalmazza is ezt a funkciót:

Az alkalmazás szintén elérhető a BigBoss repóból, ingyen. A YouTubeToMP3 ismertségén túl talán ennek a feature-nek a népszerűségét is mutatja, hogy a frissítést tíz perc alatt több, mint ezren töltötték le:

Fejlesztők, kellemes használatot a libraryhez, a kedves felhasználók pedig élvezzék az innováció áldásos hatásait! :D

Kapcsolódó cikkek:

Az oldal tartalmának írásos engedély nélküli, bárminemű utánközlése tilos.
©2007-2013, Szifon.com - Minden jog fenntartva!