此题比较简单,网上也有各种各样的wp。wp可以分为两类,一类分析算法,另一类是爆破。此题采用的是异或算法,所以分析算法求flag是最快的。而爆破的话,可以重写成等价的代码,如C++、python,或者直接采用主动调用的方式进行爆破,如Frida的主动调用。采用主动调用的好处是不用重写,而我采用Frida RPC进行主动调用的目的是想利用python丰富的库,方便爆破,缺点相对于直接用JS代码进行爆破是太慢了,慢了200多倍。
输入注册码,显示如下:
在代码中搜索"您的注册码已保存",相关代码如下:
然后跳转到saveSN
函数所在的类MyApp
,代码如下:
接着分析libmyjni.so
文件,在JNI_OnLoad
函数中注册了initSN
、saveSN
和work
函数,代码如下:
首先分析initSN
函数,其中setValue
函数的作用是设置com/gdufs/xman/MyApp
类的静态字段m
的值,
initSN
函数只做了一件事,即如果/sdcard/reg.dat
文件的内容是"EoPAoY62@ElRD",com/gdufs/xman/MyApp
类的静态字段m
的值则设置为1,否则设置为0
通过对com/gdufs/xman/MyApp
类的静态字段m
交叉引用发现,如果m
的值为1,则显示已注册,所以我们怀疑输入的注册码通过一系列的计算后得到的值会保存到/sdcard/reg.dat
文件中,如果得到的值为"EoPAoY62@ElRD",则输入的注册码即为flag
saveSN
也只干了一件事,把输入的注册码经过异或运算之后存到了/sdcard/reg.dat
文件中
我们可以试试,直接把/sdcard/reg.dat
文件的内容替换成"EoPAoY62@ElRD",会是什么效果,如下:
则我们的flag格式为xman{注册码}
通过上面的分析过程可知,我们可以通过Frida主动调用的方式爆破出flag,首先给出直接用JS代码进行爆破的脚本,再给出RPC爆破的脚本。
花了大概两分钟爆破出了flag
JavaScript代码
Python代码
采用RPC爆破的方式花了429分钟才爆破出了flag,花的时间是直接使用JS代码进行爆破的214倍
如果出现各种无法解决的问题,尝试换成跟我一样的设备和系统——pixel 3、android 9.0
错误是全局引用表溢出了,具体意思是Java对象的全局引用表溢出了,你在循环里new Java对象就可能会溢出,因为frida对Java对象的引用就是用的全局引用,不知道什么时候才释放,反正循环的时候没有释放
多次调用Java.choose
程序就会卡住,好像跟frida版本无关,不知道为什么,所以我直接new
一个对象进行主动调用
排列使用的字典是没有重复字符的,所以3个字符中不可能同时出现两个相同的字符,下面的代码就会出现这种问题
解决办法是把字典自加多次,代码如下
链接:https://pan.baidu.com/s/1NRPGU_j6CK7bgB363FN33w
提取码:09wb
public
class
RegActivity extends Activity {
private Button btn_reg;
private EditText edit_sn;
public void onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_reg);
this.btn_reg
=
(Button) findViewById(R.
id
.button1);
this.edit_sn
=
(EditText) findViewById(R.
id
.editText1);
this.btn_reg.setOnClickListener(new View.OnClickListener() {
/
*
class
com.gdufs.xman.RegActivity.AnonymousClass1
*
/
public void onClick(View v) {
String sn
=
RegActivity.this.edit_sn.getText().toString().trim();
if
(sn
=
=
null || sn.length()
=
=
0
) {
Toast.makeText(RegActivity.this,
"您的输入为空"
,
0
).show();
return
;
}
((MyApp) RegActivity.this.getApplication()).saveSN(sn);
new AlertDialog.Builder(RegActivity.this).setTitle(
"回复"
).setMessage(
"您的注册码已保存"
).setPositiveButton(
"好吧"
, new DialogInterface.OnClickListener() {
/
*
class
com.gdufs.xman.RegActivity.AnonymousClass1.AnonymousClass1
*
/
public void onClick(DialogInterface dialog,
int
which) {
Process.killProcess(Process.myPid());
}
}).show();
}
});
}
}
public
class
RegActivity extends Activity {
private Button btn_reg;
private EditText edit_sn;
public void onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_reg);
this.btn_reg
=
(Button) findViewById(R.
id
.button1);
this.edit_sn
=
(EditText) findViewById(R.
id
.editText1);
this.btn_reg.setOnClickListener(new View.OnClickListener() {
/
*
class
com.gdufs.xman.RegActivity.AnonymousClass1
*
/
public void onClick(View v) {
String sn
=
RegActivity.this.edit_sn.getText().toString().trim();
if
(sn
=
=
null || sn.length()
=
=
0
) {
Toast.makeText(RegActivity.this,
"您的输入为空"
,
0
).show();
return
;
}
((MyApp) RegActivity.this.getApplication()).saveSN(sn);
new AlertDialog.Builder(RegActivity.this).setTitle(
"回复"
).setMessage(
"您的注册码已保存"
).setPositiveButton(
"好吧"
, new DialogInterface.OnClickListener() {
/
*
class
com.gdufs.xman.RegActivity.AnonymousClass1.AnonymousClass1
*
/
public void onClick(DialogInterface dialog,
int
which) {
Process.killProcess(Process.myPid());
}
}).show();
}
});
}
}
public
class
MyApp extends Application {
public static
int
m
=
0
;
public native void initSN();
public native void saveSN(String
str
);
public native void work();
static {
System.loadLibrary(
"myjni"
);
}
public void onCreate() {
initSN();
Log.d(
"com.gdufs.xman m="
, String.valueOf(m));
super
.onCreate();
}
}
public
class
MyApp extends Application {
public static
int
m
=
0
;
public native void initSN();
public native void saveSN(String
str
);
public native void work();
static {
System.loadLibrary(
"myjni"
);
}
public void onCreate() {
initSN();
Log.d(
"com.gdufs.xman m="
, String.valueOf(m));
super
.onCreate();
}
}
var fputs_str
=
null;
function Hook() {
Java.perform(function () {
const imports
=
Module.enumerateImportsSync(
"libmyjni.so"
);
const imports_len
=
imports.length;
var fputs_addr
=
null;
for
(var i
=
0
; i < imports_len; i
+
+
) {
if
(imports[i].name
=
=
"fputs"
) {
fputs_addr
=
imports[i].address;
break
;
}
}
if
(fputs_addr !
=
null) {
Interceptor.attach(fputs_addr, {
onEnter: function (args) {
fputs_str
=
args[
0
].readCString();
},
onLeave: function (retval) {
}
})
}
})
}
function Invoke(try_str) {
Java.perform(function () {
Java.choose(
"com.gdufs.xman.MyApp"
, {
onMatch: function (instance) {
instance.saveSN(try_str);
},
onComplete: function () {
}
})
})
}
function Main() {
Hook();
Java.perform(function () {
const three_character_array
=
new Array(
"EoP"
,
"AoY"
,
"62@"
,
"ElR"
);
const last_character
=
"D"
;
const my_dict
=
"abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()?_"
const myapp_class_obj
=
Java.use(
"com.gdufs.xman.MyApp"
);
const three_character_array_len
=
three_character_array.length;
const my_dict_len
=
my_dict.length
const myapp
=
Java.use(
"com.gdufs.xman.MyApp"
).$new();
var flag
=
"";
for
(var i
=
0
; i < three_character_array_len; i
+
+
) {
var found
=
false;
for
(var j
=
0
; j < my_dict_len; j
+
+
) {
if
(found
=
=
true) {
break
;
}
for
(var k
=
0
; k < my_dict_len; k
+
+
) {
if
(found
=
=
true) {
break
;
}
for
(var m
=
0
; m < my_dict_len; m
+
+
) {
const try_str
=
my_dict[j]
+
my_dict[k]
+
my_dict[m];
console.log(`try_str: ${try_str}`);
myapp.saveSN(try_str);
if
(three_character_array[i]
=
=
fputs_str) {
flag
+
=
try_str;
console.log(`found: ${try_str}`);
found
=
true;
break
;
}
}
}
}
}
for
(var i
=
0
; i < my_dict_len; i
+
+
) {
const try_str
=
my_dict[i];
console.log(`try_str: ${try_str}`);
Invoke(try_str);
if
(last_character
=
=
fputs_str) {
flag
+
=
try_str;
console.log(`found: ${try_str}`);
break
;
}
}
console.log(`flag: xman{${flag}}`);
})
}
/
/
不能用setImmediate(Main),会出现Process crashed: SIGSYS SYS_SECCOMP
/
/
需要延迟一下再爆破
setTimeout(Main,
500
);
var fputs_str
=
null;
function Hook() {
Java.perform(function () {
const imports
=
Module.enumerateImportsSync(
"libmyjni.so"
);
const imports_len
=
imports.length;
var fputs_addr
=
null;
for
(var i
=
0
; i < imports_len; i
+
+
) {
if
(imports[i].name
=
=
"fputs"
) {
fputs_addr
=
imports[i].address;
break
;
}
}
if
(fputs_addr !
=
null) {
Interceptor.attach(fputs_addr, {
onEnter: function (args) {
fputs_str
=
args[
0
].readCString();
},
onLeave: function (retval) {
}
})
}
})
}
function Invoke(try_str) {
Java.perform(function () {
Java.choose(
"com.gdufs.xman.MyApp"
, {
onMatch: function (instance) {
instance.saveSN(try_str);
},
onComplete: function () {
}
})
})
}
function Main() {
Hook();
Java.perform(function () {
const three_character_array
=
new Array(
"EoP"
,
"AoY"
,
"62@"
,
"ElR"
);
const last_character
=
"D"
;
const my_dict
=
"abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()?_"
const myapp_class_obj
=
Java.use(
"com.gdufs.xman.MyApp"
);
const three_character_array_len
=
three_character_array.length;
const my_dict_len
=
my_dict.length
const myapp
=
Java.use(
"com.gdufs.xman.MyApp"
).$new();
var flag
=
"";
for
(var i
=
0
; i < three_character_array_len; i
+
+
) {
var found
=
false;
for
(var j
=
0
; j < my_dict_len; j
+
+
) {
if
(found
=
=
true) {
break
;
}
for
(var k
=
0
; k < my_dict_len; k
+
+
) {
if
(found
=
=
true) {
break
;
}
for
(var m
=
0
; m < my_dict_len; m
+
+
) {
const try_str
=
my_dict[j]
+
my_dict[k]
+
my_dict[m];
console.log(`try_str: ${try_str}`);
myapp.saveSN(try_str);
if
(three_character_array[i]
=
=
fputs_str) {
flag
+
=
try_str;
console.log(`found: ${try_str}`);
found
=
true;
break
;
}
}
}
}
}
for
(var i
=
0
; i < my_dict_len; i
+
+
) {
const try_str
=
my_dict[i];
console.log(`try_str: ${try_str}`);
Invoke(try_str);
if
(last_character
=
=
fputs_str) {
flag
+
=
try_str;
console.log(`found: ${try_str}`);
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2021-4-21 11:17
被genliese编辑
,原因: