GVKun编程网logo

android-@Inject设置为不注入属性(android inject注入)

11

这篇文章主要围绕android-@Inject设置为不注入属性和androidinject注入展开,旨在为您提供一份详细的参考资料。我们将全面介绍android-@Inject设置为不注入属性的优缺点

这篇文章主要围绕android-@Inject设置为不注入属性android inject注入展开,旨在为您提供一份详细的参考资料。我们将全面介绍android-@Inject设置为不注入属性的优缺点,解答android inject注入的相关问题,同时也会为您带来$inject.instantiate VS $inject.get VS $injector.invoke in angularjs、Android APT 实现控件注入框架SqInject的示例、android InputManager injectInputEvent、Android so注入(inject)和Hook技术学习(三)——Got表hook之导出表hook的实用方法。

本文目录一览:

android-@Inject设置为不注入属性(android inject注入)

android-@Inject设置为不注入属性(android inject注入)

我正在尝试使用Android Dagger ‡ implementation并将dispatchingAndroidInjector注入到我的Application类中:

class MyApp : Application(), HasActivityInjector {

    private lateinit var dispatchingAndroidInjector: dispatchingAndroidInjector<Activity>
        @Inject set    

    override fun onCreate() {
        super.onCreate()
        AppInjector.init(this)
    }

    override fun activityInjector(): ActivityInjector<Activity> {
        return dispatchingAndroidInjector
    }

}

但是我得到一个IllegalArgumentExecption,指出“ lateinit属性尚未初始化”:

06-19 10:57:30.773 10797-10797/com.example.app E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.example.app, PID: 10797
        java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.app/com.example.app.ui.MainActivity}: kotlin.UninitializedPropertyAccessException: lateinit property dispatchingAndroidInjector has not been initialized
            at android.app.ActivityThread.performlaunchActivity(ActivityThread.java:2666)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2727)
            at android.app.ActivityThread.-wrap12(ActivityThread.java)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1478)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:154)
            at android.app.ActivityThread.main(ActivityThread.java:6121)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)
         Caused by: kotlin.UninitializedPropertyAccessException: lateinit property dispatchingAndroidInjector has not been initialized
            at com.example.app.MyApp.activityInjector(MyApp.kt:28)
            at dagger.android.AndroidInjection.inject(AndroidInjection.java:55)
            at com.example.app.injection.AppInjector.handleActivity(AppInjector.kt:41)
            at com.example.app.injection.AppInjector.access$handleActivity(AppInjector.kt:14)
            at com.example.app.injection.AppInjector$init$1.onActivityCreated(AppInjector.kt:21)
            at android.app.Application.dispatchActivityCreated(Application.java:197)
            at android.app.Activity.onCreate(Activity.java:961)
            at android.support.v4.app.BaseFragmentActivityGingerbread.onCreate(BaseFragmentActivityGingerbread.java:54)
            at android.support.v4.app.FragmentActivity.onCreate(FragmentActivity.java:319)
            at com.example.app.ui.MainActivity.onCreate(MainActivity.kt:20)
            at android.app.Activity.performCreate(Activity.java:6682)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
            at android.app.ActivityThread.performlaunchActivity(ActivityThread.java:2619)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2727) 
            at android.app.ActivityThread.-wrap12(ActivityThread.java) 
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1478) 
            at android.os.Handler.dispatchMessage(Handler.java:102) 
            at android.os.Looper.loop(Looper.java:154) 
            at android.app.ActivityThread.main(ActivityThread.java:6121) 
            at java.lang.reflect.Method.invoke(Native Method) 
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:889) 
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:779)

这是用于创建AppComponent和注入MyApp依赖项的AppInjector类:

object AppInjector {

    fun init(app: MyApp) {
        DaggerAppComponent.builder().application(app).build().inject(app)
        // ...
    }

}

和AppComponent接口:

