앵하니의 더 나은 보안
Frida Encryption/Socket Viewer 본문
증권 어플리케이션 진단 시 작성해 사용했던 frida 스크립트를 공유한다.
증권사는 실시간 매수/매도 때문에 빠른 통신에 민감하다.
그래서 증권사 어플리케이션에서는 HTTP 통신이 아니라 TCP/IP 소켓 통신을 수행한다.
이에 따라 어플리케이션에서 TCP/IP 소켓 내용을 확인하기 위해선 소켓 관련 api를 후킹해 패킷을 확인해야한다.
근데 또 소켓 데이터는 byte array 자료형이라, byte array 자료형의 16진수와 그에 맵핑되는 문자들을 편하게 보기위해
show_byte 함수를 작성해 사용했다.
암/복호화 후킹은 덤
자바 api에서 사용하는 암/복호화 하는 동작은 모두 확인할 수 있다.
var view_length = 16;
var print_encrypt = 1;
var print_socket = 1;
function show_byte(byte_arr, filter){
var buffer = byte_arr;
var byte_hex = ""
var one_line = "";
var show_byte_text = ""
var check_show_byte = 0
var view_hex = 0
var com_string = "";
for(var i = 0; i < buffer.length; ++i){
view_hex = buffer[i] & 0xff
var hex_data = (view_hex).toString(16);
if (hex_data.length == 1){
hex_data = "0"+hex_data;
}
byte_hex += hex_data + " ";
if (view_hex == 0)
one_line += "●"
else if (31 < view_hex && view_hex < 127)//아스키 코드로 표현할 수 있는 문자 범위 내라면~
one_line += (String.fromCharCode(view_hex));
else if(234<=view_hex && view_hex<=237)//ea, eb, ec, ed로 시작한다면~ uft-8 디코딩
{
var utf8_encoded = "%"+hex_data+"%"+(buffer[i+1] & 0xff).toString(16)+"%"+(buffer[i+2] & 0xff).toString(16);
try{
var decode_utf8 = decodeURIComponent(utf8_encoded);
one_line += decode_utf8;
}
catch{
one_line += "■";
}
}
else{
one_line += "■";
}
if (i%view_length==view_length-1){//설정된 view_length(기본 16)단위로 줄넘김 삽입
check_show_byte = 1
show_byte_text += byte_hex+"\t"+one_line+"\n";
com_string+=one_line;
one_line = ""
byte_hex =""
}
}
if (!check_show_byte)//지정한 view_length 보다 bytearray가 짧을때
show_byte_text += byte_hex+"\t"+one_line+"\n";
if(filter!=""){//필터로 넘어온 문자열 값이 포함된 소켓만 보여주기
if (!(com_string.indexOf(filter)==-1)){
return show_byte_text+"\n"+com_string+"\n";
}
else{
return "";
}
}
else
return show_byte_text+"\n"+com_string+"\n";
}
Java.perform(function () {
if(print_encrypt){
Java.use('javax.crypto.spec.SecretKeySpec').$init.overload('[B','java.lang.String').implementation = function(arg1,arg2) {
console.log("\n\n[*] SecretKeySpec key data ("+arg2+")\n"+show_byte(arg1,""));
return this.$init(arg1,arg2);
};
var check_plain = Java.use("javax.crypto.Cipher");
check_plain.doFinal.overload("[B").implementation = function(arg1){
var retval = this.doFinal(arg1);
console.log("[*] plain text\n"+show_byte(arg1,""));
console.log("[*] encrypted text\n"+show_byte(retval,""));
return retval;
};
}
if(print_socket){
var read_hk = Java.use("java.io.InputStream");
read_hk.read.overload("[B","int","int").implementation = function(arg1,arg2,arg3){
console.log("read's arg1\n"+show_byte(arg1,"")+"\n");
console.log("read's arg2 > "+arg2)
console.log("read's arg3 > "+arg3)
return this.read(arg1,arg2,arg3)
};
var socket_out_check = Java.use('java.io.OutputStream');
socket_out_check.write.overload("[B").implementation = function(arg1){
console.log("\n[Socket out]\n"+show_byte(arg1,"")+"\n")
this.write(arg1);
};
var write_hk = Java.use("java.io.ByteArrayOutputStream");
write_hk.write.overload("[B","int","int").implementation = function(arg1,arg2,arg3){
console.log("write's arg1\n "+show_byte(arg1,"")+"\n");
this.write(arg1,arg2,arg3)
};
var read_hk = Java.use("java.io.ByteArrayInputStream");
read_hk.read.overload("[B","int","int").implementation = function(arg1,arg2,arg3){
console.log("ByteArrayInputStream read's arg1\n"+show_byte(arg1,"")+"\n");
return this.read(arg1,arg2,arg3)
};
Java.use("java.io.BufferedInputStream").read.overload('[B', 'int', 'int').implementation = function(arg1,arg2,arg3){
console.log("BufferedInputStream read's data\n"+show_byte(arg1,"")+"\n");
return this.read(arg1,arg2,arg3);
};
Java.use("java.io.DataInputStream").read.overload("[B", "int", "int").implementation = function(arg1,arg2,arg3){
console.log("DataInput read's data\n"+show_byte(arg1,"")+"\n");
return this.read(arg1,arg2,arg3);
}
}
});
혹 다른 byte array의 데이터를 보고싶다면
console.log(show_byte(arg1,arg2))로 작성해 사용하면 된다.
arg1 은 보고자하는 바이트형 데이터의 변수를 뜻한다.
arg2 는 필터링할 문자열을 뜻하는데, 바이트 데이터(16진수 데이터)에 맵핑되는 문자열 내에 arg2의 문자열이
포함되면 해당 소켓의 데이터를 문자열 형식으로 반환하고, 포함되지 않으면 빈 문자열을 반환한다.
한마디로 그냥 보고싶은 문자열이 포함된 소켓 데이터만 출력하게 하는 역할이다.
ex) console.log(show_byte(byte_data, "test1234"));
> byte_data를 16진수와 그에 맵핑되는 문자열로 보려하는데,
그 중에서도 test1234가 있는 byte_data만 출력해줘라
만약 필터링할 문자열 없이 모든 소켓 데이터를 보고자 한다면 arg2로 빈문자열을 전달하면 된다.
ex) console.log(show_byte(byte_data, ""));
'보안 기술 > Android' 카테고리의 다른 글
base.apk에 lib폴더가(so파일이) 없을 때 (0) | 2022.07.25 |
---|---|
Android Studio 단말기 메모리 내 이미지 확인(5.1 ~ 7) (0) | 2022.07.18 |
Android ADB 무선 연결 (0) | 2022.07.18 |
Android 11 버전 이상에서의 apk 사이닝 (0) | 2022.07.18 |
Android 취약점 진단 팁 (0) | 2022.07.18 |