共有回帖数 1 个
-
PS Houdini是中国团队独立开发的。项目发起,概念验证,原型以及最后的产品化都由中国团队完成。很骄傲我们有第一流的团队,和第一流的产品能力。
下面是原回答,根据 @dxnil 的评论是完全错误的,仅保留来记录我原本的猜测确实都是胡扯。
请大家帮我点反对+没有帮助来折叠了吧~
==================================================
例如说Houdini的血缘,可能带有一些跟NVIDIA的Project Denver相似的血缘——并不是说Houdini跟Denver之间有任何直接关系,而是它们俩涉及的某些技术可能有共同的来源。
- 这个跟项目在哪里发起、哪里立项、哪里实现并没有直接关系。来源就是来源。
作为一个dynamic binary translator,资料上说Houdini是把ARM指令动态翻译为x86指令。但事实上确实如此么?Intel Atom里是否有未公开的隐藏指令集/微指令架构/执行模式,而Houdini是否利用它来更高效地实现动态二进制翻译,外界无从得知。
这种层面的信息,在没有好的分析思路指导的前提下,单靠反汇编libhoudini.so从汇编的茫茫大海中想分析出什么,希望渺茫。
Intel对Android on x86 (Atom)的支持是一整套的,不只是Houdini来模拟ARM的native指令,还有x86版的Dalvik VM与ART。如果说Atom上有这么个未公开的部分,或许Intel版Dalvik / ART也会去使用它,例如说让Dalvik VM的JIT编译器直接编译到这个上面,绕开表面的x86指令集。到底有没有(过)这样的实现呢?
还等真的知道内幕的人在可以透露的范围内说说Houdini的奥秘~
Houdini的开发工作应该有一部分是在Intel上海做的。不知道Intel上海的大大们上不上知乎呢…
楼主 2016-10-29 13:48 回复
-
我猜测其中的一个原因是,Houdini会将一个进程对一些白名单中的函数调用迁移到主机架构上做,所以会快很多。
证据是你用ida去看libhoudini.so的话,应该能看到有一个字符串表,会被引用,里面包含了非常多常见函数。
举个例子,在原本的进程即将调用了puts这个函数的时候,qemu的做法是进入arm 的libc.so 继续模拟arm指令,直到调用syscall时进行调用约定的翻译;houdini的做法是直接获得虚拟cpu的r0,然后调用x86的puts(r0),自然就快了很多。
这意味着如果被模拟的进程如果主要的开销都在这些白名单的函数中,那么就很容易达到和x86一样的执行效率
2016/08/19更新:
今天刚好又找出libhoudini.so拖到IDA里 看了看,我这里再只列出一些分析的结果,希望有一些参考意义吧。。
分析的libhoudini.so是http://forum.xda-developers.com/showthread.php?t=2528952 处的Genymotion-ARM-Translation_v1.1.zip
这里只写下我自己的一些分析结果,函数名,类型名都是自己定义的。
首先,在0x0023B680处开始有一个数组,共2986个元素,元素定义如下
struct white_list_item_t
{
const char *func_name;
const char *type_of_args;
void *func_in_x86_lib;
int fake_virtual_address_of_this_symbol;
int unkonwn2;
void *wrapper_from_virtual_arm_to_x86;
};
这个数组里的元素,func_in_x86_lib初始化都是0, fake_virtual_address_of_this_symbol可能为0或者为一个0x0fxxxx的数值,unkonwn2未知,func_name是一个函数名称,如AMREncodeInit,fputs等等,而type_of_args则是一个如"4_4_4"这样的字符串,似乎说明了这个函数的参数大小和个数,但是没有使用到,所以我没有去进一步确认。
值得关注的是warpper_from_virtual_arm_to_x86,这是一个函数指针。
在这两千多个元素中,这个域被初始化共有几十种不同的值,下面给出的是给出了其中一个代码比较简单函数的IDA反编译结果(手工做出过修正):
int __cdecl sub_17CC30(int a1, int a2, int a3, int a4)
{
int v4; // ebx@0
//省略..
int v20; // [sp-4h] [bp-8h]@0
v7 = v4;
v8 = (int (__stdcall *)(int, int, int, int, _DWORD))white_list[a1].func_in_x86_lib;
v9 = (int *)sub_12CEC0();
//省略若干。。
sub_175500();
*v9 = (v8)(
*v9,
v9[1],
v9[2],
v9[3],
*(_DWORD *)v9[13]
);
//省略若干
return result;
}
v9 = (int *)sub_12CEC0();
这里应该是获得了当前的虚拟arm cpu context的结构体指针,然后v8则是这个函数对应的func_in_x86_lib。
*v9 = (v8)(
*v9,
v9[1],
v9[2],
v9[3],
*(_DWORD *)v9[13]
);
这句话翻译过来则是r0 = func_in_x86_lib(r0, r1, r2, r3, *(sp)); 也就是从寄存器和栈上取了5个参数,完成调用之后将返回值放回到r0中。。(因为arm里sp也就是r13,返回地址放在寄存器而不是栈上,所以当时的栈顶就是第5个参数)
如果你看sub12CEC0这个函数所在的white_list_item_t元素的话,会发现func_name是
_ZN7android14SurfaceFlinger15postMessageSyncERKNS_2spINS_11MessageBaseEEExj
也就是
android::SurfaceFlinger::postMessageSync(android::spandroid::MessageBase const&, long long, unsigned int)
算上指向对象自己的this指针以及long long 64位,刚好是5个。。
之前提到 white_item_t中的func_in_x86_lib都是0,那么代码中理应有地方对他进行了赋值。。我这里仅找到一处在下面
int *__usercall sub_E5F50@eax(int a1@eax)
{
int v1; // edi@1
int v2; // eax@1
int v3; // esi@2
int (__stdcall *v4)(_DWORD, int, int, int, int, int, int, int, int, int); // ebx@3
unsigned int v5; // edi@4
int v6; // eax@5
void *v7; // ebx@5
white_list_item_t *v8; // eax@8
int *result; // eax@10
int v10; // ebx@12
_DWORD *v11; // eax@13
int v12; // ebx@14
_DWORD *v13; // eax@15
int *v14; // [sp-3Ch] [bp-40h]@12
int v15; // [sp-38h] [bp-3Ch]@12
int v16; // [sp-34h] [bp-38h]@5
int v17; // [sp-30h] [bp-34h]@5
int v18; // [sp-2Ch] [bp-30h]@5
int v19; // [sp-28h] [bp-2Ch]@5
int v20; // [sp-24h] [bp-28h]@5
int v21; // [sp-20h] [bp-24h]@5
int v22; // [sp-1Ch] [bp-20h]@5
int v23; // [sp-18h] [bp-1Ch]@5
int v24; // [sp-14h] [bp-18h]@5
white_list_item_t *v25; // [sp-Ch] [bp-10h]@3
signed int v26; // [sp-8h] [bp-Ch]@8
signed int v27; // [sp-4h] [bp-8h]@8
int (*v28)(); // [sp+0h] [bp-4h]@8
v1 = a1;
v2 = sub_E76F0("libEGL.so", 0);
if ( v2 )
{
v3 = sub_E7830(v2, *(_DWORD *)__readfsdword(4));
if ( v3 )
goto LABEL_3;
}
else
{
v3 = 0;
}
v14 = &v15;
v10 = sub_E76F0("libGLESv1_CM.so", 0);
if ( !v10 || (v11 = (_DWORD *)sub_12CEC0(), v14 = &v15, (v3 = sub_E7830(v10, *v11)) == 0) )
{
v14 = &v15;
v12 = sub_E76F0("libGLESv2.so", 0);
if ( v12 )
{
v13 = (_DWORD *)sub_12CEC0();
v14 = &v15;
v3 = sub_E7830(v12, *v13);
}
}
LABEL_3:
v4 = *(int (__stdcall **)(_DWORD, int, int, int, int, int, int, int, int, int))(v1 + 8);
v25 = *(white_list_item_t **)(v1 + 16);
if ( *(_DWORD *)(v1 + 16) == 1 )
{
v5 = __readfsdword(28);
__writefsdword(28, 3u);
sub_C22D0(0);
}
else
{
v5 = 3;
}
v6 = v4(*(_DWORD *)__readfsdword(4), v16, v17, v18, v19, v20, v21, v22, v23, v24);
v7 = (void *)v6;
if ( v25 == (white_list_item_t *)1 )
__writefsdword(28, v5);
if ( v6 )
{
v28 = sub_CB5F0;
v27 = 24;
v26 = 2986;
v25 = white_list;
v8 = sub_BDA30(*(_DWORD *)__readfsdword(4), white_list, 2986, 24, (int (__cdecl *)(int, int))sub_CB5F0);
if ( v8 )
v8-func_in_x86_lib = v7;
}
result = (int *)__readfsdword(4);
*result = v3;
return result;
}
这个函数的最后:
v8-func_in_x86_lib = v7;
完成了这个赋值。。
然后在前面"libGLESv1_CM.so"等等字符串也一定程度上提示了这个函数是和动态链接库加载相关的(sub_E76F0这个函数中还调用了dlopen)
不过我已经看不下去了= =~
总结一下就是说,确实有一些逻辑进行了类似利用调用约定直接调用x86函数的过程。。
但是具体这里的x86函数究竟就是原本一个x86 so中的函数,还是说是一个arm中的对应函数进行binary translation的结果,亦或是别的可能。。。我就不知道了。。。
&以上的分析很大程度是以来于自己的主观猜测。。恩。。。。
1楼 2016-10-29 13:48 回复
Copyright © 2010~2015 直线网 版权所有,All Rights Reserved.沪ICP备10039589号
意见反馈 |
关于直线 |
版权声明 |
会员须知