闲来无事刷CTF,今天逛论坛看到了一个CTF《无用的CE》,没有bin文件,只有一个bugku的连接;去首页看了下不少CTF的题目,故注册了账号,每个题下载都要币,幸好初始账号有30个,研究了下,有的题目下载需要3 - 5个币不等,提交flag还能赚1 - 2个币,所以专挑差价多的搞,先下了三个试试水。前两个难度不大,感觉这个有些技巧故分享之。
看后缀是apk,丢到夜神模拟器看看。
随便写个code,点击DECRYPT按钮会Toast提示 Something wrong!
直接把APK丢到Jadx中,根据反编译情况来看没有加壳。幸好以前做过几天的APP,这代码看着很亲切的感觉。
代码解读:
通过上面的代码可以看到2个关键函数getKey readMagicImage;
双击getKey,可以看到public native String getKey(String str);
,根据以前的开发经验应该是调用c/c++的so函数了。
双击readMagicImage ,跳到函数代码处,如下
代码解读:
关键函数decrypt
双击decrypt,来到public static native int decrypt(int i, char c);
,来自so。
通过google,得知android会使用 System.loadLibrary
加载so文件。
MainActivity的这个函数验证了这个。
通过jadx可以清楚的看到资源lib下有多个版本的libnative-lib.so文件
直接保存下来,然后挑个自己熟悉的来搞,先从x86的下手。
直接拖入IDA中开始分析,通过导出表很快找到了想要的函数
双击函数直接来到函数代码处,因为牵扯到JavaVM JNIEnv的类,反汇编代码很难看清逻辑,直接F5吧。
简单重定义了几个数据类型后,代码看起来舒服多了。
看了好久的getKey函数,有意些代码实在是搞不明白。又因为不会调试分析so文件,所以卡住了。
回头看了下decrypt倒是很简单。
把流程多看了几遍,突然灵机一动,解密的是png,至少前4字节是固定的。这让我突然想起了,破译希特勒电报的故事,电报的开头固定的格式“希特勒万岁”。
通过分析bg.png,网上下png和其他三个png发现,不仅仅是magic是一样的甚至前16字节完全一样,出乎意料呀,刚开始还以只会是前4字节是一样的。
这样知道亦或算法,处理后的数据和原始数据,很容易推算数key[16]。
伪代码如下
计算出xorKey后,就可以解密ENCRYPT_PNG.data了。
解密后的png,得到图片中的flag
一直认为是数据类型影响了IDA对getKey的正常还原。
找了几篇IDA反汇编的so的文章,学了不少技巧,如导入structs,导入JNI.h头文件,以及Type Lib 增加android相关的库,来丰富IDA的还原能力。
又尝试着看看其他几个反编译F5还原的代码都一样吗,接着对x64的so进行了反编译和F5
出乎意料呀getKey如此清爽(设置几个数据类型,对几个函数调用强制Force call )
既然得到了算法,最后得到keygen.c
编译运行
public
native
String getKey(String str);
public
native
String stringFromJNI();
public
void
onCreate(Bundle bundle) {
super
.onCreate(bundle);
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.sample_text);
textView.setText(stringFromJNI());
((Button) findViewById(R.id.decrypt_button)).setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View view) {
TextView textView2 = (TextView) MainActivity.
this
.findViewById(R.id.sample_text);
String obj = ((EditText) MainActivity.
this
.findViewById(R.id.Key_text)).getText().toString();
if
(obj.length() !=
16
) {
MainActivity.
this
.showMsgToast(
"Something wrong!"
);
return
;
}
String key = MainActivity.
this
.getKey(obj);
ImageView imageView = (ImageView) MainActivity.
this
.findViewById(R.id.img);
Bitmap readMagicImage = MagicImageUtils.readMagicImage(MainActivity.
this
,
"png/encrypt_png.dat"
, key);
if
(readMagicImage ==
null
) {
MainActivity.
this
.showMsgToast(
"Something wrong!"
);
return
;
}
textView2.setText(
"Congratulations!"
);
MainActivity.
this
.showMsgToast(
"Congratulations!"
);
imageView.setImageBitmap(readMagicImage);
}
});
ImageView imageView = (ImageView) findViewById(R.id.img);
Bitmap readImage = MagicImageUtils.readImage(
this
,
"png/bg.png"
);
if
(readImage !=
null
) {
textView.setText(
" "
);
imageView.setImageBitmap(readImage);
return
;
}
showMsgToast(
"Something wrong!"
);
}
public
native
String getKey(String str);
public
native
String stringFromJNI();
public
void
onCreate(Bundle bundle) {
super
.onCreate(bundle);
setContentView(R.layout.activity_main);
TextView textView = (TextView) findViewById(R.id.sample_text);
textView.setText(stringFromJNI());
((Button) findViewById(R.id.decrypt_button)).setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View view) {
TextView textView2 = (TextView) MainActivity.
this
.findViewById(R.id.sample_text);
String obj = ((EditText) MainActivity.
this
.findViewById(R.id.Key_text)).getText().toString();
if
(obj.length() !=
16
) {
MainActivity.
this
.showMsgToast(
"Something wrong!"
);
return
;
}
String key = MainActivity.
this
.getKey(obj);
ImageView imageView = (ImageView) MainActivity.
this
.findViewById(R.id.img);
Bitmap readMagicImage = MagicImageUtils.readMagicImage(MainActivity.
this
,
"png/encrypt_png.dat"
, key);
if
(readMagicImage ==
null
) {
MainActivity.
this
.showMsgToast(
"Something wrong!"
);
return
;
}
textView2.setText(
"Congratulations!"
);
MainActivity.
this
.showMsgToast(
"Congratulations!"
);
imageView.setImageBitmap(readMagicImage);
}
});
ImageView imageView = (ImageView) findViewById(R.id.img);
Bitmap readImage = MagicImageUtils.readImage(
this
,
"png/bg.png"
);
if
(readImage !=
null
) {
textView.setText(
" "
);
imageView.setImageBitmap(readImage);
return
;
}
showMsgToast(
"Something wrong!"
);
}
public
static
native
int
decrypt(
int
i,
char
c);
public
static
Bitmap readMagicImage(Context context, String str, String str2) {
Bitmap decodeByteArray;
ArrayList<Byte> arrayList =
new
ArrayList();
Bitmap bitmap =
null
;
try
{
InputStream open = context.getAssets().open(str);
int
i =
0
;
while
(
true
) {
int
read = open.read();
if
(read <= -
1
) {
break
;
}
arrayList.add(Byte.valueOf((
byte
) decrypt(read, str2.charAt(i %
16
))));
i++;
}
byte
[] bArr =
new
byte
[arrayList.size()];
int
i2 =
0
;
for
(Byte b : arrayList) {
bArr[i2] = b.byteValue();
i2++;
}
decodeByteArray = BitmapFactory.decodeByteArray(bArr,
0
, arrayList.size());
}
catch
(IOException e) {
e = e;
}
try
{
System.out.println(decodeByteArray);
return
decodeByteArray;
}
catch
(IOException e2) {
bitmap = decodeByteArray;
e = e2;
e.printStackTrace();
return
bitmap;
}
}
public
static
native
int
decrypt(
int
i,
char
c);
public
static
Bitmap readMagicImage(Context context, String str, String str2) {
Bitmap decodeByteArray;
ArrayList<Byte> arrayList =
new
ArrayList();
Bitmap bitmap =
null
;
try
{
InputStream open = context.getAssets().open(str);
int
i =
0
;
while
(
true
) {
int
read = open.read();
if
(read <= -
1
) {
break
;
}
arrayList.add(Byte.valueOf((
byte
) decrypt(read, str2.charAt(i %
16
))));
i++;
}
byte
[] bArr =
new
byte
[arrayList.size()];
int
i2 =
0
;
for
(Byte b : arrayList) {
bArr[i2] = b.byteValue();
i2++;
}
decodeByteArray = BitmapFactory.decodeByteArray(bArr,
0
, arrayList.size());
}
catch
(IOException e) {
e = e;
}
try
{
System.out.println(decodeByteArray);
return
decodeByteArray;
}
catch
(IOException e2) {
bitmap = decodeByteArray;
e = e2;
e.printStackTrace();
return
bitmap;
}
}
static
{
System.loadLibrary(
"native-lib"
);
}
static
{
System.loadLibrary(
"native-lib"
);
}
Java_re_sdnisc2018_sdnisc_1apk2_MagicImageUtils_decrypt
000069E0
Java_re_sdnisc2018_sdnisc_1apk2_MainActivity_getKey
000067E0
Java_re_sdnisc2018_sdnisc_1apk2_MagicImageUtils_decrypt
000069E0
Java_re_sdnisc2018_sdnisc_1apk2_MainActivity_getKey
000067E0
int
__cdecl Java_re_sdnisc2018_sdnisc_1apk2_MainActivity_getKey(
int
a1, JNIEnv *a2,
int
a3,
int
a4)
{
char
*v4;
char
*v5;
JNIEnv *v6;
const
jchar *v7;
unsigned
int
v8;
char
v9;
int
v10;
char
*v12;
int
v13;
_DWORD v14[2];
char
*v15;
v4 = (
char
*)
new
(0x20u);
strcpy
(v4,
"Welcome_to_sdnisc_2018_By.Zero"
);
v12 = v4;
v5 = (
char
*)
new
(0x20u);
v6 = a2;
v15 = v5;
v14[0] = 33;
v14[1] = 16;
strcpy
(v5,
" "
);
v13 = (*a2)->GetStringLength(a2, (jstring)a4);
v7 = (*v6)->GetStringChars(v6, (jstring)a4, 0);
if
( v13 > 0 )
{
v8 = 0;
v9 = 33;
do
{
if
( (v9 & 1) == 0 )
v5 = (
char
*)v14 + 1;
v5[v8] = LOBYTE(v7[v8]) ^ v12[v8 + -30 * (v8 / 0x1E)];
++v8;
v9 = v14[0];
v5 = v15;
}
while
( v13 != v8 );
if
( (v14[0] & 1) == 0 )
v5 = (
char
*)v14 + 1;
v6 = a2;
}
v10 = (
int
)(*v6)->NewStringUTF(v6, v5);
if
( (v14[0] & 1) != 0 )
operator
delete
(v15);
operator
delete
(v12);
return
v10;
}
int
__cdecl Java_re_sdnisc2018_sdnisc_1apk2_MagicImageUtils_decrypt(
int
a1,
int
a2,
int
a3,
int
a4, unsigned
__int16
a5)
{
return
(a4 - 1) ^ a5 ^ 0x61;
}
int
__cdecl Java_re_sdnisc2018_sdnisc_1apk2_MainActivity_getKey(
int
a1, JNIEnv *a2,
int
a3,
int
a4)
{
char
*v4;
char
*v5;
JNIEnv *v6;
const
jchar *v7;
unsigned
int
v8;
char
v9;
int
v10;
char
*v12;
int
v13;
_DWORD v14[2];
char
*v15;
v4 = (
char
*)
new
(0x20u);
strcpy
(v4,
"Welcome_to_sdnisc_2018_By.Zero"
);
v12 = v4;
v5 = (
char
*)
new
(0x20u);
v6 = a2;
v15 = v5;
v14[0] = 33;
v14[1] = 16;
strcpy
(v5,
" "
);
v13 = (*a2)->GetStringLength(a2, (jstring)a4);
v7 = (*v6)->GetStringChars(v6, (jstring)a4, 0);
if
( v13 > 0 )
{
v8 = 0;
v9 = 33;
do
{
if
( (v9 & 1) == 0 )
v5 = (
char
*)v14 + 1;
v5[v8] = LOBYTE(v7[v8]) ^ v12[v8 + -30 * (v8 / 0x1E)];
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)