2012年11月29日 星期四

[iOS] tweak project development


在jailed break的手機當中, 我們如果想要取代掉/新增一些原本apple提供的功能, 我們就必須要寫tweak(我自己理解就是寫hook, 然後讓系>統會自動呼叫到你寫的callback function).

要達到這件事情, 除了之前教的先安裝theos之外, 我們還必須使用ios private framework (也就是apple沒公開的SDK), 這邊有兩種方式


  1. generate header from class-dump or class-dump-z tool
    $ class-dump -H /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/System/Library/PrivateFrameworks/SpringBoardServices.framework/SpringBoardServices
    $ ls
    BKSAccelerometerDelegate-Protocol.h              SBSCardItem.h
    CDStructures.h                                   SBSCardItemsController.h
    NSCoding-Protocol.h                              SBSCardItemsControllerRemoteInterface-Protocol.h
    NSCopying-Protocol.h                             SBSCompassAssertion.h
    NSObject-Protocol.h                              SBSLocalNotificationClient.h
    SBAppLaunchUtilities.h                           SBSPushStore.h
    SBCardItemsControllerRemoteInterface-Protocol.h  SBSRemoteNotificationClient.h
    SBLaunchAppListener.h                            XPCProxyTarget-Protocol.h
    SBSAccelerometer.h
    
  2. copy others from internet
    (1) https://github.com/nst/iOS-Runtime-Headers/tree/master/PrivateFrameworks
    (2) https://github.com/rpetrich/iphoneheaders
        這是我用的, 使用這個你必須要做一些額外的工作
        
$ find /Applications/Xcode.app/ -name IOSurfaceAPI.h
$ cp /Applications/Xcode.app//Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk/System/Library/Frameworks/IOSurface.framework/Versions/A/Headers/IOSurfaceAPI.h $THEOS/include/IOSurface/
Comment out the following line in this file (IOSurfaceAPI.h):

/* This call lets you get an xpc_object_t that holds a reference to the IOSurface.                                           
   Note: Any live XPC objects created from an IOSurfaceRef implicity increase the IOSurface's global use
   count by one until the object is destroyed. */
//xpc_object_t IOSurfaceCreateXPCObject(IOSurfaceRef aSurface)
//  IOSFC_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);
                              
/* This call lets you take an xpc_object_t created via IOSurfaceCreatePort() and recreate an IOSurfaceRef from it. */
//IOSurfaceRef IOSurfaceLookupFromXPCObject(xpc_object_t xobj)
//  IOSFC_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_NA);


我自己是使用別人產生的header, 把這些header copy到$THEOS/include 下面, 接下來就可以進行tweak project了

$ /opt/theos/bin/nic.pl 
NIC 2.0 - New Instance Creator
------------------------------
  [1.] iphone/application
  [2.] iphone/library
  [3.] iphone/preference_bundle
  [4.] iphone/tool
  [5.] iphone/tweak
Choose a Template (required): 5
Project Name (required): ooooo
Package Name [com.yourcompany.ooooo]: 
Author/Maintainer Name [ytshen]:       
[iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: 
Instantiating iphone/tweak in ooooo/...
Done.
$ ls ooooo/
Makefile    Tweak.xm    control     ooooo.plist theos


接下來你就可以進行tweak的實作, 這邊我在xcode 4.5 + mac os 10.8 lion下面會遇到一些問題, 像是找不到arm6 symbol, 這是因為後來只支援arm7.

要修改Makefile

export ARCHS=armv7
export TARGET=iphone:latest:4.3
SDKVERSION=6.0
 
include theos/makefiles/common.mk
   
TWEAK_NAME = ooooo
ooooo_FILES = Tweak.xm
   
include $(THEOS_MAKE_PATH)/tweak.mk


並且要設定environment $SYSROOT 到你sdk的目錄.

$ echo $SYSROOT 
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.0.sdk/
這邊使用一個很簡單的例子, 我們要改spring board一開始秀出一個alert (spring board就是iOS的桌面程式), 修改Tweak.xm:

#import 
 
%hook SpringBoard
 
-(void)applicationDidFinishLaunching:(id)application {
    %orig; // call the original method
    
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Welcome" 
        message:@"Welcome to your iPhone Brandon!" 
        delegate:nil 
        cancelButtonTitle:@"Thanks" 
        otherButtonTitles:nil];
    [alert show];
    [alert release];
}
%end
接下來就按照之前介紹的theos, make, make package, make package install. 就可以把這個tweak安裝在iPhone上面!

