乱码三千 – 分享实用IT技术

乱码三千 – 码出一个新世界


  • 首页

  • 归档

  • 搜索

linux 压缩和解压缩命令gz、tar、zip、bz2

发表于 2020-10-26

gzip

  • 压缩后的格式为:*.gz
  • 这种压缩方式不能保存原文件;且不能压缩目录
  • 命令举例:
1
2
3
4
5
6
7
8
#压缩
[root@localhost tmp]# gzip buodo
[root@localhost tmp]# ls
buodo.gz
#解压
[root@localhost tmp]# gunzip buodo.gz
[root@localhost tmp]# ls
buodo12345678

tar

  • 命令选项:
1
2
3
4
5
6
-z(gzip)      用gzip来压缩/解压缩文件
-j(bzip2) 用bzip2来压缩/解压缩文件
-v(verbose) 详细报告tar处理的文件信息
-c(create) 创建新的档案文件
-x(extract) 解压缩文件或目录
-f(file) 使用档案文件或设备,这个选项通常是必选的。123456
  • 命令举例:
1
2
3
4
5
6
7
#压缩
[root@localhost tmp]# tar -zvcf buodo.tar.gz buodo
[root@localhost tmp]# tar -jvcf buodo.tar.bz2 buodo

#解压
[root@localhost tmp]# tar -zvxf buodo.tar.gz
[root@localhost tmp]# tar -jvxf buodo.tar.bz21234567

zip

  • 与gzip相比:1)可以压缩目录; 2)可以保留原文件;
  • 选项:
1
-r(recursive)    递归压缩目录内的所有文件和目录1
  • 命令举例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#压缩和解压文件
[root@localhost tmp]# zip boduo.zip boduo
[root@localhost tmp]# unzip boduo.zip

#压缩和解压目录
[root@localhost tmp]# zip -r Demo.zip Demo
adding: Demo/ (stored 0%)
adding: Demo/Test2/ (stored 0%)
adding: Demo/Test1/ (stored 0%)
adding: Demo/Test1/test4 (stored 0%)
adding: Demo/test3 (stored 0%)
[root@localhost tmp]# unzip Demo.zip
Archive: Demo.zip
creating: Demo/
creating: Demo/Test2/
creating: Demo/Test1/
extracting: Demo/Test1/test4
extracting: Demo/test3 123456789101112131415161718

bzip2

  • 压缩后的格式:.bz2
  • 参数
1
-k    产生压缩文件后保留原文件1
  • 命令举例
1
2
3
4
5
6
#压缩
[root@localhost tmp]# bzip2 boduo
[root@localhost tmp]# bzip2 -k boduo

#解压
[root@localhost tmp]# bunzip2 boduo.bz2

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

adb shell中设置android系统linux内部环境变量

发表于 2020-10-26

Android系统本身就是基于linux系统, 有时候我们需要给其配置环境变量,如下操作:

配置步骤

  1. 进入shell环境
1
adb shell
  1. 更改system系统为可读写
1
mount -o remount ,rw /
  1. 将 /system/etc/目录下的 mkshrc文件拉出来进行修改 该文件几位系统环境变量配置文件
1
adb -s 设备名 pull /system/etc/mkshrc  ./
  1. 使用文本编辑器编辑 mkshrc文件,找到 ${TERM:=vt100} ${HOME:=/data} ${MKSH:=/system/bin/sh} 这么一行

    1
    ${TERM:=vt100} ${HOME:=/data} ${MKSH:=/system/bin/sh}
  2. 将环境变量依葫芦画瓢添加至改行尾部, 如

1
${TERM:=vt100} ${HOME:=/data} ${MKSH:=/system/bin/sh} ${mypath:=/system/bin/my}
  1. 然后找到export处,将自己的环境变量加到后面:

    1
    export HOME HOSTNAME MKSH SHELL TERM USER mypath
  2. 将修改完后的mkshrc文件push会Android系统进行替换

1
adb -s 设备名 push ./mkshrc /system/etc/mkshrc
  1. 进入shell环境 使用 export 命令即可查看刚刚配置的环境变量参数
1
export

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Windows环境下使用ndk-build指令编译c/c++生成so库

发表于 2020-10-23

编译环境

  • android-ndk-r9b (这里使用老版本库为例 ,将该库目录下的ndk-build.cmd添加至系统环境变量中,方便后续操作,不同版本的ndk库ndk-build位置可能不太一样,这里你也可以使用最新库)

  • Windows10

编译步骤

  1. 第一步 创建工程文件夹, 这里以E盘下App目录为例
  2. 第二步 在App文件夹下创建子文件夹jni, 并将编写好的源代码以及Android.mk文件放入
  3. 打开命令窗口,执行编译命令
1
ndk-build.cmd  NDK_PROJECT_PATH=E:\App
  1. 等待编译 完成后会在jni同级目录下生成libs和obj两个文件夹,libs目录下存放的是编译好的so库,obj目录下存放的是.o链接库, 如果有需要,使用以下命令可以清除obj目录下文件:
1
ndk-build.cmd  clean

编译扩展配置

  1. 默认情况下编译只生成armeabi架构的so库,如果要生成x86以及其他cpu架构的so,需要在Android.mk文件所在目录下新建Application.mk文件,并加入以下配置:
1
2
3
APP_ABI := armeabi armeabi-v7a x86
或者编译所有架构
APP_ABI := all # 注意必须小写
  1. 默认情况下使用Andorid.mk作为编译脚本,如果想指定编译脚本文件可以添加APP_BUILD_SCRIPT参数进行指定:
1
ndk-build.cmd  NDK_PROJECT_PATH=E:\App APP_BUILD_SCRIPT=new_android.mk
  1. 默认情况下使用Andorid.mk同级目录下找Application.mk 如果想指定Application.mk文件路径,可以添加NDK_APPLICATION_MK进行指定:
1
ndk-build.cmd  NDK_PROJECT_PATH=E:\App NDK_APPLICATION_MK=new_application.mk

注意事项

  1. 编译时默认找jni目录下源码进行编译,所以需要在工程目录下新建jni子目录,原因在此

  2. android工程在引用so库时,必须放在对应架构目录下,否则打包apk时会忽略该文件的打包, 比如指定jni目录为libs ,引用的so是x86架构的,那么必须在libs目录下再新建一个名为x86的文件夹,然后将so放进去, 否则so库不打包

附加

ndk编译脚本文件Android.mk配置:

1
2
3
4
5
6
7
8
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := key #so库引用名称 编译时会自动添加lib前缀,最后的结果为libkey.so
LOCAL_SRC_FILES := Test.cpp # 需要编译的源码

include $(BUILD_SHARED_LIBRARY)

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

SpringBoot开发之采用RestTemplate进行第三方网络请求

发表于 2020-10-19

在Spring-Boot开发中,RestTemplate同样提供了对外访问的接口API,这里主要介绍Get和Post方法的使用。Get请求提供了两种方式的接口getForObject 和 getForEntity,getForEntity提供如下三种方法的实现。

Get请求之——getForEntity(Stringurl,Class responseType,Object…urlVariables)

该方法提供了三个参数,其中url为请求的地址,responseType为请求响应body的包装类型,urlVariables为url中的参数绑定,该方法的参考调用如下:

1
2
3
4
5
// http://USER-SERVICE/user?name={name)
RestTemplate restTemplate=new RestTemplate();
Map<String,String> params=new HashMap<>();
params.put("name","dada"); //
ResponseEntity<String> responseEntity=restTemplate.getForEntity("http://USERSERVICE/user?name={name}",String.class,params);

Get请求之——getForEntity(URI url,Class responseType)

该方法使用URI对象来替代之前的url和urlVariables参数来指定访问地址和参数绑定。URI是JDK http://java.net包下的一个类,表示一个统一资源标识符(Uniform Resource Identifier)引用。参考如下:

1
2
3
4
5
6
7
RestTemplate restTemplate=new RestTemplate();
UriComponents uriComponents=UriComponentsBuilder.fromUriString("http://USER-SERVICE/user?name={name}")
.build()
.expand("dodo")
.encode();
URI uri=uriComponents.toUri();
ResponseEntity<String> responseEntity=restTemplate.getForEntity(uri,String.class).getBody();

Get请求之——getForObject

getForObject方法可以理解为对getForEntity的进一步封装,它通过HttpMessageConverterExtractor对HTTP的请求响应体body内容进行对象转换,实现请求直接返回包装好的对象内容。getForObject方法有如下:

1
2
3
getForObject(String url,Class responseType,Object...urlVariables)
getForObject(String url,Class responseType,Map urlVariables)
getForObject(URI url,Class responseType)

Post 请求

Post请求提供有三种方法,postForEntity、postForObject和postForLocation。其中每种方法都存在三种方法,postForEntity方法使用如下:

1
2
3
4
RestTemplate restTemplate=new RestTemplate();
User user=newUser("didi",30);
ResponseEntity<String> responseEntity=restTemplate.postForEntity("http://USER-SERVICE/user",user,String.class); //提交的body内容为user对象,请求的返回的body类型为String
String body=responseEntity.getBody();

postForEntity存在如下三种方法的重载

1
2
3
postForEntity(String url,Object request,Class responseType,Object... uriVariables)
postForEntity(String url,Object request,Class responseType,Map uriVariables)
postForEntity(URI url,Object request,Class responseType)

postForEntity中的其它参数和getForEntity的参数大体相同在此不做介绍。

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

SpringBoot开发之采用Feign进行第三方网络请求

发表于 2020-10-19

1、在maven项目中添加依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.2.2.RELEASE</version>
</dependency>

2、编写接口,放置在service层

这里的decisionEngine.url 是配置在properties中的 是ip地址和端口号
decisionEngine.url=http://10.2.1.148:3333/decision/person 是接口名字

1
2
3
4
5
6
@FeignClient(url = "${decisionEngine.url}",name="engine")
public interface DecisionEngineService {
  @RequestMapping(value="/decision/person",method= RequestMethod.POST)
  public JSONObject getEngineMesasge(@RequestParam("uid") String uid,@RequestParam("productCode") String productCode);

}

3、在Java的启动类上加上@EnableFeignClients

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@EnableFeignClients //参见此处
@EnableDiscoveryClient
@SpringBootApplication
@EnableResourceServer
public class Application implements CommandLineRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(Application.class);
@Autowired
private AppMetricsExporter appMetricsExporter;

@Autowired
private AddMonitorUnitService addMonitorUnitService;

public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}

4、在代码中调用接口即可

1
2
3
4
@Autowired
private DecisionEngineService decisionEngineService ;
// ...
decisionEngineService.getEngineMesasge("uid" , "productCode");

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

使用Android Studio快速集成SpringBoot项目

发表于 2020-10-19

起因

主要从事Android开发的小伙伴,偶尔进行后端开发,由于Android Studio是Idea的子系,直接使用Studio进行开发即可,无需另外下载Idea工具

配置环境

  • Android Studio
  • Jdk8
  • gradle-6.1.1

快速集成SpringBoot工程

  1. 第一步

前往 https://start.spring.io/下载启动demo, 我这里选择的是gradle工程java语言 已经添加web依赖

  1. 第二步 打开demo工程, 将修改gradle属性文件

    这一步为非必需操作, 由于我这边使用的是all版本, 为了节省网络资源,直接复用

  1. 第三步 调整build.gradle文件, 示例如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
       buildscript {
    ext {
    springIOVersion = '1.0.10.RELEASE'
    springBootVersion = '1.5.9.RELEASE'
    }
    repositories {
    mavenLocal()
    mavenCentral()
    maven { url "http://repo.spring.io/release" }
    maven { url "http://repo.spring.io/milestone" }
    maven { url "http://repo.spring.io/snapshot" }
    maven { url "https://plugins.gradle.org/m2/" }
    }
    dependencies {
    classpath "io.spring.gradle:dependency-management-plugin:${springIOVersion}"
    classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
    }
    }

    plugins {
    id 'org.springframework.boot' version '1.5.9.RELEASE'// 同步工程提示找不到 如果直接注释不影响启动 无法使用插件打包发布
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
    }

    group = 'com.example'
    version = '0.0.1-SNAPSHOT'
    sourceCompatibility = '1.8'//默认demo使用的是jdk11 这里根据自身电脑安装的环境进行修改

    repositories {//增加国内仓库 加快下载速度
    mavenCentral()
    maven{
    url ('http://maven.aliyun.com/nexus/content/groups/public/')
    }
    google()
    jcenter()
    }

    dependencies {
    compile 'org.springframework.boot:spring-boot-starter:2.3.4.RELEASE'
    compile 'org.springframework.boot:spring-boot-starter-web:2.3.4.RELEASE'
    //implementation 打包时打不进 暂时未找到原因 改用 compile
    }

    test {
    useJUnitPlatform()
    }
  2. 第四步 编写接口测试

1
2
3
4
5
6
7
8
9
*注意controller包是在引导类的同一级目录或者子包
@Controller
public class QuickStartController {
@RequestMapping("/quick")
@ResponseBody
public String quick(){
return "hello SpringBoot !";
}
}

5.第五步 启动springBoot项目

1
2
3
4
5
运行引导类的main方法,观察日志
Tomcat started on port(s): 8080 (http) with context path ''

说明该项目发布到8080端口,虚拟路径为空,浏览器访问
http:localhost:8080/quick

至此, 大功告成

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Xposed进阶之app资源替换

发表于 2020-10-15

替换Boolean, Color, Integer, int[], String and String[]类型的简单资源

1.替换系统框架(Android Framwork)资源

替换系统框架资源(对所有app 起作用)需要实现 IXposedHookZygoteInit接口的 initZygote 方法,并在该方法中调用Resources.setSystemWideReplacement(...) 方法替换资源

1
2
3
4
5
6
7
8
9
10
11
12
13
package de.robv.android.xposed.mods.tutorial;

import android.content.res.XResources;
import de.robv.android.xposed.IXposedHookZygoteInit;

public class Tutorial2 implements IXposedHookZygoteInit{

@Override
public void initZygote(StartupParam arg0) throws Throwable {
XResources.setSystemWideReplacement("android", "bool", "config_unplugTurnsOnScreen", false);
}

}

2.替换app应用资源

替换app应用资源需要实现 IXposedHookInitPackageResources 类的
andleInitPackageResources方法,并在该方法中调用res.setReplacement(...)方法替换资源,注意在该方法中不要使用XResources.setSystemWideReplacement 方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package de.robv.android.xposed.mods.tutorial;

import android.content.res.XResources;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import de.robv.android.xposed.IXposedHookInitPackageResources;
import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam;

public class Tutorial3 implements IXposedHookInitPackageResources {

@Override
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
//只替换systemui应用的资源
if (!resparam.packageName.equals("com.android.systemui"))
return;

// 替换资源的不同方式
resparam.res.setReplacement(0x7f080083, "YEAH!"); // WLAN toggle text. You should not do this because the id is not fixed. Only for framework resources, you could use android.R.string.something
resparam.res.setReplacement("com.android.systemui:string/quickpanel_bluetooth_text", "WOO!");
resparam.res.setReplacement("com.android.systemui", "string", "quickpanel_gps_text", "HOO!");
resparam.res.setReplacement("com.android.systemui", "integer", "config_maxLevelOfSignalStrengthIndicator", 6);
resparam.res.setReplacement("com.android.systemui", "drawable", "status_bar_background", new XResources.DrawableLoader() {
@Override
public Drawable newDrawable(XResources res, int id) throws Throwable {
return new ColorDrawable(Color.WHITE);
}
});//你不能直接使用Drawble类进行替换,因为Drawble类可以影响其他引用Ddrawble类实例的ImageView ,最好使用一个包装器。
}

}

替换复杂的资源

对于复制的资源,如动画资源 ,我们也能够替换,下面我们来替换battery icon

动画资源布局

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true" >
<item android:drawable="@drawable/icon1" android:duration="150"></item>
<item android:drawable="@drawable/icon2" android:duration="150"></item>
<item android:drawable="@drawable/icon3" android:duration="150"></item>
<item android:drawable="@drawable/icon4" android:duration="150"></item>
<item android:drawable="@drawable/icon5" android:duration="150"></item>
<item android:drawable="@drawable/icon6" android:duration="150"></item>

</animation-list>
123456789101112

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package de.robv.android.xposed.mods.tutorial;

import com.example.xposedmoduletest.R;

import android.content.res.XModuleResources;
import de.robv.android.xposed.IXposedHookInitPackageResources;
import de.robv.android.xposed.IXposedHookZygoteInit;
import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam;

public class Tutorial4 implements IXposedHookZygoteInit, IXposedHookInitPackageResources {

private static String MODULE_PATH = null;

@Override
public void initZygote(StartupParam startupParam) throws Throwable {
MODULE_PATH = startupParam.modulePath;

}

@Override
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
if (!resparam.packageName.equals("com.android.systemui"))
return;

XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, resparam.res);
resparam.res.setReplacement("com.android.systemui", "drawable", "stat_sys_battery",
modRes.fwd(R.drawable.animation));
resparam.res.setReplacement("com.android.systemui", "drawable", "stat_sys_battery_charge",
modRes.fwd(R.drawable.animation));
}
}

Xposed框架会将模块请求资源的请求指向你模块中的资源

替换布局

你可以用替换资源的方法来替换布局文件,但这样你不得不将目标apk中的整个layout文件复制出来进行修改,这样会使模块的Rom兼容性降低。并且如果两个以上的模块修改布局后,最后修改布局的模块会起作用。更重要的是,布局中指向其它资源的ID很难确定下来。推荐使用下面的方法修改布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package de.robv.android.xposed.mods.tutorial;

import android.graphics.Color;
import android.widget.TextView;
import de.robv.android.xposed.IXposedHookInitPackageResources;
import de.robv.android.xposed.XposedBridge;
import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam;
import de.robv.android.xposed.callbacks.XC_LayoutInflated;
import de.robv.android.xposed.callbacks.XC_LayoutInflated.LayoutInflatedParam;

public class Tutorial5 implements IXposedHookInitPackageResources{

@Override
public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable {
if (!resparam.packageName.equals("com.android.systemui"))
return;

resparam.res.hookLayout("com.android.systemui", "layout", "status_bar", new XC_LayoutInflated() {
@Override
public void handleLayoutInflated(LayoutInflatedParam liparam) throws Throwable {
TextView clock = (TextView) liparam.view.findViewById(
liparam.res.getIdentifier("clock", "id", "com.android.systemui"));
clock.setTextColor(Color.RED);
XposedBridge.log("layout resNames.fullname:"+liparam.resNames.fullName);
XposedBridge.log("layout resNames.id:"+liparam.resNames.id);
XposedBridge.log("layout resNames.name:"+liparam.resNames.name);
XposedBridge.log("layout resNames.pkg:"+liparam.resNames.pkg);
XposedBridge.log("layout resNames.type:"+liparam.resNames.type);

XposedBridge.log("layout resNames.variant:"+liparam.variant);
XposedBridge.log("layout resNames.view:"+liparam.view);




}
});
}

}

回调方法handleLayoutInflated 会在layout文件被填充后回调,在方法的 LayoutInflatedParam 对象 参数中,你可以找到你想修改的View组件。你也可以通过调用resNames来确定加载的那一个布局文件。用 variant来确定加载的布局的目录’layout-land‘。res 同时也会帮你获取资源的ID和其它的资源。

五、用反射来hook方法

每当应用加载的时候,IXposedHookLoadPackPage接口的handLoadPackage方法就会被调用执行,为了让我们在正确的进程中执行,需要先判断被加载的包是不是正确的包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package de.robv.android.xposed.mods.tutorial;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

public class Tutorial6 implements IXposedHookLoadPackage {

@Override
public void handleLoadPackage(LoadPackageParam param) throws Throwable {
if(!param.packageName.equals("com.android.systemui"))
return;



}


}

一旦我们进入到正确的进程进后,我们就能用param变中的ClassLoad来访问该进程中加载的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package de.robv.android.xposed.mods.tutorial;

import android.webkit.WebView.FindListener;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XC_MethodHook.MethodHookParam;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
public class Tutorial6 implements IXposedHookLoadPackage {

@Override
public void handleLoadPackage(LoadPackageParam param) throws Throwable {
if(!param.packageName.equals("com.android.systemui"))
return;
findAndHookMethod("com.android.systemui.statusbar.policy.Clock",param.classLoader, "updateClock", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// this will be called before the clock was updated by the original method
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// this will be called after the clock was updated by the original method
}
});


}


}

XposedHelpers是一个重要的工具类,推荐用Eclipse的同学静态导入该类中的方法 import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;。该类能够通过反射机制来访问方法、构造器、域。
findAndHookMehthod(String packageName,Class clazz, String methodName, Object... args))方法来对函数进行Hook。如果在方法前和方法后Hook,该方法最后一个参数需要实现XC_MethodHook类的beforeHookedMethod和afterHookedMethod方法,如果想要替换整个方法,则需要实现XC_MethodReplacement类的replaceHookedMethod方法
XposedBridge保存了每个Hook方法的回调方法。优先级高的回调方法被优先调用A.before -> B.before -> original method -> B.after -> A.after

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package de.robv.android.xposed.mods.tutorial;

import android.graphics.Color;
import android.webkit.WebView.FindListener;
import android.widget.TextView;
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XC_MethodHook.MethodHookParam;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;

import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;
public class Tutorial6 implements IXposedHookLoadPackage {

@Override
public void handleLoadPackage(LoadPackageParam param) throws Throwable {
if(!param.packageName.equals("com.android.systemui"))
return;
findAndHookMethod("com.android.systemui.statusbar.policy.Clock",param.classLoader, "updateClock", new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
// this will be called before the clock was updated by the original method
}
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
TextView tv = (TextView) param.thisObject;//获取调用该方法类的对象
String text = tv.getText().toString();
tv.setText(text + " :)");
tv.setTextColor(Color.RED);
}
});


}


}

搭建本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Xposed 如何Hook静态变量or构造方法or复杂参数的方法or替换函数执行内容or内部类中的函数or匿名类的函数

发表于 2020-10-14

Xposed框架Hook相关api的使用

首先创建测试类继承IXposedHookLoadPackage, 以下所有的hook方法都在handleLoadPackage方法内部进行调用

1
2
3
4
5
public class HookTest implements IXposedHookLoadPackage {
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
XposedBridge.log("HOOK初体验:" + lpparam.processName + ":" + lpparam.packageName);
}

准备需要被hook的代码用于测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
public class HookDemo {
private String Tag = "HookDemo";
private static int staticInt = 100;
public int publicInt = 200;
private int privateInt = 300;

public HookDemo(){
this("NOHook");
Log.d(Tag, "HookDemo() was called|||");
}

private HookDemo(String str){
Log.d(Tag, "HookDemo(String str) was called|||" + str);
}

public void hookDemoTest(){
Log.d(Tag, "staticInt = " + staticInt);
Log.d(Tag, "PublicInt = " + publicInt);
Log.d(Tag, "privateInt = " + privateInt);
publicFunc("NOHook");
Log.d(Tag, "PublicInt = " + publicInt);
Log.d(Tag, "privateInt = " + privateInt);
privateFunc("NOHook");
staticPrivateFunc("NOHook");

String[][] str = new String[1][2];
Map map = new HashMap<String, String>();
map.put("key", "value");
ArrayList arrayList = new ArrayList();
arrayList.add("listValue");
complexParameterFunc("NOHook", str, map, arrayList);

repleaceFunc();
anonymousInner(new Animal() {
@Override
public void eatFunc(String value) {
Log.d(Tag, "eatFunc(String value) was called|||" + value);
Log.d(Tag, "anonymoutInt = " + anonymoutInt);
}
}, "NOHook");

InnerClass innerClass = new InnerClass();
innerClass.InnerFunc("NOHook");
}

public void publicFunc(String value){
Log.d(Tag, "publicFunc(String value) was called|||" + value);
}

private void privateFunc(String value){
Log.d(Tag, "privateFunc(String value) was called|||" + value);
}

static private void staticPrivateFunc(String value){
Log.d("HookDemo", "staticPrivateFunc(Strin value) was called|||" + value);
}

private void complexParameterFunc(String value, String[][] str, Map<String,String> map, ArrayList arrayList)
{
Log.d("HookDemo", "complexParameter(Strin value) was called|||" + value);
}

private void repleaceFunc(){
Log.d(Tag, "repleaceFunc will be replace|||");
}

public void anonymousInner(Animal dog, String value){
Log.d(Tag, "anonymousInner was called|||" + value);
dog.eatFunc("NOHook");
}

private void hideFunc(String value){
Log.d(Tag, "hideFunc was called|||" + value);
}
private void argFunc(Objcet... value){
Log.d(Tag, "argFunc was called|||" + value);
}

class InnerClass{
public int innerPublicInt = 10;
private int innerPrivateInt = 20;
public InnerClass(){
Log.d(Tag, "InnerClass constructed func was called");
}
public void InnerFunc(String value){
Log.d(Tag, "InnerFunc(String value) was called|||" + value);
Log.d(Tag, "innerPublicInt = " + innerPublicInt);
Log.d(Tag, "innerPrivateInt = " + innerPrivateInt);
}
}
}
  1. 修改类中的私有静态变量
1
2
Class<?> clazz = XposedHelpers.findClass("com.example.xposedhooktarget.HookDemo", lpparam.classLoader);
XposedHelpers.setStaticIntField(clazz, "staticInt", 99);
  1. Hook无参构造函数
1
2
3
4
5
6
7
8
9
10
Class<?> clazz = XposedHelpers.findClass("com.example.xposedhooktarget.HookDemo", loadPackageParam.classLoader);
XposedHelpers.findAndHookConstructor(clazz, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("Haha, HookDemo constructed was hooked");
//大坑,此时对象还没有建立,即不能获取对象,也不能修改非静态变量的值
//XposedHelpers.setIntField(param.thisObject, "publicInt", 199);
//XposedHelpers.setIntField(param.thisObject, "privateInt", 299);
}
});
  1. Hook有参构造函数,并修改参数
1
2
3
4
5
6
XposedHelpers.findAndHookConstructor(clazz, String.class,  new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.args[0] = "Haha, HookDemo(str) are hooked";
}
});
  1. Hook公有方法publicFunc,并修改参数以及修改下publicInt和privateInt的值,再顺便调用一下隐藏函数hideFunc
1
2
3
4
5
6
7
8
9
10
11
12
13
XposedHelpers.findAndHookMethod(clazz, "publicFunc", String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.args[0] = "Haha, publicFunc are hooked";
//修改成员变量的值
XposedHelpers.setIntField(param.thisObject, "publicInt", 199);
XposedHelpers.setIntField(param.thisObject, "privateInt", 299);
//调用函数
XposedHelpers.callMethod(param.thisObject, "hideFunc", "Haha, hideFunc was hooked");


}
});
  1. Hook私有方法privateFunc,并修改参数
1
2
3
4
5
6
XposedHelpers.findAndHookMethod(clazz, "privateFunc", String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.args[0] = "Haha, privateFunc are hooked";
}
});
  1. Hook私有静态方法staticPrivateFunc, 并修改参数
1
2
3
4
5
6
XposedHelpers.findAndHookMethod(clazz, "staticPrivateFunc", String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.args[0] = "Haha, staticPrivateFunc are hooked";
}
});
  1. Hook复杂参数函数complexParameterFunc
1
2
3
4
5
6
7
8
9
Class fclass1 = XposedHelpers.findClass("java.util.Map", loadPackageParam.classLoader);
Class fclass2 = XposedHelpers.findClass("java.util.ArrayList", loadPackageParam.classLoader);
XposedHelpers.findAndHookMethod(clazz, "complexParameterFunc", String.class,
"[[Ljava.lang.String;", fclass1, fclass2, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.args[0] = "Haha, complexParameterFunc are hooked";
}
});
  1. Hook私有方法repleaceFunc, 并替换打印内容
1
2
3
4
5
6
7
XposedHelpers.findAndHookMethod(clazz, "repleaceFunc", new XC_MethodReplacement() {
@Override
protected Object replaceHookedMethod(MethodHookParam methodHookParam) throws Throwable {
Log.d("HookDemo", "Haha, repleaceFunc are replaced");
return null;
}
});
  1. Hook方法, anonymousInner, 参数是抽象类,先加载所需要的类即可
1
2
3
4
5
6
7
8
Class animalClazz  = loadPackageParam.classLoader.loadClass("com.example.xposedhooktarget.Animal");
XposedHelpers.findAndHookMethod(clazz, "anonymousInner", animalClazz, String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("HookDemo This is test");
param.args[1] = "Haha, anonymousInner are hooked";
}
});
  1. Hook匿名类的eatFunc方法,修改参数,顺便修改类中的anonymoutInt变量
1
2
3
4
5
6
7
8
XposedHelpers.findAndHookMethod("com.example.xposedhooktarget.HookDemo$1", clazz.getClassLoader(),
"eatFunc", String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.args[0] = "Haha, eatFunc are hooked";
XposedHelpers.setIntField(param.thisObject, "anonymoutInt", 499);
}
});
  1. Hook内部类InnerClass的InnerFunc方法,修改参数,顺便修改类中的innerPublicInt和innerPrivateInt变量
1
2
3
4
5
6
7
8
9
final Class<?> clazz1 = XposedHelpers.findClass("com.example.xposedhooktarget.HookDemo$InnerClass", loadPackageParam.classLoader);
XposedHelpers.findAndHookMethod(clazz1, "InnerFunc", String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
param.args[0] = "Haha, InnerFunc was hooked";
XposedHelpers.setIntField(param.thisObject, "innerPublicInt", 9);
XposedHelpers.setIntField(param.thisObject, "innerPrivateInt", 19);
}
});
  1. 对于一些延迟动态加载的代码,hook方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
XposedHelpers.findAndHookMethod("dalvik.system.DexFile", lpparam.classLoader, "loadClass",
String.class, "java.lang.ClassLoader", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String className = (String) param.args[0];
Object result = param.getResult();
if (result != null && result instanceof Class) {
if ("com.alipay.mobile.nebulauc.impl.UCWebViewClient".equals(className)) {
// hookLoadWebViewClient(((Class) result).getClassLoader());
}
}
}

});
  1. Hook有可变参数的方法
1
2
3
4
5
6
XposedHelpers.findAndHookMethod(clazz, "argFunc", Object[].class,new XC_MethodHook() {
@Override
protected Object afterHookedMethod(MethodHookParam methodHookParam) throws Throwable {

}
});

总结

  1. 无论hook公有私有方法还是静态方法 ,抑或可变参方法,甚至是带返回值的方法,统一使用findAndHookMethod这个Api

  2. xposed无法hook接口和抽象类方法

  3. Hook逻辑类可实现的接口有 :

    • 安卓系统启动的时候(使用 IXposedHookZygoteInit 接口)、

    • 一个新的app被加载的时候(使用 IXposedHookLoadPackage 接口)、

    • 一个资源被初始化的时候( 使用 IXposedHookInitPackageResources 接口 )

  4. 实现IXposedHookLoadPackage 并使用 XposedBridge.log 打印的日志,当启动第三方应用后,Android Studio控制台无法查看到日志信息,需要在xposed应用内进行查看

  5. 参数为基本数据类型时需要传对应包装类的type属性,比如int传Integer.TYPE, 否则提示找不到方法,如果参数为引用数据类型,那直接传xxx.class即可

    • beforeHookedMethod 会在调用原方法前执行,如果使用setResult则跳过原方法,并返回setResult参数中的值。

    • afterHookedMethod 会在调用原方法后执行,setResult可改变返回值

    • replaceHookedMethod 会完全替换原方法,即原方法不执行,且返回值可以直接return,setResult不生效。

注:每次修改完毕后需要重启才生效

附

Hook 修改方法返回值
1
2
3
4
5
XposedHelpers.findAndHookMethod(clazz, method, new Object[]{new XC_MethodHook() {
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
param.setResult(result); // 设置返回值
}
}});
Hook 获取方法返回值
1
2
3
4
5
XposedHelpers.findAndHookMethod(clazz, method, new Object[]{new XC_MethodHook() {
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Object result = param.getResult(); //获取返回值
}
}});
Hook 获取方法传入的参数值
1
2
3
4
5
6
7
8
9
XposedHelpers.findAndHookMethod(claName, cl, "i", String.class, String.class, Object[].class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
// 数组param.args存储的参数列表里的值
XposedBridge.log(TAG + param.args[0]);
XposedBridge.log(TAG + param.args[1]);
XposedBridge.log(TAG + param.args[2]);
}
});
Hook 给方法传值
1
2
3
Class cla = XposedHelpers.findClass(className, loadPackageParam.classLoader);
Object com = XposedHelpers.callStaticMethod(cla, "getInstance");
XposedHelpers.callMethod(com, "setDebug", true); // 传入指定值
Hook 获取Intent的值
1
2
3
4
5
6
7
8
9
10
11
12
13
private void hookGetIntent(XC_LoadPackage.LoadPackageParam loadPackageParam) {
try {
XposedHelpers.findAndHookMethod("android.app.Activity", loadPackageParam.classLoader, "getIntent", new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Intent sou = (Intent) param.getResult();
KLog.d("hookGetIntent:" + sou.toURI().toString());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
Hook 广播发送的Intent信息
1
2
3
4
5
6
7
8
9
10
11
12
13
private void hookSendBroadcast(XC_LoadPackage.LoadPackageParam loadPackageParam) {
try {
XposedHelpers.findAndHookMethod("android.content.ContextWrapper", loadPackageParam.classLoader, "sendBroadcast", Intent.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Intent sou = (Intent) param.args[0];
KLog.d("sendBroadcast:" + sou.toURI().toString());
}
});
} catch (Exception e) {
e.printStackTrace();
}
}

Xposed一般Hook的是默认的dex文件,但是现在很多的APP都有多个Dex文件,所以使用Xposed时,经常遇到类名路径正确却出现ClassNotFoundError找不到类的错误。要解决这个问题,需要获取对应Dex文件的上下文环境。

Android在加载dex文件后会创建一个Application类,然后会调用attach方法,attach方法的参数就是上下文context,而且attach方法是final方法,不会因为被覆盖而hook不到,拿到这个context就可以获取对应的classload,然后就可以顺利hook到multidex的类了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
ClassLoader cl = ((Context) param.args[0]).getClassLoader(); // 获取ClassLoader
Class<?> hookClass = null;
try {
hookClass = cl.loadClass(claName); // 获取Class
// 使用cl 和 hookClass 完成hook
XposedHelpers.setStaticIntField(hookClass, fieldName, val);
XposedHelpers.findAndHookMethod(claName, cl, "i", String.class, String.class, Object[].class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log(TAG + param.args[0]);
XposedBridge.log(TAG + param.args[1]);
XposedBridge.log(TAG + param.args[2]);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
});

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

Linux系统查看时区以及时区修改

发表于 2020-10-12

wp-config.php里加入下面代码:

这里以Ubuntu系统为例

查看时区:

1
date -R

修改时区为东八区

1
timedatectl set-timezone Asia/Shanghai
老版本系统以上修改时区指令可能不生效, 需要采用以下办法

1.运行tzselect

1
root@ubuntu:/# tzselect

在这里我们选择亚洲 Asia,确认之后选择中国(China),最后选择北京(Beijing)

**

2.复制文件到/etc目录下

1
root@ubuntu:/# cp /usr/share/zoneinfo/Asia/Shanghai  /etc/localtime

3.再次查看时间date -R,已经修改为北京时间

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

docker设置启动docker后 开启或关闭自启容器

发表于 2020-10-12

前言

我们需要在docker启动的时候自动运行容器, 具体操作分为以下两种情况

容器未创建

如果容器还没有创建, 可以采用增加容器运行参数的方式实现

命令如下:

1
docker run xxx –-restart=always

容器已创建

当然如果你的容器已经启动,可以通过update命令进行修改.
命令如下:

1
docker update –-restart=always <CONTAINER ID>

取消自启动

如果你想取消掉
命令如下:

1
docker update --restart=no <CONTAINER ID>

容器批量操作

  1. 给已启动所有容器设置自启动

    1
    docker update --restart=no $(docker ps -q)
  2. 给所有容器设置自启动 包括停止的容器

    1
    docker update --restart=no $(docker ps -aq)
  3. 查看容器是否配置成功

    1
    docker inspect 容器名

    image-20210916151358153

本文为作者原创 转载时请注明出处 谢谢

乱码三千 – 点滴积累 ,欢迎来到乱码三千技术博客站

1…333435…48

乱码三千

android程序员一枚,擅长java,kotlin,python,金融投资,欢迎交流~

479 日志
139 标签
RSS
© 2024 乱码三千
本站总访问量次
由 Hexo 强力驱动
|
主题 — NexT.Muse v5.1.4
0%