dyOpen
A dlopen library that bypasses mobile system limitation
简介
byOpen是一个绕过移动端系统�制的增强版dlfunctions库。
支�特性
Android
支æŒ?Appä¸åŠ è½½å’Œä½¿ç”¨Android系统库接å?£ï¼ˆå?³ä½¿mapsä¸è¿˜æ²¡æœ‰è¢«åŠ è½½ä¹Ÿæ”¯æŒ?)。
Android 7以上dlopen, System.load都是被é™?制调用的,虽然目å‰?网上有Nougat_dlfunctionsç‰åº“通过从maps䏿‰¾so库æ?¥ç»•è¿‡åŠ è½½é™?制。
ä¸?过对于appä¸è¿˜æ²¡è¢«åŠ è½½åˆ°mapsçš„so库,这ç§?æ–¹å¼?å°±ä¸?行了。
而byOpenä¸?仅支æŒ?fake dlopenæ–¹å¼?从mapsåŠ è½½ï¼Œè¿˜å?¯ä»¥å°†è¿˜æ²¡åŠ è½½åˆ°mapsçš„so库绕过系统é™?åˆ¶å¼ºè¡ŒåŠ è½½è¿›æ?¥ä½¿ç”¨ï¼Œå®žçŽ°æ›´åŠ é€šç”¨åŒ–å¾—dlopen。
注:目�的实现方��论上还是比较通用的,至少我这Android 10上测试ok,但还没完整详细测试过,是�使用请自行评估。
相关原�
具体实现原�还是比较简�的,主�还是借鉴了一�绕过Android P对�SDK接��制的简�方法的�想和实现方�。
è™½ç„¶è¿™ç¯‡æ–‡ç« ä¸ä¸»è¦?目的是为了绕过hide api,ä¸?过它里é?¢ä½¿ç”¨çš„将自己å?‡è£…æˆ?系统调用的方å¼?ï¼Œä¸€æ ·å?¯ä»¥ç”¨åˆ°System.loadLibrary上去,让系统以为是系统自身在调用System.loadLibrary
从而绕过Android Nçš„classloader-namespaceé™?制,将系统/system/libä¸ä»»æ„?soåº“åŠ è½½åˆ°mapsä¸ï¼Œç„¶å?Žå†?通过fake dlopen的方å¼?去dlsym。
增强版fake dlopen
关于fake dlopen的方�实现,网上已有很多实现,比如:
byOpenå?‚考了里é?¢çš„实现,é‡?新实现了一é??,并且å?šäº†ä¸€äº›å°?改进:
- ä¸?在/proc/self/mapsä¸çš„系统库,也能绕过é™?åˆ¶å¼ºè¡ŒåŠ è½½è¿›æ?¥ä½¿ç”¨
- 除了从.dynsym䏿£€ç´¢ç¬¦å?·ï¼Œè¿˜æ”¯æŒ?从.symtab䏿£€ç´¢ç¬¦å?·ï¼ˆå?‚考:Enhanced_dlfunctions,顺带修å¤?了里é?¢çš„一些bug)
- 整个dlopen过程å?ªæœ‰ä¸€æ¬¡malloc分é…?(çœ?去整个符å?·è¡¨çš„内å˜åˆ†é…?å’Œcopy)
Android例å?
Android相关测试App例å?在:Android Sample
注:目å‰?自带的App测试例å?里é?¢çš„系统库我写æ»äº†ï¼Œæœ‰äº›ç³»ç»Ÿç‰ˆæœ¬ä¸Šæœ‰å?¯èƒ½ä¸?å˜åœ¨ï¼Œè¯·å…ˆæ”¹æˆ?用户自己的库和符å?·å??,å†?编译测试
public class MainActivity extends AppCompatActivity {
private static final String SYSTEM_LIBRARY = "curl";
private static final String SYMBOL_NAME = "curl_version";除了Native版本dlopen接å?£ï¼ŒbyOpené¢?外æ??供了java版本的System.loadLibrary接å?£åœ¨javaå±‚ç›´æŽ¥ç»•è¿‡ç³»ç»Ÿåº“åŠ è½½ã€‚
关键代ç ?如下:
static public boolean loadLibrary(String libraryName) {
Method forName = Class.class.getDeclaredMethod("forName", String.class);
Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
Class<?> systemClass = (Class<?>) forName.invoke(null, "java.lang.System");
Method loadLibrary = (Method) getDeclaredMethod.invoke(systemClass, "loadLibrary", new Class[]{String.class});
loadLibrary.invoke(systemClass, libraryName);
}而native版本的dlopen_android.c实现ä¸ï¼Œæˆ‘å°†è¿™æ®µç»•è¿‡çš„ç³»ç»ŸåŠ è½½çš„æ–¹å¼?,通过jnié‡?新实现了一é??,然å?Žå’Œfake dlopenæ— ç¼?结å?ˆåˆ°äº†ä¸€èµ·ã€‚
iOS
虽然ioså?¯ä»¥ç›´æŽ¥ä½¿ç”¨dlopenï¼Œä½†æ˜¯å®¡æ ¸ä¸Šä¼šæœ‰é£Žé™©ï¼Œè‹¹æžœæœ‰å?¯èƒ½ä¼šå¯¹æ??交AppStoreçš„app扫æ??相关dlopen/dlsymç‰è°ƒç”¨ï¼Œæ?¥åˆ¤æ–是å?¦å˜åœ¨ä¸€äº›æ•?感的ç§?有调用。
为了在通过调用一些ç§?有接å?£çš„æ—¶å€™é?¿å…?被苹果检测到,byOpen也通过自己实现dlopen/dlsym直接从已ç»?åŠ è½½è¿›æ?¥çš„images列表里é?¢ç›´æŽ¥æŸ¥æ‰¾å¯¹åº”symbol地å?€æ?¥è°ƒç”¨ã€‚
å½“ç„¶ï¼Œä¸ºäº†æ›´åŠ å®‰å…¨ï¼Œç›¸å…³è°ƒç”¨çš„åº“ç¬¦å?·ç¡¬ç¼–ç ?å—符串ç‰ï¼Œç”¨æˆ·å?¯ä»¥è‡ªè¡Œå?šå±‚å?˜æ?¢åŠ å¯†ï¼Œä¸?è¦?直接编译进app。
接�用法
相关��库和接�在:dlopen.h
相关使用方�跟原生dlopen完全相�:
typedef by_char_t const* (*curl_version_t)();
by_pointer_t handle = by_dlopen("libcurl.so", BY_RTLD_LAZY);
if (handle)
{
by_pointer_t addr = by_dlsym(handle, "curl_version");
if (addr)
{
curl_version_t curl_version = (curl_version_t)addr;
by_print("curl_version: %s", curl_version());
}
by_dlclose(handle);
}编译
编译需�先安装:xmake
Android
直接编译库
$ xmake f -p android --ndk=~/file/android-ndk-r20b
$ xmake通过gradle编译测试Apk
$ cd src/android
$ ./gradlew app:assembleDebug通过xmake直接编译apk
$ xmake apk_build通过xmake直接安装测试apk
$ xmake apk_testiOS
直接编译库
$ xmake f -p iphoneos -a [armv7|arm64]
$ xmakeMacOS
我们也�以在macOS下编译测试,也是支�的:
$ xmake
$ xmake run