首页
社区
课程
招聘
[原创]记一次中联X科的试岗实战项目
发表于: 2023-4-4 15:09 29342

[原创]记一次中联X科的试岗实战项目

2023-4-4 15:09
29342

因为笔者在此之前完全没有安卓逆向的工作经验,所以对方在面试结束后提出远程试岗七天,从而观察笔者的工作能力,结果因为笔者设备的问题无法达到对方的要求,只能说这大概就是有缘无份吧,这里做一份零基础的总结,会一步一步记录自己踩得每一个坑以及心路历程,希望能给后来的新人()一些指引,求加精。

开发一个 xposed 插件,可以在 whatsApp 中导入通讯录功能,输入是手机号,输出是这个手机号对应的id和个人信息,对方还跟贴心的给出了项目预览图,应该是对方近期接到的项目,也可以看出对方没有白嫖我的意思。

首先遇到的难题就是这是个国外的 APP,需要**上网,笔者电脑端目前使用的是付费代理配合 Clash,这里就说一下在电脑上开启代理后可以科学上网之后的设置,重点在于要开启允许局域网的选项和记录端口(记住7890这个端口之后有用)

打开cmd命令行输入ipconfig命令查看当前电脑的ip地址,笔者电脑当前的ip为 192.168.43.208

在工作机 nexus 5 上连接wifi,要和电脑连接同一个wifi(确保ip的 C 段是一致的),连接成功后修改wifi的配置选项(长按或者点右侧的小箭头),将代理改为手动,主机名设置为电脑的 ip 地址,端口设置为 clash 的端口 7890,保存成功后这部手机就也可以走电脑的clash代理科学上网。

点开APP随便浏览了一下功能,根据对方给出的预览图,可以知道首先是需要定位这个界面的 onCreat 界面,首先考虑的就是直接搜字符串,比如“邀请使用”这四个字,但是拖入 jadx 一番搜索后什么也没有。

这时我就想到,会不是因为是国外的app,默认是英文所以没搜到,于是我把软件调整为英文,观察到英文界面存在Contants Help 等字样,并逐一进行了搜索,但依然没有结果,这里推测可能是对字符串进行了加密处理

字符串走不通就换条路,既然是定位界面,那么显然通过 adb 命令查看最上层的界面是个好办法,这里得有点耐心,多翻一翻找到 whatsapp 相关的地方,可以看到当前界面为 ACTIVITY com.whatsapp/.contact.picker.ContactPicker

在 jadx 中找到 ContactPicker 的 onCreat 方法,接下来只要直接 HOOK onCreat 方法就成功一半了

xposed的开发环境配置其实我在另一篇笔记里写过,这里为了大家方便()就粘贴过来一份

环境配置较为繁琐,分为以下步骤

复制 XposedBridgeApi-82.jar 到工程中供使用

切换至 Project 模式,在app目录下新建文件夹lib,将 XposedBridgeApi-82.jar 复制到 app/lib 文件夹下

配置依赖

新建 Empty Activity 并在 AndroidManifest.xml 中添加代码

新建入口类 Main.java 并实现 IXposedHookLoadPackage 接口

复制入口类名

右键入口类 Main — Copy Path/Reference — Copy Reference

配置入口类名文件

app/src/main 文件夹下新建文件夹 assets,app/src/main/assets 文件夹下新建文件 xposed_init,将复制的入口类名粘贴在文件中即可

这里就不讲过多的理论了,jadx中右键想要hook的方法可以直接生成xposed的代码片段,这样我们就有了现成的框架

Main.java

本以为简单的hook却成了噩梦的开始,因!为!有!反!调!试!

最开始笔者是在nexus5中直接安装的xposed框架,应该是软件检测了这个框架,在jadx中可以看到是有一个Native层AbortHook方法的

正在笔者一筹莫展的时候,对方询问了一下我的进度,好嘛,打了瞌睡就有枕头,对方直接给了反调对抗思路,那就是用面具刷edxposed

