2015-05-13 13 views
17

Ho creato una semplice applicazione, un'app contatore, che premendo un pulsante si aumenta un intero di uno e si aggiorna una vista testo. Il codice può essere visto sotto:Assegnazione strana, TextView to Bundle, dopo la decompilazione, perché?

public class MainActivity extends Activity { 
    public static int count = 0; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     final TextView textView = (TextView) findViewById(R.id.count); 
     textView.setText(Integer.toString(count)); 
     final Button button = (Button) findViewById(R.id.button); 
     button.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
       count++; 
       textView.setText(Integer.toString(count)); 
      } 
     }); 
    } 
    ... 
} 

Dopo decompilazione la stessa applicazione con dex2jar e JD-GUI che ho ricevuto il seguente codice di nuovo:

public class MainActivity extends Activity { 
    public static int count = 0; 

    protected void onCreate(final Bundle paramBundle) { 
     super.onCreate(paramBundle); 
     setContentView(2130903040); 
     paramBundle = (TextView)findViewById(2131296257); 
     paramBundle.setText(Integer.toString(count)); 
     ((Button)findViewById(2131296256)).setOnClickListener(new View.OnClickListener() { 
      public void onClick(View paramAnonymousView) { 
       MainActivity.count += 1; 
       paramBundle.setText(Integer.toString(MainActivity.count)); 
      } 
     }); 
    } 
    ... 
} 

Sulla riga seguente:

 paramBundle = (TextView)findViewById(2131296257); 
     paramBundle.setText(Integer.toString(count)); 

Com'è possibile che il sistema imposti la visualizzazione di testo sul paramBundle? E perché sta succedendo questo? paramBundle è di tipo Bundle e TextView non è una sottoclasse di Bundle, inoltre più Bundle è definitivo in base alla versione decompilata. Qualcosa è andato storto dopo aver decompilato? Le informazioni del decompilatore sono errate o perché otteniamo questo risultato?


Edit:

# virtual methods 
.method protected onCreate(Landroid/os/Bundle;)V 
    .locals 3 
    .param p1, "savedInstanceState" # Landroid/os/Bundle; 

    .prologue 
    .line 17 
    invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V 

    .line 18 
    const/high16 v2, 0x7f030000 

    invoke-virtual {p0, v2}, Lcom/example/rawa/helloworld/MainActivity;->setContentView(I)V 

    .line 20 
    const v2, 0x7f090001 

    invoke-virtual {p0, v2}, Lcom/example/rawa/helloworld/MainActivity;->findViewById(I)Landroid/view/View; 

    move-result-object v1 

    check-cast v1, Landroid/widget/TextView; 

    .line 21 
    .local v1, "textView":Landroid/widget/TextView; 
    sget v2, Lcom/example/rawa/helloworld/MainActivity;->count:I 

    invoke-static {v2}, Ljava/lang/Integer;->toString(I)Ljava/lang/String; 

    move-result-object v2 

    invoke-virtual {v1, v2}, Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;)V 

    .line 22 
    const/high16 v2, 0x7f090000 

    invoke-virtual {p0, v2}, Lcom/example/rawa/helloworld/MainActivity;->findViewById(I)Landroid/view/View; 

    move-result-object v0 

    check-cast v0, Landroid/widget/Button; 

    .line 23 
    .local v0, "button":Landroid/widget/Button; 
    new-instance v2, Lcom/example/rawa/helloworld/MainActivity$1; 

    invoke-direct {v2, p0, v1}, Lcom/example/rawa/helloworld/MainActivity$1;-><init>(Lcom/example/rawa/helloworld/MainActivity;Landroid/widget/TextView;)V 

    invoke-virtual {v0, v2}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V 

    .line 30 
    return-void 
.end method 

io sono sicuramente un esperto Smali, solo un novizio. Ma ho decodificato l'applicazione usando apktool e ho ricevuto il codice smali qui sopra. Da quanto ho capito, la funzione salvata (paramBundle) è caricata in p1 (= v3) e usata in onCreate, e non è usata in alcun modo nella riga 20 o 21. A me questo punto verso un errore di decomposizione? Tieni presente che apktool consente di creare nuovamente l'applicazione e quindi non è possibile perdere dati durante la decompilazione.

+3

Mi piace questa domanda –

+0

@ElJazouli Sono contento che tu l'abbia trovato interessante, per favore vota la domanda in modo da ottenere più attenzione. – Rawa

+0

puoi postare un link al tuo APK, cercherò di decompilarlo e vedere se si tratta di un problema di decompilazione o altro, perché è davvero strano –

risposta

2

La causa è che il tipo di variabili locali è cambiato, ma alcuni decompilatori non riescono a gestirlo correttamente.

Ecco il codice onCreate decompilato con dex2jar + javap:

protected void onCreate(android.os.Bundle); 
    Code: 
     0: aload_0 
     1: aload_1 
     2: invokespecial #20     // Method android/app/Activity.onCreate:(Landroid/os/Bundle;)V 
     5: aload_0 
     6: ldc   #21     // int 2130903040 
     8: invokevirtual #25     // Method setContentView:(I)V 
     11: aload_0 
     12: ldc   #26     // int 2131230720 
     14: invokevirtual #30     // Method findViewById:(I)Landroid/view/View; 
     17: checkcast  #32     // class android/widget/TextView 
     20: astore_1 
     21: aload_1 
     22: getstatic  #12     // Field count:I 
     25: invokestatic #38     // Method java/lang/Integer.toString:(I)Ljava/lang/String; 
     28: invokevirtual #42     // Method android/widget/TextView.setText:(Ljava/lang/CharSequence;)V 
     31: aload_0 
     32: ldc   #43     // int 2131230721 
     34: invokevirtual #30     // Method findViewById:(I)Landroid/view/View; 
     37: checkcast  #45     // class android/widget/Button 
     40: new   #6     // class com/example/test/MainActivity$1 
     43: dup 
     44: aload_0 
     45: aload_1 
     46: invokespecial #48     // Method com/example/test/MainActivity$1."<init>":(Lcom/example/test/MainActivity;Landroid/widget/TextView;)V 
     49: invokevirtual #52     // Method android/widget/Button.setOnClickListener:(Landroid/view/View$OnClickListener;)V 
     52: return 

Qui aload_0 è variabile locale per oggetto MainActivity, e aload_1 è variabile locale per oggetto Bundle. La fonte del problema si è verificata al codice # 20, quando memorizza il riferimento dell'oggetto TextView appena richiamato nella variabile locale 1 (astore_1), che precedentemente memorizzava l'oggetto Bundle!

Questo viene eseguito poiché l'oggetto Bundle non viene utilizzato per il resto del metodo, pertanto è più efficiente riutilizzare la sua variabile locale anziché una nuova variabile. Tuttavia, ciò significa anche che i decompilatori devono lavorare molto duramente per produrre un codice Java corretto.

+0

Grazie per la spiegazione approfondita. – Rawa

+0

A proposito, possiamo essere sicuri che la causa di questo è Java Decompiler? Oppure l'output fornito da dex2jar è la fonte dell'errore? Perché Apktool non riutilizza la stessa variabile. – Rawa

+1

@Rawa Sono tornato indietro e ho verificato la stessa classe compilata da javac prima che venisse elaborata in dex. Ho scoperto che javac non esegue alcuna ottimizzazione, quindi vengono utilizzate 4 variabili anziché 2 nella versione decompilata. Detto ciò, ritengo (non un esperto) che le variabili locali dei bytecode Java possano memorizzare dati di tipi diversi, il che potrebbe essere il motivo per cui alcuni decompilatori riutilizzano le variabili locali mentre altri non lo fanno. Dopotutto è più lavoro per il decompilatore capire che alcune variabili possono essere riutilizzate piuttosto che afferrare ciecamente le variabili quando sono necessarie, non lo farebbero se fosse sbagliato. – Kai

0

Il codice smali che hai fornito sembra corretto. L'esecuzione dell'app funziona anche con il codice sorgente originale. Tuttavia, il codice fornito da jd-gui non compila nemmeno.

Mi sono incuriosito e ho decompilato la tua app con dex2jar. Ho caricato la MainActivity.class risultante in this website.

La parte interessante: Alcuni decompilatori (Procyon e Fernflower) generato il codice corretto e resi variabili separate sia per il fascio e la TextView. JAD e CFR tuttavia ha commesso lo stesso errore di jd-gui. Entrambi hanno utilizzato la variabile Bundle per TextView.


Sembra che si possa incolpare jd-gui per l'errore. Purtroppo, non posso dirti perché accade.