题型:安卓-Hook-Native分析
DAS46-BabyAndroid.zip
题目不支持模拟器运行,只能真机
附件里面除了APK,还有一段抓包后的响应体
出题人说是加密后的数据
所以我们的目标就是解密这段数据了
看看数据形式
有两个==
我们可以推测里面可能至少会用到base64编码
嗯,基本不可能只考base64
但具体是什么加密,我们目前不得而知
故,用上 算法助手
算法助手作用域勾选
+算法助手开关
同时勾选算法分析4个开关
打开目标软件
让我们设置图形解锁
无所谓,这个不是关键点
进入软件后就会发现软件功能就是做笔记的
我们尝试下做个笔记
然后打开算法助手
非常nice
分析出了加密的算法AES/ECB/PKCS5Padding
而下面的SHA-256,不用管,没有人拿这个当加密算法的,解密不出来的
我们点进去看看
加密密钥都已经被Hook出来了
DQ0NDQ0NDQ0NDQ0NDQ0NDQ==
而加密的内容从1变成49
这中间应该还有一些操作
不可能就这么梭出来的
我们拿密钥和密文去在线网站上解密
解密出来的明显不是最后的flag
我们可以验证下
用49.000000
和密钥进行加密
看看结果是不是FXb5k9BUw5T1EkGuNStrRw==
嗯,结果确实是
那至少我们解出来了一层加密
接下来我们就要开始看dex了
不过我们不直接看
我们先看看算法助手里面的调用堆栈
看看是哪个函数调用了刚刚的加密
还是刚刚的图片
我们看到其中有一行是
那我们先看看加密函数吧
MT查看Dex
我们发现了上面这个
但这明显和刚刚不一样啊,一个0一个o
又找不到带o的
这里面肯定是有蹊跷的
我们先点进去看看
加密是这个加密类型
但明显key不对劲
这里显示的key是DASCTF
那这时候
我们就需要考虑是不是这东西从其他地方加载了dex(例如PYCC2024的challengemobile
)
其实
我们查看APK的时候可以发现
在assets目录下有个sex.jpg
但它又不是图片
大概这个就存储了dex数据
我们用算法助手看看是不是这样
重启,保存一个笔记
然后发现确实是读入了sex.jpg
我们点进去看调用堆栈
发现这两个比较特殊
我们再用MT找到先相关的函数
看看如上代码
loadData
函数 getAssest 获取文件
然后调用rc4解密函数和一个key来解密
最终返回所解密的内容
那我们直接Hook loadData
函数
来看看
但这里我们要换成SimpleHookR 或者用Frida
因为最新版算法助手自动把数据转换成编码字节集了
我们用SimpleHookR记录这个函数的参返
我们把返回值复制下来
用python脚本把其转换化成dex文件
用MT打开试试
发现了我们之前在找的Encrypto
现在我们去看看另一个红框里的东西
我们可以发现
就是doInBackground
函数调用了loadData
并且还使用了classLoader
去加载我们刚刚得到得到的类
然后我们发现cipher
就是加密后的数据
它是由我们输入的文本经sendInit
处理,然后再被刚刚加载的类加密得来的
我们跳转去看看sendInit
原来就在刚刚的NoteActivity里面
这里 native
说明我们要去分析/lib里面的so文件了
当然,我们可以先Hook一下,验证一下
参数1
返回49.000000
没问题,就是他
我们用ida64打开so
我们直接找带sendInit
的
我刚看到这个伪代码的时候确实是崩溃的
因为我伪代码分析能力太弱了
压根看不懂
丢给chatgpt都没救
但看到都有2个解了
就一个一个点进去看
到encrypt(v18);
这个其实满显眼的函数时
直觉告诉我,就是这个了
丢给chatgpt
分析是什么离散啥啥的,简称DCT
又问有没有逆向的
chatgpt给出了IDCT的python脚本
运行脚本就会得到flag
TwMkYUkg4bYsY0hL99ggYWnVjWyXQrWAdNmToB0eBXbS6wBzL6ktorjNWI9VOroTU4HgIUYyzGLpcHzd1zNGT
+
bFZZI7IoxJwpcgXfdwW1LSmiNSP
+
PuSUsqAzNclF1nJ07b4tYyLWg0zTypbzWsLhOIM
+
6uci3RFZLREUCALafi01M8mS
+
KMNxX1Pyn8mSP
+
KKKjQ5S5fasHRSn
+
L9qBFws0mWavpfI0QEiMgarxv0iGhYU8cfgonWyL70RvoXET5VUDP1vfYWIBLzzzaAqLC0OiMtUK3TTATSU7yijdgXm18OKMcGIke
/
NZIM6Sr5fL3t6psDOOkw2C
/
5uYrJVPn
+
D6U9KTL64bgREppDqMOvhvbhtuf
/
S3ASW
/
+
rhtPMtoaD8FxDg0wWSLZA53fQfNA
=
=
TwMkYUkg4bYsY0hL99ggYWnVjWyXQrWAdNmToB0eBXbS6wBzL6ktorjNWI9VOroTU4HgIUYyzGLpcHzd1zNGT
+
bFZZI7IoxJwpcgXfdwW1LSmiNSP
+
PuSUsqAzNclF1nJ07b4tYyLWg0zTypbzWsLhOIM
+
6uci3RFZLREUCALafi01M8mS
+
KMNxX1Pyn8mSP
+
KKKjQ5S5fasHRSn
+
L9qBFws0mWavpfI0QEiMgarxv0iGhYU8cfgonWyL70RvoXET5VUDP1vfYWIBLzzzaAqLC0OiMtUK3TTATSU7yijdgXm18OKMcGIke
/
NZIM6Sr5fL3t6psDOOkw2C
/
5uYrJVPn
+
D6U9KTL64bgREppDqMOvhvbhtuf
/
S3ASW
/
+
rhtPMtoaD8FxDg0wWSLZA53fQfNA
=
=
at site.qifen.note.ui.Encrypto.encrypt(Encrypto.java:
37
)
at site.qifen.note.ui.Encrypto.encrypt(Encrypto.java:
37
)
package
site.qifen.note.ui;
import
android.util.Base64;
import
java.security.MessageDigest;
import
javax.crypto.Cipher;
import
javax.crypto.spec.SecretKeySpec;
public
class
Encrypt0 {
private
static
final
String KEY =
"DSACTF"
;
private
static
final
String TAG =
"Encrypto"
;
public
static
String encrypt(String data)
throws
Exception {
MessageDigest sha = MessageDigest.getInstance(
"SHA-1"
);
byte
[] keyBytes = sha.digest(KEY.getBytes(
"UTF-8"
));
byte
[] keyBytes16 =
new
byte
[
16
];
System.arraycopy(keyBytes,
0
, keyBytes16,
0
,
16
);
SecretKeySpec secretKeySpec =
new
SecretKeySpec(keyBytes16,
"AES"
);
Cipher cipher = Cipher.getInstance(
"AES/ECB/PKCS5Padding"
);
cipher.init(
1
, secretKeySpec);
byte
[] encryptedBytes = cipher.doFinal(data.getBytes(
"UTF-8"
));
return
Base64.encodeToString(encryptedBytes,
2
);
}
}
package
site.qifen.note.ui;
import
android.util.Base64;
import
java.security.MessageDigest;
import
javax.crypto.Cipher;
import
javax.crypto.spec.SecretKeySpec;
public
class
Encrypt0 {
private
static
final
String KEY =
"DSACTF"
;
private
static
final
String TAG =
"Encrypto"
;
public
static
String encrypt(String data)
throws
Exception {
MessageDigest sha = MessageDigest.getInstance(
"SHA-1"
);
byte
[] keyBytes = sha.digest(KEY.getBytes(
"UTF-8"
));
byte
[] keyBytes16 =
new
byte
[
16
];
System.arraycopy(keyBytes,
0
, keyBytes16,
0
,
16
);
SecretKeySpec secretKeySpec =
new
SecretKeySpec(keyBytes16,
"AES"
);
Cipher cipher = Cipher.getInstance(
"AES/ECB/PKCS5Padding"
);
cipher.init(
1
, secretKeySpec);
byte
[] encryptedBytes = cipher.doFinal(data.getBytes(
"UTF-8"
));
return
Base64.encodeToString(encryptedBytes,
2
);
}
}
public
byte
[] loadData(String str) {
try
{
InputStream open = getAssets().open(str);
byte
[] encryptedData =
new
byte
[open.available()];
open.read(encryptedData);
open.close();
byte
[] key =
"DASCTF"
.getBytes();
return
rc4Decrypt(key, encryptedData);
}
catch
(IOException e) {
Log.e(
"错误"
,
"加载数据时发生错误"
, e);
return
null
;
}
}
private
byte
[] rc4Decrypt(
byte
[] key,
byte
[] data) {
int
[] S =
new
int
[
256
];
for
(
int
i =
0
; i <
256
; i++) {
S[i] = i;
}
int
j =
0
;
for
(
int
i2 =
0
; i2 <
256
; i2++) {
j = ((S[i2] + j) + (key[i2 % key.length] &
255
)) %
256
;
int
temp = S[i2];
S[i2] = S[j];
S[j] = temp;
}
int
i3 = data.length;
byte
[] result =
new
byte
[i3];
int
i4 =
0
;
int
j2 =
0
;
for
(
int
k =
0
; k < data.length; k++) {
i4 = (i4 +
1
) %
256
;
j2 = (S[i4] + j2) %
256
;
int
temp2 = S[i4];
S[i4] = S[j2];
S[j2] = temp2;
int
t = (S[i4] + S[j2]) %
256
;
result[k] = (
byte
) (data[k] ^ S[t]);
}
return
result;
}
public
byte
[] loadData(String str) {
try
{
InputStream open = getAssets().open(str);
byte
[] encryptedData =
new
byte
[open.available()];
open.read(encryptedData);
open.close();
byte
[] key =
"DASCTF"
.getBytes();
return
rc4Decrypt(key, encryptedData);
}
catch
(IOException e) {
Log.e(
"错误"
,
"加载数据时发生错误"
, e);
return
null
;
}
}
private
byte
[] rc4Decrypt(
byte
[] key,
byte
[] data) {
int
[] S =
new
int
[
256
];
for
(
int
i =
0
; i <
256
; i++) {
S[i] = i;
}
int
j =
0
;
for
(
int
i2 =
0
; i2 <
256
; i2++) {
j = ((S[i2] + j) + (key[i2 % key.length] &
255
)) %
256
;
int
temp = S[i2];
S[i2] = S[j];
S[j] = temp;
}
int
i3 = data.length;
byte
[] result =
new
byte
[i3];
int
i4 =
0
;
int
j2 =
0
;
for
(
int
k =
0
; k < data.length; k++) {
i4 = (i4 +
1
) %
256
;
j2 = (S[i4] + j2) %
256
;
int
temp2 = S[i4];
S[i4] = S[j2];
S[j2] = temp2;
int
t = (S[i4] + S[j2]) %
256
;
result[k] = (
byte
) (data[k] ^ S[t]);
}
return
result;
}
def
to_unsigned_bytes(byte_list):
return
bytes([(b
+
256
)
%
256
for
b
in
byte_list])
your_byte_list
=
[数据]
converted_bytes
=
to_unsigned_bytes(your_byte_list)
with
open
(
'dump.dex'
,
'wb'
) as
file
:
file
.write(converted_bytes)
print
(
"Data written to dump.dex"
)
def
to_unsigned_bytes(byte_list):
return
bytes([(b
+
256
)
%
256
for
b
in
byte_list])
your_byte_list
=
[数据]
converted_bytes
=
to_unsigned_bytes(your_byte_list)
with
open
(
'dump.dex'
,
'wb'
) as
file
:
file
.write(converted_bytes)
print
(
"Data written to dump.dex"
)
package
site.qifen.note.ui;
import
android.util.Base64;
import
javax.crypto.Cipher;
import
javax.crypto.spec.SecretKeySpec;
public
class
Encrypto {
private
static
final
String KEY =
"DSACTF"
;
private
static
final
String TAG =
"Encrypto"
;
private
static
byte
[] customHash(String input) {
byte
[] keyBytes =
new
byte
[
16
];
int
[] temp =
new
int
[
16
];
for
(
int
i =
0
; i < input.length(); i++) {
int
charVal = input.charAt(i);
for
(
int
j =
0
; j <
16
; j++) {
temp[j] = ((temp[j] *
31
) + charVal) %
251
;
}
}
for
(
int
i2 =
0
; i2 <
16
; i2++) {
keyBytes[i2] = (
byte
) (temp[i2] %
256
);
}
return
keyBytes;
}
public
static
String encrypt(String data)
throws
Exception {
byte
[] keyBytes = customHash(KEY);
SecretKeySpec secretKeySpec =
new
SecretKeySpec(keyBytes,
"AES"
);
Cipher cipher = Cipher.getInstance(
"AES/ECB/PKCS5Padding"
);
cipher.init(
1
, secretKeySpec);
byte
[] encryptedBytes = cipher.doFinal(data.getBytes(
"UTF-8"
));
return
Base64.encodeToString(encryptedBytes,
2
);
}
}
package
site.qifen.note.ui;
import
android.util.Base64;
import
javax.crypto.Cipher;
import
javax.crypto.spec.SecretKeySpec;
public
class
Encrypto {
private
static
final
String KEY =
"DSACTF"
;
private
static
final
String TAG =
"Encrypto"
;
private
static
byte
[] customHash(String input) {
byte
[] keyBytes =
new
byte
[
16
];
int
[] temp =
new
int
[
16
];
for
(
int
i =
0
; i < input.length(); i++) {
int
charVal = input.charAt(i);
for
(
int
j =
0
; j <
16
; j++) {
temp[j] = ((temp[j] *
31
) + charVal) %
251
;
}
}
for
(
int
i2 =
0
; i2 <
16
; i2++) {
keyBytes[i2] = (
byte
) (temp[i2] %
256
);
}
return
keyBytes;
}
public
static
String encrypt(String data)
throws
Exception {
byte
[] keyBytes = customHash(KEY);
SecretKeySpec secretKeySpec =
new
SecretKeySpec(keyBytes,
"AES"
);
Cipher cipher = Cipher.getInstance(
"AES/ECB/PKCS5Padding"
);
cipher.init(
1
, secretKeySpec);
byte
[] encryptedBytes = cipher.doFinal(data.getBytes(
"UTF-8"
));
return
Base64.encodeToString(encryptedBytes,
2
);
}
}
package
site.qifen.note.ui;
import
android.os.AsyncTask;
import
android.os.Build;
import
android.util.Log;
import
dalvik.system.InMemoryDexClassLoader;
import
java.lang.reflect.Method;
import
java.nio.ByteBuffer;
import
java.text.SimpleDateFormat;
import
java.util.Date;
import
site.qifen.note.model.Note;
import
site.qifen.note.model.sendRequest;
import
site.qifen.note.util.NoteUtil;
class
NoteActivity$EncryptAndSendTask
extends
AsyncTask<String, Void, String> {
final
NoteActivity
this
$
0
;
private
NoteActivity$EncryptAndSendTask(NoteActivity noteActivity) {
this
.
this
$
0
= noteActivity;
}
@Override
public
String doInBackground(String... params) {
String contentText = params[
0
];
try
{
byte
[] dexData =
this
.
this
$
0
.loadData(
"Sex.jpg"
);
ByteBuffer dexBuffer = ByteBuffer.wrap(dexData);
InMemoryDexClassLoader classLoader =
null
;
if
(Build.VERSION.SDK_INT >=
26
) {
classLoader =
new
InMemoryDexClassLoader(dexBuffer,
this
.
this
$
0
.getClassLoader());
}
Class<?> checkerClass = classLoader.loadClass(
"site.qifen.note.ui.Encrypto"
);
Method checkMethod = checkerClass.getMethod(
"encrypt"
, String.
class
);
this
.
this
$
0
.contentText_back = contentText;
String cipher = (String) checkMethod.invoke(checkerClass.getDeclaredConstructor(
new
Class[
0
]).newInstance(
new
Object[
0
]),
this
.
this
$
0
.sendInit(contentText));
String response = sendRequest.sendPost(
"http://yuanshen.com/"
,
"data="
+ cipher);
Log.d(
"JNITest"
,
"Server Response: "
+ response);
return
cipher;
}
catch
(Exception e) {
e.printStackTrace();
return
null
;
}
}
@Override
public
void
onPostExecute(String cipher) {
if
(cipher !=
null
) {
String titleText =
this
.
this
$
0
.noteWriteTitleEdit.getText().toString();
String tagText =
this
.
this
$
0
.noteWriteTagEdit.getText().toString();
String date =
new
SimpleDateFormat(
"yyyy-MM-dd HH:mm"
).format(
new
Date());
if
(NoteActivity.access$
100
(
this
.
this
$
0
) ==
null
) {
NoteActivity.access$
200
(
this
.
this
$
0
).insertNote(
new
Note(tagText, titleText,
this
.
this
$
0
.contentText_back, date,
false
));
NoteUtil.toast(
"保存成功"
);
this
.
this
$
0
.finish();
return
;
}
NoteActivity.access$
100
(
this
.
this
$
0
).setTitle(titleText);
NoteActivity.access$
100
(
this
.
this
$
0
).setContent(
this
.
this
$
0
.contentText_back);
NoteActivity.access$
100
(
this
.
this
$
0
).setDate(date);
NoteActivity.access$
100
(
this
.
this
$
0
).setTag(
this
.
this
$
0
.contentText_back);
NoteActivity.access$
200
(
this
.
this
$
0
).updateNote(NoteActivity.access$
100
(
this
.
this
$
0
));
NoteUtil.toast(
"修改成功"
);
this
.
this
$
0
.finish();
return
;
}
NoteUtil.toast(
"加密失败"
);
}
}
package
site.qifen.note.ui;
import
android.os.AsyncTask;
import
android.os.Build;
import
android.util.Log;
import
dalvik.system.InMemoryDexClassLoader;
import
java.lang.reflect.Method;
import
java.nio.ByteBuffer;
import
java.text.SimpleDateFormat;
import
java.util.Date;
import
site.qifen.note.model.Note;
import
site.qifen.note.model.sendRequest;
import
site.qifen.note.util.NoteUtil;
class
NoteActivity$EncryptAndSendTask
extends
AsyncTask<String, Void, String> {
final
NoteActivity
this
$
0
;
private
NoteActivity$EncryptAndSendTask(NoteActivity noteActivity) {
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!