Reference

http://brandontreb.com/beginning-jailbroken-ios-development-your-first-tweak/
http://www.andyibanez.com/2012/07/02/create-a-mobilesubstrate-tweaks/
class-dump:
http://stevenygard.com/projects/class-dump/

2012年11月16日 星期五

[C++] static array size macro

We usually get array size by using the following macro.
#define count_of(arg) (sizeof(arg) / sizeof(arg[0]))
But this code would not get any compile error when we pass the pointer as argument like below:
void Test(int C[3])
{
  int A[3];
  int *B = Foo();
  size_t x = count_of(A); // Ok
  x = count_of(B); // Error
  x = count_of(C); // Error
}
One way is to declare like this: (pass the reference which indicate the number of array)
void Test(int (&C)[3])
{
  ...
}
// reference http://www.cplusplus.com/articles/D4SGz8AR/
Now the following code snippet is creating template function. The function template is named ArraySizeHelper, for a function that takes one argument, a reference to a T [N], and returns a reference to a char [N]. If you give it a non-array type (e.g. a T *), then the template parameter inference will fail.
// reference http://www.viva64.com/en/a/0074/
// http://stackoverflow.com/questions/6376000/how-does-this-array-size-template-work
template 
char (&ArraySizeHelper(T (&array)[N]))[N];
#define arraysize(array) (sizeof(ArraySizeHelper(array)))

2012年10月25日 星期四

[GCC] C++ compiler option for static analysis

GCC compiler option for some C++ checking.

==========================================


-Weffc++ (C++ and Objective-C++ only)                                                                                                                                                   
   Warn about violations of the following style guidelines from Scott Meyers' Effective C++ book:
   
   ·   Item 11:  Define a copy constructor and an assignment operator for classes with dynamically allocated memory.
   
   ·   Item 12:  Prefer initialization to assignment in constructors.
   
   ·   Item 14:  Make destructors virtual in base classes.
   
   ·   Item 15:  Have "operator=" return a reference to *this.
   
   ·   Item 23:  Don't try to return a reference when you must return an object.
   
   Also warn about violations of the following style guidelines from Scott Meyers' More Effective C++ book:
   
   ·   Item 6:  Distinguish between prefix and postfix forms of increment and decrement operators.
   
   ·   Item 7:  Never overload "&&", "||", or ",".
   
   When selecting this option, be aware that the standard library headers do not obey all of these guidelines; use grep -v to filter out those warnings.


-Woverloaded-virtual (C++ and Objective-C++ only)
    Warn when a function declaration hides virtual functions from a
    base class.

-Wsign-promo (C++ and Objective-C++ only)
    Warn when overload resolution chooses a promotion from unsigned or
    enumerated type to a signed type, over a conversion to an unsigned
    type of the same size.  Previous versions of G++ would try to
    preserve unsignedness, but the standard mandates the current
    behavior.

-Wextra
    This enables some extra warning flags that are not enabled by
    -Wall.

2012年10月17日 星期三

[C++] flexible array

最近看到squid某個patch, 看到比較少用的flexible array, 在clang下面支援不足 (non-POD type), 所取得的備案.
What is Flexible arrays: Flexible array通常是使用在struct 最後一個member, 來讓struct可以使用不定長度的array. Ex:
     struct line {
       int length;
       char contents[0];
     };
     
     struct line *thisline = (struct line *)
       malloc (sizeof (struct line) + this_length);
     thisline->length = this_length;

Reference:
http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Zero-Length.html

Notes:
Flexible was introduced in C99 standard but not in C++. Clang didn't support non-POD type flexible array.

Discuss thread

remove flexible array for clang support 
http://www.squid-cache.org/mail-archive/squid-dev/201208/0008.html

find bug about previous patch
http://www.squid-cache.org/mail-archive/squid-dev/201210/0156.html

other solution
http://www.squid-cache.org/mail-archive/squid-dev/201210/0186.html

It introduce a wrapper which help to preserve shared memory usage by using placement-new operator. The caller must allocate enough memory when constructing FlexibleArray which capacity must match the memory.

+template 
+class FlexibleArray
+{
+public:
+    explicit FlexibleArray(const int capacity) {
+        if (capacity > 1) // the first item is initialized automatically
+            new (items+1) Item[capacity-1];
+    }
+
+    Item &operator [](const int idx) { return items[idx]; }
+    const Item &operator [](const int idx) const { return items[idx]; }
+
+    //const Item *operator ()() const { return items; }
+    //Item *operator ()() { return items; }
+
+    Item *raw() { return items; }
+
+private:
+    Item items[1]; // ensures proper alignment of array elements
+};

2012年7月26日 星期四

[iOS] JB program writting


iOS下面的APP限制蠻多的 (ex: 進入背景之後, 最多只有10min可以作網路相關的事情), 如果想要突破限制就得要JB, 紀錄一下如何寫JB之後的程式.

在iOS底下, 所有的APP可以放在兩個地方/Applications/<your app>.app and /var/mobile/Applications/<id>/<your app>.app (一般經由iTune or XCode 安裝的APP都會在此), 所有在/var/mobile/Applications的app 都會跑在自己的sandbox, 而且也沒有讀取整個系統的權限, 為了能夠突破iOS的限制, 以下是如何產生可以直接安裝到/Applications 取得root 權限 APP作法 (注意必須手機要經過JB):

一般都是使用 Theos 來產生你想要得APP project.

安裝步驟:
安裝iOS SDK
安裝theos
利用theos建立新的project (可以看到自動幫你產生出的檔案)

$ export THEOS=/opt/theos
$ svn co http://svn/howett.net/svn/theos/trunk $THEOS
$ curl -s http://dl.dropbox.com/u/3157793/ldid > ~/Desktop/ldid
$ chmod +x ~/Desktop/ldid
$ mv ~/Desktop/ldid $THEOS/bin/ldid
$ sudo port install dpkg
$ ./$THEOS/bin/nic.pl
IC 1.0 - New Instance Creator
------------------------------
  [1.] iphone/application
  [2.] iphone/library
  [3.] iphone/preference_bundle
  [4.] iphone/tool
  [5.] iphone/tweak
Choose a Template (required): 1
Project Name (required): zzz
Package Name [com.yourcompany.zzz]: 
Author/Maintainer Name [ytshen]: zzz
Instantiating iphone/application in zzz/...
Done.
$ls zzz
Makefile              RootViewController.h  control               theos
Resources             RootViewController.mm main.m                zzzApplication.mm
$


從Makefile上面你可以更改你的application name, 所有你需要compile object-c file (ex: .m .mm), 還有你需要include的framework:

include theos/makefiles/common.mk                                                                               
                                                                                                                
APPLICATION_NAME = xxx                                                                                 
xxx_FILES = main.m XXXApplication.mm RootViewController.mm 
xxx_FRAMEWORKS = UIKit CoreGraphics 
                                                                                                                
include $(THEOS_MAKE_PATH)/application.mk


接下來就是要如何build project, 並且deploy到手機上面:

$ export SDKVERSION=5.1
$ export THEOS_DEVICE_IP=192.168.1.11

# build project
$ make
Making all for application xxx...
 Compiling main.m...
 Compiling XXXApplication.mm...
 Compiling RootViewController.mm...
 Linking application XXX...
 Stripping XXX...
 Signing XXX...

# build deb
$ make package
Making all for application CameraUpload...
make[2]: Nothing to be done for `internal-application-compile'.
Making stage for application XXX...
 Copying resource directories into the application wrapper...
dpkg-deb: building package `com.ttt.xxx' in `./com.ttt.xxx_0.0.1-50_iphoneos-arm.deb'.

# install deb in device 
$ make package install


其他像是你想要更改APP icon或是跟一些resource相關, 你可以修改 Resource/Info.plist 達到你的要求.

Debugging:
使用這種方式的APP在debug上面沒辦法使用xcode的debug console, 所以只能透過syslog. 所以你可以透過Cydia安裝 "Erica Utilities" (for 'tail' utility), "Mobile Terminal", "OpenSSH", "SBSettings", "syslogd", "Syslog Toggle" 來達到debug的目的 安裝完之後, 你就可以透過ssh連上device在上面直接觀看syslog log.

root# tail -f /var/log/syslog 
Jul 27 13:19:16 hayokushin-mato-iPhone com.apple.launchd[1] (com.ikey.bbot): (com.ikey.bbot) Throttling respawn: Will start in 10 seconds
Jul 27 13:19:16 hayokushin-mato-iPhone syncdefaultsd[1374]: com.apple.stocks has no valid com.apple.developer.ubiquity-kvstore-identifier entitlement
Jul 27 13:19:16 hayokushin-mato-iPhone syncdefaultsd[1374]: Can't get application info for com.apple.stocks
Jul 27 13:19:31 hayokushin-mato-iPhone syncdefaultsd[1374]: com.apple.stocks has no valid com.apple.developer.ubiquity-kvstore-identifier entitlement

Reference:

2012年6月17日 星期日

[TCP] TIME_WAIT state


Usually when I terminate the server and want to bind the port again, it will show some error that I can not bind the port because the port is in TIME_WAIT state. As a result, I will need to set socket option (SO_REUSEADDR) to re-bind the port. In the past, I only know that there is some work left inside the kernel to handle the connection (from http://stackoverflow.com/questions/577885/uses-of-so-reuseaddr). But according to the link in the bottom, there could be two issues which we need TIME_WAIT state!


  1. there is no way to be sure that the last ack was communicated successfully, TIME_WAIT to make sure it can wait and retransmission last ack if the other end sned FIN again.
  2. there may be "wandering duplicates" left on the net that must be dealt with if they are delivered.


More detail in TIME_WAIT explainthis and protocol design implication

2012年4月22日 星期日

[C++] class member pointer usage

Use C++ member pointer for container.
#include <iostream>
#include <vector>
#include <map>
#include <functional>
                
class Foo       
{               
public:         
    Foo() {}    
    ~Foo() {}   
    int A(int a)
    {           
        return a+1;
    }             
    int B(int a)
    {           
        return a+2;
    }           
};              
                
int main()      
{               
    // use mem_fun_ref which can be used for for_each ... etc
    std::vector<std::mem_fun1_ref_t<int, Foo, int> > vec;   
    std::map<std::string, std::mem_fun1_ref_t<int, Foo, int> > map1;
    Foo a;      
    std::string s("aa");
                
    vec.push_back(std::mem_fun_ref(&Foo::A));
    map1.insert(std::pair<std::string, std::mem_fun1_ref_t<int, Foo, int> >(s, std::mem_fun_ref(&Foo::A)));
    std::cout << vec[0](a, 1) << std::endl;
    std::cout << map1.begin()->second(a, 2) << std::endl;   
                
    // use function pointer directly
    typedef int (Foo::*FuncPtr)(int);
    std::vector<FuncPtr> vec2;
    vec2.push_back(&Foo::A);
    vec2.push_back(&Foo::B);
    std::cout << (a.*(vec2[1]))(3) << std::endl; // it should be 5
                
    std::map<std::string, FuncPtr> map2;
    map2[s] = &Foo::B;
    std::cout < (a.*(map2[s]))(3) < std::endl; // it should be 5
    return 0;   
}