xposed是一个用于全局hook的框架。

许多破解工具,都通过xposed去实现它的功能,如果不想自己的app被xposed修改的话,可以在自己应用内偷偷把xposed的开关关掉。

开关的位置在这里:

https://github.com/rovo89/XposedBridge/blob/art/app/src/main/java/de/robv/android/xposed/XposedBridge.java

变量名叫disableHooks

package de.robv.android.xposed;import android.annotation.SuppressLint;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.util.Log;import com.android.internal.os.RuntimeInit;
import com.android.internal.os.ZygoteInit;import java.io.File;
import java.io.IOException;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;import dalvik.system.PathClassLoader;
import de.robv.android.xposed.XC_MethodHook.MethodHookParam;
import de.robv.android.xposed.callbacks.XC_InitPackageResources;
import de.robv.android.xposed.callbacks.XC_LoadPackage;import static de.robv.android.xposed.XposedHelpers.getIntField;
import static de.robv.android.xposed.XposedHelpers.setObjectField;/*** This class contains most of Xposed's central logic, such as initialization and callbacks used by* the native side. It also includes methods to add new hooks.*/
@SuppressWarnings("JniMissingFunction")
public final class XposedBridge {/*** The system class loader which can be used to locate Android framework classes.* Application classes cannot be retrieved from it.** @see ClassLoader#getSystemClassLoader*/public static final ClassLoader BOOTCLASSLOADER = ClassLoader.getSystemClassLoader();/** @hide */public static final String TAG = "Xposed";/** @deprecated Use {@link #getXposedVersion()} instead. */@Deprecatedpublic static int XPOSED_BRIDGE_VERSION;/*package*/ static boolean isZygote = true;private static int runtime = 0;private static final int RUNTIME_DALVIK = 1;private static final int RUNTIME_ART = 2;/*package*/ static boolean disableHooks = false;// This field is set "magically" on MIUI./*package*/ static long BOOT_START_TIME;private static final Object[] EMPTY_ARRAY = new Object[0];// built-in handlersprivate static final Map<Member, CopyOnWriteSortedSet<XC_MethodHook>> sHookedMethodCallbacks = new HashMap<>();/*package*/ static final CopyOnWriteSortedSet<XC_LoadPackage> sLoadedPackageCallbacks = new CopyOnWriteSortedSet<>();/*package*/ static final CopyOnWriteSortedSet<XC_InitPackageResources> sInitPackageResourcesCallbacks = new CopyOnWriteSortedSet<>();private XposedBridge() {}/*** Called when native methods and other things are initialized, but before preloading classes etc.* @hide*/@SuppressWarnings("deprecation")protected static void main(String[] args) {// Initialize the Xposed framework and modulestry {if (!hadInitErrors()) {initXResources();SELinuxHelper.initOnce();SELinuxHelper.initForProcess(null);runtime = getRuntime();XPOSED_BRIDGE_VERSION = getXposedVersion();if (isZygote) {XposedInit.hookResources();XposedInit.initForZygote();}XposedInit.loadModules();} else {Log.e(TAG, "Not initializing Xposed because of previous errors");}} catch (Throwable t) {Log.e(TAG, "Errors during Xposed initialization", t);disableHooks = true;}// Call the original startup codeif (isZygote) {ZygoteInit.main(args);} else {RuntimeInit.main(args);}}/** @hide */protected static final class ToolEntryPoint {protected static void main(String[] args) {isZygote = false;XposedBridge.main(args);}}private static void initXResources() throws IOException {// Create XResourcesSuperClass.Resources res = Resources.getSystem();File resDexFile = ensureSuperDexFile("XResources", res.getClass(), Resources.class);// Create XTypedArraySuperClass.Class<?> taClass = TypedArray.class;try {TypedArray ta = res.obtainTypedArray(res.getIdentifier("preloaded_drawables", "array", "android"));taClass = ta.getClass();ta.recycle();} catch (Resources.NotFoundException nfe) {XposedBridge.log(nfe);}Runtime.getRuntime().gc();File taDexFile = ensureSuperDexFile("XTypedArray", taClass, TypedArray.class);// Inject a ClassLoader for the created classes as parent of XposedBridge's ClassLoader.ClassLoader myCL =  XposedBridge.class.getClassLoader();String paths = resDexFile.getAbsolutePath() + File.pathSeparator + taDexFile.getAbsolutePath();PathClassLoader dummyCL = new PathClassLoader(paths, myCL.getParent());setObjectField(myCL, "parent", dummyCL);}@SuppressLint("SetWorldReadable")private static File ensureSuperDexFile(String clz, Class<?> realSuperClz, Class<?> topClz) throws IOException {XposedBridge.removeFinalFlagNative(realSuperClz);File dexFile = DexCreator.ensure(clz, realSuperClz, topClz);dexFile.setReadable(true, false);return dexFile;}private native static boolean hadInitErrors();private static native int getRuntime();/*package*/ static native boolean startsSystemServer();/*package*/ static native String getStartClassName();/*package*/ native static boolean initXResourcesNative();/*** Returns the currently installed version of the Xposed framework.*/public static native int getXposedVersion();/*** Writes a message to the Xposed error log.** <p class="warning"><b>DON'T FLOOD THE LOG!!!</b> This is only meant for error logging.* If you want to write information/debug messages, use logcat.** @param text The log message.*/public synchronized static void log(String text) {Log.i(TAG, text);}/*** Logs a stack trace to the Xposed error log.** <p class="warning"><b>DON'T FLOOD THE LOG!!!</b> This is only meant for error logging.* If you want to write information/debug messages, use logcat.** @param t The Throwable object for the stack trace.*/public synchronized static void log(Throwable t) {Log.e(TAG, Log.getStackTraceString(t));}/*** Hook any method (or constructor) with the specified callback. See below for some wrappers* that make it easier to find a method/constructor in one step.** @param hookMethod The method to be hooked.* @param callback The callback to be executed when the hooked method is called.* @return An object that can be used to remove the hook.** @see XposedHelpers#findAndHookMethod(String, ClassLoader, String, Object...)* @see XposedHelpers#findAndHookMethod(Class, String, Object...)* @see #hookAllMethods* @see XposedHelpers#findAndHookConstructor(String, ClassLoader, Object...)* @see XposedHelpers#findAndHookConstructor(Class, Object...)* @see #hookAllConstructors*/public static XC_MethodHook.Unhook hookMethod(Member hookMethod, XC_MethodHook callback) {if (!(hookMethod instanceof Method) && !(hookMethod instanceof Constructor<?>)) {throw new IllegalArgumentException("Only methods and constructors can be hooked: " + hookMethod.toString());} else if (hookMethod.getDeclaringClass().isInterface()) {throw new IllegalArgumentException("Cannot hook interfaces: " + hookMethod.toString());} else if (Modifier.isAbstract(hookMethod.getModifiers())) {throw new IllegalArgumentException("Cannot hook abstract methods: " + hookMethod.toString());}boolean newMethod = false;CopyOnWriteSortedSet<XC_MethodHook> callbacks;synchronized (sHookedMethodCallbacks) {callbacks = sHookedMethodCallbacks.get(hookMethod);if (callbacks == null) {callbacks = new CopyOnWriteSortedSet<>();sHookedMethodCallbacks.put(hookMethod, callbacks);newMethod = true;}}callbacks.add(callback);if (newMethod) {Class<?> declaringClass = hookMethod.getDeclaringClass();int slot;Class<?>[] parameterTypes;Class<?> returnType;if (runtime == RUNTIME_ART) {slot = 0;parameterTypes = null;returnType = null;} else if (hookMethod instanceof Method) {slot = getIntField(hookMethod, "slot");parameterTypes = ((Method) hookMethod).getParameterTypes();returnType = ((Method) hookMethod).getReturnType();} else {slot = getIntField(hookMethod, "slot");parameterTypes = ((Constructor<?>) hookMethod).getParameterTypes();returnType = null;}AdditionalHookInfo additionalInfo = new AdditionalHookInfo(callbacks, parameterTypes, returnType);hookMethodNative(hookMethod, declaringClass, slot, additionalInfo);}return callback.new Unhook(hookMethod);}/*** Removes the callback for a hooked method/constructor.** @deprecated Use {@link XC_MethodHook.Unhook#unhook} instead. An instance of the {@code Unhook}* class is returned when you hook the method.** @param hookMethod The method for which the callback should be removed.* @param callback The reference to the callback as specified in {@link #hookMethod}.*/@Deprecatedpublic static void unhookMethod(Member hookMethod, XC_MethodHook callback) {CopyOnWriteSortedSet<XC_MethodHook> callbacks;synchronized (sHookedMethodCallbacks) {callbacks = sHookedMethodCallbacks.get(hookMethod);if (callbacks == null)return;}callbacks.remove(callback);}/*** Hooks all methods with a certain name that were declared in the specified class. Inherited* methods and constructors are not considered. For constructors, use* {@link #hookAllConstructors} instead.** @param hookClass The class to check for declared methods.* @param methodName The name of the method(s) to hook.* @param callback The callback to be executed when the hooked methods are called.* @return A set containing one object for each found method which can be used to unhook it.*/@SuppressWarnings("UnusedReturnValue")public static Set<XC_MethodHook.Unhook> hookAllMethods(Class<?> hookClass, String methodName, XC_MethodHook callback) {Set<XC_MethodHook.Unhook> unhooks = new HashSet<>();for (Member method : hookClass.getDeclaredMethods())if (method.getName().equals(methodName))unhooks.add(hookMethod(method, callback));return unhooks;}/*** Hook all constructors of the specified class.** @param hookClass The class to check for constructors.* @param callback The callback to be executed when the hooked constructors are called.* @return A set containing one object for each found constructor which can be used to unhook it.*/@SuppressWarnings("UnusedReturnValue")public static Set<XC_MethodHook.Unhook> hookAllConstructors(Class<?> hookClass, XC_MethodHook callback) {Set<XC_MethodHook.Unhook> unhooks = new HashSet<>();for (Member constructor : hookClass.getDeclaredConstructors())unhooks.add(hookMethod(constructor, callback));return unhooks;}/*** This method is called as a replacement for hooked methods.*/private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,Object thisObject, Object[] args) throws Throwable {AdditionalHookInfo additionalInfo = (AdditionalHookInfo) additionalInfoObj;if (disableHooks) {try {return invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes,additionalInfo.returnType, thisObject, args);} catch (InvocationTargetException e) {throw e.getCause();}}Object[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot();final int callbacksLength = callbacksSnapshot.length;if (callbacksLength == 0) {try {return invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes,additionalInfo.returnType, thisObject, args);} catch (InvocationTargetException e) {throw e.getCause();}}MethodHookParam param = new MethodHookParam();param.method = method;param.thisObject = thisObject;param.args = args;// call "before method" callbacksint beforeIdx = 0;do {try {((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);} catch (Throwable t) {XposedBridge.log(t);// reset result (ignoring what the unexpectedly exiting callback did)param.setResult(null);param.returnEarly = false;continue;}if (param.returnEarly) {// skip remaining "before" callbacks and corresponding "after" callbacksbeforeIdx++;break;}} while (++beforeIdx < callbacksLength);// call original method if not requested otherwiseif (!param.returnEarly) {try {param.setResult(invokeOriginalMethodNative(method, originalMethodId,additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));} catch (InvocationTargetException e) {param.setThrowable(e.getCause());}}// call "after method" callbacksint afterIdx = beforeIdx - 1;do {Object lastResult =  param.getResult();Throwable lastThrowable = param.getThrowable();try {((XC_MethodHook) callbacksSnapshot[afterIdx]).afterHookedMethod(param);} catch (Throwable t) {XposedBridge.log(t);// reset to last result (ignoring what the unexpectedly exiting callback did)if (lastThrowable == null)param.setResult(lastResult);elseparam.setThrowable(lastThrowable);}} while (--afterIdx >= 0);// returnif (param.hasThrowable())throw param.getThrowable();elsereturn param.getResult();}/*** Adds a callback to be executed when an app ("Android package") is loaded.** <p class="note">You probably don't need to call this. Simply implement {@link IXposedHookLoadPackage}* in your module class and Xposed will take care of registering it as a callback.** @param callback The callback to be executed.* @hide*/public static void hookLoadPackage(XC_LoadPackage callback) {synchronized (sLoadedPackageCallbacks) {sLoadedPackageCallbacks.add(callback);}}/*** Adds a callback to be executed when the resources for an app are initialized.** <p class="note">You probably don't need to call this. Simply implement {@link IXposedHookInitPackageResources}* in your module class and Xposed will take care of registering it as a callback.** @param callback The callback to be executed.* @hide*/public static void hookInitPackageResources(XC_InitPackageResources callback) {synchronized (sInitPackageResourcesCallbacks) {sInitPackageResourcesCallbacks.add(callback);}}/*** Intercept every call to the specified method and call a handler function instead.* @param method The method to intercept*/private native synchronized static void hookMethodNative(Member method, Class<?> declaringClass, int slot, Object additionalInfo);private native static Object invokeOriginalMethodNative(Member method, int methodId,Class<?>[] parameterTypes, Class<?> returnType, Object thisObject, Object[] args)throws IllegalAccessException, IllegalArgumentException, InvocationTargetException;/*** Basically the same as {@link Method#invoke}, but calls the original method* as it was before the interception by Xposed. Also, access permissions are not checked.** <p class="caution">There are very few cases where this method is needed. A common mistake is* to replace a method and then invoke the original one based on dynamic conditions. This* creates overhead and skips further hooks by other modules. Instead, just hook (don't replace)* the method and call {@code param.setResult(null)} in {@link XC_MethodHook#beforeHookedMethod}* if the original method should be skipped.** @param method The method to be called.* @param thisObject For non-static calls, the "this" pointer, otherwise {@code null}.* @param args Arguments for the method call as Object[] array.* @return The result returned from the invoked method.* @throws NullPointerException*             if {@code receiver == null} for a non-static method* @throws IllegalAccessException*             if this method is not accessible (see {@link AccessibleObject})* @throws IllegalArgumentException*             if the number of arguments doesn't match the number of parameters, the receiver*             is incompatible with the declaring class, or an argument could not be unboxed*             or converted by a widening conversion to the corresponding parameter type* @throws InvocationTargetException*             if an exception was thrown by the invoked method*/public static Object invokeOriginalMethod(Member method, Object thisObject, Object[] args)throws NullPointerException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {if (args == null) {args = EMPTY_ARRAY;}Class<?>[] parameterTypes;Class<?> returnType;if (runtime == RUNTIME_ART && (method instanceof Method || method instanceof Constructor)) {parameterTypes = null;returnType = null;} else if (method instanceof Method) {parameterTypes = ((Method) method).getParameterTypes();returnType = ((Method) method).getReturnType();} else if (method instanceof Constructor) {parameterTypes = ((Constructor<?>) method).getParameterTypes();returnType = null;} else {throw new IllegalArgumentException("method must be of type Method or Constructor");}return invokeOriginalMethodNative(method, 0, parameterTypes, returnType, thisObject, args);}/*package*/ static void setObjectClass(Object obj, Class<?> clazz) {if (clazz.isAssignableFrom(obj.getClass())) {throw new IllegalArgumentException("Cannot transfer object from " + obj.getClass() + " to " + clazz);}setObjectClassNative(obj, clazz);}private static native void setObjectClassNative(Object obj, Class<?> clazz);/*package*/ static native void dumpObjectNative(Object obj);/*package*/ static Object cloneToSubclass(Object obj, Class<?> targetClazz) {if (obj == null)return null;if (!obj.getClass().isAssignableFrom(targetClazz))throw new ClassCastException(targetClazz + " doesn't extend " + obj.getClass());return cloneToSubclassNative(obj, targetClazz);}private static native Object cloneToSubclassNative(Object obj, Class<?> targetClazz);private static native void removeFinalFlagNative(Class<?> clazz);/*package*/ static native void closeFilesBeforeForkNative();/*package*/ static native void reopenFilesAfterForkNative();/*package*/ static native void invalidateCallersNative(Member[] methods);/** @hide */public static final class CopyOnWriteSortedSet<E> {private transient volatile Object[] elements = EMPTY_ARRAY;@SuppressWarnings("UnusedReturnValue")public synchronized boolean add(E e) {int index = indexOf(e);if (index >= 0)return false;Object[] newElements = new Object[elements.length + 1];System.arraycopy(elements, 0, newElements, 0, elements.length);newElements[elements.length] = e;Arrays.sort(newElements);elements = newElements;return true;}@SuppressWarnings("UnusedReturnValue")public synchronized boolean remove(E e) {int index = indexOf(e);if (index == -1)return false;Object[] newElements = new Object[elements.length - 1];System.arraycopy(elements, 0, newElements, 0, index);System.arraycopy(elements, index + 1, newElements, index, elements.length - index - 1);elements = newElements;return true;}private int indexOf(Object o) {for (int i = 0; i < elements.length; i++) {if (o.equals(elements[i]))return i;}return -1;}public Object[] getSnapshot() {return elements;}}private static class AdditionalHookInfo {final CopyOnWriteSortedSet<XC_MethodHook> callbacks;final Class<?>[] parameterTypes;final Class<?> returnType;private AdditionalHookInfo(CopyOnWriteSortedSet<XC_MethodHook> callbacks, Class<?>[] parameterTypes, Class<?> returnType) {this.callbacks = callbacks;this.parameterTypes = parameterTypes;this.returnType = returnType;}}
}

可以自己应用内使用反射去修改它,但不要认为它很轻松,先试试

 Class.forName("de.robv.android.xposed.XposedBridge")

抛出异常,找不到这个类

然后在jni里,使用

env->FindClass("de/robv/android/xposed/XposedBridge")

返回NULL,也找不到这个类

这是因为它不在当前的ClassLoader中。

很蛋疼是吧,没事,先看看下面的代码:

https://github.com/rovo89/Xposed/blob/master/libxposed_common.cpp

bool initXposedBridge(JNIEnv* env) {classXposedBridge = env->FindClass(CLASS_XPOSED_BRIDGE);if (classXposedBridge == NULL) {ALOGE("Error while loading Xposed class '%s':", CLASS_XPOSED_BRIDGE);logExceptionStackTrace();env->ExceptionClear();return false;}classXposedBridge = reinterpret_cast<jclass>(env->NewGlobalRef(classXposedBridge));ALOGI("Found Xposed class '%s', now initializing", CLASS_XPOSED_BRIDGE);if (register_natives_XposedBridge(env, classXposedBridge) != JNI_OK) {ALOGE("Could not register natives for '%s'", CLASS_XPOSED_BRIDGE);logExceptionStackTrace();env->ExceptionClear();return false;}methodXposedBridgeHandleHookedMethod = env->GetStaticMethodID(classXposedBridge, "handleHookedMethod","(Ljava/lang/reflect/Member;ILjava/lang/Object;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");if (methodXposedBridgeHandleHookedMethod == NULL) {ALOGE("ERROR: could not find method %s.handleHookedMethod(Member, int, Object, Object, Object[])", CLASS_XPOSED_BRIDGE);logExceptionStackTrace();env->ExceptionClear();return false;}return true;
}

这里可以看到会把这个类的jclass引用,保存在classXposedBridge变量里,它对应的符号是

_ZN6xposed17classXposedBridgeE

所以可以通过符号去获取它,然后修复它。

void disableXposed(JNIEnv *env) {do {void* pLibxposedArtSo = dlopen("libxposed_art.so", RTLD_NOW);if (NULL == pLibxposedArtSo)break;jclass *pClassXposedBridge = (jclass *)dlsym(pLibxposedArtSo, "_ZN6xposed17classXposedBridgeE");if (NULL == pClassXposedBridge)break;jfieldID fieldDisableHooks = env->GetStaticFieldID(*pClassXposedBridge, "disableHooks","Z");if (NULL == fieldDisableHooks)break;env->SetStaticBooleanField(*pClassXposedBridge, fieldDisableHooks, JNI_TRUE);} while (false);env->ExceptionClear();
}

这样子就在本应用内关掉了xposed的功能,不怕黑客使用xposed破解你的应用了。开不开心。

xposed的总开关相关推荐

  1. 科技部部长:基础研究是科技创新“总开关”

    来源:中国新闻网 中新社北京5月19日电 (记者 孙自法)"基础研究是科技创新的'总开关'!"言及基础研究在中国科技发展.增强原始创新能力中的地位与作用,中国科学技术部部长王志刚这 ...

  2. 解决在iOS8环境下,当用户关闭定位服务总开关时,无法将APP定位子选项加入定位权限列表的问题...

    关键点:- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizati ...

  3. android 是否允许打印debug级别日志的开关_log日志功能及设置方法

    log日志功能及设置方法的详细介绍,首先引入Python中有个logging模块可以完成相关信息的记录,在debug时用它往往事半功倍,下面一起跟随小编过来看看吧. 一.日志级别(从低到高): DEB ...

  4. diag开关什么意思_1P空气开关便宜、好用,为什么电工师傅却要我们买2P空气开关?...

    高低压开关柜.变压器及图纸大集合!(共31个培训资料+8套CAD图纸) 电子版资料,请留个邮箱,直接发邮箱,谨慎购买,原价168元,秒杀55.8元,限24小时! 1. 图文详解高压开关柜 2. 高压开 ...

  5. Android源码编译:任意界面屏幕边缘上滑弹出快捷操作栏【一键加速、开关控制】

    <The Fucking Source Code> 注:以下均为android源码Framework层修改. 设计实现在任意界面从屏幕边缘上滑弹出快捷操作栏,包括亮度调节.正在后台运行的程 ...

  6. ESP8266 Blinker 小爱同学 本地控制 手机配网 四路开关 物联网 arduino编程详细注释

    作为一名注册12年CSDN的电脑爱好者,没有从事喜欢的IT行业还是颇有那么一丝丝的想念. 通过一段时间的arduino编程和ESP8266学习,参考示例程序等,完善了自己的一份小爱同学四开关控制的程序 ...

  7. ESP8266利用Bliker、小爱同学和本地按钮控制4路开关

    ESP8266利用Bliker.小爱同学和本地按钮控制4路开关 Blinker函数说明: Button.color("#FFFF00"); //设置app按键是纯黄色,16进制颜色 ...

  8. Air780E小程序远程开关-LuatOS版本

    目前,Air780E的CSDK已经开源了,LuatOS也作为其中的一个部分开源了出来,今天学习一下如何通过小程序远程控制开发板上灯的开关,学会以后可以制作远程开关控制各种设备. 本教程无需拥有服务器和 ...

  9. 【已解决】华为手机如何关闭智慧助手·今天(负一屏) | 华为荣耀八手机智慧助手开关介绍 | 华为手机关闭负一屏开关后,仍接收到负一屏服务相关通知提醒怎么办

    智慧助手·今天(负一屏)为桌面最左边屏幕,向右滑动即可进入,与智慧助手(智慧语音.小艺小艺).左下角/右下角斜上方滑入的服务中心非同一个应用. 文章目录 开启/关闭负一屏 方式1:在桌面主屏幕中的桌面 ...

最新文章

  1. HDU 2055 An easy problem
  2. MySQL 开启远程登录权限
  3. STL源码剖析 迭代器的概念和traits编程技法
  4. 基于PCDN技术的无延时直播方案
  5. python中迭代器有哪些_Python迭代器:什么是Python中的迭代器以及如何使用它?
  6. LeetCode 242. 有效的字母异位词 (计数排序思想字符处理)
  7. securecrt 中文横着显示解决
  8. 机器学习总结(一):线性回归、岭回归、Lasso回归
  9. 关于cad2010的激活
  10. 图像处理中的高斯滤波器
  11. 如何解决竞价推广中的恶意点击?
  12. Linux系统下安装Chrome浏览器
  13. 【毕业设计】智能指纹识别门禁系统 - 单片机 嵌入式 物联网
  14. 微分方程——线性微分方程
  15. php 分页样式css样式,thinkphp5分页CSS样式代码
  16. php文章下一页,php实现文章上一页下一页的实例
  17. 中国曲谱网爬虫研究(Python)
  18. 微信小程序之一个页面多个转发分享按钮,如何识别不同的按钮
  19. python编程-----利用爬虫获取自如房间信息(二)
  20. 黑盒测试,白盒测试与灰盒测试的比较和区别

热门文章

  1. [译]PG15加速排序性能
  2. linux文件操作管理,linux 文件管理操作入门
  3. uniapp 手持pda 扫描 功能
  4. ubuntu18.04配置静态ip
  5. HDU 1846 Brave Game 巴士博弈
  6. Flak-SQLAlchemy安装和介绍
  7. uv视差检测障碍物_社区组成–视差效果,节拍检测,精美游戏和艺术研究
  8. 问题 F: 【数论】青蛙的约会
  9. 【金融财经】金融市场一周简报(2018-03-16)
  10. 华为云迁移工具推荐最佳实践:Xen虚拟化迁移到华为云