梆libSecShell 流程分析
2024-9-28 17:8:5 Author: mp.weixin.qq.com(查看原文) 阅读量:3 收藏

本文仅限于技术讨论,不得用于非法途径,后果自负。


init_proc



在这个so中init_proc 负责在内存中解密so中的JNI_OnLoad,并且填充导入表,值得注意的是,如果你hook了initProc 那么会导致JNI_OnLoad解密失败,这部分原因笔者不在细究,有兴趣的自行研究。


dump so

由于初始so关键信息被抹除,我们需要在init_proc 执行完之后dump so 用作静态分析,这里采用hook libc的__dl__ZL10call_arrayIPFviPPcS1_EEvPKcPT_jbS5_(每台手机有可能不一样),在调用initarray的第一个函数时dump。


修复so

这里使用的是git上https://github.com/Chenyangming9/SoFixer修复后发现导入表被initProc填充为绝对地址,需要还原符号。
发现ida 自动生成的extern表 这里直接拿到ida自动生成的表地址,通过frida的符号解析出so中的导入表中的每一个表指向的函数名称,进行比对然后将dump下来的so中的导入表填充ida的extern表 dump脚本。
dump() {
if (this.libso == null) {
return -1;
}

var file_path = this.path + "/" + this.soName;
logd("dump so:" + this.soName + " to " + file_path);
var file_handle = new File(file_path, "wb+");
if (file_handle && file_handle != null) {
Memory.protect(ptr(this.libso.base.toString()), this.libso.size, 'rwx');
logd("libso_buffer:" + ptr(this.libso.base.toString()) + " " + this.libso.size);
var libso_buffer = ptr(this.libso.base.toString()).readByteArray(this.libso.size);

this.patchGot(libso_buffer!)
var pGot = new BigInt64Array(libso_buffer!, 0x1352B8, 424)

//创建extern 表
var table = [{ key: 0x155000, value:"sleep"}, { key: 0x155008, value:"popen"}, { key: 0x155010, value:"mprotect"}, { key: 0x155018, value:"sigemptyset"}, { key: 0x155020, value:"lseek64"}, { key: 0x155028, value:"deflateEnd"}, { key: 0x155030, value:"pipe"}, { key: 0x155038, value:"atoi"}, { key: 0x155040, value:"pthread_create"}, { key: 0x155048, value:"wait"}, { key: 0x155050, value:"realloc"}, { key: 0x155058, value:"open"}, { key: 0x155060, value:"pthread_key_create"}, { key: 0x155068, value:"inflate"}, { key: 0x155070, value:"pthread_once"}, { key: 0x155078, value:"__cxa_finalize"}, { key: 0x155080, value:"ftell"}, { key: 0x155088, value:"ptrace"}, { key: 0x155090, value:"siglongjmp"}, { key: 0x155098, value:"mkdir"}, { key: 0x1550A0, value:"setpgid"}, { key: 0x1550A8, value:"calloc"}, { key: 0x1550B0, value:"fread"}, { key: 0x1550B8, value:"syslog"}, { key: 0x1550C0, value:"stpcpy"}, { key: 0x1550C8, value:"inflateInit2_"}, { key: 0x1550D0, value:"AAsset_getBuffer"}, { key: 0x1550D8, value:"strncmp"}, { key: 0x1550E0, value:"read"}, { key: 0x1550E8, value:"fstat"}, { key: 0x1550F0, value:"inotify_rm_watch"}, { key: 0x1550F8, value:"strncasecmp"}, { key: 0x155100, value:"AAsset_close"}, { key: 0x155108, value:"pthread_mutex_init"}, { key: 0x155110, value:"signal"}, { key: 0x155118, value:"abort"}, { key: 0x155120, value:"closedir"}, { key: 0x155128, value:"strerror"}, { key: 0x155130, value:"lstat"}, { key: 0x155138, value:"lstat64"}, { key: 0x155140, value:"_exit"}, { key: 0x155148, value:"__errno"}, { key: 0x155150, value:"srand"}, { key: 0x155158, value:"snprintf"}, { key: 0x155160, value:"getpid"}, { key: 0x155168, value:"dl_iterate_phdr"}, { key: 0x155170, value:"strcat"}, { key: 0x155178, value:"sscanf"}, { key: 0x155180, value:"android_set_abort_message"}, { key: 0x155188, value:"deflate"}, { key: 0x155190, value:"islower"}, { key: 0x155198, value:"isupper"}, { key: 0x1551A0, value:"write"}, { key: 0x1551A8, value:"toupper"}, { key: 0x1551B0, value:"getenv"}, { key: 0x1551B8, value:"strcasecmp"}, { key: 0x1551C0, value:"strrchr"}, { key: 0x1551C8, value:"access"}, { key: 0x1551D0, value:"time"}, { key: 0x1551D8, value:"rand"}, { key: 0x1551E0, value:"__sF"}, { key: 0x1551E8, value:"memcmp"}, { key: 0x1551F0, value:"fclose"}, { key: 0x1551F8, value:"lseek"}, { key: 0x155200, value:"fputs"}, { key: 0x155208, value:"rewind"}, { key: 0x155210, value:"fputc"}, { key: 0x155218, value:"__stack_chk_fail"}, { key: 0x155220, value:"fgets"}, { key: 0x155228, value:"select"}, { key: 0x155230, value:"fork"}, { key: 0x155238, value:"gettimeofday"}, { key: 0x155240, value:"dlclose"}, { key: 0x155248, value:"pthread_cond_wait"}, { key: 0x155250, value:"strftime"}, { key: 0x155258, value:"memchr"}, { key: 0x155260, value:"prctl"}, { key: 0x155268, value:"ioctl"}, { key: 0x155270, value:"strcasestr"}, { key: 0x155278, value:"pthread_setspecific"}, { key: 0x155280, value:"strncpy"}, { key: 0x155288, value:"opendir"}, { key: 0x155290, value:"dlsym"}, { key: 0x155298, value:"atol"}, { key: 0x1552A0, value:"openlog"}, { key: 0x1552A8, value:"__stack_chk_guard"}, { key: 0x1552B0, value:"environ"}, { key: 0x1552B8, value:"__android_log_print"}, { key: 0x1552C0, value:"inotify_init"}, { key: 0x1552C8, value:"unlink"}, { key: 0x1552D0, value:"inflateEnd"}, { key: 0x1552D8, value:"setenv"}, { key: 0x1552E0, value:"sysconf"}, { key: 0x1552E8, value:"strchr"}, { key: 0x1552F0, value:"tolower"}, { key: 0x1552F8, value:"fseek"}, { key: 0x155300, value:"strcmp"}, { key: 0x155308, value:"flock"}, { key: 0x155310, value:"fgetc"}, { key: 0x155318, value:"sprintf"}, { key: 0x155320, value:"strncat"}, { key: 0x155328, value:"sigaction"}, { key: 0x155330, value:"pthread_mutex_lock"}, { key: 0x155338, value:"mmap"}, { key: 0x155340, value:"setjmp"}, { key: 0x155348, value:"closelog"}, { key: 0x155350, value:"pthread_getspecific"}, { key: 0x155358, value:"AAssetManager_open"}, { key: 0x155360, value:"memmove"}, { key: 0x155368, value:"ferror"}, { key: 0x155370, value:"isxdigit"}, { key: 0x155378, value:"inotify_add_watch"}, { key: 0x155380, value:"AAsset_getLength"}, { key: 0x155388, value:"readlink"}, { key: 0x155390, value:"strstr"}, { key: 0x155398, value:"getpagesize"}, { key: 0x1553A0, value:"strdup"}, { key: 0x1553A8, value:"strtok"}, { key: 0x1553B0, value:"usleep"}, { key: 0x1553B8, value:"kill"}, { key: 0x1553C0, value:"readdir"}, { key: 0x1553C8, value:"fdopen"}, { key: 0x1553D0, value:"strlen"}, { key: 0x1553D8, value:"crc32"}, { key: 0x1553E0, value:"exit"}, { key: 0x1553E8, value:"close"}, { key: 0x1553F0, value:"vasprintf"}, { key: 0x1553F8, value:"remove"}, { key: 0x155400, value:"dlopen"}, { key: 0x155408, value:"stat"}, { key: 0x155410, value:"localtime"}, { key: 0x155418, value:"rename"}, { key: 0x155420, value:"munmap"}, { key: 0x155428, value:"get_crc_table"}, { key: 0x155430, value:"fprintf"}, { key: 0x155438, value:"malloc"}, { key: 0x155440, value:"memcpy"}, { key: 0x155448, value:"waitpid"}, { key: 0x155450, value:"deflateInit2_"}, { key: 0x155458, value:"connect"}, { key: 0x155460, value:"memset"}, { key: 0x155468, value:"fopen"}, { key: 0x155470, value:"AAssetManager_fromJava"}, { key: 0x155478, value:"socket"}, { key: 0x155480, value:"pthread_cond_broadcast"}, { key: 0x155488, value:"sigsetjmp"}, { key: 0x155490, value:"pclose"}, { key: 0x155498, value:"strtol"}, { key: 0x1554A0, value:"pthread_kill"}, { key: 0x1554A8, value:"free"}, { key: 0x1554B0, value:"fscanf"}, { key: 0x1554B8, value:"strcpy"}, { key: 0x1554C0, value:"__system_property_get"}, { key: 0x1554C8, value:"pwrite"}, { key: 0x1554D0, value:"pthread_exit"}, { key: 0x1554D8, value:"symlink"}, { key: 0x1554E0, value:"vfprintf"}, { key: 0x1554E8, value:"pthread_mutex_unlock"}, { key: 0x1554F0, value:"clock_gettime"}, { key: 0x1554F8, value:"__cxa_atexit"}, { key: 0x155500, value:"isspace"}]
var base =this.libso.base

for (var i = 0; i < pGot.length; i++) {
var addr = pGot[i]
var funcName = DebugSymbol.fromAddress(ptr(addr.toString())).toString().split("!")[1]
logd("pgot1:" + i + " " + ptr(addr.toString()) + " " + funcName)
table.forEach(function (item: any) {
var name = item["value"].toString()
if (funcName?.indexOf(name) !=-1 && funcName?.length > 0) {
pGot[i] = BigInt(ptr(item["key"]).toString())
logd("replace pgot:" + i + funcName + " " + ptr(0x1352B8).add(i*8).add(base).readPointer()+" "+ ptr(pGot[i-1].toString()) +" "+ name + " to " + ptr(item["key"]))
return
}
})
if(ptr(pGot[i].toString()) > base){
pGot[i] = BigInt(ptr(pGot[i].toString()).sub(base).toString())
}

}
logd(pGot.toString())
logd("dump so:" + this.soName + " to " + file_path);
file_handle.write(libso_buffer!);
file_handle.flush();
file_handle.close();
log("[dump]:"+ file_path);
}
}


