문제 정보
키값을 찾으시오
문제 파일
SmartApp L04.apk
실행
앱을 재서명하고 설치하면 다음과 같은 화면이 나온다.

우측 상단에 있는 Settings 버튼은 아무 작동도 하지 않고,
아무 키나 입력했을때 Check Key 버튼을 누르면 Wrong!! try again:) 이라는 메시지를 띄운다.
분석
jadx-gui 툴로 분석을 해보면 다음과 같은 코드로 작동한다는 것을 알 수 있었다.
MainActivity.java
/* loaded from: /Users/seohyun-gyu/Downloads/SmartApp L04/classes.dex */
public class MainActivity extends Activity {
TextView aView;
Button button;
EditText editText;
...
public void addListenerOnButton() {
this.button = (Button) findViewById(R.id.button1);
this.editText = (EditText) findViewById(R.id.editText1);
this.aView = (TextView) findViewById(R.id.textView1);
this.aView.setText("Key를 찾았다면 입력하세요.");
this.button.setOnClickListener(new View.OnClickListener() { // from class: com.namdaehyeon.zfinekey4.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View arg0) {
String CalcKey = "";
String getKeyField = MainActivity.this.editText.getText().toString();
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(getKeyField.getBytes("UTF-8"));
StringBuffer hexString = new StringBuffer();
for (byte b : hash) {
String hex = Integer.toHexString(b & 255);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
CalcKey = hexString.toString();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e2) {
e2.printStackTrace();
}
if (CalcKey.equals("0c9e938c9a6a58e9ca93289c8455b1dd462a1b722f7e1c11a34f7983260ab992")) {
MainActivity.this.aView.setText("Correct!! 정답입니다. ^^b");
} else {
MainActivity.this.aView.setText("Wrong!! try again :)");
}
}
});
}
}
/* loaded from: /Users/seohyun-gyu/Downloads/SmartApp L04/classes.dex */
public class MainActivity extends Activity {
TextView aView;
Button button;
EditText editText;
...
public void addListenerOnButton() {
this.button = (Button) findViewById(R.id.button1);
this.editText = (EditText) findViewById(R.id.editText1);
this.aView = (TextView) findViewById(R.id.textView1);
this.aView.setText("Key를 찾았다면 입력하세요.");
this.button.setOnClickListener(new View.OnClickListener() { // from class: com.namdaehyeon.zfinekey4.MainActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View arg0) {
String CalcKey = "";
String getKeyField = MainActivity.this.editText.getText().toString();
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(getKeyField.getBytes("UTF-8"));
StringBuffer hexString = new StringBuffer();
for (byte b : hash) {
String hex = Integer.toHexString(b & 255);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
CalcKey = hexString.toString();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e2) {
e2.printStackTrace();
}
if (CalcKey.equals("0c9e938c9a6a58e9ca93289c8455b1dd462a1b722f7e1c11a34f7983260ab992")) {
MainActivity.this.aView.setText("Correct!! 정답입니다. ^^b");
} else {
MainActivity.this.aView.setText("Wrong!! try again :)");
}
}
});
}
}
/* loaded from: /Users/seohyun-gyu/Downloads/SmartApp L04/classes.dex */ public class MainActivity extends Activity { TextView aView; Button button; EditText editText; ... public void addListenerOnButton() { this.button = (Button) findViewById(R.id.button1); this.editText = (EditText) findViewById(R.id.editText1); this.aView = (TextView) findViewById(R.id.textView1); this.aView.setText("Key를 찾았다면 입력하세요."); this.button.setOnClickListener(new View.OnClickListener() { // from class: com.namdaehyeon.zfinekey4.MainActivity.1 @Override // android.view.View.OnClickListener public void onClick(View arg0) { String CalcKey = ""; String getKeyField = MainActivity.this.editText.getText().toString(); try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); byte[] hash = digest.digest(getKeyField.getBytes("UTF-8")); StringBuffer hexString = new StringBuffer(); for (byte b : hash) { String hex = Integer.toHexString(b & 255); if (hex.length() == 1) { hexString.append('0'); } hexString.append(hex); } CalcKey = hexString.toString(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (NoSuchAlgorithmException e2) { e2.printStackTrace(); } if (CalcKey.equals("0c9e938c9a6a58e9ca93289c8455b1dd462a1b722f7e1c11a34f7983260ab992")) { MainActivity.this.aView.setText("Correct!! 정답입니다. ^^b"); } else { MainActivity.this.aView.setText("Wrong!! try again :)"); } } }); } }
Check Key 버튼을 누를 때, 입력한 Key의 SHA-256 해시값을 만든다.
해시가 0c9e938c9a6a58e9ca93289c8455b1dd462a1b722f7e1c11a34f7983260ab992여야 한다.
맞으면, Correct!! 정답입니다. ^^b 메시지를 띄운다.
zFindKey Class
seohyun-gyu@MacBook-Pro ~ % strings -t x /Users/seohyun-gyu/Downloads/SmartApp\ L04/classes.dex | grep helloWorld
1850 helloWorldA
185d helloWorldB
186a helloWorldC
1877 helloWorldD
seohyun-gyu@MacBook-Pro ~ % strings -t x /Users/seohyun-gyu/Downloads/SmartApp\ L04/classes.dex | grep helloWorld
1850 helloWorldA
185d helloWorldB
186a helloWorldC
1877 helloWorldD
seohyun-gyu@MacBook-Pro ~ % strings -t x /Users/seohyun-gyu/Downloads/SmartApp\ L04/classes.dex | grep helloWorld 1850 helloWorldA 185d helloWorldB 186a helloWorldC 1877 helloWorldD
사실 zFindKey 클래스에 숨겨진 helloWorldD 메소드가 존재한다.
따라서 helloWorldD 메소드를 복원시키기 위해 아래와 같이 패치시켰다.
- 0x1CE7 (Direct method count): 04 -> 05로 수정 (숨겨진 helloWorldD 메소드가 하나 더 있기 때문에 1 증가)
- 0x1CFB (direct_method): 00 -> 01로 수정
- 0x1CFC (access flags): 00 -> 09로 수정
- 0x1CFD (Offset to the code): 00 00 -> D0 19
(0x19b8 – 0xcb8 = 0xd00, 0x1984 – 0xc84 = 0xd00이므로 base는 0xd00,
helloWorldD 메소드의 실행 주소 위치는 0xcd0에 있으므로 base + 0xcd0 = 0x19d0)

패치하고, jadx-gui 툴 설정에서 플러그인 > [dex-input] verify dex file checksum before load를 NO로 바꾼다.
그러면 정상적으로 jadx-gui 툴로 helloWorldD 메소드를 볼 수 있다.
helloWorldD 메소드에 있는 자바 코드를 그대로 옮겨와서 디코딩시키면 Key를 얻을 수 있었다.
public class helloWorldD {
public static void helloWorldD() {
int len = "a49895d0bb9589d09983d0b29f919e80829f9a959384aaaa919e97".length();
byte[] data = new byte[(len / 2) + 1];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit("a49895d0bb9589d09983d0b29f919e80829f9a959384aaaa919e97".charAt(i), 16) << 4) + Character.digit("a49895d0bb9589d09983d0b29f919e80829f9a959384aaaa919e97".charAt(i + 1), 16));
}
int v10 = "a49895d0bb9589d09983d0b29f919e80829f9a959384aaaa919e97".length() / 2;
byte[] AA = new byte[v10];
int v16 = 0;
int v20 = 0;
while (v16 < data.length - 1) {
AA[v20] = (byte) (data[v16] ^ 240);
v16++;
v20++;
}
int Newlen = AA.length;
String ResultData = new String();
for (int i2 = 0; i2 < Newlen; i2++) {
ResultData = String.valueOf(String.valueOf(ResultData) + Integer.toHexString((AA[i2] >> 4) & 15)) + Integer.toHexString(AA[i2] & 15);
}
//decode HEX string
byte[] byteArray = new byte[ResultData.length() / 2];
for (int i = 0; i < byteArray.length; i++) {
int index = i * 2;
int j = Integer.parseInt(ResultData.substring(index, index + 2), 16);
byteArray[i] = (byte) j;
}
String decodedString = new String(byteArray);
System.out.println("ResultData: " + decodedString);
}
public static void main(String[] args) {
helloWorldD();
}
}
public class helloWorldD {
public static void helloWorldD() {
int len = "a49895d0bb9589d09983d0b29f919e80829f9a959384aaaa919e97".length();
byte[] data = new byte[(len / 2) + 1];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit("a49895d0bb9589d09983d0b29f919e80829f9a959384aaaa919e97".charAt(i), 16) << 4) + Character.digit("a49895d0bb9589d09983d0b29f919e80829f9a959384aaaa919e97".charAt(i + 1), 16));
}
int v10 = "a49895d0bb9589d09983d0b29f919e80829f9a959384aaaa919e97".length() / 2;
byte[] AA = new byte[v10];
int v16 = 0;
int v20 = 0;
while (v16 < data.length - 1) {
AA[v20] = (byte) (data[v16] ^ 240);
v16++;
v20++;
}
int Newlen = AA.length;
String ResultData = new String();
for (int i2 = 0; i2 < Newlen; i2++) {
ResultData = String.valueOf(String.valueOf(ResultData) + Integer.toHexString((AA[i2] >> 4) & 15)) + Integer.toHexString(AA[i2] & 15);
}
//decode HEX string
byte[] byteArray = new byte[ResultData.length() / 2];
for (int i = 0; i < byteArray.length; i++) {
int index = i * 2;
int j = Integer.parseInt(ResultData.substring(index, index + 2), 16);
byteArray[i] = (byte) j;
}
String decodedString = new String(byteArray);
System.out.println("ResultData: " + decodedString);
}
public static void main(String[] args) {
helloWorldD();
}
}
public class helloWorldD { public static void helloWorldD() { int len = "a49895d0bb9589d09983d0b29f919e80829f9a959384aaaa919e97".length(); byte[] data = new byte[(len / 2) + 1]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit("a49895d0bb9589d09983d0b29f919e80829f9a959384aaaa919e97".charAt(i), 16) << 4) + Character.digit("a49895d0bb9589d09983d0b29f919e80829f9a959384aaaa919e97".charAt(i + 1), 16)); } int v10 = "a49895d0bb9589d09983d0b29f919e80829f9a959384aaaa919e97".length() / 2; byte[] AA = new byte[v10]; int v16 = 0; int v20 = 0; while (v16 < data.length - 1) { AA[v20] = (byte) (data[v16] ^ 240); v16++; v20++; } int Newlen = AA.length; String ResultData = new String(); for (int i2 = 0; i2 < Newlen; i2++) { ResultData = String.valueOf(String.valueOf(ResultData) + Integer.toHexString((AA[i2] >> 4) & 15)) + Integer.toHexString(AA[i2] & 15); } //decode HEX string byte[] byteArray = new byte[ResultData.length() / 2]; for (int i = 0; i < byteArray.length; i++) { int index = i * 2; int j = Integer.parseInt(ResultData.substring(index, index + 2), 16); byteArray[i] = (byte) j; } String decodedString = new String(byteArray); System.out.println("ResultData: " + decodedString); } public static void main(String[] args) { helloWorldD(); } }
ResultData: The Key is BoanprojectZZang
ResultData: The Key is BoanprojectZZang
ResultData: The Key is BoanprojectZZang
KEY
BoanprojectZZang
