Frida를 이용한 훅스크립트 자동 생성 기능 2차 내용이다.

완벽한 자동화를 목표했으나, 하면 할수록 넘어야할 산이 매우 많은 것을 느끼고 있다.


개발 2차시에 큰 목표는 다음과 같다.

  • 다중 Class/Method 후킹 스크립트 생성
  • Arg/Ret 입력/출력 시 데이터형에 따른 이슈 확인



이번에도 추가된 주요 기능만을 설명하겠다.


다중 함수 후킹 코드는 기본 후킹 코드을 세분화하여 필요한 횟수만큼 반복하는 형태로 구현하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# self.target 은 타겟 Class정보를 Dic{}형태로 가진 List[] 변수
for i in range(len(self.target)):
    tClass = self.target[i]['class']
    tMethod = self.target[i]['method']
    mode = self.target[i]['mode']
    
    if self.target[i]['exe'> 0:
        exe = 'true'
 
    hcode += self.hook_head % (i, tClass, i, i, tMethod, i, i, tClass, tMethod, i, i, tMethod, i, tMethod, tMethod, tMethod)
            
    if (mode%2== 0:
        hcode += self.hook_arg_input
            
    if mode > 2:
        hcode += self.hook_tail % (exe, tMethod, tMethod, 'true')
    else:
        hcode += self.hook_tail % (exe, tMethod, tMethod, 'false')



후킹한 함수의 Argument Type이 object인 경우 해당 클래스 인스턴스의 멤버변수 정보(type, name, value)를 출력하는 기능이다.

arguments[index].class.getDeclaredFields() 해당 함수 실행 시 클래스멤버변수 정보를 배열로 반환한다.

# getDeclaredMethods( )는 클래스멤버함수 정보를 반환

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if(String(typeof(arguments[index])) == "object"){
    var var_name;
    var var_type;
    ownVars = arguments[index].class.getDeclaredFields();
 
    for(var i in ownVars){
        var_name = String(ownVars[i]).split('.');
        var_name = var_name[var_name.length-1];
 
        if(var_name!='$change' && var_name!='serialVersionUID') {
            var_type = String(ownVars[i]).split(' ');
            var_type = var_type[var_type.length-2];
            arg_dump += gubunja + "           L (" + var_type + ") " + var_name + " = " + String(eval("arguments[index]."+var_name+"['value']"));
        }
    }
}



Android 진단 시 유용하게 사용중인 frida를 이용하여 좀더 편리하게 작업하고자 후킹 자동화 코드를 개발하게 되었다.

# 어디까지나 내가 쓰기 편하기 위해 만든 내 입맛 맞춤용이다.


이러한 코드를 개발하게된 계기는 house 라는 반자동화(?) 모듈을 사용하는데 너무나도 훌륭한 모듈이지만 몇가지 기능이 아쉬워 직접 개발해보게 되었다.

그래서 house 코드들을 참고/분석하여 짜집기 식으로 개발하고 있다. (house 추천!!)


#house URL : https://github.com/nccgroup/house


코드 실행 시 다음의 입력을 요구하며, 요구에 맞게 선택하면 관련 기능의 스크립트를 자동으로 생성하여 Attach 하게된다.

  • class명을 입력하면 class내 method 목록이 나열되며, 목록 중 후킹 대상 method 선택(입력) 
  • method argument 타입 등의 정보는 자동으로 인식
  • method arg와 ret 데이터를 조회만 할것인지 조작까지 할 것인지 선택
  • arg와 ret 데이터 중 어떤 정보를 조작할지 선택
  • 후킹 함수의 실행 여부 선택(함수 실행을 못하도록 제한하는 경우를 고려)


아직 개선이 많이 필요한 코드이기에 전체 코드가 아닌 주요 부분만 포스트 하도록 하겠다.



Class 내 선언된 Method 정보(권한, 리턴타입, 함수명, 인자타입) 확인

1
2
3
4
5
6
7
8
9
10
Java.perform(function() {
    var method = "Methods:";
    var hook = Java.use("%s");
    var ownMethod = hook.class.getDeclaredMethods();
    ownMethod.forEach(function(value){
        method += String(value) + "/";
    });
    send(method);
    hook.$dispose;
});


후킹 대상 함수의 실행 콜 스택을 나열

1
2
3
function getCaller(){
    return clazz_Thread.currentThread().getStackTrace().slice(2,5).reverse().toString();
}



후킹 대상 함수를 지정 시 overload 함수가 있는지 알수 있으며, 반복문을 통해 모두 후킹

# 특정 함수만으로 제한하도록 구현할까 하다 우선 모두 후킹하도록 구현

1
2
3
4
5
6
hook0 = Java.use("클래스명");
var overloadz_hook0 = eval("hook0.메서드.overloads");
var count_over_hook0 = overloadz_hook0.length;
 
for (var i = 0; i < count_over_hook0; i++)
    hget_hook = eval('overloadz_hook0[i]');


후킹 대상 함수의 리턴 타입

1
var ret_type = String(hget_hook.returnType['className']);



동적으로 데이터를 입력하고 스크립트에서 받기

1
2
#python code
script.post({'type''input''payload': data})


1
2
3
4
5
6
7
//hook script
while (true){
    var op = recv('input'function(value) {
        recv_data = value.payload;
    });
    op.wait();
}



HMAC 개념 자료 : http://crono.tistory.com/3?category=779861

Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
 
public class Main extends AppCompatActivity {
    private Button button;
    private EditText ed;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final Hmac hmsha256 = new Hmac();
 
        button = (Button)findViewById(R.id.button);
        ed = (EditText)findViewById(R.id.input);
 
        button.setOnClickListener(new Button.OnClickListener(){
            @Override
            public void onClick(View view){
                String result;
                if (ed.toString() != ""){
                    result = hmsha256.hget(ed.getText().toString());
                    Log.d("HMAC-SHA256",result);
                    Toast.makeText(getApplicationContext(), result, Toast.LENGTH_LONG).show();
                }
            }
        });
    }
}



Hmac.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
 
 
public class Hmac {
    // hash 알고리즘 선택
    private static final String ALGOLISM = "HmacSHA256";
    // hash 암호화 key
    private static final String key = "nsHc6458";
 
 
    public static String hget(String message) {
        try {
            // hash 알고리즘과 암호화 key 적용
            Mac hasher = Mac.getInstance(ALGOLISM);
            hasher.init(new SecretKeySpec(key.getBytes(), ALGOLISM));
 
            // messages를 암호화 적용 후 byte 배열 형태의 결과 리턴
            byte[] hash = hasher.doFinal(message.getBytes());
            return byteToString(hash);
        }
        catch (NoSuchAlgorithmException e){
            e.printStackTrace();
        }
        catch (InvalidKeyException e){
            e.printStackTrace();
        }
        return "";
    }
 
    // byte[]의 값을 16진수 형태의 문자로 변환하는 함수
    private static String byteToString(byte[] hash) {
        StringBuffer buffer = new StringBuffer();
 
        for (int i = 0; i < hash.length; i++) {
            int d = hash[i];
            d += (d < 0)? 256 : 0;
            if (d < 16) {
                buffer.append("0");
            }
            buffer.append(Integer.toString(d, 16));
        }
        return buffer.toString();
    }
}


EditText 문자를 입력 받아 공유된 key를 바탕으로 sha256으로 암호화하여 hash를 반환하는 코드들로 검색 당시 많은 예제 코드에서 위에서 사용된 암호화 class 와 method를 사용하고 있었다.


하여, 위 코드를 바탕으로 HMAC 적용 App 진단 시 공략 포인트를 체크하고자 한다.


SecreKeySpec( ) class 초기화 시 Key와 hash알고리즘 정보가 인자로 전달되므로 암호화 관련 주요한 정보를 얻을 수 있는 공략 포인트가 될 것이다.


*.doFinal( ) method에서는 메시지의 byte형태의 데이터를 받아 Hash화 하는 함수로 Hook을 통해 변환될 데이터를 확인할 수 있다.


만약 진단 대상 APP에 hget( ) 과 같은 Method가 있다면 hook을 통해 입력 메세지를 얻을 수 있고, return 값을 조작하여 데이터 조작도 가능할 것이다.

return 값 조작 시에는 동일한 hash알고리즘과 Key를 이용하여 조작 메시지를 hash화하여야 한다.


hash값 조작 시 사용할 HMAC 파이썬(v2) 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import hashlib
import hmac
import base64
 
message = bytes("nshc is possible").encode('utf-8')
shared_key = bytes("nsHc6458").encode('utf-8')
 
hash = hmac.new(shared_key, message, hashlib.sha256)
 
print hash
 
result = hash.hexdigest()
 
print "Hash : %s(%d)" % (result, len(result))
#print "Base64(hash) : %s" % base64.b64encode(result)


1. MAC(Message Authentication Code)

메시지 인증 코드(Message Authentication Code, 약칭 MAC)는 메시지의 인증에 쓰이는 작은 크기의 정보이다. 이 MAC을 이용하여, 메시지의 무결성 및 신뢰성을 보장하는데 사용한다. 


MAC의 Algorithm은 인증을 위한 Secret Key와 임의 길이의 Message를 입력 받아 MAC을 출력하는 Keyed Hash Function을 사용한다. 

MAC은 Cryptographic Hash Function과 같은 특성을 가진다. 그 메인 속성은 다음과 같다.


    • Hash value로 계산하기 쉽다.
    • 생성된 Hash를 통해서 Message를 generate 하는 것이 불가능하다.
    • Hash를 수정하지 않고, Message를 수정하는것이 불가능하다.
    • 다른 두 Message가 동일한 Hash를 보내는것이 불가능하다.

사용되는 Hash algorithm은 MD5, SHA-1, SHA-2 등 일반적인 암호화 알고리즘을 그대로 사용할 수 있으며, 사용된 알고리즘에 따라서 고정길이의 Hash value가 생성된다.


MAC을 간단하게 도식화 하면 다음과 같다.



MAC에 사용되는 Key에 따라서, CMAC, HMAC, UMAC, VMAC 등으로 나뉜다.




2. HMAC(Hash-based Message Authentication Code)

HMAC은 Keyed-Hash Message Authentication Code로  Key를 조합하여 Hash 함수를 구하는 방식이다.  


간단히 HMAC을 설명하면, 송신자와 수신자만이 공유하고 있는 Key와 Message를 혼합하여 Hash 값을 만드는 것이다.


채널을 통해 보낸 메시지가 훼손되었는지 여부를 확인하는데 사용할 수 있다. MAC의 특성상 역산이 불가능 하므로, 수신된 메시지와 Hash 값을 다시 계산하여,계산된 HMAC과 전송된 HMAC이 일치하는지를 확인하는 방식이다.


대칭블록암호에 기반을 둔 MAC 방식으로, 알고리즘에 따라서 HMAC value size가 달라진다. 


  • HMAC_MD5("", "")     = 0x74e6f7298a9c2d168935f58c001bad88
  • HMAC_SHA1("", "")    = 0xfbdb1d1b18aa6c08324b7d64b71fb76370690e1d
  • HMAC_SHA256("", "") = 0xb613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad 




'Mobile 진단 > 참고자료' 카테고리의 다른 글

해쉬함수 별 결과값 길이  (0) 2018.09.05

1. md5의 출력값 길이는 128비트

md5 hash값(저장 : 16진수로 32바이트)

ex) 8380482228e75045a7d14e063bde014b


2. sha-1의 출력값 길이는 160비트

sha-1 hash값(저장 : 16진수로 40바이트)

ex) 764C46AE8BC50C4823E50F18DA45B9A21E8DD10B


3. sha-256의 출력값 길이는 256비트

sha-256 hash값(저장 : 16진수로 64바이트)

ex) be178c0543eb17f5f3043021c9e5fcf30285e557a4fc309cce97ff9ca6182912

'Mobile 진단 > 참고자료' 카테고리의 다른 글

HMAC(Hash-based Message Authentication Code)  (0) 2018.09.05

+ Recent posts