Android Studio集成资源混淆打包

关于Android Studio集成资源混淆打包

GitHub源码查看

这个Demo是通过集成微信早前开源的资源混淆工具到Android Studio里,在生成正式包时,自动化进行资源混淆,简化操作,提高效率。主要是在gradle脚本中添加一些代码,详见app/build.gradle

微信团队开源的资源混淆工具

其它注意事项

1、示例为了方便演示,将签名文件放到了resources目录下,而local.properties也是不应该加入版本控制的,这个需要注意。

2、相关资源混淆配置放在resources/AndResGuard/config.xml里,请加入相应的不需要混淆资源的配置(如通过反射方法获取的资源),详细配置详见点击这里

3、为了保证资源混淆后的文件名统一性,建议将生成的resource_mapping(build/output/resguard目录下)文件替换resources/AndResGuard/resource_mapping.txt

关于Android长按出现复制粘贴栏在顶部占位问题解决方法

关于Android长按出现复制粘贴栏在顶部占位问题解决方法

大家都知道,在Android长按WebViewEditText等组件时,4.0以上的系统顶部会出现复制/粘贴栏(以下使用使用操作栏作为描述),而如果有使用ActionBar,则操作栏就会悬浮在ActionBar上,但如果没有使用ActionBar,则会插入顶部并且占居一定的位置,如果是自定义的顶部栏,那么新插入的操作栏就会影响美观与体验(如下图)。所以,带着这个问题,去研究与分析Activity的界面层次布局,从而找出解决方案。

在研究了一遍ActionBar的界面层次源码后,得到的了解决方法

将操作栏设置为悬浮方式:

1、在Activity使用setContentView方法之前,调用requestWindowFeature(Window.FEATURE_ACTION_MODE_OVERLAY);

2、如果是继承ActionBarActivity,则调用以下方法
supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_MODE_OVERLAY);

了解Android微信里的WebView是如何实现分享的功能

了解Android微信里的WebView是如何实现分享的功能

本文是通过一些技巧,使用javascriptandroid webview之间的通讯,去获得微信公众号文章的分享标题与图片链接等信息。目的是模仿微信的浏览器是如何获取内容去进行分享,从而将这种技术引入到自己的项目中。而微信浏览器里,读取分享内容有两种方式,一种是通过读取网页的标题与首张图片链接作为分享的内容;另一种是公众号后台生成的文章与授权第三方网站使用js-sdk的去自定义分享内容。

先对比两种分享的效果

区别还是挺大的,要做更好的用户体验,就需要有耐心去突破。

普通网页抓取的分享内容

换取标题,这个比较简单,webview.getTitle()

而读取首张图片地址,简单方式可以在WebViewClient里覆写onLoadResource方法,然后判断是否为图片的链接,如下

@Override
public void onLoadResource(WebView view, String url) {
    super.onLoadResource(view, url);
    //读取加载中的资源里的图片
    if(cacheFirstLoadImageUrl == null && !TextUtils.isEmpty(url)) {
        if(url.contains(".png") || url.contains(".jpg")) {
            cacheFirstLoadImageUrl = url;
        }
    }
}

当然,如果网页没有图片,则可以用网页的favicon,webview.getFavicon()

抓取微信公众号文章的分享内容

在微信公众号的后台,有一个素材管理,可以通过它里面的文章编辑器来编写或修改文章,然后保存发布后,会固定生成一个链接。而文章链接是会以固定的host地址mp.weixin.qq.com进行发布,如下:

http://mp.weixin.qq.com/s?__biz=MzA5MTA2MzkyOQ==&mid=257050763&idx=1&sn=6984b946e5fd618a5f42e071d1729a7a&scene=0#rd

然后分析网页源码,在底部可以发现如下javascript代码:



<script type="text/javascript"> var not_in_mm_css = "http://res.wx.qq.com/mmbizwap/zh_CN/htmledition/style/page/appmsg/not_in_mm2637ae.css"; var windowwx_css = "http://res.wx.qq.com/mmbizwap/zh_CN/htmledition/style/page/appmsg/page_mp_article_improve_pc2637ae.css"; var tid = ""; var aid = ""; var appuin = "MzA5MTA2MzkyOQ=="; var source = "0"; var scene = 75; var itemidx = ""; var nickname = "CodeBoy助手"; var ct = "1438614177"; var user_name = "gh_7809f2e62230"; var user_name_new = ""; var fakeid = ""; var version = ""; var is_limit_user = "0"; var msg_title = "CodeBoy微信抢红包外挂"; var msg_desc = "Android微信抢红包外挂实现源码与详解"; var msg_cdn_url = "http://mmbiz.qpic.cn/mmbiz/mdBqiaB1jx3ATKrHYu0rg7tTIWEVLqz5nQuMNlvgianJDD7G33iao85IUVgQpjO11icJsmojlCY2XFAapyXiaMiafW5g/0?wx_fmt=jpeg"; var msg_link = "http://mp.weixin.qq.com/s?__biz=MzA5MTA2MzkyOQ==&amp;mid=257050763&amp;idx=1&amp;sn=6984b946e5fd618a5f42e071d1729a7a#rd"; var user_uin = "0"*1; var msg_source_url = 'http://www.happycodeboy.com/index.php/archives/10/#rd'; var img_format = 'jpeg'; var networkType; var appmsgid = '' || '257050763'; var comment_id = "0" * 1; var svr_time = "1438966394" * 1; var comment_enabled = "" * 1; var is_need_reward = "0" * 1; var is_https_res = ("" * 1) && (location.protocol == "https:"); var devicetype = ""; var source_username = ""; var show_comment = ""; var __appmsgCgiData = { can_use_page : "0"*1, card_pos : "", copyright_stat : "0", hd_head_img : "http://wx.qlogo.cn/mmhead/Q3auHgzwzM54RqtxygzEE174cxI2BWaTDiaTtjWNaTObpib5bCnlGl4A/0"||(window.location.protocol+"//"+window.location.host + "http://res.wx.qq.com/mmbizwap/zh_CN/htmledition/images/pic/appmsg/pic_rumor_link.2x264e76.jpg") }; var _empty_v = "http://res.wx.qq.com/mmbizwap/zh_CN/htmledition/images/pic/pages/voice/empty26f1f1.mp3"; seajs.use("appmsg/index.js"); </script>

通过以上代码,我们就可以知道其中一些变量的对应关系

字段 含义
msg_title 标题
msg_desc 描述
msg_cdn_url 图片链接
msg_link 分享链接

通过WebView读取javascript全局变量

通过上述分析,我们已经知道分享文章里的标题、描述、图片、链接是从哪里得到的,那接下来,我们就通过编码方式,去读取。

首先,我们要添加一个javascrtip访问到java层代码的入口,目的是为了回调javascript的全局变量到java层,webView.addJavascriptInterface(new CodeBoyJsInterface(), "codeboy");CodeBoyJsInterface里有一个callme方法。

接下来,在加载一个页面成功后,我们就要调用javascript的方法去读到全局变量。如:webview.loadUrl("javascript:window.codeboy.callme(msg_title)"); 这样就在CodeBoyJsInterface.callme的方法里回接收到msg_title这个变量的值。就这样,可以实现一个获取javascript页面里变量,从而有更多进一步的操作。

以下是示例的完整代码:

package com.codeboy.android;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v7.app.AppCompatActivity;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

import org.json.JSONException;
import org.json.JSONObject;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    //缓存第一个加载的图片链接
    private String cacheFirstLoadImageUrl = null;

    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        webView = (WebView) findViewById(R.id.webview);
        webView.setWebViewClient(new CBWebViewClient());

        WebSettings settings = webView.getSettings();

        //允许javascript的功能
        settings.setJavaScriptEnabled(true);

        //自适应屏幕
        settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);

        settings.setUseWideViewPort(true);
        settings.setLoadWithOverviewMode(true);

        //添加一个允许javascript访问方法,如 window.codeboy.callme("hello codeboy");
        webView.addJavascriptInterface(new CodeBoyJsInterface(), "codeboy");

        //加载测试的地址
        webView.loadUrl("http://mp.weixin.qq.com/s?__biz=MzA5MTA2MzkyOQ==&mid=257050763&idx=1&sn=6984b946e5fd618a5f42e071d1729a7a&scene=0#rd");
    }

    /*
    * tips
    * 实现Parcelable接口,主要是避免因为混淆而导致方法名变更
    * */
    @SuppressLint("JavascriptInterface")
    public class CodeBoyJsInterface implements Parcelable {

        public CodeBoyJsInterface() {
        }

        //解决throws Uncaught Error: Error calling method on NPObject on Android
        @JavascriptInterface
        public void callme(final String str) {
            //这里是javascript里回调的,注意回调是通过非UI线程
            try {
                JSONObject json = new JSONObject(str);
                String msg_title = getJsonStr(json, "msg_title");
                String msg_desc = getJsonStr(json, "msg_desc");
                String msg_link = getJsonStr(json, "msg_link");
                String msg_cdn_url = getJsonStr(json, "msg_cdn_url");

                Log.d(TAG, "msg_title:" + msg_title + " msg_desc:" + msg_desc
                        + " msg_link:" + msg_link + " msg_cdn_url:" + msg_cdn_url);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        private String getJsonStr(JSONObject json, String key) {
            try {
                String value = json.getString(key);
                //对数据进行转义
                return Html.fromHtml(value).toString();
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        public int describeContents() {
            //ignore
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            //ignore
        }
    }

    class CBWebViewClient extends WebViewClient {

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            Log.v(TAG, "onPageFinished 加载完成:" + url);

            //页面加载完毕后,判断是否为微信的公众平台文章
            if (url.startsWith("http://mp.weixin.qq.com") || url.startsWith("https://mp.weixin.qq.com")) {
                //读取分享的信息,将数据以json的格式返回,方便日后扩展
                view.loadUrl("javascript:window.codeboy.callme(JSON.stringify({" +
                        "\"msg_title\":msg_title.toString()," +
                        "\"msg_desc\":msg_desc.toString()," +
                        "\"msg_link\":msg_link.toString()," +
                        "\"msg_cdn_url\":msg_cdn_url.toString()" +
                        "}))");
            }
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            cacheFirstLoadImageUrl = null;
        }

        @Override
        public void onLoadResource(WebView view, String url) {
            super.onLoadResource(view, url);
            //读取加载中的资源里的图片
            if (cacheFirstLoadImageUrl == null && !TextUtils.isEmpty(url)) {
                if (url.contains(".png") || url.contains(".jpg")) {
                    cacheFirstLoadImageUrl = url;
                }
            }
        }
    }
}

以下是运行的打印结果:


08-08 02:01:54.728 17198-17352/com.codeboy.android D/MainActivity﹕ msg_title:CodeBoy微信抢红包外挂 msg_desc:Android微信抢红包外挂实现源码与详解 msg_link:http://mp.weixin.qq.com/s?__biz=MzA5MTA2MzkyOQ==&mid=257050763&idx=1&sn=6984b946e5fd618a5f42e071d1729a7a#rd msg_cdn_url:http://mmbiz.qpic.cn/mmbiz/mdBqiaB1jx3ATKrHYu0rg7tTIWEVLqz5nQuMNlvgianJDD7G33iao85IUVgQpjO11icJsmojlCY2XFAapyXiaMiafW5g/0?wx_fmt=jpeg

版权声明:

文章为原创,CodeBoy版权所有,如需转载请注明出处。

本文来自:http://www.happycodeboy.com/index.php/archives/13/

关注微信订阅号:CodeBoy助手

Codeboy微信抢红包

CodeBoy微信抢红包外挂

效果预览

源码下载地址:https://github.com/lendylongli/qianghongbao

apk下载地址 : 下载最新版本

前言

Codeboy抢红包是我在2015年春节过年期间编写的一个开源的兴趣项目,只要是将整个核心抢红包的流程编写出来,至于再复杂的一些操作就没深入研究。当这个项目发布后,也是反应挺大的,很多网友也找到我了与交流,也有做淘宝的人给钱让我去增加一些功能,当然我是拒绝的。而本文通过抢红包这个示例去讲解AccessibilityService的用途,希望大家能举一反三去学习这个辅助服务的强大之处。

免责声明

本软件仅供学习使用,完全模拟人工操作,抢红包速度取决于你手机的性能与网络,不涉及任何第三方软件接口,本软件已开放源代码,无病毒、不收集用户隐私信息,禁止使用本软件参与赌博活动。一切因使用“Codeboy抢红包”造成的任何后果,Codeboy抢红包概不负责,亦不承担任何法律责任!

作者声明

在这里,我声明一下,我所做的是自己有兴趣的事情,只是通过开源的方式让大家去学习相关技术,并不是为了营利,而我也知道淘宝上有人直接拿我的应用去售卖,这些都是没经过我的允许,我也没有半点收益,我留下联系方式是为了方便开发者之间的讨论与学习,所以请商业合作的与小白不要加我QQ,谢谢。
由于加我的人太多,大部分是咨询抢红包的事情,在这里也再声明一下,不再更新抢红包的工作,在这方面也不再想交流什么,如果你有什么好的idea(非红包方面),可以共同探讨。

技术详述

一开始大家都会觉得做一个Android外挂会汲取很多东西或者底层的东西,但当发现Android里有一个叫AccessibilityService的服务时,一切都变得很简单。

关于AccessibilityService

先看看官网的介绍Accessibility
Many Android users have different abilities that require them to interact with their Android devices in different ways. These include users who have visual, physical or age-related limitations that prevent them from fully seeing or using a touchscreen, and users with hearing loss who may not be able to perceive audible information and alerts...

Android官网详解accessibility

上面大概的意思就是Accessibility是一个辅助服务,主要是面向一些使用Android手机的用户有相关障碍(年龄、视觉、听力、身体等),这个功能可以更容易使用手机,可以帮用户在点击屏幕或者显示方面得到帮助等等。接下来就是查找相关API,看能做到哪个地步。

Accessibility相关API描述

当然accessibility除了可以辅助点击界面的事件外,还可以用作自动化测试,或者一键返回,是一个非常强大与实用的功能,具体实例可以看我另一个App虚拟按键助手 请往下载 GooglePlay市场应用宝

关于抢红包的流程

在有以上的一些关于辅助服务的基础知识后,我们就可以分析怎样自动化抢红包。
大家使用过微信都知道,如果不是在微信的可见界面范围(在桌面或者在使用其它应用时),在收到新的消息,就会在通知栏提醒用户。而在微信的消息列表界面,就不会弹出通知栏,所以可以区分这两种情况。然后抓取相关关键字作进一步处理。

1、在非微信消息列表界面,收到通知消息的事件,判断通知栏里的文本是否有[微信红包]的关键字,有则可以判断为用户收到红包的消息(当然,你可以故意发一条包括这个关键字的文本消息去整蛊你的朋友)。然后,我们就自动化触发这个消息的意图事件(Intent);

2、在通知栏跳进微信界面后,是去到com.tencent.mm.ui.LauncherUI这个Activity界面。我们知道,红包的消息上,包括了关键字领取红包或者Viewid,那我们就根据这个关键字找到相应的View,然后再触发ACTION_CLICK(点击事件);

3、在点击红包后,会跳到com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI这个拆红包的Activity,当然老方法,找关键字拆红包id,然后触发自动化点击事件。

这样就可以完成整个自动化完成抢红包的流程了,所以核心就是找关键字,然后模拟用户点击事件,就这么简单。以下详细说一下代码的实现。

以下是通过DDMS工具里的Dump View Hierarchy For UI Automator 去分析微信UI结构。

使用AccessibilityService去一步步监听微信的动作

1、新建一个继承AccessibilityService的类,如QiangHongBaoService,然后在AndroidManifest.xml里声明组件,如下

<service
        android:label="@string/app_name"
        android:name=".QiangHongBaoService"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService"/>
        </intent-filter>
        <meta-data
            android:name="android.accessibilityservice"
            android:resource="@xml/qianghongbao_service_config"/>
    </service>

在meta-data里声明的是辅助配置,这个是Android4.0之后才支持的写法,在4.0之前的系统要在代码里声明。

2、在res/xml目录下生成辅助服务的配置文件qianghongbao_service_config.xml

<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/accessibility_description"
android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged"
android:packageNames="com.tencent.mm"
android:accessibilityFeedbackType="feedbackGeneric"
android:notificationTimeout="100"
android:accessibilityFlags=""
android:canRetrieveWindowContent="true"/>

android:description 这个是设置服务的描述,在用户授权的界面可以看到。

android:accessibilityEventTypes 这个是配置要监听的辅助事件,我们只需要用到typeNotificationStateChanged(通知变化事件)、typeWindowStateChanged(界面变化事件)

android:packageNames 这个是要监听应用的包名,如果要监听多个应用,则用,去分隔,这里我们只需要监听微信的就可以了

android:accessibilityFeedbackType 这个是设置反馈方式

FeedbackType 描述
feedbackSpoken 语音反馈
feedbackHaptic 触感反馈
feedbackAudible 表示声音(不是语音)反馈
feedbackVisual 视觉反馈
feedbackGeneric 通用反馈
feedbackAllMask 所有以上的反馈

详细看AccessibilityServiceInfo类文档描述

3、在以上都配置好后,我们就可以在QiangHongBaoService这个服务里进行编码了,要做的就是将整个UI跳转流程与逻辑串联起来。

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
    //接收事件,如触发了通知栏变化、界面变化等  
}

@Override
protected boolean onKeyEvent(KeyEvent event) {
    //接收按键事件
    return super.onKeyEvent(event);
}

@Override
public void onInterrupt() {
  //服务中断,如授权关闭或者将服务杀死
}

@Override
protected void onServiceConnected() {
    super.onServiceConnected();
    //连接服务后,一般是在授权成功后会接收到
}

具体内容请看源码

其它

如何防止外挂

在了解整个核心后,获取事件不外乎就是通过文本与id判断,那么就可以将文本改为图标方式,将id改为动态id(每次显示都是随机生成),这样一来就可以提高外挂的门槛。

如何发红包会安全点

现在抢红包就看谁的外挂工具反应够快,如何去干扰这些外挂,其实也有点小技巧,就是在发红包前,发送文本[微信红包],可以导致部分外挂工具失效。

版本归作者所有,转载请注明出处:http://www.happycodeboy.com/index.php/archives/10/

关于作者

英文名:Leon

个人博客:http://www.happycodeboy.com

Email:codeboy2013@gmail.com