티스토리 뷰

- 안내

제게 크랙을 부탁하는 메일을 전송하지 마세요.


※ 주의

1. 유료 어플의 크랙파일 배포는 저작권 법에 의해 처벌될 수 있습니다.

2. 크랙한 apk파일은 함부로 배포하지 않아야 합니다.

3. 이 강좌를 따라해서 발생하는 모든 문제는 여러분께 있으며 이 방법이 100%는 아닙니다.

4. 모든 어플이 이 글과 같은 구조가 아닙니다 그건 스스로 파악하셔야 합니다.

5. 해외는 보안이 뚤릴경우 개발이 부진해서 뚤린것으로 인식, 더 좋은 방법을 개발하려하는 경향이 있지만,

국내는 허접한 보안 환경을 탓하지 않고 크랙한 그 행위 자체를 탓하는 경향이 많아 조심해야 합니다.

6. 이글은 링크로만 전해주시고 그대로 퍼가지 마세요.


안드로이드 어플을 사용하다 보면, 사용할수 있는 기기를 제한한다던지, 마켓에서 라이센스를 확인하여 실행을 막던지하는 어플이 많이 있습니다.

그 예로 기기 제한의 경우 전에 크랙한 V노트가 있겠고, 라이센스 확인은 대표적으로 파워 앰프가 존재합니다.

(파워앰프는 난독화가 되어 있어, 디컴파일도 안되는점 이해하시고, 크랙이 잘 안됩니다. 삽질하지 마시길....)


이런 어플들이 언제까지나 크랙되지 않는다면 안심이고 다행이지만 안타깝게도 안드로이드는 java를 이용하여 어플을 만들기 때문에 디컴파일/분해가 가능합니다.

즉 네이티브로 만들지 않는이상 모든 어플이 이론상 뚤릴수 있습니다.



이번에는 주로 사용되는 크랙방법과, 제가 주로 사용하는 크랙방법에 대해 모두 다뤄보겠습니다.


다시한번 말하지만 이 방법을 사용하여 어플을 크랙한뒤에, 그 파일을 절대 배포하지 마세요.

만약 그 어플이 유료 앱일경우 저작권 위반이 됩니다.


학습용으로만 이 글을 읽어주세요.




크랙 방법에는 몇가지 종류가 있습니다.

1. 건너뛰기 (점프)

2. 무조건 true반환

(각각 이름은 제가 직접 지은것으로 혼동 하지 마세요.)


두가지로 분류하였지만 사실상 다 비슷한 방법이며, 경우에 따라 두가지를 한번에 써야 할수도 있습니다.

먼저 건너뛰기 부터 해보겠습니다.




1. 체크 부분 건너뛰기 (점프)

첫번째인만큼 간단하게 기기명을 검사하는 부분을 점프해보도록 하겠습니다.


기기명 검사의 원리는 아래와 같습니다.


즉 java파일에서 기기명을 가져옵니다.

그다음 그 값을 확인하여 정상일경우 통과, 아닐경우 알림또는 토스트를 띄우는 거죠.


점프의 원리는 어떠한 경우에도 어플이 실행될수 있도록 검사 부분을 통과하는 겁니다.

이 방법은 대부분의 어플에서 사용이 가능합니다.


점프를 그림으로 표현하면 아래와 같습니다.


이렇게 라이센스 검사를 아에 패스해 버리는 거죠.



여기서 팁하나 드리겠습니다.

어떤 개발자든 어플 종료로 넘어갈때는 알림을 띄우게 되있습니다.


저 글자는 R.string으로 가져오므로 res/values-ko/string.xml을 찾아보면 나옵니다.


이제 string의 이름 not_support_device을 res/values/public.xml에서 찾아주세요.


마지막으로 id값인 0x7f0b01cd을 smali폴더에서 찾아주시면 저 문장을 띄우는곳이 어디인지를 알수 있습니다.



라이센스 체크 부분이 어디인지 발견했으면 이제 수정해 봅시다.