字符串解密

程序使用的大部分字符串都被加密,加密方法为栈赋值密文,解密函数解密密文。

解密函数

__int64 __fastcall sub_181D4(__int64 result, int a2, char a3)
{
int v3; // w5
char v4; // w7

v3 = 0;
v4 = *(result + 1) ^ a3;
while ( a2 > v3 )
{
*(result + v3) = v4 ^ *(result + v3 + 2);
++v3;
}
*(result + v3) = 0;
return result;
}


间接跳转

为了干扰ida的引用分析,使用间接跳转。


init_array

以下将按程序顺序讲述程序执行功能。

getAndroidVersion

通过_system_property_get("ro.build.version.sdk"),_system_property_get("ro.build.version.release")来获取androdi版本。

getDalvikAndART

_system_property_get("ro.yunos.version"),_system_property_get("ro.yunos.version.release") 获取java虚拟机的类型。

getLibcApi

使用dlopen打开libc dlsym获取下列几个函数:
mprotect
mmap
munmap
fopen
fclose
fgets
fwrite
fread
sprintf
pthread_create
其中fopen,fclose,fgets,fwrite,fread,sprintf,pthread_create 被定义为了全局结构体,其余为全局函数指针因此,ida定义结构结构体。
00000000 struct libcPFuncAry // sizeof=0x38
00000000 { // XREF: .data:g_func_fopen_ptr/r
00000000 FILE *(__fastcall *pFopen)(const char *filename, const char *modes);
00000000 // XREF: sub_158AC+30/r
00000000 // sub_15AE0+14/r ...
00000008 int (__fastcall *pFclose)(FILE *stream); // XREF: sub_E1F78/o
00000008 // sub_E1F78+4/r
00000010 char *(__fastcall *pFgets)(char *s, int n, FILE *stream);
00000010 // XREF: inotify_init_function+28/o
00000010 // inotify_init_function+AC/r
00000018 unsigned __int64 (__fastcall *pFwrite)(__int64 a1, unsigned __int64 a2, unsigned __int64 a3, __int64 a4);
00000018 // XREF: sub_1989C/o
00000018 // sub_1989C+C/r ...
00000020 unsigned __int64 (__fastcall *pFread)(char *a1, unsigned __int64 a2, unsigned __int64 a3, __int64 a4);
00000020 // XREF: .text&ARM.extab:0000000000037FE8/o
00000020 // .text&ARM.extab:0000000000037FF4/r
00000028 __int64 (*pSprintf)(__int64 a1, unsigned __int8 *a2, ...);
00000028 // XREF: struc_func_ptr_ctor+18/o
00000028 // struc_func_ptr_ctor+1C/r
00000030 __int64 (__fastcall *pPthread_create)(_QWORD *a1, __int64 a2, __int64 a3, __int64 a4);
00000030 // XREF: operator new(ulong)+44/o
00000030 // operator new(ulong)+4C/r ...
00000038 };
值得疑惑的是 这个函数还顺手通过 _system_property_get("ro.board.platform")获取主板名称,对"rk3399"进行比对,结果存放全局变量。

findDex2Oat

fopen("/proc/18607/cmdline","r"),比较/system/bin/dex2oat,如果找到返回。
##getLD_OPT_PACKAGENAME
通过getEnv获取这两个字段 没有看懂在干嘛,不过条件没有满足,程序返回了。


JNI_OnLoad

这里ida没有识别为函数,我们需要手动添加地址,从开头向下翻翻到
BL __stack_chk_fail
就是结束地址了。

ida_funcs.add_func(startEa.endEa)

switch识别

由于大量使用了控制流混淆导致ida识别switch出现问题这里我们手动告诉ida。
Edit =>Other=>Specify switch idiom
手动修复下。

checkcpuabi

获取cpuabi到全局变量。

get SoName

获取so名称到全局变量。

decodeStr

解密 com_SecShell_SecShell_H。

getLibcApi

重新获取Api。

getPKGNAME

获取 com_SecShell_SecShell_H 中的PKGNAME字段。

解密数据

接下来一顿骚操作,我晕了,解密数据,略过。

反射调用

FindClass android/app/ActivityThread
GetStaticMethodID currentActivityThread
CallStaticObjectMethod currentActivityThread
GetMethodID getSystemContext
CallObjectMethod getSystemContext
FindClass android/app/ContextImpl
GetMethodID getPackageManager
CallObjectMethod getPackageManager
GetMethodID getPackageInfo
NewStringUTF 包名
CallObjectMethod getPackageInfo
GetFieldID applicationInfo
GetObjectField applicationInfo:Landroid/content/pm/ApplicationInfo
>GetObjectClass applicationInfo:Landroid/content/pm/ApplicationInfo
GetFieldID sourceDir
GetObjectField sourceDir
GetStringUTFChars sourceDir
GetFieldID dataDir
GetObjectField dataDir
GetStringUTFChars dataDir
GetFieldID nativeLibraryDir
GetObjectField nativeLibraryDir
GetStringUTFChars nativeLibraryDir