@Singleton
@Component(modules = arrayOf(
    AndroidInjectionModule::class,
    AppModule::class,
    MainActivityModule::class
)) interface AppComponent {

    @Component.Builder interface Builder {

        @BindsInstance fun application(app: Application): Builder

        fun build(): AppComponent

    }

    fun inject(app: MyApp)

}

我在属性访问器上使用@Inject注释,如the documentation中所述,但似乎无法正常工作.我究竟做错了什么?

解决方法:

我目前无法对其进行测试,但是我认为这是因为您的财产是私有的. Dagger不使用反射,因此必须在类外部从其可见的位置才能使其运行.

(显式写出set对此无济于事,因为在这种情况下,它仅表示一个私有的setter.请注意,字段对于所有属性都是私有的,而非private属性则具有适当的可见性的getter / setter.)

您还应该能够仅注释属性而不是其设置方法,因此这将是最终结果:

@Inject
lateinit var dispatchingAndroidInjector: dispatchingAndroidInjector<Activity>

如果您希望属性为私有,请考虑使用构造函数注入.

$inject.instantiate VS $inject.get VS $injector.invoke in angularjs

$inject.instantiate VS $inject.get VS $injector.invoke in angularjs

AngularJS中的$inject.instantiate,$injector.get和$injector.invoke之间有什么区别?
给予以下服务:
app.service('myService',function ($q,$http) {
  return {
    q:    $q,http: $http
  };
});

$injector.get(name,[caller]);

返回所请求服务的实例.

$injector.get('myService');
// { q: $q,http: $http }

$injector.invoke(fn,[self],[localals]);

调用提供的方法,并从$inject中传递给定的参数.

$injector.invoke(function (myService,$http) {
  console.log(myService); // { q: $q,http: $http };
  console.log(this);      // { v: 'im this!' };
  console.log($http);     // null
},{ v: 'im this!' },{ $http: null });

$injector.instantiate(Type,[locals]);

创建给定类型的新实例.使用构造函数,然后使用构造函数注释中指定的参数调用新实例.

假设以下’class’:

function Person (fName,lName,$http,$q) {
  return {
    first_name: fName,last_name:  lName,http: $http,q:    $q
  }
}

现在,如果我们想在我们的控制器中创建一个新的Person,我们可以这样做:

app.controller('...',function ($injector) {
  var $http = $injector.get('$http');
  var $q    = $injector.get('$q');
  var p     = new Person('kasper','lewau',$q);

  console.log(p); // { first_name: 'kasper',last_name: 'lewau',q: $q };
});

想象一下,人有〜20个依赖关系,我们用$inject.get方法获取每个人的每一个.

繁琐!而且 – 你需要保持你的参数&参数同步啊.

相反,你可以这样做:

app.controller('...',function ($injector) {
  var p = $injector.instantiate(Person,{
    fName: 'kasper',lName: 'lewau'
  });
  console.log(p); // { first_name: 'kasper',q: $q };
});

而且 – 如果我们想要,我们可以提供本地的.instantiate调用,以便覆盖内部$inject.get()在实例化时通常会得到的内容.

var p = $injector.instantiate(Person,{
  fName: 'kasper',lName: 'lewau'
},{ $http: 'nothing!',$q: 'nothing!' });
console.log(p); // { first_name: 'kasper',http: 'nothing!',q: 'nothing!' };

我希望解释三者之间的区别.如果您需要更多有关差异的信息,我会推荐这些文章:

> http://taoofcode.net/studying-the-angular-injector/
> http://taoofcode.net/studying-the-angular-injector-annotate/
> http://taoofcode.net/studying-the-angular-injector-invoke/
> http://taoofcode.net/studying-the-angular-injector-getservice/
> http://taoofcode.net/studying-the-angular-js-injector-instantiate/

Android APT 实现控件注入框架SqInject的示例

Android APT 实现控件注入框架SqInject的示例

作者

大家好,我叫小鑫,也可以叫我蜡笔小鑫;

本人17年毕业于中山大学,于2018年7月加入37手游安卓团队,曾经就职于久邦数码担任安卓开发工程师;