.method public onCreate(Landroid/os/Bundle;)V


    // 윗부분 코드 생략


    .line 65

    :cond_0

    sget-object v4, Landroid/os/Build;->MODEL:Ljava/lang/String;

    // [01] 부분

    // 기기명을 가져오는 부분


    const/4 v5, 0x2


    invoke-virtual {v4, v6, v5}, Ljava/lang/String;->substring(II)Ljava/lang/String;


    move-result-object v2


    .line 67

    .local v2, "modelName":Ljava/lang/String;

    const-string v4, "IM"


    invoke-virtual {v2, v4}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z


    move-result v4


    if-nez v4, :cond_2

    // [02] 부분

    // 정상적이면 cond_2로 점프해서 어플 종료 부분을 건너뜁니다


    .line 69

    const v4, 0x7f0b01cd

    // 아까 찾은 어플 종료 토스트 문구


    invoke-static {p0, v4, v7}, Landroid/widget/Toast;->makeText(Landroid/content/Context;II)Landroid/widget/Toast;


    move-result-object v4


    invoke-virtual {v4}, Landroid/widget/Toast;->show()V

    // [03] 부분

    // 토스트 알림을 띄웁니다


    .line 70

    invoke-virtual {p0}, Lcom/pantech/app/skypen_extend/page/SkyPenLauncher;->finish()V

    // [04] 부분

    // 어플을 종료합니다


    .line 105

    :cond_1

    :goto_0

    return-void


    .line 74

    :cond_2

    // [05] 부분

    // 이 부분으로 넘어와야 어플이 정상 실행됩니다

    sget-boolean v4, Lcom/pantech/app/skypen_extend/SkyPenFeature;->USE_MARKET:Z


    // 아래 코드 생략


.end method


위 코드는 onCreate메소드 안에 있는 코드입니다.

onCreate는 어플이 실행될때(자세하게는 액티비티가 실행될때) 처음으로 호출되는 메소드 입니다.


[01] 부분을 봐주세요.

저부분이 현재 기기의 모델명을 가져오는 부분입니다.


[03] 부분이 토스트 알림을 띄우는 부분이고,


[04] 부분이 어플을 종료하는 부분입니다.


[02] 부분에서 equals이라는 것이 등장하는대 equals은 같다 라는 뜻입니다.

그 아래 if-nez v4, :cond_2을 주목해서 볼 필요가 있습니다.


smali문법을 잠시 확인해 보면,

if-nez v0, :cond_0

v0 값이 0이 아니라면 cond_0 으로 넘어간다.

라는 뜻입니다.

v0, v1....이 smali에서 임의로 붙는 변수 이름이라는것을 생각해보고,

cond_0으로 점프한다는 것을 생각하면,


if-nez v4, :cond_2

이 문구가 정말 중요하다는 것을 알수 있습니다.


[05] 부분으로 넘어가야 어플이 정말 실행되는데요.

그렇다면 저 if문에서 아래부분 [04]를 생략하고 [05]부분으로 점프해야 합니다.



방법은 두가지 정도 있습니다.

어플을 종료하는 부분을 지워버리던가, 점프를 하는 if문에서 어떤 값이든 true가 나오도록 하던가.


전자의 경우 잘 맟춰가며 지워버리면 되고, 후자의 경우 if-nez의 반대인 if-eqz을 추가하면 됩니다, (smali의 if를 살펴볼려면 맨 아래 참조)



.method public onCreate(Landroid/os/Bundle;)V

    

 // 윗부분 코드 생략


    .line 65

    :cond_0

    sget-object v4, Landroid/os/Build;->MODEL:Ljava/lang/String;


    const/4 v5, 0x2


    invoke-virtual {v4, v6, v5}, Ljava/lang/String;->substring(II)Ljava/lang/String;


    move-result-object v2


    .line 67

    .local v2, "modelName":Ljava/lang/String;

    const-string v4, "IM"


    invoke-virtual {v2, v4}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z


    move-result v4


    if-nez v4, :cond_2


    if-eqz v4, :cond_2


    .line 69

    const v4, 0x7f0b01cd


    invoke-static {p0, v4, v7}, Landroid/widget/Toast;->makeText(Landroid/content/Context;II)Landroid/widget/Toast;


    move-result-object v4


    invoke-virtual {v4}, Landroid/widget/Toast;->show()V


    .line 70

    invoke-virtual {p0}, Lcom/pantech/app/skypen_extend/page/SkyPenLauncher;->finish()V


    .line 105

    :cond_1

    :goto_0

    return-void


    .line 74

    :cond_2

    sget-boolean v4, Lcom/pantech/app/skypen_extend/SkyPenFeature;->USE_MARKET:Z


    // 아래 코드 생략


.end method


제일 간단하게 한 문구를 추가해서 0이든 0이 아니든 cond_2로 점프할수 있도록 수정하면 됩니다.


if를 지우고 goto를 사용하여 어떤 경우든 통과되도록 작업해도 될거라 예상합니다.

goto :cond_2



이렇게 점프 방법을 알아봤습니다.

if문을 프로그래밍적으로 작업 실행 흐름 제어의 용도로 많이 쓰는데요.

smali에서 if는 ~이면 -으로 건너뛴다의 의미가 강하기 때문에 제가 점프한다. 라고 지칭했습니다.





2. 무조건 true 반환

두번째 방법은 무조건 true를 반환하도록 하는 방법입니다.



이렇게 어플을 실행한후 라이센스 메소드를 호출하여, 그 메소드에서 true, false값을 반환하는 어플의 경우 어울리는 방법입니다.

(코드 수정이 간단)


무조건 true 반환 예제는 직접 만들어 사용했습니다.


먼저 java소스코드를 보겠습니다.

public class MainActivity extends Activity {

    EditText editText;

    Button button;


    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        

        editText = (EditText) findViewById(R.id.editText);

        button = (Button) findViewById(R.id.button);

        button.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

String inputText = editText.getText().toString();

                        // [01] 부분 : editText에 입력한 값을 가져옵니다

Boolean license = SerialCheck(inputText);

                        // [02] 부분 : SerialCheck메소드를 호출하여 반환되는 값을 저장합니다

if(! license){

Toast.makeText(MainActivity.this, "잘못된 키", Toast.LENGTH_SHORT).show();

finish();

}else{

Toast.makeText(MainActivity.this, "정상 키", Toast.LENGTH_LONG).show();

}

}

});

    }

    

    public boolean SerialCheck(String inputText){

    if(inputText.equals("미르의 IT 정복기"))

            return true;

            // [03] 부분 : 입력한 값이 맞을경우 true를 반환합니다

        return false;

    }

}


이 예제를 잘 분석해 봅시다.

심플하긴 하나 이 하나의 파일안에 무조건 true로 반환이라는 목표가 모두 들어 있습니다.


[01] 부분을 보면 EditText에 입력한 값을 가져오도록 하고 있습니다.


[02] 부분에서는 SerialCheck라는 메소드가 호출되고 있는데요 이때 입력한 값을 넘겨주고 있습니다.

즉 시리얼 체크는 SerialCheck메소드에서 이루어 지고 있네요.


[03] 부분이 중요합니다.

입력한 값이 시리얼 키와 동일할경우 true를 반환하지만,

다를경우 false를 반환하고 있습니다.


그렇다면 어떤 값을 넣어도 true가 반환되도록 smali를 수정하면 되겠죠?


smali를 보겠습니다.

MainActivity$1.smali


.method public onClick(Landroid/view/View;)V


    // 윗부분 코드 생략


    invoke-virtual {v2}, Landroid/widget/EditText;->getText()Landroid/text/Editable;


    move-result-object v2


    invoke-interface {v2}, Landroid/text/Editable;->toString()Ljava/lang/String;


    move-result-object v0


    .line 29

    .local v0, "inputText":Ljava/lang/String;

    iget-object v2, p0, Lwhdghks913/tistory/exampleserial/MainActivity$1;->this$0:Lwhdghks913/tistory/exampleserial/MainActivity;


    invoke-virtual {v2, v0}, Lwhdghks913/tistory/exampleserial/MainActivity;->SerialCheck(Ljava/lang/String;)Z

    // [01] 부분 : 시리얼 체크 메소드를 호출하는 부분입니다


    move-result v2


    invoke-static {v2}, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;


    move-result-object v1


    .line 30

    .local v1, "license":Ljava/lang/Boolean;

    invoke-virtual {v1}, Ljava/lang/Boolean;->booleanValue()Z


    move-result v2


    if-nez v2, :cond_0


    .line 31

    iget-object v2, p0, Lwhdghks913/tistory/exampleserial/MainActivity$1;->this$0:Lwhdghks913/tistory/exampleserial/MainActivity;


    const-string v3, "\uc798\ubabb\ub41c \ud0a4"


    const/4 v4, 0x0


    invoke-static {v2, v3, v4}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;


    move-result-object v2


    invoke-virtual {v2}, Landroid/widget/Toast;->show()V


    .line 32

    iget-object v2, p0, Lwhdghks913/tistory/exampleserial/MainActivity$1;->this$0:Lwhdghks913/tistory/exampleserial/MainActivity;



    invoke-virtual {v2}, Lwhdghks913/tistory/exampleserial/MainActivity;->finish()V

    // [02] 부분 : 시리얼 키가 안맞을경우 종료하는 부분


    .line 36

    :goto_0

    return-void


    .line 34

    :cond_0

    iget-object v2, p0, Lwhdghks913/tistory/exampleserial/MainActivity$1;->this$0:Lwhdghks913/tistory/exampleserial/MainActivity;


    const-string v3, "\uc815\uc0c1 \ud0a4"


    const/4 v4, 0x1


    invoke-static {v2, v3, v4}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;


    move-result-object v2


    invoke-virtual {v2}, Landroid/widget/Toast;->show()V


    goto :goto_0

.end method



MainActivity.smali


.method public SerialCheck(Ljava/lang/String;)Z

    .locals 1

    .param p1, "inputText"    # Ljava/lang/String;


    .prologue

    .line 41

    const-string v0, "\ubbf8\ub974\uc758 IT \uc815\ubcf5\uae30"


    invoke-virtual {p1, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z


    move-result v0


    if-eqz v0, :cond_0


    .line 42

    const/4 v0, 0x1


    .line 43

    :goto_0

    return v0


    :cond_0

    const/4 v0, 0x0

    // [03] 부분 : true또는 false를 반환하는 부분


    goto :goto_0

.end method


[01] 부분을 봐주세요.

이부분이 버튼을 눌렀을때 SerialCheck라는 메소드가 호출되는 부분입니다.


[02] 부분은 반환되는 값이 true면 진행, false면 종료부분인대 이부분은 그닥 건들 필요는 없습니다.


[03] 부분이 매우매우 중요합니다.

중요해서 빨강 상자로 묶었습니다.


if-eqz를 볼까요?

맨아래 참고를 보시면 if-eqz는 v0이 0이면 cond_0으로 넘어가는 구문입니다.

0은 false를 뜻하고 있습니다.


즉 위에 있는 equals(같다)가 false일경우 if-eqz로 인해 cond_0으로 넘어가게 됩니다.


true일경우 if-eqz를 지나쳐서 아래에 있는 .line 42을 실행하게 되고,

const/4 v0, 0x1으로 true가 반환됩니다.


그렇다면,

const/4 v0, 0x0을 const/4 v0, 0x1으로 바꿔주면 어떤 경우든지 true가 반환될겁니다.


.method public SerialCheck(Ljava/lang/String;)Z

    .locals 1

    .param p1, "inputText"    # Ljava/lang/String;


    .prologue

    .line 41

    const-string v0, "\ubbf8\ub974\uc758 IT \uc815\ubcf5\uae30"


    invoke-virtual {p1, v0}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z


    move-result v0


    if-eqz v0, :cond_0


    .line 42

    const/4 v0, 0x1


    .line 43

    :goto_0

    return v0


    :cond_0

    const/4 v0, 0x1


    goto :goto_0

.end method



정말 되는지 확인해 봅시다.


먼저 수정하기 전 상황입니다.

   


이걸 보시면 "미르의 IT 정복기"라는 문구만 정상 키 라고 나오는 걸 확인할수 있습니다.


아래는 수정한 후 입니다.

Error key라고 입력해도 정상 키가 나타나는것을 확인할 수 있습니다.




이렇게 해서 무조건 true반환이라는 경우도 살펴봤습니다.




이 포스팅 마치기 까지 3시간 정도 걸린거 같아요. ㄷㄷ

그리고 연구 목적으로만 사용하시길 바랍니다 크랙한 유료 어플의 apk를 배포하면 법으로 처벌받을 수도 있습니다.

유료 어플은 구입해서 사용하는것이 진리입니다.


정말 힘들게 작성한 글인만큼 꼭 덧글 달아주세요. 제발!



[참고] smali if문법


if-eqz v0, :cond_0

v0이 0이면 cond_0으로 넘어간다.


if-nez v0, :cond_0

v0이 0이 아니라면 cond_0으로 넘어간다.


if-eq v0, v1, :cond_0

v0과 v1이 같으면 cond_0로 넘어간다.


if-ne v0, v1, :cond_0

v0과 v1이 같지 않다면 cond_0으로 넘어간다.


if-ge v0, v1, :cond_0

v0이 v1보다 크거나 같으면 cond_0으로 넘어간다.


if-le v0, v1, :cond_0

v0이 v1보다 작거나 같으면 cond_0으로 넘어간다.



이 글과 함께 보면 더 도움이 되는 글

[Development/App] - 인앱결제(Inapp Billing)와 언락커(Unlocker) 크랙하기 (DRM Crack)


저작자 표시 비영리 변경 금지
신고
크리에이티브 커먼즈 라이선스
Creative Commons License
댓글
댓글쓰기 폼