我觉着在MagiskRoot前最好先刷一下机,因为这样才能保证你提取的boot文件和手机的系统是对应的,其实刷机还是蛮简单的,谷歌的手机双击bat就可以,小米手机官网有现成的刷机工具,这里就说两个坑,一个是fastboot模式中遇到 wait for devices 的问题,这其实是你的电脑还缺少一个驱动,根据我的经验,下载驱动精灵,它会提示你再安装一个驱动就可以了,另一个坑就是小米的刷机工具右下角默认是刷机后lock,这tm就简直是坑爹,记得改成双清,不然又tm把bl给锁上了(lock再刷机会有0s问题,需要重新解锁)。

在官网下载手机的刷机包,反复解压,直到找到其中的boot.img文件,把这个文件拷贝到手机中

在手机中安装 magisk.apk,依次点击,安装 — 选项 — 下一步 — 方式 — 选择修补一个文件 — 选择刚刚存放在手机中的 boot.img 文件 — 开始,等待执行结束你会发现在 boot.img 所在的目录中多了一个文件(有时候这个文件在电脑中看不见,在手机中重命名后就能看见了,不知道为啥),将这个文件拷贝到刷机包 boot.img 所在的目录,将刷机包原本的boot.img 重命名为 boot.img.bak ,将magisk 生成的这个文件重命名为 boot.img,此时刷机包中的 boot.img 就被 magisk 生成的 boot.img 替换了

有两种方式刷入修改后的 boot.img ,我喜欢偷懒直接刷机,毕竟点击鼠标更简单哈

笔者最开始使用的设备是nexus5,笔者先后经历了

这个安装过程大概历时三天,此时我的心态已然崩溃(因为试岗七天已经过了4天,买设备也来不及),直接开始躺平,这种状态一直持续到试岗失败,退出群聊

事情的转机来自于我老妈说她的小米6X电池不太行了,此时的我转念一想,换个手机我手里不就有个安卓9的手机了么,就这样,小米6X就变成了我的Android逆向工程机。

小米6X的edxposed安装依然遇到版本问题,这里我总结一下使用的版本:

Magisk-v23.0.apk

riru-v25.4.4-release.zip

EdXposed-v0.5.2.2_4683-master-release.zip

EdXposedManager-4.6.2-46200-org.meowcat.edxposed.manager-release.apk

安装xposed插件后,可以看到此时已经成功Hook(nexus5坑我不浅!!!)

此时我们先考虑在改界面添加一个TextView,那么问题就变成了获取Context的问题,根据之前学习的经验,可以通过 findAndHookConstructor来解决,下面上代码

重启手机后也如预期般的,显示了 hook text 字样

接下来我们只要遍历通讯录后把内容设置到 textView 上就可以了,这部分的内容在之前的 Android 安全笔记中也有提过,读写系统应用通讯录的ContentProvider,其重点在于以下几点:

读写系统应用通讯录的ContentProvider需要权限,分别为

android.permission.READ_CONTACTS 和 android.permission.READ_CONTACTS

数据库中直接看到的 mimetype_id 项并不存在,该项为多表查询,真实字段为 mimetype,可以通过在代码中遍历列名观察到

mimetype 是 String类型,而不是在数据库中看到的 int 类型

添加联系人时,应先在 raw_contacts 中添加一个空项,然后再在 data 中添加各种数据

注意:虽然添加了读写通讯录的权限,但依然要在手机中手动配置应用读写通讯录的权限,这里我踩过坑!!!

如果不会的话请移步我之前的笔记中 ContentProvider 部分,这里我们就不啰嗦了,直接上完整代码

重启后可以看到通讯录的详细信息已经出现在了 whatsapp 中,主体框架已经搭建完毕,剩下就是一些排版和琐碎的工作,这里就不继续演示了(毕竟已经退出群聊了,而且我发现我好像 hook 错界面了,尴尬ing…)

这次的试岗可以说收获颇丰,学习()并巩固了非常多的知识点,这都是之前逆向 creakme 不曾遇到的问题,最重要的是 whatsapp 也算是知名度较高的 app 了,今后的面试官问起来也算是有逆向分析过大型 app 的经验,而且我在这里也给新人们说一个事情,那就是面试官非常喜欢在看雪发表过优秀文章的人,就比如说我之前的帖子被加为优秀后被我写在了简历里,之后面试的每一个面试官都对这个事情非常的感兴趣,好吧,我承认是我的简历平平无奇没有别的看点,但在这里也还是希望和我一样的新人在看雪多发文章一起交流,一起进步。

项目名称 WhatsApp插件开发
作者 刘XX
时间 2023年3月27日
机器 Nexus5、小米6X
 
 
Microsoft Windows [Version 10.0.19044.1288]
(c) 2019 Microsoft Corporation. All rights reserved.
 
C:\Users\Administrator>ipconfig
 
Windows IP Configuration
 
 
Ethernet adapter 以太网:
 
   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :
 
Wireless LAN adapter 本地连接* 1:
 
   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :
 
Wireless LAN adapter 本地连接* 2:
 
   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :
 
Ethernet adapter VMware Network Adapter VMnet1:
 
   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::6842:f609:4368:dd6f%8
   IPv4 Address. . . . . . . . . . . : 192.168.241.1
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . :
 
Ethernet adapter VMware Network Adapter VMnet8:
 
   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::fc33:c1cf:5ea9:f741%16
   IPv4 Address. . . . . . . . . . . : 192.168.139.1
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . :
 
Wireless LAN adapter WLAN:
 
   Connection-specific DNS Suffix  . : lan
   IPv6 Address. . . . . . . . . . . : 240e:36c:d9c:d300:4c18:2120:55bd:c26f
   Temporary IPv6 Address. . . . . . : 240e:36c:d9c:d300:9b4:f4ee:7eb4:ce25
   Link-local IPv6 Address . . . . . : fe80::4c18:2120:55bd:c26f%18
   IPv4 Address. . . . . . . . . . . : 192.168.43.208
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : fe80::1%18
                                       192.168.43.1
 
Ethernet adapter 蓝牙网络连接 2:
 
   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :
 
C:\Users\Administrator>
Microsoft Windows [Version 10.0.19044.1288]
(c) 2019 Microsoft Corporation. All rights reserved.
 
C:\Users\Administrator>ipconfig
 
Windows IP Configuration
 
 
Ethernet adapter 以太网:
 
   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :
 
Wireless LAN adapter 本地连接* 1:
 
   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :
 
Wireless LAN adapter 本地连接* 2:
 
   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :
 
Ethernet adapter VMware Network Adapter VMnet1:
 
   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::6842:f609:4368:dd6f%8
   IPv4 Address. . . . . . . . . . . : 192.168.241.1
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . :
 
Ethernet adapter VMware Network Adapter VMnet8:
 
   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::fc33:c1cf:5ea9:f741%16
   IPv4 Address. . . . . . . . . . . : 192.168.139.1
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . :
 
Wireless LAN adapter WLAN:
 
   Connection-specific DNS Suffix  . : lan
   IPv6 Address. . . . . . . . . . . : 240e:36c:d9c:d300:4c18:2120:55bd:c26f
   Temporary IPv6 Address. . . . . . : 240e:36c:d9c:d300:9b4:f4ee:7eb4:ce25
   Link-local IPv6 Address . . . . . : fe80::4c18:2120:55bd:c26f%18
   IPv4 Address. . . . . . . . . . . : 192.168.43.208
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : fe80::1%18
                                       192.168.43.1
 
Ethernet adapter 蓝牙网络连接 2:
 
   Media State . . . . . . . . . . . : Media disconnected
   Connection-specific DNS Suffix  . :
 
C:\Users\Administrator>
 
 
 
 
 