CheckPakeName

校验包名

getMPAAS

GetLibcArt

打开/proc/self/maps 获取lib_libart 地址。

解密字符串

RegisterNatives

注册jni函数

loadDex

拼接字符串

hooklibcNewFunc

防dexDump
https://bbs.kanxue.com/thread-223320.htm

nopDex2Oat

loadDex

解密字符串
调javaapi

d.e

解密字符串
dalvik/system/DexPathList
akeInMemoryDexElements
([Ljava/nio/ByteBuffer;Ljava/util/List;)[Ldalvik/system/DexPathList$Element;
java/nio/ByteBuffer
wra
([B)Ljava/nio/ByteBuffer;
java/util/ArrayList
size
加载dex 这里可以dump了。

antiDebug

checkFridaTaskGdbus

打开/proc/self/task /proc/self/task/6268/status 查找frida特征。

createAntiThread

antiThread

####透明加密
https://bbs.kanxue.com/thread-226358.htm

CheckPakename

打开/proc/self/cmdline 比较包名。

结语

至此,加固分析结束,由于trace只会走流程其他分析流程可能走不到,但是控制流混淆太恶心了,告一段落了。

看雪ID:method

https://bbs.kanxue.com/user-home-856431.htm

*本文为看雪论坛优秀文章,由 method 原创,转载请注明来自看雪社区

# 往期推荐

1、Alt-Tab Terminator注册算法逆向

2、恶意木马历险记

3、VMP源码分析:反调试与绕过方法

4、Chrome V8 issue 1486342浅析

5、Cython逆向-语言特性分析

球分享

球点赞

球在看

点击阅读原文查看更多


文章来源: https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458576493&idx=1&sn=187da8e7f97b2f6caab5ea01e27e5c21&chksm=b18dd6e786fa5ff1ffaffe32e7017829aacfe4b608cc7c6be29c19a52717de6cebc649e100c8&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh