会跟喜欢的还是对你好的结婚测试数据你好

你好测试_百度贴吧
贴吧热议榜
使用签名档&&
保存至快速回贴tensorflow作为google开源的项目,现在赶超了caffe,好像成为最受欢迎的深度学习框架。确实在编写的时候更能感受到代码的真实存在,这点和caffe不同,caffe通过编写配置文件进行网络的生成。环境tensorflow是0.10的版本,注意其他版本有的语句会有错误,这是tensorflow版本之间的兼容问题。
还需要安装PIL:pip install Pillow
图片的格式:
– 图像标准化,可安装在20×20像素的框内,同时保留其长宽比。
– 图片都集中在一个28×28的图像中。
– 像素以列为主进行排序。像素值0到255,0表示背景(白色),255表示前景(黑色)。
创建一个.png的文件,背景是白色的,手写的字体是黑色的,
下面是数据测试的代码,一个两层的卷积神经网,然后用save进行模型的保存。
# coding: UTF-8
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)
training = mnist.train.images
trainlable = mnist.train.labels
testing = mnist.test.images
testlabel = mnist.test.labels
print ("MNIST loaded")
# 获取交互式的方式
sess = tf.InteractiveSession()
# 初始化变量
x = tf.placeholder("float", shape=[None, 784])
y_ = tf.placeholder("float", shape=[None, 10])
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
生成权重函数,其中shape是数据的形状
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
生成偏执项 其中shape是数据形状
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
strides=[1, 2, 2, 1], padding='SAME')
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
x_image = tf.reshape(x, [-1, 28, 28, 1])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7*7*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
cross_entropy = -tf.reduce_sum(y_*tf.log(y_conv))
train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
# 保存网络训练的参数
saver = tf.train.Saver()
sess.run(tf.initialize_all_variables())
for i in range(8000):
batch = mnist.train.next_batch(50)
if i%100 == 0:
train_accuracy = accuracy.eval(feed_dict={
x:batch[0], y_: batch[1], keep_prob: 1.0})
print "step %d, training accuracy %g"%(i, train_accuracy)
train_step.run(feed_dict={x: batch[0], y_: batch[1], keep_prob: 0.5})
save_path = saver.save(sess, "model_mnist.ckpt")
print("Model saved in life:", save_path)
print "test accuracy %g"%accuracy.eval(feed_dict={
x: mnist.test.images, y_: mnist.test.labels, keep_prob: 1.0})
其中input_data.py如下代码,是进行mnist数据集的下载的:代码是由mnist数据集提供的官方下载的版本。
# Copyright 2015 Google Inc. All Rights Reserved.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Functions for downloading and reading MNIST data."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import gzip
import tensorflow.python.platform
import numpy
from six.moves import urllib
from six.moves import xrange
# pylint: disable=redefined-builtin
import tensorflow as tf
SOURCE_URL = 'http://yann.lecun.com/exdb/mnist/'
def maybe_download(filename, work_directory):
"""Download the data from Yann's website, unless it's already here."""
if not os.path.exists(work_directory):
os.mkdir(work_directory)
filepath = os.path.join(work_directory, filename)
if not os.path.exists(filepath):
filepath, _ = urllib.request.urlretrieve(SOURCE_URL + filename, filepath)
statinfo = os.stat(filepath)
print('Successfully downloaded', filename, statinfo.st_size, 'bytes.')
return filepath
def _read32(bytestream):
dt = numpy.dtype(numpy.uint32).newbyteorder('&')
return numpy.frombuffer(bytestream.read(4), dtype=dt)[0]
def extract_images(filename):
"""Extract the images into a 4D uint8 numpy array [index, y, x, depth]."""
print('Extracting', filename)
with gzip.open(filename) as bytestream:
magic = _read32(bytestream)
if magic != 2051:
raise ValueError(
'Invalid magic number %d in MNIST image file: %s' %
(magic, filename))
num_images = _read32(bytestream)
rows = _read32(bytestream)
cols = _read32(bytestream)
buf = bytestream.read(rows * cols * num_images)
data = numpy.frombuffer(buf, dtype=numpy.uint8)
data = data.reshape(num_images, rows, cols, 1)
return data
def dense_to_one_hot(labels_dense, num_classes=10):
"""Convert class labels from scalars to one-hot vectors."""
num_labels = labels_dense.shape[0]
index_offset = numpy.arange(num_labels) * num_classes
labels_one_hot = numpy.zeros((num_labels, num_classes))
labels_one_hot.flat[index_offset + labels_dense.ravel()] = 1
return labels_one_hot
def extract_labels(filename, one_hot=False):
"""Extract the labels into a 1D uint8 numpy array [index]."""
print('Extracting', filename)
with gzip.open(filename) as bytestream:
magic = _read32(bytestream)
if magic != 2049:
raise ValueError(
'Invalid magic number %d in MNIST label file: %s' %
(magic, filename))
num_items = _read32(bytestream)
buf = bytestream.read(num_items)
labels = numpy.frombuffer(buf, dtype=numpy.uint8)
if one_hot:
return dense_to_one_hot(labels)
return labels
class DataSet(object):
def __init__(self, images, labels, fake_data=False, one_hot=False,
dtype=tf.float32):
"""Construct a DataSet.
one_hot arg is used only if fake_data is true.
`dtype` can be either
`uint8` to leave the input as `[0, 255]`, or `float32` to rescale into
dtype = tf.as_dtype(dtype).base_dtype
if dtype not in (tf.uint8, tf.float32):
raise TypeError('Invalid image dtype %r, expected uint8 or float32' %
if fake_data:
self._num_examples = 10000
self.one_hot = one_hot
assert images.shape[0] == labels.shape[0], (
'images.shape: %s labels.shape: %s' % (images.shape,
labels.shape))
self._num_examples = images.shape[0]
# Convert shape from [num examples, rows, columns, depth]
# to [num examples, rows*columns] (assuming depth == 1)
assert images.shape[3] == 1
images = images.reshape(images.shape[0],
images.shape[1] * images.shape[2])
if dtype == tf.float32:
# Convert from [0, 255] -& [0.0, 1.0].
images = images.astype(numpy.float32)
images = numpy.multiply(images, 1.0 / 255.0)
self._images = images
self._labels = labels
self._epochs_completed = 0
self._index_in_epoch = 0
def images(self):
return self._images
def labels(self):
return self._labels
def num_examples(self):
return self._num_examples
def epochs_completed(self):
return self._epochs_completed
def next_batch(self, batch_size, fake_data=False):
"""Return the next `batch_size` examples from this data set."""
if fake_data:
fake_image = [1] * 784
if self.one_hot:
fake_label = [1] + [0] * 9
fake_label = 0
return [fake_image for _ in xrange(batch_size)], [
fake_label for _ in xrange(batch_size)]
start = self._index_in_epoch
self._index_in_epoch += batch_size
if self._index_in_epoch & self._num_examples:
# Finished epoch
self._epochs_completed += 1
# Shuffle the data
perm = numpy.arange(self._num_examples)
numpy.random.shuffle(perm)
self._images = self._images[perm]
self._labels = self._labels[perm]
# Start next epoch
self._index_in_epoch = batch_size
assert batch_size &= self._num_examples
end = self._index_in_epoch
return self._images[start:end], self._labels[start:end]
def read_data_sets(train_dir, fake_data=False, one_hot=False, dtype=tf.float32):
class DataSets(object):
data_sets = DataSets()
if fake_data:
def fake():
return DataSet([], [], fake_data=True, one_hot=one_hot, dtype=dtype)
data_sets.train = fake()
data_sets.validation = fake()
data_sets.test = fake()
return data_sets
TRAIN_IMAGES = 'train-images-idx3-ubyte.gz'
TRAIN_LABELS = 'train-labels-idx1-ubyte.gz'
TEST_IMAGES = 't10k-images-idx3-ubyte.gz'
TEST_LABELS = 't10k-labels-idx1-ubyte.gz'
VALIDATION_SIZE = 5000
local_file = maybe_download(TRAIN_IMAGES, train_dir)
train_images = extract_images(local_file)
local_file = maybe_download(TRAIN_LABELS, train_dir)
train_labels = extract_labels(local_file, one_hot=one_hot)
local_file = maybe_download(TEST_IMAGES, train_dir)
test_images = extract_images(local_file)
local_file = maybe_download(TEST_LABELS, train_dir)
test_labels = extract_labels(local_file, one_hot=one_hot)
validation_images = train_images[:VALIDATION_SIZE]
validation_labels = train_labels[:VALIDATION_SIZE]
train_images = train_images[VALIDATION_SIZE:]
train_labels = train_labels[VALIDATION_SIZE:]
data_sets.train = DataSet(train_images, train_labels, dtype=dtype)
data_sets.validation = DataSet(validation_images, validation_labels,
dtype=dtype)
data_sets.test = DataSet(test_images, test_labels, dtype=dtype)
return data_sets
然后进行代码的测试:
# import modules
import sys
import tensorflow as tf
from PIL import Image, ImageFilter
def predictint(imvalue):
This function returns the predicted integer.
The imput is the pixel values from the imageprepare() function.
# Define the model (same as when creating the model file)
x = tf.placeholder(tf.float32, [None, 784])
W = tf.Variable(tf.zeros([784, 10]))
b = tf.Variable(tf.zeros([10]))
def weight_variable(shape):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial)
def bias_variable(shape):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial)
def conv2d(x, W):
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')
def max_pool_2x2(x):
return tf.nn.max_pool(x, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding='SAME')
W_conv1 = weight_variable([5, 5, 1, 32])
b_conv1 = bias_variable([32])
x_image = tf.reshape(x, [-1, 28, 28, 1])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
h_pool1 = max_pool_2x2(h_conv1)
W_conv2 = weight_variable([5, 5, 32, 64])
b_conv2 = bias_variable([64])
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
h_pool2 = max_pool_2x2(h_conv2)
W_fc1 = weight_variable([7 * 7 * 64, 1024])
b_fc1 = bias_variable([1024])
h_pool2_flat = tf.reshape(h_pool2, [-1, 7 * 7 * 64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
W_fc2 = weight_variable([1024, 10])
b_fc2 = bias_variable([10])
y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
init_op = tf.initialize_all_variables()
saver = tf.train.Saver()
Load the model_mnist.ckpt file
file is stored in the same directory as this python script is started
Use the model to predict the integer. Integer is returend as list.
Based on the documentatoin at
https://www.tensorflow.org/versions/master/how_tos/variables/index.html
with tf.Session() as sess:
sess.run(init_op)
saver.restore(sess, "model_mnist.ckpt")
# print ("Model restored.")
prediction = tf.argmax(y_conv, 1)
return prediction.eval(feed_dict={x: [imvalue], keep_prob: 1.0}, session=sess)
def imageprepare(argv):
This function returns the pixel values.
The imput is a png file location.
im = Image.open(argv).convert('L')
width = float(im.size[0])
height = float(im.size[1])
newImage = Image.new('L', (28, 28), (255))
# creates white canvas of 28x28 pixels
if width & height:
# check which dimension is bigger
# Width is bigger. Width becomes 20 pixels.
nheight = int(round((20.0 / width * height), 0))
# resize height according to ratio width
if (nheight == 0):
# rare case but minimum is 1 pixel
nheigth = 1
# resize and sharpen
img = im.resize((20, nheight), Image.ANTIALIAS).filter(ImageFilter.SHARPEN)
wtop = int(round(((28 - nheight) / 2), 0))
# caculate horizontal pozition
newImage.paste(img, (4, wtop))
# paste resized image on white canvas
# Height is bigger. Heigth becomes 20 pixels.
nwidth = int(round((20.0 / height * width), 0))
# resize width according to ratio height
if (nwidth == 0):
# rare case but minimum is 1 pixel
nwidth = 1
# resize and sharpen
img = im.resize((nwidth, 20), Image.ANTIALIAS).filter(ImageFilter.SHARPEN)
wleft = int(round(((28 - nwidth) / 2), 0))
# caculate vertical pozition
newImage.paste(img, (wleft, 4))
# paste resized image on white canvas
# newImage.save("sample.png")
tv = list(newImage.getdata())
# get pixel values
# normalize pixels to 0 and 1. 0 is pure white, 1 is pure black.
tva = [(255 - x) * 1.0 / 255.0 for x in tv]
return tva
# print(tva)
def main(argv):
Main function.
imvalue = imageprepare(argv)
predint = predictint(imvalue)
print (predint[0])
# first value in list
if __name__ == "__main__":
main('2.png')
其中我用于测试的代码如下:
可以将图片另存到路径下面,然后进行测试。
(1)载入我的手写数字的图像。
(2)将图像转换为黑白(模式“L”)
(3)确定原始图像的尺寸是最大的
(4)调整图像的大小,使得最大尺寸(醚的高度及宽度)为20像素,并且以相同的比例最小化尺寸刻度。
(5)锐化图像。这会极大地强化结果。
(6)把图像粘贴在28×28像素的白色画布上。在最大的尺寸上从顶部或侧面居中图像4个像素。最大尺寸始终是20个像素和4 + 20 + 4 = 28,最小尺寸被定位在28和缩放的图像的新的大小之间差的一半。
(7)获取新的图像(画布+居中的图像)的像素值。
(8)归一化像素值到0和1之间的一个值(这也在TensorFlow MNIST教程中完成)。其中0是白色的,1是纯黑色。从步骤7得到的像素值是与之相反的,其中255是白色的,0黑色,所以数值必须反转。下述公式包括反转和规格化(255-X)* 1.0 / 255.0
Python(TensorFlow框架)实现手写数字识别系统
TensorFlow代码实现(一)[MNIST手写数字识别]
摄像头识别手写数字
tensorflow 如何识别自己手写的数字
TensorFlow车牌识别完整版(含车牌数据集)
没有更多推荐了,本帖已被设为精华帖!
Xposed 框架
Xposed 框架是一款可以在不修改APK的情况下影响程序运行(修改系统)的框架服务,基于它可以制作出许多功能强大的模块,且在功能不冲突的情况下同时运作。
Zygote 进程是 Android 的核心,所有的应用程序进程以及系统服务进程都是由Zygote进程 fork 出来的。Xposed Framework 深入到了 Android 核心机制中,通过改造 Zygote 来实现一些很牛逼的功能。Zygote 的启动配置在/init.rc 脚本中,由系统启动的时候开启此进程,对应的执行文件是/system/bin/app_process,这个文件完成类库加载及一些初始化函数调用的工作。
当系统中安装了 Xposed Framework 之后,会拿自己实现的 app_process 覆盖掉 Android 原生提供的文件,使得app_process在启动过程中会加载XposedBridge.jar这个jar包,从而完成对Zygote进程及其创建的Dalvik虚拟机的劫持。
更详细的框架介绍和插件开发过程可直接参看或者已有的一些
本文的主要来由是思寒在我另外一篇文章中的一句留言:
把xposed单独再发文章吧. 这是个杀手级别的框架. 是测试利器. 只是很多人并不懂其中的奥妙
既然是杀手级的东西,那肯定有不少独到的招式和技能。所以,趁着周末我也思考和整理了一下,基于该框架,测试人员都能做些什么,目前想到的主要有以下几点:
测试数据构造
自动化录制
下面就针对以上几点,结合例子作些简单的分享(部分原理和过程可能不会做太细致的解释,看不懂的可以留言)。
1、渗透测试
以Testerhome的android客户端认证授权模块为例,这里使用了OAuth 2.0的授权协议,其中有个比较重要的访问令牌access_token。通过看源码我们可以发现,在TesterUser类中有个setAccess_token方法
public void setAccess_token(String access_token) {
this.access_token = access_token;
其输入参数即是用户授权后产生的访问令牌,因此我们可以通过以下方法来截取该令牌
public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
if (!lpparam.packageName.equals("com.testerhome.nativeandroid"))
XposedBridge.log("Loaded app: " + lpparam.packageName);
findAndHookMethod("com.testerhome.nativeandroid.models.TesterUser", lpparam.classLoader,"setAccess_token", String.class,new XC_MethodHook() {
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// this will be called before the clock was updated by the original method
XposedBridge.log("Enter-&beforeHookedMethod");
XposedBridge.log("original token: " + (String)param.args[0]);
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// this will be called after the clock was updated by the original method
运行后查看日志如下:
12-19 06:19:54.458: I/Xposed(11327): Enter-&beforeHookedMethod
12-19 06:19:54.458: I/Xposed(11327): user token: 0a84d0c29a4b576634baacdebe5b3affba6b1b5b14603e
获取到令牌后就可以根据交互协议进一步获取用户相关的信息了。
当然,攻击者也可以直接修改该令牌值。
param.args[0] = "b6a8d0b02a651ac8b1afa02db3c15dcbfae2e";
这样用户登录后使用的都是非法的令牌值,也就无法获取合法的资源了。
findAndHookMethod("com.testerhome.nativeandroid.models.TesterUser",
lpparam.classLoader,"getAccess_token",new XC_MethodHook() {
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// this will be called before the clock was updated by the original method
XposedBridge.log("Enter-&beforeHookedMethod:getAccess_token");
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// this will be called after the clock was updated by the original method
XposedBridge.log("Enter-&afterHookedMethod:getAccess_token");
XposedBridge.log("hooked token: " + (String)param.getResult());
日志打印:
12-19 10:37:43.590: I/Xposed(15821): Enter-&beforeHookedMethod:setAccess_token
12-19 10:37:43.590: I/Xposed(15821): original token: 9b7c274a07e7dbcdbdfb3d9cc
12-19 10:37:43.662: I/Xposed(15821): Enter-&beforeHookedMethod:getAccess_token
12-19 10:37:43.662: I/Xposed(15821): Enter-&afterHookedMethod:getAccess_token
12-19 10:37:43.662: I/Xposed(15821): hooked token: b6a8d0b02a651ac8b1afa02db3c15dcbfae2e
类似的场景还有很多,主要就是通过阅读代码(有源码或者反编译的情况下),找到关键函数以及编码上的一些漏洞,获取关键信息或者篡改方法的出入参,达到攻击和渗透测试的目的。
2、测试数据构造
有时在客户端应用测试的过程中需要构造一些特殊的数据,如位置、网络制式、系统版本、屏幕长宽比、电量等等。其中,有些数据可手动构造,但有部分就完全不行了。此时,Xposed框架也能帮你搞定。
以系统时间为例,我们编写一个Demo应用,通过Calendar类来获取系统的时间:
Calendar c = Calendar.getInstance();
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);
int hour = c.get(Calendar.HOUR_OF_DAY);
int minute = c.get(Calendar.MINUTE);
String time = ""+year+"-"+month+"-"+day+" "+hour+":"+minute;
timeTV.setText(time);
正常情况下,其运行结果为:
然后,我们只需要Hook系统Calendar类的get方法,就能构造出自己想要的数据:
findAndHookMethod("java.util.Calendar", lpparam.classLoader,"get",int.class,new XC_MethodHook() {
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// this will be called before the clock was updated by the original method
XposedBridge.log("Enter-&beforeHookedMethod:Calendar.get");
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// this will be called after the clock was updated by the original method
XposedBridge.log("Enter-&afterHookedMethod:Calendar.get");
param.setResult((int)11);
其运行结果为:
同理,其它类似的数据都能通过Hook系统的方法来构造,甚至连已Root的手机都能伪装成未Root的(我们公司有个手机打卡软件,就可以用Root欺骗和位置伪造的方式在家里打卡,当然我没这么干过,表查我)。
3、环境监控
由于Xposed框架在系统启动的时候就加载完成了,所以其监控能力比我们自己写的后台Service应用要强很多。至于监控的对象,可以是系统的通知、弹窗、Toast信息、用户点击、电量、信号变化这类显式可感知的事件,也可以是内存、CPU、IO此类内部数据,甚至到统一的异常处理方法(如java.lang.Thread.UncaughtExceptionHandler)、底层socket接口、页面渲染方法等等,主要看你需要什么,而非它能做什么。
例子直接使用之前一篇文章中介绍过的Toast信息检查。
public class XposedHook
implements IXposedHookZygoteInit {
public void initZygote(StartupParam startupParam) throws Throwable {
//设定hook目标类和方法
XposedHelpers.findAndHookMethod(Toast.class, "show", new XC_MethodHook() {
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
//获取Toast对象
Toast t = (Toast) param.thisObject;
//获取唯一的TextView,即Toast文本
= t.getView();
List&TextView& list = new ArrayList&TextView&();
if (view instanceof TextView) {
list.add((TextView) view);
} else if (view instanceof ViewGroup) {
finaAllTextView(list, (ViewGroup) view);
if (list.size() != 1) {
throw new RuntimeException("number of TextViews in toast is not 1");
TextView text = list.get(0);
//获取文本内容
CharSequence toastMsg = text.getText();
System.out.println("XposedHookToast:"+toastMsg);
} catch (RuntimeException e) {
XposedBridge.log(e);
//获取对象中的所有TextView
private void finaAllTextView(List&TextView& addTo, ViewGroup view) {
int count = view.getChildCount();
for (int i = 0; i & count; ++i) {
View child = view.getChildAt(i);
if (child instanceof TextView) {
addTo.add((TextView) child);
} else if (child instanceof ViewGroup) {
finaAllTextView(addTo, view);
获取到的Toast信息:
Line 5251: I/System.out(
815): XposedHookToast:登录失败,可能原因是用户名或密码错误、密码过期或者帐号锁定
Line 5959: I/System.out(
815): XposedHookToast:连接服务器失败
通过这种方式,可以处理自动化脚本运行过程中出现的一些非正常事件,如意外弹窗或者消息栏通知等;也可以用于屏蔽monkey运行时可能点击退出或者注销按钮的情况。只要事先设置好目标事件和处理方式,它就能起到很好的监控作用。
4、动态埋点
如果监控的目的不是环境处理,而是信息获取,那么就演化为了埋点。既然通过Xposed能直接控制一个方法的调用前后阶段,那埋点对于它来说更像是一个天赋技能,根本不用多做修改和适配,就能直接在不动被测APP代码分毫的情况下实现易管理、有策略并且可实时变更得动态埋点。
以TesterHome客户端MainActivity中onCreate方法执行前后的系统剩余内存为例:
findAndHookMethod("com.testerhome.nativeandroid.views.MainActivity", lpparam.classLoader,"onCreate",Bundle.class,new XC_MethodHook() {
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// this will be called before the clock was updated by the original method
XposedBridge.log("Enter-&beforeHookedMethod:onCreate");
Activity app = (Activity) param.thisObject;
long availMem =getAvailMemory(app);
XposedBridge.log("availMem before onCreate:"+availMem+"KB");
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// this will be called after the clock was updated by the original method
XposedBridge.log("Enter-&afterHookedMethod:onCreate");
Activity app = (Activity) param.thisObject;
long availMem =getAvailMemory(app);
XposedBridge.log("availMem after onCreate:"+availMem+"KB");
获取系统剩余内存的方法:
public long getAvailMemory(Activity app) {
ActivityManager am = (ActivityManager)app.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
am.getMemoryInfo(mi);
return mi.availMem && 10;
安装模块重启后运行TesterHome可以看到:
12-19 23:00:48.442: I/Xposed(4476): Enter-&beforeHookedMethod:onCreate
12-19 23:00:48.442: I/Xposed(4476): availMem before onCreate:1901876KB
12-19 23:00:48.478: I/Xposed(4476): Enter-&afterHookedMethod:onCreate
12-19 23:00:48.478: I/Xposed(4476): availMem after onCreate:1900760KB
具体所需的埋点数据和过程可以参考恒温的
用这种方式埋点有以下几个好处:
无需动APP源码,适配成本低;
方式灵活,有能力介入任何过程,可收集的信息和数据完全;
易于管理,可随时添加启用或删除弃用埋点;
无需开发参与,测试可根据场景自己实现埋点方案;
当然,也有个很大的坑点:
只适合内部测试使用,无法发布给真实用户用于线上监控。
与动态埋点原理类似,既然我们可以通过添加前后过程来测试一个方法,那么当发现这个方法出现问题时,自然也可以通过动态的添加前后过程来修复该方法,也即热补丁。
目前国内安卓上比较成熟的热补丁方案主要有Dexposed 、 AndFix 、 ClassLoader 三种,前两个都是阿里的,第三个是腾讯的。其中Dexposed方案正是基于Xposed框架,但由于它只对应用自身进程的方法进行Hook,所以不需要root权限。
关于这个,更具体的信息直接看这篇文章好了
6、自动化脚本录制
这个实际上是环境监控的细分能力,既然能监控设备的所有事件,那么如果我们有针对性的对系统交互类接口和事件进行监听,记录用户和设备之间的交互流程和信息,是不是有可能直接在用户操作一遍后把对应的自动化脚本就生成出来呢?
让我们继续看个小例子:
被测应用仍为上文获取时间的Demo,界面上就一个TextView和Button,要做的事就是捕获按钮的点击事件,并解析得到该Button的信息。
为保证通用性和一致性,这里要Hook的方法肯定得尽量偏底层,通过看源代码和事件点击分发的相关机制,最终定位到android.view.View类中的performClick方法,这个方法会最终执行点击相关的操作和事件通知。
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
return true;
return false;
但是它的参数和返回值中都不包含view相关的信息,那么捕获到这个点击事件后进一步要怎么获取和它关联的控件信息呢?起初我也陷入了这样的迷圈中,不断去找有view相关参数或者返回值的方法。但后来转念一想,这个方法本来就在view这个类对象实例中,看了下xposed的api,果然有直接获取这个实例对象的方法。代码如下:
findAndHookMethod("android.view.View", lpparam.classLoader,"performClick",new XC_MethodHook() {
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// this will be called before the clock was updated by the original method
XposedBridge.log("Enter-&beforeHookedMethod:performClick");
@SuppressLint("NewApi") @Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// this will be called after the clock was updated by the original method
XposedBridge.log("Enter-&afterHookedMethod:performClick");
View node = (View)param.thisObject;
XposedBridge.log("NodeInfo:"+node.toString());
非常的简洁和顺理成章,然后执行的结果会是怎样呢?安装模块、重启设备、启动Demo后点击一下获取时间的按钮,可以看到如下日志:
12-20 02:30:49.958: I/Xposed(7346): Enter-&beforeHookedMethod:performClick
12-20 02:30:49.958: I/Xposed(7346): Enter-&afterHookedMethod:performClick
12-20 02:30:49.958: I/Xposed(7346): NodeInfo:android.widget.Button{ VFED..C. ...PH... 24,76-168,148 #7f080001 app:id/button1}
非常强大,我们看到了按钮的id:button1,与应用界面配置中设定的一致
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/textView1"
android:layout_below="@+id/textView1"
android:layout_marginTop="15dp"
android:text="获取时间" /&
甚至通过的view的getLocationOnScreen方法,我们还可以得到按钮中心点的坐标:
12-20 02:57:53.672: I/Xposed(8340): NodeInfo X:24
12-20 02:57:53.672: I/Xposed(8340): NodeInfo Y:186
类似的其它事件也可以这样捕获并提取关联控件的信息,有了这些数据后我们再按照Appium或者其它框架的API自动形成脚本是不是也就不难了。
以上就是我这两天思考和实验的结果,当然都还不完善,只是一些初步的想法,后面有时间,我会针对其中部分功能继续做更为深入的研究,希望能摸索出一些可行的方案,到时候再分享给大家。大家也可以放开脑洞,这么强大的工具能做的事情肯定也不止这些!
TesterHome首发,转载请声明
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
这些文章写的很详细, 很赞. xposed是测试界的一个神器. 希望大家多用用.
基于xposed还可以构造异常场景, 获取覆盖率,以及对app建模,
我最早接触他是从看雪论坛上发现的.
干货 今晚就啃这个了
是不是需要root权限?
这位同学,我已经看到你在N个帖子下面问过这个问题了。能不能自己先看下文档,了解之后再来问呢。。。
我看这个文档中并没有说明这种测试方法是否需要root权限,所以才问的。需不需要root权限是我决定要不要学他的前提之一,因为学这些东西是非常花时间和精力的,所以才先问清楚。为什么你会反对这个呢?
文档不止一处:。多 google 一下,你的问题就解决了。
果然是需要root。
腻害,如此神器应该学习起来
安装需要Root,运行不需要
—— 来自TesterHome官方
好东西,赞一个。
太过功利了. xposed是一道高手必经的门槛. 就好像编程不会数据结构和算法也不会影响饭碗. 但是没有它会丢失很大一部分的视野. 推荐你多学点东西. 趁年轻好好提升下. 将来带给你的红利会很大.
呵呵,谢谢你的建议
说实话这东西用在自动化上就好比高射炮打蚊子,声响大作用小。门槛太高了
东哥好比喻,这个就看怎么结合测试场景了
好东西,多谢分享,怒赞……^_^
16楼 已删除
这个框架有什么劣势呢?
确实,实际上我提的主要也不是自动化方面的,比较相关的也就环境监控和脚本录制。不过门槛还是比较高,感觉可以通过一些上层框架模式化部分通用操作,降低上手门槛。
—— 来自TesterHome官方
最直观的就是不太好装。而且目前对art支持不是很成熟。
—— 来自TesterHome官方
github上有个项目叫做EagleEye. 在这个基础上做了anndroid ndk和sdk的hook封装, 也是巨复杂. 类似的其他框架还有frida, 可以通杀所有平台. 不过缺点更多.
BAT在xposed基础上做封装的比较多. 对ART的支持xposed官方还在改造中
恩,用这个的确是新的测试视野 赞1个,Hook神器,需root的。我目前研究不深,等空闲点在看。
嗯,学一些主流的测试工具性价比更高一些。当然有时间和精力多研究些深点的也挺好
楼主你好,按照你的方法大概已实现了弹出按钮的监控,但如何让他不去点确定按钮呢? 可以结合uiautomator来做吗?
今天在和自动化结合着用的时候,又发现个蛮大的坑,它没法和instrumentation共存。看了下源码,如果hook对象进程是instrumentation启动的,它就直接退了:
if (instrumentationName != null) {
XposedBridge.log("Instrumentation detected, disabling framework for " + reportedPackageName);
disableHooks = true;
不是太清楚你的具体需求,监控弹出的按钮,又不去点击?
dexposed不需要root,之前想做一个apk来hook 正在测试的应用有没有crash,但是总报一些异常,就没有深入研究下去了
就是你在文中提到的,monkey会不小心点到退出按钮。这时可以监控到按钮弹出,但怎么才能不让它去点确定键呢?
你可以直接禁用弹出这个对话框的方法,或者禁用确定按钮的监听事件 ,在beforeHookedMethod中直接调用 param.setResult(null);
还可以这样,,6啊。多谢,多谢。
您好,想跟您请教个问题,安装framework时遇到的问题:手机(huawei P6 android4.2.2)已经root了,但是在挂载/system为可写入时,报错mount:Operation not permitted无法将/system可写入挂载。然后我手动在命令行执行mount -o remount,rw /system,都没有任何提示信息的。
由于华为手机的主题和Xposed框架有冲突,所以EMUI的Xposed框架安装包都是独立定制的,你可以试试
还有个问题想请教一下,XposedInstaller安装xposed框架的时候,都是通过su命令来获取root权限,但是我的华为手机里面并没有su命令啊,难道是我root的方式不对?
root不完全,用superuser更新下二进制文件
不仅是root不完全,由于华为手机的一些特殊设置,还需要安装busybox,同时rm -f /system/set_immutable.list 删除这个文件,之后才能成功安装Xposed框架。多谢指点啦
Xposed,现在都转移动端测试了?传统WEB好少,,,
很强大,用不用主要看测试场景。
好强大,感觉测试可以做的越来越多了~~
你好,我是测试媛的山地(测试媛群号:),我们最近想做一个线下的交流活动,不知道i感兴趣不?我qq:,感兴趣的话可以交流下测试技术
很好,这才是xposed的正道。
为什么我安装xposed后,手机就一直处于重启状态,开都开不了?
中提及了此贴
大神你好,我这边在执行hook的方法的时候添加了一个通过包名过滤,但是没有写死,通过解析sd卡的一个配置文件来确定我想过滤的包名。然后通过变更这个配置文件来更换我想过滤的包名。然后在解析这个文件的时候,大多数是好的,但是有一些就提示fileNotFound permission denied。但是我能确定的是我想hook的这个apk他是有动态权限申请的,并且也经过授权了。不知道是什么问题,代码是一样的,但是结果不一样,用的是华为的机器,有没有可能是xposed 兼容性的问题,还是其他什么地方?跪求!
求大神指点。
已经很久没有使用这个框架了,所以你的问题我没法直接回答。不过,有几个问题定位思路你可以参考下:1、换个模拟器试试,相对而言模拟器的权限开放更加彻底;2、单独将hook失败的apk拿出来验证下,不用配置文件的方式;3、确认下hook失败的apk权限设置和其它apk有什么区别。
应该是系统级应用没有权限访问sd卡下的配置文件,你可以试试把配置文件放在/system分区下
后方可回复, 如果你还没有账号请点击这里 。
xubin98246 (虚冰丶夜)
第 5467 位会员 /
共收到 45 条回复}

我要回帖

更多关于 《你好!陌生人》测试 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信