C:\Users\Administrator>adb shell dumpsys activity top
........
TASK com.whatsapp id=167 userId=0
  ACTIVITY com.whatsapp/.contact.picker.ContactPicker 9264d37 pid=4208
    Local Activity 7762ff6 State:
      mResumed=true mStopped=false mFinished=false
      mChangingConfigurations=false
      mCurrentConfig={1.0 ?mcc?mnc [zh_CN] ldltr sw392dp w392dp h714dp 440dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2030) mAppBounds=Rect(0, 0 - 1080, 2030) mWindowingMode=fullscreen mActivityType=standard} s.8 themeChanged=0 themeChangedFlags=0}
      mLoadersStarted=true
      Active Fragments in 59ae076:
        #0: 05m{499b677 #0 androidx.lifecycle.LifecycleDispatcher.report_fragment_tag}
          mFragmentId=#0 mContainerId=#0 mTag=androidx.lifecycle.LifecycleDispatcher.report_fragment_tag
          mState=5 mIndex=0 mWho=android:fragment:0 mBackStackNesting=0
          mAdded=true mRemoving=false mFromLayout=false mInLayout=false
          mHidden=false mDetached=false mMenuVisible=true mHasMenu=false
          mRetainInstance=false mRetaining=false mUserVisibleHint=true
          mFragmentManager=FragmentManager{59ae076 in HostCallbacks{21522e4}}
          mHost=android.app.Activity$HostCallbacks@21522e4
          Child FragmentManager{3f9834d in 05m{499b677}}:
            FragmentManager misc state:
              mHost=android.app.Activity$HostCallbacks@21522e4
              mContainer=android.app.Fragment$1@1cc8e02
              mParent=05m{499b677 #0 androidx.lifecycle.LifecycleDispatcher.report_fragment_tag}
              mCurState=5 mStateSaved=false mDestroyed=false
C:\Users\Administrator>adb shell dumpsys activity top
........
TASK com.whatsapp id=167 userId=0
  ACTIVITY com.whatsapp/.contact.picker.ContactPicker 9264d37 pid=4208
    Local Activity 7762ff6 State:
      mResumed=true mStopped=false mFinished=false
      mChangingConfigurations=false
      mCurrentConfig={1.0 ?mcc?mnc [zh_CN] ldltr sw392dp w392dp h714dp 440dpi nrml long port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1080, 2030) mAppBounds=Rect(0, 0 - 1080, 2030) mWindowingMode=fullscreen mActivityType=standard} s.8 themeChanged=0 themeChangedFlags=0}
      mLoadersStarted=true
      Active Fragments in 59ae076:
        #0: 05m{499b677 #0 androidx.lifecycle.LifecycleDispatcher.report_fragment_tag}
          mFragmentId=#0 mContainerId=#0 mTag=androidx.lifecycle.LifecycleDispatcher.report_fragment_tag
          mState=5 mIndex=0 mWho=android:fragment:0 mBackStackNesting=0
          mAdded=true mRemoving=false mFromLayout=false mInLayout=false
          mHidden=false mDetached=false mMenuVisible=true mHasMenu=false
          mRetainInstance=false mRetaining=false mUserVisibleHint=true
          mFragmentManager=FragmentManager{59ae076 in HostCallbacks{21522e4}}
          mHost=android.app.Activity$HostCallbacks@21522e4
          Child FragmentManager{3f9834d in 05m{499b677}}:
            FragmentManager misc state:
              mHost=android.app.Activity$HostCallbacks@21522e4
              mContainer=android.app.Fragment$1@1cc8e02
              mParent=05m{499b677 #0 androidx.lifecycle.LifecycleDispatcher.report_fragment_tag}
              mCurState=5 mStateSaved=false mDestroyed=false
 
<meta-data android:name = "xposedmodule" android:value="true"/>
<meta-data android:name = "xposeddescription" android:value="Xposed模块示例"/>
<meta-data android:name = "xposedminversion" android:value="54"/>
<meta-data android:name = "xposedmodule" android:value="true"/>
<meta-data android:name = "xposeddescription" android:value="Xposed模块示例"/>
<meta-data android:name = "xposedminversion" android:value="54"/>
package com.example.xposeddemo;
 
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
 
public class Main implements IXposedHookLoadPackage {
 
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
 
    }
}
package com.example.xposeddemo;
 
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
 
public class Main implements IXposedHookLoadPackage {
 
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
 
    }
}
 
XposedHelpers.findAndHookMethod("com.whatsapp.contact.picker.ContactPicker", classLoader, "onCreate", android.os.Bundle.class, new XC_MethodHook() {
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
        super.beforeHookedMethod(param);
    }
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        super.afterHookedMethod(param);
        Log.d("lxz","hook start");
    }
});
XposedHelpers.findAndHookMethod("com.whatsapp.contact.picker.ContactPicker", classLoader, "onCreate", android.os.Bundle.class, new XC_MethodHook() {
    @Override
    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
        super.beforeHookedMethod(param);
    }
    @Override
    protected void afterHookedMethod(MethodHookParam param) throws Throwable {
        super.afterHookedMethod(param);
        Log.d("lxz","hook start");
    }
});
 
 
 
 
 
 
 
package com.example.xposeddemo;
 
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.content.ContentResolver;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
 
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
 
public class Main implements IXposedHookLoadPackage {
    private String packageName = "com.whatsapp";
    private String className = packageName + ".contact.picker.ContactPicker";
    Context context;
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        hookMainAcivityInit(loadPackageParam);
        XposedHelpers.findAndHookMethod(className,
                loadPackageParam.classLoader, "onCreate", android.os.Bundle.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                Log.d("lxz", "hook start");
                //获取界面
                final Activity mActivity = (Activity) param.thisObject;
                //创建一个 TextView
                TextView textView = new TextView(context);
                // 创建布局,设置参数
                FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                // 设置控件到底端的距离
                params.bottomMargin = 100;
                // 设置控件的位置
                params.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
                // 设置控件文本
                textView.setText("hook text");
                // 添加 TextView 到 Activity 中
                mActivity.addContentView(textView,params);
            }
        });
    }
 
    private void hookMainAcivityInit(XC_LoadPackage.LoadPackageParam loadPackageParam)
    {
        String packageName = loadPackageParam.packageName;
        if(!packageName.equals(packageName))
            return;
 
        Class hookClass = XposedHelpers.findClass(
                className,loadPackageParam.classLoader);
 
        XposedHelpers.findAndHookConstructor(
                hookClass,
                new XC_MethodHook() {
                    @Override
                    protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                        super.beforeHookedMethod(param);
                        context = (Context) param.thisObject;
                    }
                }
        );
    }
}
package com.example.xposeddemo;
 
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.content.ContentResolver;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
 
import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;
 
public class Main implements IXposedHookLoadPackage {
    private String packageName = "com.whatsapp";
    private String className = packageName + ".contact.picker.ContactPicker";
    Context context;
    @Override
    public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
        hookMainAcivityInit(loadPackageParam);
        XposedHelpers.findAndHookMethod(className,
                loadPackageParam.classLoader, "onCreate", android.os.Bundle.class, new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) throws Throwable {
                super.afterHookedMethod(param);
                Log.d("lxz", "hook start");
                //获取界面
                final Activity mActivity = (Activity) param.thisObject;
                //创建一个 TextView
                TextView textView = new TextView(context);

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2023-4-7 15:04 被kanxue编辑 ,原因:
收藏
免费 10
支持
分享
最新回复 (7)
雪    币: 2313
活跃值: (1950)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2

最后于 2023-4-8 10:01 被黑龙lilad编辑 ,原因:
2023-4-6 04:39
0
雪    币: 861
活跃值: (69)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
楼主能留联系方式吗,想和你沟通交流一下。
2023-4-10 18:07
0
雪    币: 466
活跃值: (2669)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
nexus系列在dalvik时代用的,现在都用pixel 系列
2023-4-10 19:18
1
雪    币: 3535
活跃值: (31011)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
不强求自己
2023-4-11 09:08
1
雪    币: 14633
活跃值: (17729)
能力值: ( LV12,RANK:290 )
在线值:
发帖
回帖
粉丝
6
感谢分享
2023-4-11 09:31
0
雪    币: 2
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
7
2023-4-12 09:39
0
雪    币: 2161
活跃值: (4172)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
8
感谢分享
2023-4-14 07:58
0
游客
登录 | 注册 方可回帖
返回
//