首页
社区
课程
招聘
[原创]Android下NDK开发-文件权限和用户篇
发表于: 2012-11-19 19:41 17754

[原创]Android下NDK开发-文件权限和用户篇

2012-11-19 19:41
17754

想象RE一样可以管理用户和权限,发现没有java版函数。
命令行方式暂不考虑。
后来发现,可以用C、C++,使用库.
只好学习linux,发现命名行的ls,不好;又找了其他c函数,就它们了。
花了一天手机浏览,现学现用,需要cygwin提供linux平台,官方NDK,也可以用CRT插件,我用的编程软件是eclipse。

正好可以发一篇NDK的相关内容(主要是,一些结构转化String数组等)。

具体见在源码里(我新建的叫android_os_FileHelper.cpp)。

编译后的so在附件了,配合java就可以使用了。

android_os_FileHelper.cpp文件

//#include <stdio.h>
//#include <unistd.h>

#define LOG_TAG "FileHelper"
#include <string.h>
#include <android/log.h>
#include "jni.h"

//#include "JNIHelp.h"

#include <sys/errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>

//struct passwd *getpwuid(uid_t uid);
//getpwuid函数是通过用户的uid查找用户的passwd数据。如果出错时,它们都返回一个空指针并设置errno的值,用户可以根据perror函数查看出错的信息。
//strcut group * getgrgid(gid_t gid);
//getgrgid()用来依参数gid指定的组识别码逐一搜索组文件,找到时便将该组的数据以group结构返回。
//group结构请参考getgrent()。返回group结构数据,如果返回NULL则表示已无数据,或有错误发生。
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
//int chmod( const char *filename, int pmode );
//改变文件的读写许可设置,如果改变成功返回0,否则返回-1
#include <C:\cygwin\usr\include\io.h>
//int chown(const char * path, uid_t owner, gid_t group);
//改变文件所有者 types.h,unistd.h 成功则返回0, 失败返回-1, 错误原因存于errno.
// chown()会将参数path 指定文件的所有者变更为参数owner 代表的用户,而将该文件的组变更为参数group 组。
//如果参数owner 或group 为-1,对应的所有者或组不会有所改变。
//root 与文件所有者皆可改变文件组,但所有者必须是参数group 组的成员。
//当root 用chown()改变文件所有者或组时,该文件若具有S_ISUID或S_ISGID 权限,则会清除此权限位,
//此外如果具有S_ISGID 权限但不具S_IXGRP 位,则该文件会被强制锁定,文件模式会保留。
#include <unistd.h>
#include <assert.h>


#if HAVE_ANDROID_OS
#include <sys/ioctl.h>
#include <linux/msdos_fs.h>
#endif

#define  li(x...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,x)

#ifdef __cplusplus
extern "C" {
#endif

namespace android {
/*
 static jclass gFileStatusClass;
 static jfieldID gFileStatusDevFieldID;
 static jfieldID gFileStatusInoFieldID;
 static jfieldID gFileStatusModeFieldID;
 static jfieldID gFileStatusNlinkFieldID;
 static jfieldID gFileStatusUidFieldID;
 static jfieldID gFileStatusGidFieldID;
 static jfieldID gFileStatusSizeFieldID;
 static jfieldID gFileStatusBlksizeFieldID;
 static jfieldID gFileStatusBlocksFieldID;
 static jfieldID gFileStatusAtimeFieldID;
 static jfieldID gFileStatusMtimeFieldID;
 static jfieldID gFileStatusCtimeFieldID;

 static jclass gPassWordClass;
 static jfieldID gPassWordNameFieldID;
 static jfieldID gPassWordPasswdFieldID;
 static jfieldID gPassWordUidFieldID;
 static jfieldID gPassWordGidFieldID;
 static jfieldID gPassWordGecosFieldID;
 static jfieldID gPassWordDirFieldID;
 static jfieldID gPassWordShellFieldID;

 static jclass gGroupClass;
 static jfieldID gGroupNameFieldID;
 static jfieldID gGroupPasswdFieldID;
 static jfieldID gGroupGidFieldID;
 static jfieldID gGroupMemFieldID;
 */
jint Java_android_os_FileHelper_setPermissions(JNIEnv* env, jobject clazz, jstring file, jint mode,
		jint uid, jint gid) {
#if HAVE_ANDROID_OS
	const jchar* str = env->GetStringCritical(file, 0);
	String8 file8;
	if (str)
	{
		file8 = String8(str, env->GetStringLength(file));
		env->ReleaseStringCritical(file, str);
	}
	if (file8.size() <= 0)
	{
		return ENOENT;
	}
	if (uid >= 0 || gid >= 0)
	{
		int res = chown(file8.string(), uid, gid);
		if (res != 0)
		{
			return errno;
		}
	}
	return chmod(file8.string(), mode) == 0 ? 0 : errno;
#else
	return ENOSYS;
#endif
}

jint Java_android_os_FileHelper_getPermissions(JNIEnv* env, jobject clazz, jstring file,
		jintArray outArray) {
#if HAVE_ANDROID_OS
	const jchar* str = env->GetStringCritical(file, 0);
	String8 file8;
	if (str)
	{
		file8 = String8(str, env->GetStringLength(file));
		env->ReleaseStringCritical(file, str);
	}
	if (file8.size() <= 0)
	{
		return ENOENT;
	}
	struct stat st;
	if (stat(file8.string(), &st) != 0)
	{
		return errno;
	}
	jint* array = (jint*)env->GetPrimitiveArrayCritical(outArray, 0);
	if (array)
	{
		int len = env->GetArrayLength(outArray);
		if (len >= 1)
		{
			array[0] = st.st_mode;
		}
		if (len >= 2)
		{
			array[1] = st.st_uid;
		}
		if (len >= 3)
		{
			array[2] = st.st_gid;
		}
	}
	env->ReleasePrimitiveArrayCritical(outArray, array, 0);
	return 0;
#else
	return ENOSYS;
#endif
}

//Landroid/os/FileHelper;.stat (Ljava/lang/String;Landroid/os/FileHelper$FileStatus;)Z
jboolean Java_android_os_FileHelper_stat(JNIEnv* env, jobject clazz, jstring path,jobject fileStatus) {
	li("-------stat");
	static jclass gFileStatusClass = env->FindClass("android/os/FileHelper$FileStatus");
	if (gFileStatusClass == NULL) {
		li("Unable to find class android.os.FileHelper$FileStatus");
		return false;
	}
	jfieldID gFileStatusDevFieldID = env->GetFieldID(gFileStatusClass, "dev", "I");
	jfieldID gFileStatusInoFieldID = env->GetFieldID(gFileStatusClass, "ino", "I");
	jfieldID gFileStatusModeFieldID = env->GetFieldID(gFileStatusClass, "mode", "I");
	jfieldID gFileStatusNlinkFieldID = env->GetFieldID(gFileStatusClass, "nlink", "I");
	jfieldID gFileStatusUidFieldID = env->GetFieldID(gFileStatusClass, "uid", "I");
	jfieldID gFileStatusGidFieldID = env->GetFieldID(gFileStatusClass, "gid", "I");
	jfieldID gFileStatusSizeFieldID = env->GetFieldID(gFileStatusClass, "size", "J");
	jfieldID gFileStatusBlksizeFieldID = env->GetFieldID(gFileStatusClass, "blksize", "I");
	jfieldID gFileStatusBlocksFieldID = env->GetFieldID(gFileStatusClass, "blocks", "J");
	jfieldID gFileStatusAtimeFieldID = env->GetFieldID(gFileStatusClass, "atime", "J");
	jfieldID gFileStatusMtimeFieldID = env->GetFieldID(gFileStatusClass, "mtime", "J");
	jfieldID gFileStatusCtimeFieldID = env->GetFieldID(gFileStatusClass, "ctime", "J");

	const char* pathStr = env->GetStringUTFChars(path, NULL);
	jboolean ret = false;

	struct stat s;
	int res = stat(pathStr, &s);
	if (res == 0) {
		ret = true;
		if (fileStatus != NULL) {
			env->SetIntField(fileStatus, gFileStatusDevFieldID, s.st_dev);
			env->SetIntField(fileStatus, gFileStatusInoFieldID, s.st_ino);
			env->SetIntField(fileStatus, gFileStatusModeFieldID, s.st_mode);
			env->SetIntField(fileStatus, gFileStatusNlinkFieldID, s.st_nlink);
			env->SetIntField(fileStatus, gFileStatusUidFieldID, s.st_uid);
			env->SetIntField(fileStatus, gFileStatusGidFieldID, s.st_gid);
			env->SetLongField(fileStatus, gFileStatusSizeFieldID, s.st_size);
			env->SetIntField(fileStatus, gFileStatusBlksizeFieldID, s.st_blksize);
			env->SetLongField(fileStatus, gFileStatusBlocksFieldID, s.st_blocks);
			env->SetLongField(fileStatus, gFileStatusAtimeFieldID, s.st_atime);
			env->SetLongField(fileStatus, gFileStatusMtimeFieldID, s.st_mtime);
			env->SetLongField(fileStatus, gFileStatusCtimeFieldID, s.st_ctime);
		}
	}

	env->ReleaseStringUTFChars(path, pathStr);

	return ret;
}

jobject Java_android_os_FileHelper_getpwuid(JNIEnv* env, jobject clazz, jint uid) {
	li("---------getpwuid");
	jclass cPassWord = env->FindClass("android/os/FileHelper$PassWord");
	if (cPassWord == NULL) {
		li("Unable to find class android.os.FileHelper$PassWord");
		return 0;
	}
	jfieldID gPassWordNameFieldID = env->GetFieldID(cPassWord, "name", "Ljava/lang/String;");
	jfieldID gPassWordPasswdFieldID = env->GetFieldID(cPassWord, "passwd", "Ljava/lang/String;");
	jfieldID gPassWordUidFieldID = env->GetFieldID(cPassWord, "uid", "I");
	jfieldID gPassWordGidFieldID = env->GetFieldID(cPassWord, "gid", "I");
	//jfieldID gPassWordGecosFieldID = env->GetFieldID(cPassWord, "gecos", "Ljava/lang/String;");
	jfieldID gPassWordDirFieldID = env->GetFieldID(cPassWord, "dir", "Ljava/lang/String;");
	jfieldID gPassWordShellFieldID = env->GetFieldID(cPassWord, "shell", "Ljava/lang/String;");
	jobject passwd = (env)->AllocObject(cPassWord);
	struct passwd *pw;
	pw = getpwuid(uid);
	if (pw == 0)
		return 0;
	li(pw->pw_name);
	env->SetObjectField(passwd, gPassWordNameFieldID, env->NewStringUTF(pw->pw_name));
	env->SetObjectField(passwd, gPassWordPasswdFieldID, env->NewStringUTF(pw->pw_passwd));
	env->SetIntField(passwd, gPassWordUidFieldID, pw->pw_uid);
	env->SetIntField(passwd, gPassWordGidFieldID, pw->pw_gid);
	//env->SetObjectField(passwd, gPassWordGecosFieldID, env->NewStringUTF(pw->pw_gecos));
	env->SetObjectField(passwd, gPassWordDirFieldID, env->NewStringUTF(pw->pw_dir));
	env->SetObjectField(passwd, gPassWordShellFieldID, env->NewStringUTF(pw->pw_shell));
	return passwd;
}

//#define SetObjectField(pEnv,pObj, pFieldID, pVal) pEnv->SetObjectField(pObj, pFieldID, pVal);DeleteLocalRef(pEnv, pVal)

jobject Java_android_os_FileHelper_getgrgid(JNIEnv* env, jobject clazz, jint gid) {
//获取Java中的实例类
	li("----------getgrgid");
	jclass cGroup = env->FindClass("android/os/FileHelper$Group");
	if (cGroup == NULL) {
		li("Unable to find class android.os.FileHelper$Group");
		return 0;
	}
//获取类中每一个变量的定义
	jfieldID gGroupNameFieldID = env->GetFieldID(cGroup, "name", "Ljava/lang/String;");
	jfieldID gGroupPasswdFieldID = env->GetFieldID(cGroup, "passwd", "Ljava/lang/String;");
	jfieldID gGroupGidFieldID = env->GetFieldID(cGroup, "gid", "I");
	jfieldID gGroupMemFieldID = env->GetFieldID(cGroup, "mem", "[Ljava/lang/String;");
	//分配新 Java 对象而不调用该对象的任何构造函数。返回该对象的引用。 clazz 参数务必不要引用数组类。
	jobject group = (env)->AllocObject(cGroup);
	struct group *gr;
	gr = getgrgid(gid);
	if (gr == 0)
		return 0;
	jclass jcstring = env->FindClass("java/lang/String"); //定义数组中元素类型

	jstring strTemp;
	//给每一个实例的变量赋值
	strTemp = env->NewStringUTF(gr->gr_name);
	env->SetObjectField(group, gGroupNameFieldID, strTemp);
	env->SetObjectField(group, gGroupPasswdFieldID, env->NewStringUTF(gr->gr_passwd));
	env->SetIntField(group, gGroupGidFieldID, gr->gr_gid);

	int i = 0;
	while (gr->gr_mem[i++])
		;
	jobjectArray mems;
//创建一个数组类型为String类
//
//jarray  NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement);
//构造新的数组,它将保存类 elementClass 中的对象。所有元素初始值均设为 initialElement。
//参数:
//env:JNI 接口指针。
//length:数组大小。
//elementClass:数组元素类。
//initialElement:初始值。
//返回值:
//Java 数组对象。如果无法构造数组,则为 NULL。
//抛出:
//OutOfMemoryError:如果系统内存不足。

	mems = (env)->NewObjectArray(i, jcstring, 0);
	//env->SetIntField(group, gGroupMemFieldID, gr->mem);
	for (--i; i >= 0; i--) {
		strTemp = env->NewStringUTF(gr->gr_mem[i]);
		//添加到objcet数组中
		(env)->SetObjectArrayElement(mems, i, strTemp);
		(env)->DeleteLocalRef(strTemp);
	}
	env->SetObjectField(group, gGroupMemFieldID, mems);
	(env)->DeleteLocalRef(mems);
	return group;
}
/*
jstring
Java_android_os_FileHelper_stringFromJNI( JNIEnv* env,jobject thiz )
{
    return (env)->NewStringUTF("Hello from JNI !");
}*/

jint Java_android_os_FileHelper_chown(JNIEnv* env, jobject clazz, jstring file, jint uid, jint gid) {
#if HAVE_ANDROID_OS
	const jchar* str = env->GetStringCritical(file, 0);
	String8 file8;
	if (str)
	{
		file8 = String8(str, env->GetStringLength(file));
		env->ReleaseStringCritical(file, str);
	}
	if (file8.size() <= 0)
	{
		return ENOENT;
	}

	if (uid >= 0 || gid >= 0)
	{
		int res = chown(file8.string(), uid, gid);
		if (res != 0)
		{
			return errno;
		}
	}
	return res;
#else
	return ENOSYS;
#endif
}

jint Java_android_os_FileHelper_chmod(JNIEnv* env, jobject clazz, jstring file, jint mode) {
#if HAVE_ANDROID_OS
	const jchar* str = env->GetStringCritical(file, 0);
	String8 file8;
	if (str)
	{
		file8 = String8(str, env->GetStringLength(file));
		env->ReleaseStringCritical(file, str);
	}
	if (file8.size() <= 0)
	{
		return ENOENT;
	}

	return chmod(file8.string(), mode) == 0 ? 0 : errno;;
#else
	return ENOSYS;
#endif
}

jint Java_android_os_FileHelper_getFatVolumeId(JNIEnv* env, jobject clazz, jstring path) {
#if HAVE_ANDROID_OS
	if (path == NULL)
	{
		jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
		return -1;
	}
	const char *pathStr = env->GetStringUTFChars(path, NULL);
	int result = -1;
	// only if our system supports this ioctl
#ifdef VFAT_IOCTL_GET_VOLUME_ID
	int fd = open(pathStr, O_RDONLY);
	if (fd >= 0)
	{
		result = ioctl(fd, VFAT_IOCTL_GET_VOLUME_ID);
		close(fd);
	}
#endif

	env->ReleaseStringUTFChars(path, pathStr);
	return result;
#else
	return -1;
#endif
}

/**
* Table of methods associated with a single class.
*/
/*
//Landroid/os/FileHelper;.stringFromJNI ()Ljava/lang/String;
static JNINativeMethod gMethods[] = { { "setPermissions",
		"(Ljava/lang/String;III)I",
		(void*) Java_android_os_FileHelper_setPermissions }, { "getPermissions",
		"(Ljava/lang/String;[I)I",
		(void*) Java_android_os_FileHelper_getPermissions }, { "getFatVolumeId",
		"(Ljava/lang/String;)I",
		(void*) Java_android_os_FileHelper_getFatVolumeId }, { "stat",
		"(Ljava/lang/String;Landroid/os/FileHelper$FileStatus;)Z",
		(void*) Java_android_os_FileHelper_stat }, { "getpwuid",
		"(I)Landroid/os/FileHelper$PassWord;",
		(void*) Java_android_os_FileHelper_getpwuid }, { "getgrgid",
		"(II)Landroid/os/FileHelper$Group;",
		(void*) Java_android_os_FileHelper_getgrgid }, { "chown",
		"(Ljava/lang/String;II)I", (void*) Java_android_os_FileHelper_chown }, {
		"chmod", "(Ljava/lang/String;I)I",
		(void*) Java_android_os_FileHelper_chmod },
		{ "stringFromJNI", "()Ljava/lang/String;",
				(void*) Java_android_os_FileHelper_stringFromJNI } };

static const char* const kFileHelperPathName = "android/os/FileUtils";

#define JNIREG_CLASS "android/os/FileUtils"//指定要注册的类


// Register several native methods for one class.

static int registerNativeMethods(JNIEnv* env, const char* className,
        JNINativeMethod* gMethods, int numMethods)
{
	jclass clazz;
	clazz = (env)->FindClass(className);
	if (clazz == NULL) {
		return JNI_FALSE;
	}
	if ((env)->RegisterNatives(clazz, gMethods, numMethods) < 0) {
		return JNI_FALSE;
	}

	return JNI_TRUE;
}

// Register native methods for all classes we know about.

static int registerNatives(JNIEnv* env)
{
	if (!registerNativeMethods(env, JNIREG_CLASS, gMethods, sizeof(gMethods) / sizeof(gMethods[0])))
		return JNI_FALSE;

	return JNI_TRUE;
}

// Set some test stuff up.
//
//Returns the JNI version on success, -1 on failure.

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
	JNIEnv* env = NULL;
	jint result = -1;

	if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
		return -1;
	}
	assert(env != NULL);

	if (!registerNatives(env)) {//注册
		return -1;
	}
	// success -- return valid version number
	result = JNI_VERSION_1_4;

	return result;
}
*/

/*
 int register_Java_android_os_FileHelper(JNIEnv* env) {
 jclass clazz;

 clazz = env->FindClass(kFileHelperPathName);
 LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.FileHelper");

 gFileStatusClass = env->FindClass("android/os/FileHelper$FileStatus");
 LOG_FATAL_IF(gFileStatusClass == NULL,
 "Unable to find class android.os.FileHelper$FileStatus");

 gFileStatusDevFieldID = env->GetFieldID(gFileStatusClass, "dev", "I");
 gFileStatusInoFieldID = env->GetFieldID(gFileStatusClass, "ino", "I");
 gFileStatusModeFieldID = env->GetFieldID(gFileStatusClass, "mode", "I");
 gFileStatusNlinkFieldID = env->GetFieldID(gFileStatusClass, "nlink", "I");
 gFileStatusUidFieldID = env->GetFieldID(gFileStatusClass, "uid", "I");
 gFileStatusGidFieldID = env->GetFieldID(gFileStatusClass, "gid", "I");
 gFileStatusSizeFieldID = env->GetFieldID(gFileStatusClass, "size", "J");
 gFileStatusBlksizeFieldID = env->GetFieldID(gFileStatusClass, "blksize",
 "I");
 gFileStatusBlocksFieldID = env->GetFieldID(gFileStatusClass, "blocks", "J");
 gFileStatusAtimeFieldID = env->GetFieldID(gFileStatusClass, "atime", "J");
 gFileStatusMtimeFieldID = env->GetFieldID(gFileStatusClass, "mtime", "J");
 gFileStatusCtimeFieldID = env->GetFieldID(gFileStatusClass, "ctime", "J");

 gPassWordClass = env->FindClass("android/os/FileHelper$PassWord");
 LOG_FATAL_IF(gFileStatusClass == NULL,
 "Unable to find class android.os.FileHelper$PassWord");

 gPassWordNameFieldID = env->GetFieldID(gPassWordClass, "name",
 "Ljava/lang/String;");
 gPassWordPasswdFieldID = env->GetFieldID(gPassWordClass, "passwd",
 "Ljava/lang/String;");
 gPassWordUidFieldID = env->GetFieldID(gPassWordClass, "uid", "I");
 gPassWordGidFieldID = env->GetFieldID(gPassWordClass, "gid", "I");
 gPassWordGecosFieldID = env->GetFieldID(gPassWordClass, "gecos",
 "Ljava/lang/String;");
 gPassWordDirFieldID = env->GetFieldID(gPassWordClass, "dir",
 "Ljava/lang/String;");
 gPassWordShellFieldID = env->GetFieldID(gPassWordClass, "shell",
 "Ljava/lang/String;");

 gGroupClass = env->FindClass("android/os/FileHelper$Group");
 LOG_FATAL_IF(gFileStatusClass == NULL,"Unable to find class android.os.FileHelper$Group");

 gGroupNameFieldID = env->GetFieldID(gGroupClass, "name","Ljava/lang/String;");
 gGroupPasswdFieldID = env->GetFieldID(gGroupClass, "passwd","Ljava/lang/String;");
 gGroupGidFieldID = env->GetFieldID(gGroupClass, "gid", "I");
 gGroupMemFieldID = env->GetFieldID(gGroupClass, "mem","[Ljava/lang/String;");

 return AndroidRuntime::registerNativeMethods(env, kFileHelperPathName,
 methods, NELEM(methods));
 }
 */
}
#ifdef __cplusplus
}
#endif



[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

上传的附件:
收藏
免费 6
支持
分享
最新回复 (4)
雪    币: 338
活跃值: (91)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
2
上一张截图



命令行类似如:
1

ProcessBuilder pb  =  new  ProcessBuilder ( "/system/bin/sh" ); 

pb . directory ( new  File ( "/" )); //设置shell的当前目录。    
try  {   
     Process proc  =  pb . start ();   
     //获取输入流,可以通过它获取SHELL的输出。    
     BufferedReader in  =  new  BufferedReader ( new  InputStreamReader ( proc . getInputStream ()));   
     BufferedReader err  =  new  BufferedReader ( new  InputStreamReader ( proc . getErrorStream ()));   
     //获取输出流,可以通过它向SHELL发送命令。    
     PrintWriter out  =  new  PrintWriter ( new  BufferedWriter ( new  OutputStreamWriter ( proc   
                     . getOutputStream ())),  true );   
     out . println ( "pwd" );   
     out . println ( "su root" ); //执行这一句时会弹出对话框(以下程序要求授予最高权限...),要求用户确认。    
     out . println ( "cd /data/data" ); //这个目录在系统中要求有root权限才可以访问的。    
     out . println ( "ls -l" ); //这个命令如果能列出当前安装的APK的数据文件存放目录,就说明我们有了ROOT权限。    
     out . println ( "exit" );   
     // proc.waitFor();    
     String line ;   
     while  (( line  =  in . readLine ()) !=  null ) {   
         System . out . println ( line );    // 打印输出结果 
     }   
     while  (( line  =  err . readLine ()) !=  null ) {   
         System . out . println ( line );   // 打印错误输出结果 
     }   
     in . close ();   
     out . close ();   
     proc . destroy ();   
}  catch  ( Exception e ) {   
     System . out . println ( "exception:"  +  e );   
}        


2


Process process  =  Runtime . getRuntime (). exec ( "superuser" ); 
上传的附件:
2012-11-19 19:49
0
雪    币: 47147
活跃值: (20485)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
3
感谢分享,现在你己转正
2012-11-19 22:14
0
雪    币: 338
活跃值: (91)
能力值: ( LV8,RANK:120 )
在线值:
发帖
回帖
粉丝
4
多谢啊
2012-11-22 10:27
0
雪    币: 24
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
非常不错,学习了
2012-12-18 21:02
0
游客
登录 | 注册 方可回帖
返回
//