目前是37手游安卓团队的海外负责人,负责相关业务开发;同时兼顾一些基础建设相关工作。

背景

在游戏发行中,经常需要切包,如果直接使用R.id.xxx,在回编译时,由于resources.arsc会重新编译,R类中的id值和resources.arsc中的对应关系会异常,导致程序异常。我们有两种解决方法。

一种是在切包过程中纠正R类的值,实现方案具体介绍可以查看

游戏发行切包资源索引冲突解决方案,链接如下:

//www.jb51.net/article/207579.htm

另一种解决方案则是使用getIdentifier获取资源ID,放弃使用R类,这种方式中,编码较为麻烦,并且getIdentifier中需要写的是字符串,容易写错,并且编译过程中是发现不了的。基于这种情况,我们开发了基于getIdentifier的控件注入框架

一、APT技术简介

1、APT定义

APT(Annotation Processing Tool)即注解处理器,是一种处理注解的工具,确切的说它是javac的一个工具,它用来在编译时扫描和处理注解。注解处理器以Java代码作为输入,生成.java文件作为输出

2、注解定义

1、注解是一种能被添加到java代码中的元数据,类、方法、变量、参数和包都可以用注解修饰。

2、注解对于它所修饰的代码没有直接的影响

3、APT原理简介

Annotation processing是在编译阶段执行的,它的原理就是读入Java源代码,解析注解,然后生成新的Java代码。新生成的Java代码最后被编译成Java字节码,注解解析器(Annotation Processor)不能改变读入的Java 类,比如不能加入或删除Java方法

二、APT实战使用

1、SqInject框架来源

在手游发行中,经常需要切包,将游戏接完SDK1的包,通过反编译,替换smali文件及其他资源文件的方式,替换为渠道SDK2的渠道包。在这个反编译回编译的过程中,资源索引ID(即R类和resources.arsc中的ID映射关系)会发生冲突导致程序异常,即不做特殊处理的话,渠道SDK及发行SDK是不能直接使用R类的,要使用getIdentifier获取资源ID

要求在程序中使用getIdentifier,在开发过程中是比较麻烦的事情。在这样的条件下,我们也无法使用如butterknife这样的框架。因此,我们模仿butterknife开发了一套基于getIdentifier的控件注入框架SqInject。下面介绍SqInject的实现,先来看下简单使用哈

public class MainActivity extends AppCompatActivity {
	
 //绑定ID
 @BindView(SqR.id.tv)
 TextView hello;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 SqInject.bind(this);
 Log.e("SqInject", hello.getText().toString());
 }

 //绑定点击事件
 @OnClick(SqR.id.tv)
 public void click(View view) {
 Intent intent = new Intent(MainActivity.this, TestActivity.class);
 startActivity(intent);
 }
}

2、SqInject的实现原理

2.1、注解处理器模块实现

上文说到APT常用于生成代码,在SqInject中APT注解处理环节中,流程如下图所示:

在编译过程中扫描注解,生成Java代码,而后再次编译

在SqInject代码中,实现如下:

@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class SqInjectProcessor extends AbstractProcessor {

 ...
	
 //核心方法
 @Override
 public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
 //控件类注解解析,ResChecker检查资源id合法性,合法则生成"类名+$ViewBinder类,否则编译失败
 BindViewBuilder bindViewBuilder = new BindViewBuilder(roundEnvironment, mResChecker, mElementUtils, mTypeUtils, mFiler, mMessager);
 bindViewBuilder.build();
 //id类注解解析,ResChecker检查资源id合法性,合法则生成"类名+$IdBinder类",否则编译失败
 BindIdsBuilder bindIdsBuilder = new BindIdsBuilder(roundEnvironment, mResChecker, mElementUtils, mTypeUtils, mFiler, mMessager);
 bindIdsBuilder.build();
 return false;
 }
 
}

在生成控件注入相关代码之前,框架中会先检测资源id的合法性,本框架中在使用注解时,传入的是字符串。可资源名称是有可能不存在对应资源的,框架会做相应的检测

2.2、资源检测

Android编译资源过程中,会生成R类,也就是说只有在R类中存在的,用getIdentifier才能够获取到,那么我们可以用R类来检测传入的参数是否合理,代码如下:

/**
 * 检测资源id在R文件中是否存在
 * @param name
 * @param type
 * @return
 */
 public boolean isIdValid(String name, String type) {
 String RClassName = mPackageNeme + ".R." + type;
 TypeElement RClassType = mElementUtils.getTypeElement(RClassName);
 if (RClassType == null) {
  mMessager.printMessage(Diagnostic.Kind.ERROR, RClassName + "不存在,请检查是否包名有误,或者类型错误");
 } else {
  //遍历属性
  for (Element element : RClassType.getEnclosedElements()) {
  String fieldName = element.getSimpleName().toString();
  if (name.equals(fieldName)) {
   return true;
  }
  }
 }
 return false;
 }

2.3、解析注解,生成代码

以解析BindView为例,代码如下:

/**
 * 解析BindView注解
 * @return
 */
 private Map<TypeElement, List<VariableElement>> parseBindView(){
 Set<Element> elements = (Set<Element>) mRoundEnvironment.getElementsAnnotatedWith(BindView.class);
 if (!Utils.isEmpty(elements)) {
  Map<TypeElement, List<VariableElement>> map = new HashMap<>();
  for (Element element : elements) {
  if (element instanceof VariableElement) {
   //获取该属性所在类
   TypeElement targetElement = (TypeElement) element.getEnclosingElement();
   mTargetSet.add(targetElement);
   if (map.get(targetElement) == null) {
   List<VariableElement> targetStringLists = new ArrayList<>();
   targetStringLists.add((VariableElement) element);
   map.put(targetElement, targetStringLists);
   } else {
   map.get(targetElement).add((VariableElement) element);
   }
  }
  }
  return map;
 }
 return null;
 }

解析完BindView注解后,使用javapoet生成代码,篇幅有限,下面仅列出获取参数和生成代码的一小部分

if (mBindViewIdTargetMap != null && mBindViewIdTargetMap.get(targetElement) != null) {
  List<VariableElement> viewElements = mBindViewIdTargetMap.get(targetElement);
  //方法体
  for (VariableElement viewElement : viewElements) {
   //获取属性名
   String fieldName = viewElement.getSimpleName().toString();
   //获取类型
   TypeMirror typeMirror = viewElement.asType();
   TypeElement fieldClassElement = (TypeElement) mTypeUtils.asElement(typeMirror);
   mMessager.printMessage(Diagnostic.Kind.NOTE, "注解的字段类型为: " + fieldClassElement.getQualifiedName().toString());
   TypeElement fieldType = mElementUtils.getTypeElement(fieldClassElement.getQualifiedName());
   ClassName fieldClassName = ClassName.get(fieldType);
   //获取@BindView注解的值,即名称
   String name = viewElement.getAnnotation(BindView.class).value();
   //检测名称是否合法
   if(!mResChecker.isIdValid(name, "id")){
   mMessager.printMessage(Diagnostic.Kind.ERROR, "R文件中不存在id为" + name + "的值");
   }
   methodBuilder.addStatement("target.$N = $T.findRequiredViewAsType(source, $S, $S, $T.class)", fieldName, JptConstants.UTILS, name, "field " + fieldName,fieldClassName);
  }
  }

小小总结一下,在注解处理器中,最重要的两个环节,一个是解析注解,获取参数,然后是利用javapoet框架生成代码

下面看下生成的代码

public class MainActivity$ViewBinder implements ViewBinder<MainActivity> {
 @Override
 public void bindView(final MainActivity target, final View source) {
 //这里就是上面的代码生成的
 target.hello = ViewUtils.findRequiredViewAsType(source, "tv", "field hello", TextView.class);
 IdUtils.findViewByName("tv", source).setOnClickListener(new DebouncingOnClickListener() {
 public void doClick(View v) {
 target.click(v);
 }
 } );
 }
}

到这里,就介绍完了使用APT生成代码了哈。本文中提到的框架SqInject是我们日常工作中会使用到的SqInject框架,该框架以开源,地址是: github.com/37sy/SqInje… 有兴趣的欢迎star哈

以上就是Android APT 实现控件注入框架SqInject的示例的详细内容,更多关于Android APT控件注入框架SqInject的资料请关注其它相关文章!

您可能感兴趣的文章:
  • android实现圆环倒计时控件
  • Android Studio给各种控件加边框的操作方法
  • Android自定义view实现倒计时控件
  • Android实现简单的下拉刷新控件
  • Android日期选择控件使用详解
  • Android自定义控件横向柱状统计图
  • 详解Android控件状态依赖框架

android InputManager injectInputEvent

android InputManager injectInputEvent

我读过this.我无法编译coredump给出的答案.我可以清楚地看到InputManager.java中的injectInputEvent(Android源代码).它也是公开的.但是我无法编译它.可能是它的私人api,有一种方法可以访问它..

解决方法:

API被隐藏.您可以通过反射访问它:

InputManager im = (InputManager) getSystemService(Context. INPUT_SERVICE);

Class[] paramTypes = new Class[2];
paramTypes[0] = InputEvent.class;
paramTypes[1] = Integer.TYPE;

Object[] params = new Object[2];
params[0] = newEvent;
params[1] = 0;

try {
    Method hiddenMethod = im.getClass().getmethod("injectInputEvent", paramTypes);
    hiddenMethod.invoke(im, params);
} catch (NoSuchMethodException | illegalaccessexception | IllegalArgumentException | InvocationTargetException e) {
    e.printstacktrace();
}

Android so注入(inject)和Hook技术学习(三)——Got表hook之导出表hook

Android so注入(inject)和Hook技术学习(三)——Got表hook之导出表hook

前文介绍了导入表hook,现在来说下导出表的hook。导出表的hook的流程如下。
1、获取动态库基值   

 1 void* get_module_base(pid_t pid, const char* module_name){
 2     FILE* fp;
 3     long addr = 0;
 4     char* pch;
 5     char filename[32];
 6     char line[1024];
 7     
 8     // 格式化字符串得到 "/proc/pid/maps"
 9     if(pid < 0){
10         snprintf(filename, sizeof(filename), "/proc/self/maps");
11     }else{
12         snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
13     }
14 
15     // 打开文件/proc/pid/maps,获取指定pid进程加载的内存模块信息
16     fp = fopen(filename, "r");
17     if(fp != NULL){
18         // 每次一行,读取文件 /proc/pid/maps中内容
19         while(fgets(line, sizeof(line), fp)){
20             // 查找指定的so模块
21             if(strstr(line, module_name)){
22                 // 分割字符串
23                 pch = strtok(line, "-");
24                 // 字符串转长整形
25                 addr = strtoul(pch, NULL, 16);
26 
27                 // 特殊内存地址的处理
28                 if(addr == 0x8000){
29                     addr = 0;
30                 }
31                 break;
32             }
33         }
34     }
35     fclose(fp);
36     return (void*)addr;
37 }

2、计算program header table实际地址 

通过ELF文件头获取到程序表头的偏移地址及表头的个数

 1     Elf32_Ehdr *header = (Elf32_Ehdr*)(base_addr);
 2     if (memcmp(header->e_ident, "\177ELF", 4) != 0) {
 3         return 0;
 4     }
 5     int phOffset = header->e_phoff;
 6     int phNumber = header->e_phnum;
 7     int phPhyAddr = phOffset + base_addr;
 8 
 9     int i = 0;
10 
11     Elf32_Phdr* phdr_table = (Elf32_Phdr*)(base_addr + phOffset);
12     if (phdr_table == 0)
13     {
14         LOGD("[+] phdr_table address : 0");
15         return 0;
16     }

3、遍历program header table,找到类型为PT_DYNAMIC的区段(动态链接段),ptype等于2即为dynamic,获取到p_offset 

这里需要参照程序表头结构体的相关信息,程序表头结构体结构如下:

struct Elf32_Phdr {
  Elf32_Word p_type;   // Type of segment
  Elf32_Off  p_offset; // File offset where segment is located, in bytes
  Elf32_Addr p_vaddr;  // Virtual address of beginning of segment
  Elf32_Addr p_paddr;  // Physical address of beginning of segment (OS-specific)
  Elf32_Word p_filesz; // Num. of bytes in file image of segment (may be zero)
  Elf32_Word p_memsz;  // Num. of bytes in mem image of segment (may be zero)
  Elf32_Word p_flags;  // Segment flags
  Elf32_Word p_align;  // Segment alignment constraint
};

因此得到dynamic段对应的地址:

1 for (i = 0; i < phNumber; i++)
2 {
3         if (phdr_table[i].p_type == PT_DYNAMIC)
4         {
5             dynamicAddr = phdr_table[i].p_vaddr + base_addr;
6             dynamicSize = phdr_table[i].p_memsz;
7             break;
8         }
9 }

4、开始遍历dynamic段结构,d_tag为6即为GOT表地址 

同样需要参考动态链接段每项的结构体:

typedef struct dynamic {
    Elf32_Sword d_tag;
    union {
    Elf32_Sword d_val;
    Elf32_Addr d_ptr;
    } d_un;
} Elf32_Dyn;

遍历方法为:

1 for(i=0; i < dynamicSize / 8; i++)
2 {
3     int val = dynamic_table[i].d_un.d_val;
4     if (dynamic_table[i].d_tag == 6)
5     {
6         symbolTableAddr = val + base_addr;
7         break;
8     }
9 }

5、遍历GOT表,查找GOT表中标记的目标函数地址,替换为新函数的地址。 

我们需要知道符号表的结构:

/* Symbol Table Entry */
typedef struct elf32_sym {
    Elf32_Word    st_name;        /* name - index into string table */
    Elf32_Addr    st_value;        /* symbol value */
    Elf32_Word    st_size;         /* symbol size */
    unsigned char    st_info;     /* type and binding */
    unsigned char    st_other;    /* 0 - no defined meaning */
    Elf32_Half    st_shndx;        /* section header index */
} Elf32_Sym;

然后替换成目标函数的st_value值,即偏移地址

 1 while(1)
 2 {
 3     //LOGD("[+] func Addr : %x", symTab[i].st_value);
 4     if(symTab[i].st_value == oldFunc)
 5     {
 6         //st_value 保存的是偏移地址
 7         symTab[i].st_value = newFunc;
 8         LOGD("[+] New Give func Addr : %x", symTab[i].st_value);
 9         break;
10     }
11     i++;
12 }

注意点: 

1、我们知道代码段一般都只会设置为可读可执行的,因此需要使用mprotect改变内存页为可读可写可执行;
2、如果执行完这些操作后hook并没有生效,可能是由于缓存的原因,需要使用cacheflush函数对该内存进行操作。
3、获取目标函数的偏移地址,可以通过dlsym得到绝对地址,再减去基址。

我们以hook libvivosgmain.so中的check_signatures函数为例,完整代码如下:

  1 #include <unistd.h>
  2 #include <stdio.h>
  3 #include <stdlib.h>
  4 #include <android/log.h>
  5 #include <EGL/egl.h>
  6 #include <GLES/gl.h>
  7 #include <elf.h>
  8 #include <fcntl.h>
  9 #include <dlfcn.h>
 10 #include <sys/mman.h>
 11 
 12 #define LOG_TAG "INJECT"
 13 #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##args)
 14 
 15 
 16 int (*old_check_signatures)();
 17 int new_check_signatures(){
 18     LOGD("[+] New call check_signatures.\n");
 19     if(old_check_signatures == -1){
 20         LOGD("error.\n");
 21     }
 22     return old_check_signatures();
 23 }
 24 
 25 void* get_module_base(pid_t pid, const char* module_name){
 26     FILE* fp;
 27     long addr = 0;
 28     char* pch;
 29     char filename[32];
 30     char line[1024];
 31     
 32     // 格式化字符串得到 "/proc/pid/maps"
 33     if(pid < 0){
 34         snprintf(filename, sizeof(filename), "/proc/self/maps");
 35     }else{
 36         snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
 37     }
 38 
 39     // 打开文件/proc/pid/maps,获取指定pid进程加载的内存模块信息
 40     fp = fopen(filename, "r");
 41     if(fp != NULL){
 42         // 每次一行,读取文件 /proc/pid/maps中内容
 43         while(fgets(line, sizeof(line), fp)){
 44             // 查找指定的so模块
 45             if(strstr(line, module_name)){
 46                 // 分割字符串
 47                 pch = strtok(line, "-");
 48                 // 字符串转长整形
 49                 addr = strtoul(pch, NULL, 16);
 50 
 51                 // 特殊内存地址的处理
 52                 if(addr == 0x8000){
 53                     addr = 0;
 54                 }
 55                 break;
 56             }
 57         }
 58     }
 59     fclose(fp);
 60     return (void*)addr;
 61 }
 62 
 63 #define LIB_PATH "/data/app-lib/com.bbk.appstore-2/libvivosgmain.so"
 64 int hook_check_signatures(){
 65 
 66     // 获取目标pid进程中"/data/app-lib/com.bbk.appstore-2/libvivosgmain.so"模块的加载地址
 67     void* base_addr = get_module_base(getpid(), LIB_PATH);
 68     LOGD("[+] libvivosgmain.so address = %p \n", base_addr);
 69 
 70     //计算program header table实际地址
 71     Elf32_Ehdr *header = (Elf32_Ehdr*)(base_addr);
 72     if (memcmp(header->e_ident, "\177ELF", 4) != 0) {
 73         return 0;
 74     }
 75     
 76     void* handle = dlopen("/data/app-lib/com.bbk.appstore-2/libvivosgmain.so", RTLD_LAZY);
 77     //获取原函数地址
 78     void* funcaddr = dlsym(handle, "check_signatures");
 79     LOGD("[+] libvivosgmain.so check_signatures address = %p \n", (int)funcaddr);
 80 
 81     int phOffset = header->e_phoff;
 82     int phNumber = header->e_phnum;
 83     int phPhyAddr = phOffset + base_addr;
 84     LOGD("[+] phOffset  : %x", phOffset);
 85     LOGD("[+] phNumber  : %x", phNumber);
 86     LOGD("[+] phPhyAddr : %x", phPhyAddr);
 87     int i = 0;
 88 
 89     Elf32_Phdr* phdr_table = (Elf32_Phdr*)(base_addr + phOffset);
 90     if (phdr_table == 0)
 91     {
 92         LOGD("[+] phdr_table address : 0");
 93         return 0;
 94     }
 95 
 96     /*
 97     // Program header for ELF32.
 98     struct Elf32_Phdr {
 99       Elf32_Word p_type;   // Type of segment
100       Elf32_Off  p_offset; // File offset where segment is located, in bytes
101       Elf32_Addr p_vaddr;  // Virtual address of beginning of segment
102       Elf32_Addr p_paddr;  // Physical address of beginning of segment (OS-specific)
103       Elf32_Word p_filesz; // Num. of bytes in file image of segment (may be zero)
104       Elf32_Word p_memsz;  // Num. of bytes in mem image of segment (may be zero)
105       Elf32_Word p_flags;  // Segment flags
106       Elf32_Word p_align;  // Segment alignment constraint
107     };
108     */
109     //遍历program header table,ptype等于2即为dynamic,获取到p_offset
110     unsigned long dynamicAddr = 0;
111     unsigned int dynamicSize = 0;
112 
113     for (i = 0; i < phNumber; i++)
114     {
115         if (phdr_table[i].p_type == PT_DYNAMIC)
116         {
117             dynamicAddr = phdr_table[i].p_vaddr + base_addr;
118             dynamicSize = phdr_table[i].p_memsz;
119             break;
120         }
121     }
122     LOGD("[+] Dynamic Addr : %x", dynamicAddr);
123     LOGD("[+] Dynamic Size : %x", dynamicSize);
124 
125     /*
126     typedef struct dynamic {
127         Elf32_Sword d_tag;
128         union {
129         Elf32_Sword d_val;
130         Elf32_Addr d_ptr;
131         } d_un;
132     } Elf32_Dyn;
133     */
134     //开始遍历dynamic段结构,d_tag为6即为GOT表地址
135     int symbolTableAddr = 0;
136     Elf32_Dyn* dynamic_table = (Elf32_Dyn*)(dynamicAddr);
137 
138     for(i=0; i < dynamicSize / 8; i++)
139     {
140         int val = dynamic_table[i].d_un.d_val;
141         if (dynamic_table[i].d_tag == 6)
142         {
143             symbolTableAddr = val + base_addr;
144             break;
145         }
146     }
147     LOGD("Symbol Table Addr : %x", symbolTableAddr);
148 
149     /*
150     typedef struct elf32_sym {
151         Elf32_Word st_name;
152         Elf32_Addr st_value;
153         Elf32_Word st_size;
154         unsigned char st_info;
155         unsigned char st_other;
156         Elf32_Half st_shndx;
157     } Elf32_Sym;
158     */
159     //遍历GOT表,查找GOT表中标记的check_signatures函数地址,替换为new_check_signatures的地址
160     int giveValuePtr = 0;
161     int fakeValuePtr = 0;
162     int newFunc = (int)new_check_signatures - (int)base_addr;
163     int oldFunc = (int)funcaddr - (int)base_addr;
164     i = 0;
165     LOGD("[+] newFunc Addr : %x", newFunc);
166     LOGD("[+] oldFunc Addr : %x", oldFunc);
167 
168     // 获取当前内存分页的大小
169     uint32_t page_size = getpagesize();
170     // 获取内存分页的起始地址(需要内存对齐)
171     uint32_t mem_page_start = (uint32_t)(((Elf32_Addr)symbolTableAddr)) & (~(page_size - 1));
172     LOGD("[+] mem_page_start = %lx, page size = %lx\n", mem_page_start, page_size);
173     mprotect((uint32_t)mem_page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);
174     Elf32_Sym* symTab = (Elf32_Sym*)(symbolTableAddr);
175     while(1)
176     {
177         //LOGD("[+] func Addr : %x", symTab[i].st_value);
178         if(symTab[i].st_value == oldFunc)
179         {
180             //st_value 保存的是偏移地址
181             symTab[i].st_value = newFunc;
182             LOGD("[+] New Give func Addr : %x", symTab[i].st_value);
183             break;
184         }
185         i++;
186     }
187     mprotect((uint32_t)mem_page_start, page_size, PROT_READ | PROT_EXEC);
188 
189     return 0;
190 }
191 
192 int hook_entry(char* a){
193     LOGD("[+] Start hooking.\n");
194     hook_check_signatures();
195     return 0;
196 }

我们还是通过执行“Android so注入( inject)和Hook技术学习(一)”文中的inject程序将本程序注入到目标进程进行hook,运行结果如下:

参考资料:

https://blog.csdn.net/u011247544/article/details/78564564

我们今天的关于android-@Inject设置为不注入属性android inject注入的分享就到这里,谢谢您的阅读,如果想了解更多关于$inject.instantiate VS $inject.get VS $injector.invoke in angularjs、Android APT 实现控件注入框架SqInject的示例、android InputManager injectInputEvent、Android so注入(inject)和Hook技术学习(三)——Got表hook之导出表hook的相关信息,可以在本站进行搜索。

本文标签: