前言:本文仅针对少部分中低端Android(山寨)机,请勿对各大厂商对号入座~不是鼓励大家作假,只是让大家对手机评测的水分有更深的了解~
现在国内Android市场如火如荼,Android机的价格从几百到几千不等,差异还是比较大的。我们经常能听到有人巴拉巴拉说自己几百买了个多好多好的手机,CPU xxx,内存 xxx,像素 xxx,跑分完爆某米几条街,比某米实惠强大太多了。那么我们今天就来扒一拔这些数据一定是真的吗?这些跑分排名一定准确吗?
小白型作假
先从我们最关心的CPU信号来看:
一般可以从我们手机的设置--关于手机中选项中查看到当前的cpu型号、主频、核数等信息,如下所示:
这部分的作假有一种最简单愚蠢的方法,直接在当前Activity找到该String将其修改掉,设置中的这些菜单项一般都是
Preference
的子类,我们可以在xml文件中找到其相应summary
、title
的字符即可,如下:
android:summary="@string/cpu_setting_summary"
android:title="@string/cp_setting_title"
修改后的效果图:
这很简单吧?相信刚接触Android一天的同学都能明白。但是为什么又说愚蠢呢?很明显,这要修改的话只能修改掉当前Activity的字符,万一其他地方也有读取显示cpu信息,就会造成显示不同信息的冲突,另外当用户使用第三方apk评测的时候也会瞬间原形毕露,所以这样的修改方法可以直接pass。
进阶型作假
上面提及了第三APK评测时的作假问题,那我们怎样才能修改使得第三方的APK也能"掩护"我们作假呢?其实有了思路就会发现非常简单。因为作假一般都是在rom层开发的时候考虑的,所以我们可以直接修改framework框架层的东西。最终显示在鲁大师、安兔兔等跑分软件上的信息本质肯定也是一个TextView,但我们又不能去修改鲁大师、安兔兔等代码,所以退而求其次,我们修改framework层中的TextView.java(frameworks/base/core/java/android/widget/TextView.java),大致思路可以参考下面的代码:
private void setText(CharSequence text, BufferType type,
boolean notifyBefore, int oldlen) {
if (text == null) {
text = "";
}
String packageName = getContext().getPackageName();
if (packageName.equals("com.antutu.ABenchMark")
|| packageName.equals("com.cpuid.cpu_z")
|| packageName.equals("com.primatelabs.geekbench")) {
String str1 = text.toString();
if (str1.contains("hahaha")) {
text = "lalalala";
}
}
}
}
我们直接在框架层TextView的setText方法最前面添加了一段逻辑,判断一下当前应用包名的及显示的字符名是否符合我们需要作假的评测软件,如果满足直接替换作假信息的字符,当然这里只是最间的字符替换,其他作假情况也完全可以按以上思路直接修改framework中的源码!这样的话当第三方APP运行在我们的系统中时就能"掩护"我们了~但是,这种方案也有一个致命的缺点,万一用户不用鲁大师呢?而是用360、腾讯或者其他第三方评测应用呢?难道我们还要一个一个包名类名的添加至setText()
中?这显然不符和设计代码的原理。
终极作假
这里以手机内置存储空间为例,作假让其在任何时候都能向外界提供"假信息"。手机的存储空间并不是直接在系统上层(这里指application、framework层)上报的,而是从底层(kernel、hal、jni层)传递给上层处理。所以为了一劳永逸,我们直接在底层动手!在其向上层传递的时候,我们就改变它的部分信息,不必再考虑上层使用什么第三方app。
观察系统源码libcore/luni/src/main/native/libcore_io_Posix.cpp,我们可以找到此函数:
static jobject Posix_statvfs(JNIEnv* env, jobject, jstring javaPath) {
ScopedUtfChars path(env, javaPath);
if (path.c_str() == NULL) {
return NULL;
}
struct statvfs sb;
int rc = TEMP_FAILURE_RETRY(statvfs(path.c_str(), &sb));
if (rc == -1) {
throwErrnoException(env, "statvfs");
return NULL;
}
//return makeStructStatVfs_fake(env, sb, false);
return makeStructStatVfs(env, sb);
}
最后的return makeStructStatVfs(env, sb);
便是向上层传递的存储空间信息,因此我们直接模仿makeStructStatVfs(env, sb)
自己写一个makeStructStatVfs_fake(env, sb, false)
函数,将此return给上层:
static jobject makeStructStatVfs_fake(JNIEnv* env, const struct statvfs& sb, bool internal) {
jlong f_blocks;
jlong f_bfree;
jlong f_bavail;
int value = 0;
jlong f_blocks_temp ;
int size =0 ;
//[CDS]Libcore_io_Posix bsize:4096
//[CDS]Libcore_io_Posix frsize:4096
//[CDS]Libcore_io_Posix blocks:207369
//[CDS]Libcore_io_Posix bfree:189725
//[CDS]Libcore_io_Posix bavail:189725
size = property_getInt("fake_system_rom_size", size);
if(internal) { //internal storage
value = property_getInt("internal_fake_rom_size", 0);
} else { //phone storage
value = property_getInt("phone_fake_rom_size", 0);
}
if(value == 0) {
f_blocks = sb.f_blocks;
f_bfree = sb.f_bfree;
f_bavail = sb.f_bavail;
} else {
f_blocks = (jlong)(((jlong)value * 256 * 4096) / sb.f_bsize);
f_blocks_temp = (jlong)(((jlong)(value - size) * 256 * 4096) / sb.f_bsize);
f_bfree = (jlong)((jfloat)((jfloat) f_blocks_temp / sb.f_blocks ) * sb.f_bfree );
f_bavail = (jlong)((jfloat)((jfloat) f_blocks_temp / sb.f_blocks) * sb.f_bavail);
}
static jmethodID ctor = env->GetMethodID(JniConstants::structStatVfsClass, "<init>",
"(JJJJJJJJJJJ)V");
return env->NewObject(JniConstants::structStatVfsClass, ctor,
static_cast<jlong>(sb.f_bsize),
static_cast<jlong>(sb.f_frsize),
static_cast<jlong>(f_blocks),
static_cast<jlong>(f_bfree),
static_cast<jlong>(f_bavail),
static_cast<jlong>(sb.f_files),
static_cast<jlong>(sb.f_ffree),
static_cast<jlong>(sb.f_favail),
static_cast<jlong>(sb.f_fsid),
static_cast<jlong>(sb.f_flag),
max_name_length);
}
看似一长段代码,其实就只是将sb.f_bsize
、sb.f_bfree
、sb.f_bavail
替换成我们作假的大小和数量。看一下效果图:
作假前
作假后
除了这些,用户肯定还关心像素吧?这其实也没什么难度。camera像素作假一般会做到二千万像素,五千万也不是不可能,如果是仅仅针对工具检测推荐修改的地方为:frameworks\base\core\java\android\hardware\Camera.java中的getSupportedPictureSizes()方法:
public List<Size> getSupportedPictureSizes() {
String str = get((mStereo3DMode ? KEY_STEREO3D_PRE : "") + KEY_PICTURE_SIZE + SUPPORTED_VALUES_SUFFIX);
if(DisplayManagerGlobal.getCurrentFakeProcess()){
ArrayList<Size> sizeList = splitSize(str);
Size large_size = sizeList.get(sizeList.size() - 1);
if(large_size.width * large_size.height > 2560 * 1920) str += ",4608x3456";
}
return splitSize(str);
}
最后在此说明本文并不是作假教学,也没针对任何手机!只是想让大家谨慎购机,让大家明白,良辰有一百种作假的方法,而你却无可奈何~~~