Add sources.
This commit is contained in:
parent
5325148ff2
commit
bb825a0d92
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -17,3 +17,5 @@ project/plugins/project/
|
||||||
.scala_dependencies
|
.scala_dependencies
|
||||||
.worksheet
|
.worksheet
|
||||||
|
|
||||||
|
# IntelliJ IDEA
|
||||||
|
.idea/
|
||||||
|
|
13
README.md
13
README.md
|
@ -1,3 +1,12 @@
|
||||||
# DalvikBytecodeAnalysis
|
# Dalvik bytecode analysis
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
[SBT](https://www.scala-sbt.org/)
|
||||||
|
|
||||||
|
## System requirements
|
||||||
|
|
||||||
|
[Apktool](https://ibotpeaches.github.io/Apktool/) in the path.
|
||||||
|
|
||||||
|
[Z3](https://github.com/Z3Prover/z3) version >= 4.6.1
|
||||||
|
|
||||||
Master's thesis project.
|
|
14
build.sbt
Normal file
14
build.sbt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
name := "MasterThesisProject"
|
||||||
|
|
||||||
|
version := "0.1"
|
||||||
|
|
||||||
|
scalaVersion := "2.12.6"
|
||||||
|
|
||||||
|
// https://mvnrepository.com/artifact/org.smali/baksmali
|
||||||
|
libraryDependencies += "org.smali" % "baksmali" % "2.2.3"
|
||||||
|
|
||||||
|
// https://mvnrepository.com/artifact/com.typesafe.scala-logging/scala-logging
|
||||||
|
libraryDependencies += "com.typesafe.scala-logging" %% "scala-logging" % "3.9.0"
|
||||||
|
|
||||||
|
// https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
|
||||||
|
libraryDependencies += "ch.qos.logback" % "logback-classic" % "1.2.3"
|
BIN
lib/com.microsoft.z3.jar
Normal file
BIN
lib/com.microsoft.z3.jar
Normal file
Binary file not shown.
1
project/build.properties
Normal file
1
project/build.properties
Normal file
|
@ -0,0 +1 @@
|
||||||
|
sbt.version = 1.1.1
|
182
src/main/resources/android/Callbacks.txt
Normal file
182
src/main/resources/android/Callbacks.txt
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
android.accounts.OnAccountsUpdateListener
|
||||||
|
android.animation.Animator$AnimatorListener
|
||||||
|
android.animation.LayoutTransition$TransitionListener
|
||||||
|
android.animation.TimeAnimator$TimeListener
|
||||||
|
android.animation.ValueAnimator$AnimatorUpdateListener
|
||||||
|
android.app.ActionBar$OnMenuVisibilityListener
|
||||||
|
android.app.ActionBar$OnNavigationListener
|
||||||
|
android.app.ActionBar$TabListener
|
||||||
|
android.app.Application$ActivityLifecycleCallbacks
|
||||||
|
android.app.DatePickerDialog$OnDateSetListener
|
||||||
|
android.app.FragmentBreadCrumbs$OnBreadCrumbClickListener
|
||||||
|
android.app.FragmentManager$OnBackStackChangedListener
|
||||||
|
android.app.KeyguardManager$OnKeyguardExitResult
|
||||||
|
android.app.LoaderManager$LoaderCallbacks
|
||||||
|
android.app.PendingIntent$OnFinished
|
||||||
|
android.app.SearchManager$OnCancelListener
|
||||||
|
android.app.SearchManager$OnDismissListener
|
||||||
|
android.app.TimePickerDialog$OnTimeSetListener
|
||||||
|
android.bluetooth.BluetoothProfile$ServiceListener
|
||||||
|
android.content.ServiceConnection
|
||||||
|
android.content.ClipboardManager$OnPrimaryClipChangedListener
|
||||||
|
android.content.ComponentCallbacks
|
||||||
|
android.content.ComponentCallbacks2
|
||||||
|
android.content.DialogInterface$OnCancelListener
|
||||||
|
android.content.DialogInterface$OnClickListener
|
||||||
|
android.content.DialogInterface$OnDismissListener
|
||||||
|
android.content.DialogInterface$OnKeyListener
|
||||||
|
android.content.DialogInterface$OnMultiChoiceClickListener
|
||||||
|
android.content.DialogInterface$OnShowListener
|
||||||
|
android.content.IntentSender$OnFinished
|
||||||
|
android.content.Loader$OnLoadCanceledListener
|
||||||
|
android.content.Loader$OnLoadCompleteListener
|
||||||
|
android.content.SharedPreferences$OnSharedPreferenceChangeListener
|
||||||
|
android.content.SyncStatusObserver
|
||||||
|
android.database.sqlite.SQLiteTransactionListener
|
||||||
|
android.drm.DrmManagerClient$OnErrorListener
|
||||||
|
android.drm.DrmManagerClient$OnEventListener
|
||||||
|
android.drm.DrmManagerClient$OnInfoListener
|
||||||
|
android.gesture.GestureOverlayView$OnGestureListener
|
||||||
|
android.gesture.GestureOverlayView$OnGesturePerformedListener
|
||||||
|
android.gesture.GestureOverlayView$OnGesturingListener
|
||||||
|
android.graphics.SurfaceTexture$OnFrameAvailableListener
|
||||||
|
android.hardware.Camera$AutoFocusCallback
|
||||||
|
android.hardware.Camera$AutoFocusMoveCallback
|
||||||
|
android.hardware.Camera$ErrorCallback
|
||||||
|
android.hardware.Camera$FaceDetectionListener
|
||||||
|
android.hardware.Camera$OnZoomChangeListener
|
||||||
|
android.hardware.Camera$PictureCallback
|
||||||
|
android.hardware.Camera$PreviewCallback
|
||||||
|
android.hardware.Camera$ShutterCallback
|
||||||
|
android.hardware.SensorEventListener
|
||||||
|
android.hardware.display.DisplayManager$DisplayListener
|
||||||
|
android.hardware.input.InputManager$InputDeviceListener
|
||||||
|
android.inputmethodservice.KeyboardView$OnKeyboardActionListener
|
||||||
|
android.location.GpsStatus$Listener
|
||||||
|
android.location.GpsStatus$NmeaListener
|
||||||
|
android.location.LocationListener
|
||||||
|
android.media.AudioManager$OnAudioFocusChangeListener
|
||||||
|
android.media.AudioRecord$OnRecordPositionUpdateListener
|
||||||
|
android.media.JetPlayer$OnJetEventListener
|
||||||
|
android.media.MediaPlayer$OnBufferingUpdateListener
|
||||||
|
android.media.MediaPlayer$OnCompletionListener
|
||||||
|
android.media.MediaPlayer$OnErrorListener
|
||||||
|
android.media.MediaPlayer$OnInfoListener
|
||||||
|
android.media.MediaPlayer$OnPreparedListener
|
||||||
|
android.media.MediaPlayer$OnSeekCompleteListener
|
||||||
|
android.media.MediaPlayer$OnTimedTextListener
|
||||||
|
android.media.MediaPlayer$OnVideoSizeChangedListener
|
||||||
|
android.media.MediaRecorder$OnErrorListener
|
||||||
|
android.media.MediaRecorder$OnInfoListener
|
||||||
|
android.media.MediaScannerConnection$MediaScannerConnectionClient
|
||||||
|
android.media.MediaScannerConnection$OnScanCompletedListener
|
||||||
|
android.media.SoundPool$OnLoadCompleteListener
|
||||||
|
android.media.audiofx.AudioEffect$OnControlStatusChangeListener
|
||||||
|
android.media.audiofx.AudioEffect$OnEnableStatusChangeListener
|
||||||
|
android.media.audiofx.BassBoost$OnParameterChangeListener
|
||||||
|
android.media.audiofx.EnvironmentalReverb$OnParameterChangeListener
|
||||||
|
android.media.audiofx.Equalizer$OnParameterChangeListener
|
||||||
|
android.media.audiofx.PresetReverb$OnParameterChangeListener
|
||||||
|
android.media.audiofx.Virtualizer$OnParameterChangeListener
|
||||||
|
android.media.audiofx.Visualizer$OnDataCaptureListener
|
||||||
|
android.media.effect$EffectUpdateListener
|
||||||
|
android.net.nsd.NsdManager$DiscoveryListener
|
||||||
|
android.net.nsd.NsdManager$RegistrationListener
|
||||||
|
android.net.nsd.NsdManager$ResolveListener
|
||||||
|
android.net.sip.SipRegistrationListener
|
||||||
|
android.net.wifi.p2p.WifiP2pManager$ActionListener
|
||||||
|
android.net.wifi.p2p.WifiP2pManager$ChannelListener
|
||||||
|
android.net.wifi.p2p.WifiP2pManager$ConnectionInfoListener
|
||||||
|
android.net.wifi.p2p.WifiP2pManager$DnsSdServiceResponseListener
|
||||||
|
android.net.wifi.p2p.WifiP2pManager$DnsSdTxtRecordListener
|
||||||
|
android.net.wifi.p2p.WifiP2pManager$GroupInfoListener
|
||||||
|
android.net.wifi.p2p.WifiP2pManager$PeerListListener
|
||||||
|
android.net.wifi.p2p.WifiP2pManager$ServiceResponseListener
|
||||||
|
android.net.wifi.p2p.WifiP2pManager$UpnpServiceResponseListener
|
||||||
|
android.os.CancellationSignal$OnCancelListener
|
||||||
|
android.os.IBinder$DeathRecipient
|
||||||
|
android.os.MessageQueue$IdleHandler
|
||||||
|
android.os.RecoverySystem$ProgressListener
|
||||||
|
android.preference.Preference$OnPreferenceChangeListener
|
||||||
|
android.preference.Preference$OnPreferenceClickListener
|
||||||
|
android.preference.PreferenceFragment$OnPreferenceStartFragmentCallback
|
||||||
|
android.preference.PreferenceManager$OnActivityDestroyListener
|
||||||
|
android.preference.PreferenceManager$OnActivityResultListener
|
||||||
|
android.preference.PreferenceManager$OnActivityStopListener
|
||||||
|
android.security.KeyChainAliasCallback
|
||||||
|
android.speech.RecognitionListener
|
||||||
|
android.speech.tts.TextToSpeech$OnInitListener
|
||||||
|
android.speech.tts.TextToSpeech$OnUtteranceCompletedListener
|
||||||
|
android.view.ActionMode$Callback
|
||||||
|
android.view.ActionProvider$VisibilityListener
|
||||||
|
android.view.GestureDetector$OnDoubleTapListener
|
||||||
|
android.view.GestureDetector$OnGestureListener
|
||||||
|
android.view.InputQueue$Callback
|
||||||
|
android.view.KeyEvent$Callback
|
||||||
|
android.view.MenuItem$OnActionExpandListener
|
||||||
|
android.view.MenuItem$OnMenuItemClickListener
|
||||||
|
android.view.ScaleGestureDetector$OnScaleGestureListener
|
||||||
|
android.view.SurfaceHolder$Callback
|
||||||
|
android.view.SurfaceHolder$Callback2
|
||||||
|
android.view.TextureView$SurfaceTextureListener
|
||||||
|
android.view.View$OnAttachStateChangeListener
|
||||||
|
android.view.View$OnClickListener
|
||||||
|
android.view.View$OnCreateContextMenuListener
|
||||||
|
android.view.View$OnDragListener
|
||||||
|
android.view.View$OnFocusChangeListener
|
||||||
|
android.view.View$OnGenericMotionListener
|
||||||
|
android.view.View$OnHoverListener
|
||||||
|
android.view.View$OnKeyListener
|
||||||
|
android.view.View$OnLayoutChangeListener
|
||||||
|
android.view.View$OnLongClickListener
|
||||||
|
android.view.View$OnSystemUiVisibilityChangeListener
|
||||||
|
android.view.View$OnTouchListener
|
||||||
|
android.view.ViewGroup$OnHierarchyChangeListener
|
||||||
|
android.view.ViewStub$OnInflateListener
|
||||||
|
android.view.ViewTreeObserver$OnDrawListener
|
||||||
|
android.view.ViewTreeObserver$OnGlobalFocusChangeListener
|
||||||
|
android.view.ViewTreeObserver$OnGlobalLayoutListener
|
||||||
|
android.view.ViewTreeObserver$OnPreDrawListener
|
||||||
|
android.view.ViewTreeObserver$OnScrollChangedListener
|
||||||
|
android.view.ViewTreeObserver$OnTouchModeChangeListener
|
||||||
|
android.view.accessibility.AccessibilityManager$AccessibilityStateChangeListener
|
||||||
|
android.view.animation.Animation$AnimationListener
|
||||||
|
android.view.inputmethod.InputMethod$SessionCallback
|
||||||
|
android.view.inputmethod.InputMethodSession$EventCallback
|
||||||
|
android.view.textservice.SpellCheckerSession$SpellCheckerSessionListener
|
||||||
|
android.webkit.DownloadListener
|
||||||
|
android.widget.AbsListView$MultiChoiceModeListener
|
||||||
|
android.widget.AbsListView$OnScrollListener
|
||||||
|
android.widget.AbsListView$RecyclerListener
|
||||||
|
android.widget.AdapterView$OnItemClickListener
|
||||||
|
android.widget.AdapterView$OnItemLongClickListener
|
||||||
|
android.widget.AdapterView.OnItemSelectedListener
|
||||||
|
android.widget.AutoCompleteTextView$OnDismissListener
|
||||||
|
android.widget.CalendarView$OnDateChangeListener
|
||||||
|
android.widget.Chronometer$OnChronometerTickListener
|
||||||
|
android.widget.CompoundButton$OnCheckedChangeListener
|
||||||
|
android.widget.DatePicker$OnDateChangedListener
|
||||||
|
android.widget.ExpandableListView$OnChildClickListener
|
||||||
|
android.widget.ExpandableListView$OnGroupClickListener
|
||||||
|
android.widget.ExpandableListView$OnGroupCollapseListener
|
||||||
|
android.widget.ExpandableListView$OnGroupExpandListener
|
||||||
|
android.widget.Filter$FilterListener
|
||||||
|
android.widget.NumberPicker$OnScrollListener
|
||||||
|
android.widget.NumberPicker$OnValueChangeListener
|
||||||
|
android.widget.NumberPicker$OnDismissListener
|
||||||
|
android.widget.PopupMenu$OnMenuItemClickListenermk
|
||||||
|
android.widget.PopupWindow$OnDismissListener
|
||||||
|
android.widget.RadioGroup$OnCheckedChangeListener
|
||||||
|
android.widget.RatingBar$OnRatingBarChangeListener
|
||||||
|
android.widget.SearchView$OnCloseListener
|
||||||
|
android.widget.SearchView$OnQueryTextListener
|
||||||
|
android.widget.SearchView$OnSuggestionListener
|
||||||
|
android.widget.SeekBar$OnSeekBarChangeListener
|
||||||
|
android.widget.ShareActionProvider$OnShareTargetSelectedListener
|
||||||
|
android.widget.SlidingDrawer$OnDrawerCloseListener
|
||||||
|
android.widget.SlidingDrawer$OnDrawerOpenListener
|
||||||
|
android.widget.SlidingDrawer$OnDrawerScrollListener
|
||||||
|
android.widget.TabHost$OnTabChangeListener
|
||||||
|
android.widget.TextView$OnEditorActionListener
|
||||||
|
android.widget.TimePicker$OnTimeChangedListener
|
||||||
|
android.widget.ZoomButtonsController$OnZoomListener
|
155
src/main/resources/android/EntryPoints.txt
Normal file
155
src/main/resources/android/EntryPoints.txt
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
%handler
|
||||||
|
Landroid/os/Handler; handleMessage(Landroid/os/Message;)V
|
||||||
|
%content provider
|
||||||
|
Landroid/content/ContentProvider; <init>()V
|
||||||
|
Landroid/content/ContentProvider; onCreate()Z
|
||||||
|
Landroid/content/ContentProvider; onLowMemory()V
|
||||||
|
Landroid/content/ContentProvider; onTrimMemory(I)V
|
||||||
|
%application
|
||||||
|
Landroid/app/Application; <init>()V
|
||||||
|
Landroid/app/Application; onCreate()V
|
||||||
|
Landroid/app/Application; onLowMemory()V
|
||||||
|
Landroid/app/Application; onTerminate()V
|
||||||
|
Landroid/app/Application; onTrimMemory(I)V
|
||||||
|
%activity
|
||||||
|
Landroid/app/Activity; onCreate(Landroid/os/Bundle;)V
|
||||||
|
Landroid/app/Activity; onDestroy()V
|
||||||
|
Landroid/app/Activity; <init>()V
|
||||||
|
Landroid/app/Activity; <clinit>()V
|
||||||
|
Landroid/app/Activity; onResume()V
|
||||||
|
Landroid/app/Activity; onStart()V
|
||||||
|
Landroid/app/Activity; onStop()V
|
||||||
|
Landroid/app/Activity; onPause()V
|
||||||
|
Landroid/app/Activity; onRestart()V
|
||||||
|
Landroid/app/Activity; onUserLeaveHint()V
|
||||||
|
Landroid/app/Activity; onPostResume()V
|
||||||
|
Landroid/app/Activity; onNewIntent(Landroid/content/Intent;)V
|
||||||
|
Landroid/app/Activity; onPostCreate(Landroid/os/Bundle;)V
|
||||||
|
Landroid/app/Activity; onSaveInstanceState(Landroid/os/Bundle;)V
|
||||||
|
Landroid/app/Activity; onRestoreInstanceState(Landroid/os/Bundle;)V
|
||||||
|
Landroid/app/Activity; onLowMemory()V
|
||||||
|
Landroid/app/Activity; onBackPressed()V
|
||||||
|
Landroid/app/Activity; onContentChanged()V
|
||||||
|
Landroid/app/Activity; onDetachedFromWindow()V
|
||||||
|
Landroid/app/Activity; onEnterAnimationComplete()V
|
||||||
|
Landroid/app/Activity; onNavigateUp()V
|
||||||
|
Landroid/app/Activity; onSearchRequested()V
|
||||||
|
Landroid/app/Activity; attachBaseContext(Landroid/content/Context;)V
|
||||||
|
Landroid/app/Activity; onActivityResult(IILandroid/content/Intent;)V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onCreate(Landroid/os/Bundle;)V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onDestroy()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; <init>()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; <clinit>()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onResume()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onStart()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onStop()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onPause()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onRestart()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onUserLeaveHint()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onPostResume()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onNewIntent(Landroid/content/Intent;)V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onPostCreate(Landroid/os/Bundle;)V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onSaveInstanceState(Landroid/os/Bundle;)V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onRestoreInstanceState(Landroid/os/Bundle;)V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onLowMemory()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onBackPressed()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onContentChanged()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onDetachedFromWindow()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onEnterAnimationComplete()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onNavigateUp()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onSearchRequested()V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; attachBaseContext(Landroid/content/Context;)V
|
||||||
|
Landroid/support/v7/app/ActionBarActivity; onActivityResult(IILandroid/content/Intent;)V
|
||||||
|
%service
|
||||||
|
Landroid/app/Service; onBind(Landroid/content/Intent;)Landroid/os/IBinder;
|
||||||
|
Landroid/app/Service; onConfigurationChanged(Landroid/content/res/Configuration;)V
|
||||||
|
Landroid/app/Service; onCreate()V
|
||||||
|
Landroid/app/Service; onDestroy()V
|
||||||
|
Landroid/app/Service; onLowMemory()V
|
||||||
|
Landroid/app/Service; onDestroy()V
|
||||||
|
Landroid/app/Service; onRebind(Landroid/content/Intent;)V
|
||||||
|
Landroid/app/Service; onStart(Landroid/content/Intent;I)V
|
||||||
|
Landroid/app/Service; onStartCommand(Landroid/content/Intent;II)I
|
||||||
|
Landroid/app/Service; onTaskRemoved(Landroid/content/Intent;)V
|
||||||
|
Landroid/app/Service; onTrimMemory(I)V
|
||||||
|
Landroid/app/Service; onUnbind(Landroid/content/Intent;)Z
|
||||||
|
Landroid/app/Service; <init>()V
|
||||||
|
Landroid/app/Service; <clinit>()V
|
||||||
|
%broadcast receiver
|
||||||
|
Landroid/content/BroadcastReceiver; onReceive(Landroid/content/Context;Landroid/content/Intent;)V
|
||||||
|
Landroid/content/BroadcastReceiver; <init>()V
|
||||||
|
Landroid/content/BroadcastReceiver; <clinit>()V
|
||||||
|
%fragment
|
||||||
|
Landroid/app/Fragment; onActivityCreated(Landroid/os/Bundle;)V
|
||||||
|
Landroid/app/Fragment; onCreate(Landroid/os/Bundle;)V
|
||||||
|
Landroid/app/Fragment; onDestroy()V
|
||||||
|
Landroid/app/Fragment; onDestroyOptionsMenu()V
|
||||||
|
Landroid/app/Fragment; onDetach()V
|
||||||
|
Landroid/app/Fragment; onLowMemory()V
|
||||||
|
Landroid/app/Fragment; onPause()V
|
||||||
|
Landroid/app/Fragment; onResume()V
|
||||||
|
Landroid/app/Fragment; onStart()V
|
||||||
|
Landroid/app/Fragment; onStop()V
|
||||||
|
Landroid/app/Fragment; onTrimMemory(I)V
|
||||||
|
Landroid/app/Fragment; <init>()V
|
||||||
|
Landroid/app/Fragment; <clinit>()V
|
||||||
|
Landroid/app/Fragment; onAttach(Landroid/app/Activity;)V
|
||||||
|
Landroid/support/v4/app/FragmentActivity; onActivityCreated(Landroid/os/Bundle;)V
|
||||||
|
Landroid/support/v4/app/FragmentActivity; onCreate(Landroid/os/Bundle;)V
|
||||||
|
Landroid/support/v4/app/FragmentActivity; onDestroy()V
|
||||||
|
Landroid/support/v4/app/FragmentActivity; onDestroyOptionsMenu()V
|
||||||
|
Landroid/support/v4/app/FragmentActivity; onDetach()V
|
||||||
|
Landroid/support/v4/app/FragmentActivity; onLowMemory()V
|
||||||
|
Landroid/support/v4/app/FragmentActivity; onPause()V
|
||||||
|
Landroid/support/v4/app/FragmentActivity; onResume()V
|
||||||
|
Landroid/support/v4/app/FragmentActivity; onStart()V
|
||||||
|
Landroid/support/v4/app/FragmentActivity; onStop()V
|
||||||
|
Landroid/support/v4/app/FragmentActivity; onTrimMemory(I)V
|
||||||
|
Landroid/support/v4/app/FragmentActivity; <init>()V
|
||||||
|
Landroid/support/v4/app/FragmentActivity; <clinit>()V
|
||||||
|
Landroid/support/v4/app/FragmentActivity; onAttach(Landroid/app/Activity;)V
|
||||||
|
Landroid/support/v4/app/Fragment; onActivityCreated(Landroid/os/Bundle;)V
|
||||||
|
Landroid/support/v4/app/Fragment; onCreate(Landroid/os/Bundle;)V
|
||||||
|
Landroid/support/v4/app/Fragment; onDestroy()V
|
||||||
|
Landroid/support/v4/app/Fragment; onDestroyOptionsMenu()V
|
||||||
|
Landroid/support/v4/app/Fragment; onDetach()V
|
||||||
|
Landroid/support/v4/app/Fragment; onLowMemory()V
|
||||||
|
Landroid/support/v4/app/Fragment; onPause()V
|
||||||
|
Landroid/support/v4/app/Fragment; onResume()V
|
||||||
|
Landroid/support/v4/app/Fragment; onStart()V
|
||||||
|
Landroid/support/v4/app/Fragment; onStop()V
|
||||||
|
Landroid/support/v4/app/Fragment; onTrimMemory(I)V
|
||||||
|
Landroid/support/v4/app/Fragment; <init>()V
|
||||||
|
Landroid/support/v4/app/Fragment; <clinit>()V
|
||||||
|
Landroid/support/v4/app/Fragment; onAttach(Landroid/app/Activity;)V
|
||||||
|
%list fragment
|
||||||
|
Landroid/app/ListFragment; <init>()V
|
||||||
|
Landroid/app/ListFragment; <clinit>()V
|
||||||
|
Landroid/app/ListFragment; onAttach(Landroid/app/Activity;)V
|
||||||
|
Landroid/app/ListFragment; onCreate(Landroid/os/Bundle;)V
|
||||||
|
Landroid/app/ListFragment; onListItemClick(Landroid/widget/ListView;Landroid/view/View;IJ)V
|
||||||
|
Landroid/app/ListFragment; onStart()V
|
||||||
|
Landroid/app/ListFragment; onStop()V
|
||||||
|
Landroid/app/ListFragment; onDetach()V
|
||||||
|
Landroid/app/ListFragment; onActivityCreated(Landroid/os/Bundle;)V
|
||||||
|
Landroid/app/ListFragment; onTrimMemory(I)V
|
||||||
|
Landroid/app/ListFragment; onActivityCreated(Landroid/os/Bundle;)V
|
||||||
|
Landroid/app/ListFragment; onDestroy()V
|
||||||
|
Landroid/app/ListFragment; onDestroyOptionsMenu()V
|
||||||
|
Landroid/app/ListFragment; onPause()V
|
||||||
|
Landroid/app/ListFragment; onResume()V
|
||||||
|
Landroid/support/v4/app/ListFragment; <init>()V
|
||||||
|
Landroid/support/v4/app/ListFragment; <clinit>()V
|
||||||
|
Landroid/support/v4/app/ListFragment; onAttach(Landroid/app/Activity;)V
|
||||||
|
Landroid/support/v4/app/ListFragment; onCreate(Landroid/os/Bundle;)V
|
||||||
|
Landroid/support/v4/app/ListFragment; onListItemClick(Landroid/widget/ListView;Landroid/view/View;IJ)V
|
||||||
|
Landroid/support/v4/app/ListFragment; onStart()V
|
||||||
|
Landroid/support/v4/app/ListFragment; onStop()V
|
||||||
|
Landroid/support/v4/app/ListFragment; onDetach()V
|
||||||
|
Landroid/support/v4/app/ListFragment; onActivityCreated(Landroid/os/Bundle;)V
|
||||||
|
Landroid/support/v4/app/ListFragment; onTrimMemory(I)V
|
||||||
|
Landroid/support/v4/app/ListFragment; onActivityCreated(Landroid/os/Bundle;)V
|
||||||
|
Landroid/support/v4/app/ListFragment; onDestroy()V
|
||||||
|
Landroid/support/v4/app/ListFragment; onDestroyOptionsMenu()V
|
||||||
|
Landroid/support/v4/app/ListFragment; onPause()V
|
||||||
|
Landroid/support/v4/app/ListFragment; onResume()V
|
40985
src/main/resources/android/SourcesAndSinks.txt
Normal file
40985
src/main/resources/android/SourcesAndSinks.txt
Normal file
File diff suppressed because it is too large
Load Diff
BIN
src/main/resources/libz3.so
Executable file
BIN
src/main/resources/libz3.so
Executable file
Binary file not shown.
BIN
src/main/resources/libz3java.so
Executable file
BIN
src/main/resources/libz3java.so
Executable file
Binary file not shown.
91
src/main/scala/Main.scala
Normal file
91
src/main/scala/Main.scala
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
import java.io.{File, PrintWriter}
|
||||||
|
import java.time.{Duration, Instant, LocalTime}
|
||||||
|
import java.util
|
||||||
|
|
||||||
|
import analysis.{Analyzer, PreAnalyzer}
|
||||||
|
import ch.qos.logback.classic.{Level, Logger}
|
||||||
|
import com.microsoft.z3._
|
||||||
|
import com.typesafe.scalalogging.StrictLogging
|
||||||
|
import constraints.Z3Constraint
|
||||||
|
import domain._
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import parser.Parser
|
||||||
|
|
||||||
|
object Main extends App with StrictLogging {
|
||||||
|
|
||||||
|
val help = """Usage:
|
||||||
|
-apk=FILE Apk file to analyze
|
||||||
|
-print-rules Print the textual description of the constraints
|
||||||
|
-print-goal Print the Z3 goal
|
||||||
|
"""
|
||||||
|
|
||||||
|
// Check parameters
|
||||||
|
if (args.length == 0) {
|
||||||
|
println(help)
|
||||||
|
System.exit(1)
|
||||||
|
}
|
||||||
|
val apkArg = args.find(a => a.startsWith("-apk"))
|
||||||
|
if (apkArg.isEmpty) {
|
||||||
|
println("No apk given")
|
||||||
|
System.exit(1)
|
||||||
|
}
|
||||||
|
val apkLocation: String = apkArg.get.split("=")(1)
|
||||||
|
val printRules: Boolean = args.exists(a => a.startsWith("-print-rules"))
|
||||||
|
val printGoal: Boolean = args.exists(a => a.startsWith("-print-goal"))
|
||||||
|
|
||||||
|
val parser = new Parser(apkLocation)
|
||||||
|
var start = Instant.now()
|
||||||
|
logger.info(s"Start: ${LocalTime.now()}")
|
||||||
|
|
||||||
|
// Context
|
||||||
|
val cfg = new util.HashMap[String, String]
|
||||||
|
cfg.put("model", "true")
|
||||||
|
val ctx = new Context(cfg)
|
||||||
|
// Goal
|
||||||
|
val g = ctx.mkGoal(true, false, false)
|
||||||
|
|
||||||
|
val domainsCollection =
|
||||||
|
AbstractDomainsCollection(
|
||||||
|
new TopDomain(ctx),
|
||||||
|
new ReferenceDomain(ctx),
|
||||||
|
new TaintDomain(ctx)
|
||||||
|
)
|
||||||
|
val app = parser.dexApp
|
||||||
|
val preAnalysisResult = new PreAnalyzer(app.classes).analyze
|
||||||
|
val analyzer =
|
||||||
|
new Analyzer(ctx, domainsCollection, app, preAnalysisResult)
|
||||||
|
val constraints: Seq[Z3Constraint] = analyzer.analyze
|
||||||
|
g.add(
|
||||||
|
ctx.mkAnd(
|
||||||
|
constraints.map(c => c.boolExpr): _*
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (printRules) {
|
||||||
|
println("Rules:")
|
||||||
|
println(
|
||||||
|
constraints
|
||||||
|
.map(c => s"${c.pp.m.shortName}-${c.pp.pc} ${c.description}")
|
||||||
|
.mkString("\n")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (printGoal) {
|
||||||
|
println("Goal:")
|
||||||
|
println(g)
|
||||||
|
}
|
||||||
|
var end = Instant.now()
|
||||||
|
logger.info(
|
||||||
|
s"Time to generate goal: ${Duration.between(start, end).getSeconds} seconds")
|
||||||
|
start = Instant.now()
|
||||||
|
val solver = ctx.mkSolver
|
||||||
|
g.getFormulas.foreach(solver.add(_))
|
||||||
|
val status = solver.check()
|
||||||
|
end = Instant.now()
|
||||||
|
logger.info(
|
||||||
|
s"Time to check goal: ${Duration.between(start, end).getSeconds} seconds")
|
||||||
|
status match {
|
||||||
|
case Status.SATISFIABLE => println("OK")
|
||||||
|
case Status.UNSATISFIABLE => println("LEAK")
|
||||||
|
case Status.UNKNOWN => println(s"UNKNOWN: ${solver.getReasonUnknown}")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
158
src/main/scala/analysis/Analyzer.scala
Normal file
158
src/main/scala/analysis/Analyzer.scala
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
package analysis
|
||||||
|
|
||||||
|
import com.microsoft.z3._
|
||||||
|
import com.typesafe.scalalogging.StrictLogging
|
||||||
|
import constraints.Z3Constraint
|
||||||
|
import dex.{DexApp, DexClass, DexMethod}
|
||||||
|
import domain.AbstractDomainsCollection
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.iface.instruction.Instruction
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats._
|
||||||
|
|
||||||
|
import scala.collection.mutable.ArrayBuffer
|
||||||
|
|
||||||
|
class Analyzer(
|
||||||
|
ctx: Context,
|
||||||
|
domains: AbstractDomainsCollection,
|
||||||
|
app: DexApp,
|
||||||
|
preAnalysisResult: PreAnalysisResult
|
||||||
|
) extends StrictLogging {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methods to analyze. This collection is populated while analyzing each instruction.
|
||||||
|
*/
|
||||||
|
private var methodsToAnalyze: ArrayBuffer[(DexClass, DexMethod)] =
|
||||||
|
ArrayBuffer[(DexClass, DexMethod)]()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analyze the program.
|
||||||
|
* @return the z3 constraints.
|
||||||
|
*/
|
||||||
|
def analyze: Seq[Z3Constraint] = {
|
||||||
|
/*
|
||||||
|
Add all the entry points (from both manifest and callbacks)
|
||||||
|
to the methods to analyze.
|
||||||
|
*/
|
||||||
|
val entryPoints = app.manifest.entryPoints ++ app.entryPoints
|
||||||
|
methodsToAnalyze = ArrayBuffer(entryPoints: _*)
|
||||||
|
val env = new Environment(ctx, domains, app, preAnalysisResult)
|
||||||
|
analyzeAux(env, 0, List())
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Analyze the methods, starting from the given index, until there are no more methods to analyze.
|
||||||
|
*/
|
||||||
|
def analyzeAux(env: Environment,
|
||||||
|
index: Int,
|
||||||
|
constraints: Seq[Z3Constraint]): Seq[Z3Constraint] = {
|
||||||
|
if (index >= methodsToAnalyze.length) {
|
||||||
|
// No more methods to analyze.
|
||||||
|
return constraints
|
||||||
|
}
|
||||||
|
val (c, m) = methodsToAnalyze(index)
|
||||||
|
logger.debug(s"Analyzing method '${m.name}' of class '${c.name}'")
|
||||||
|
val newConstraints: Seq[Z3Constraint] =
|
||||||
|
m.instructions.zipWithIndex.flatMap {
|
||||||
|
case (i: Instruction, pc: Int) =>
|
||||||
|
logger.info(s"$pc - ${i.getOpcode}")
|
||||||
|
val pp = PP(c, m, pc)
|
||||||
|
i.getOpcode match {
|
||||||
|
case Opcode.CONST_4 =>
|
||||||
|
InstructionAnalyzer.const4(
|
||||||
|
i.asInstanceOf[Instruction11n],
|
||||||
|
pp,
|
||||||
|
env
|
||||||
|
)
|
||||||
|
case Opcode.CONST_STRING =>
|
||||||
|
InstructionAnalyzer.constString(
|
||||||
|
i.asInstanceOf[Instruction21c],
|
||||||
|
pp,
|
||||||
|
env
|
||||||
|
)
|
||||||
|
case Opcode.CONST_STRING_JUMBO =>
|
||||||
|
InstructionAnalyzer.constStringJumbo(
|
||||||
|
i.asInstanceOf[Instruction31c],
|
||||||
|
pp,
|
||||||
|
env
|
||||||
|
)
|
||||||
|
case Opcode.IPUT =>
|
||||||
|
InstructionAnalyzer.iput(
|
||||||
|
i.asInstanceOf[Instruction22c],
|
||||||
|
pp,
|
||||||
|
env
|
||||||
|
)
|
||||||
|
case Opcode.SGET_OBJECT =>
|
||||||
|
InstructionAnalyzer.sgetObject(
|
||||||
|
i.asInstanceOf[Instruction21c],
|
||||||
|
pp,
|
||||||
|
env
|
||||||
|
)
|
||||||
|
case Opcode.SPUT_OBJECT =>
|
||||||
|
InstructionAnalyzer.sputObject(
|
||||||
|
i.asInstanceOf[Instruction21c],
|
||||||
|
pp,
|
||||||
|
env
|
||||||
|
)
|
||||||
|
case Opcode.MOVE_OBJECT =>
|
||||||
|
InstructionAnalyzer.move(
|
||||||
|
i.asInstanceOf[Instruction12x],
|
||||||
|
pp,
|
||||||
|
env
|
||||||
|
)
|
||||||
|
case Opcode.MOVE_RESULT | Opcode.MOVE_RESULT_OBJECT =>
|
||||||
|
InstructionAnalyzer.moveResult(
|
||||||
|
i.asInstanceOf[Instruction11x],
|
||||||
|
pp,
|
||||||
|
env
|
||||||
|
)
|
||||||
|
case Opcode.IF_EQ =>
|
||||||
|
InstructionAnalyzer.ifEq(i.asInstanceOf[Instruction22t], pp, env)
|
||||||
|
case Opcode.IF_NE =>
|
||||||
|
InstructionAnalyzer.ifEq(i.asInstanceOf[Instruction22t], pp, env)
|
||||||
|
case Opcode.GOTO =>
|
||||||
|
InstructionAnalyzer.goto(i.asInstanceOf[Instruction10t], pp, env)
|
||||||
|
case Opcode.NEW_INSTANCE =>
|
||||||
|
InstructionAnalyzer.newInstance(
|
||||||
|
i.asInstanceOf[Instruction21c],
|
||||||
|
pp,
|
||||||
|
env
|
||||||
|
)
|
||||||
|
case Opcode.INVOKE_DIRECT =>
|
||||||
|
InstructionAnalyzer.invokeDirect(
|
||||||
|
i.asInstanceOf[Instruction35c],
|
||||||
|
pp,
|
||||||
|
env,
|
||||||
|
(c, m) => this.addMethodToAnalyze(c, m)
|
||||||
|
)
|
||||||
|
case Opcode.INVOKE_VIRTUAL =>
|
||||||
|
InstructionAnalyzer.invokeVirtual(
|
||||||
|
i.asInstanceOf[Instruction35c],
|
||||||
|
pp,
|
||||||
|
env,
|
||||||
|
(c, m) => this.addMethodToAnalyze(c, m)
|
||||||
|
)
|
||||||
|
case Opcode.RETURN_VOID =>
|
||||||
|
InstructionAnalyzer.retVoid(
|
||||||
|
i.asInstanceOf[Instruction10x],
|
||||||
|
pp,
|
||||||
|
env
|
||||||
|
)
|
||||||
|
case Opcode.RETURN =>
|
||||||
|
InstructionAnalyzer.ret(i.asInstanceOf[Instruction11x], pp, env)
|
||||||
|
case Opcode.CHECK_CAST | Opcode.MOVE_EXCEPTION =>
|
||||||
|
InstructionAnalyzer.nop(i, pp, env)
|
||||||
|
case _ =>
|
||||||
|
logger.warn(s"Not implemented: ${i.getOpcode}, changed to NOP")
|
||||||
|
InstructionAnalyzer.nop(i, pp, env)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
analyzeAux(env, index + 1, constraints ++ newConstraints)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def addMethodToAnalyze(dexClass: DexClass,
|
||||||
|
dexMethod: DexMethod): Unit =
|
||||||
|
if (!methodsToAnalyze.contains((dexClass, dexMethod))) {
|
||||||
|
methodsToAnalyze += ((dexClass, dexMethod))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
334
src/main/scala/analysis/Environment.scala
Normal file
334
src/main/scala/analysis/Environment.scala
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
package analysis
|
||||||
|
|
||||||
|
import com.microsoft.z3._
|
||||||
|
import dex.DexType.{ObjectType, PrimitiveType, VoidType}
|
||||||
|
import dex.{DexApp, DexClass, DexField, DexMethod}
|
||||||
|
import domain.{AbstractDomain, AbstractDomainsCollection}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Environment for the analysis.
|
||||||
|
*/
|
||||||
|
class Environment(
|
||||||
|
val ctx: Context,
|
||||||
|
val domains: AbstractDomainsCollection,
|
||||||
|
val app: DexApp,
|
||||||
|
val preAnalysisResult: PreAnalysisResult
|
||||||
|
) {
|
||||||
|
|
||||||
|
val z3Obj = new Z3Object(ctx, domains)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract registers.
|
||||||
|
*/
|
||||||
|
object R {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Index of the ret register.
|
||||||
|
*/
|
||||||
|
val retIdx: Int = -1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approximation of a register.
|
||||||
|
*/
|
||||||
|
class RApprox(
|
||||||
|
val numApprox: BoolExpr,
|
||||||
|
val referenceApprox: BoolExpr,
|
||||||
|
val taintApprox: BoolExpr
|
||||||
|
) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register at a program point.
|
||||||
|
*/
|
||||||
|
class RAtPP(
|
||||||
|
private val regCount: Int,
|
||||||
|
val numbers: Int => domains.primitiveDomain.Element,
|
||||||
|
val references: Int => domains.referenceDomain.Element,
|
||||||
|
val taints: Int => domains.taintDomain.Element,
|
||||||
|
) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approximate the register to top (taint excluded).
|
||||||
|
* @param i index of the register.
|
||||||
|
* @return a register approximation.
|
||||||
|
*/
|
||||||
|
def top(i: Int): RApprox =
|
||||||
|
new RApprox(
|
||||||
|
domains.primitiveDomain.approx(
|
||||||
|
domains.primitiveDomain.top,
|
||||||
|
this.numbers(i),
|
||||||
|
),
|
||||||
|
domains.referenceDomain.approx(
|
||||||
|
domains.referenceDomain.top,
|
||||||
|
this.references(i),
|
||||||
|
),
|
||||||
|
ctx.mkTrue()
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approximate a register to another.
|
||||||
|
* @param i index of the register that must be approximated.
|
||||||
|
* @param j index of the register that must approximates.
|
||||||
|
* @param r register at the desired program point.
|
||||||
|
* @return a register approximation.
|
||||||
|
*/
|
||||||
|
def <=(i: Int, j: Int)(r: RAtPP): RApprox =
|
||||||
|
new RApprox(
|
||||||
|
domains.primitiveDomain.approx(
|
||||||
|
this.numbers(i),
|
||||||
|
r.numbers(j)
|
||||||
|
),
|
||||||
|
domains.referenceDomain.approx(
|
||||||
|
this.references(i),
|
||||||
|
r.references(j)
|
||||||
|
),
|
||||||
|
domains.taintDomain.approx(
|
||||||
|
this.taints(i),
|
||||||
|
r.taints(j)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approximate all the registers between two program points.
|
||||||
|
* @param r registers at the desider program point.
|
||||||
|
* @return registers approximations.
|
||||||
|
*/
|
||||||
|
def <=(r: RAtPP): List[RApprox] =
|
||||||
|
(-1 until this.regCount)
|
||||||
|
.map(i => this.<=(i, i)(r))
|
||||||
|
.toList
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Approximate all the registers between two program points except for the register at the given index.
|
||||||
|
* @param n index of the register to exclude.
|
||||||
|
* @param r registers at the desider program point.
|
||||||
|
* @return registers approximations.
|
||||||
|
*/
|
||||||
|
def <=(n: Int)(r: RAtPP): List[RApprox] =
|
||||||
|
(-1 until this.regCount)
|
||||||
|
.filter(i => i != n)
|
||||||
|
.map(i => this.<=(i, i)(r))
|
||||||
|
.toList
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the registers at the given program points.
|
||||||
|
* @param pp the program point.
|
||||||
|
* @return the registers.
|
||||||
|
*/
|
||||||
|
def apply(pp: PP): RAtPP =
|
||||||
|
new RAtPP(
|
||||||
|
pp.m.registersCount,
|
||||||
|
rTypeAtPP(pp, domains.primitiveDomain, "R-prim"),
|
||||||
|
rTypeAtPP(pp, domains.referenceDomain, "R-ref"),
|
||||||
|
rTypeAtPP(pp, domains.taintDomain, "R-taint")
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a function that maps a register index to the given domain Element.
|
||||||
|
* @param pp program point of the registers.
|
||||||
|
* @param domain the chosen domain.
|
||||||
|
* @param prefix prefix to distinguish between Elements of different domains.
|
||||||
|
* @tparam E the domain Element type.
|
||||||
|
* @return function that maps the index to the Element.
|
||||||
|
*/
|
||||||
|
private def rTypeAtPP[E](
|
||||||
|
pp: PP,
|
||||||
|
domain: AbstractDomain,
|
||||||
|
prefix: String
|
||||||
|
): Int => E =
|
||||||
|
(i) =>
|
||||||
|
ctx
|
||||||
|
.mkApp(
|
||||||
|
ctx.mkFuncDecl(
|
||||||
|
s"$prefix-${pp.c.name}-${pp.m.name}-${pp.pc}",
|
||||||
|
Array[Sort](ctx.mkIntSort()),
|
||||||
|
domain.sort
|
||||||
|
),
|
||||||
|
ctx.mkInt(i)
|
||||||
|
)
|
||||||
|
.asInstanceOf[E]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a list of registers approximations into a list of Z3 constraints.
|
||||||
|
*/
|
||||||
|
implicit def toBoolExpr(approxMap: List[RApprox]): List[BoolExpr] =
|
||||||
|
approxMap
|
||||||
|
.flatMap(v => toBoolExpr(v))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert a register approximation into a list of Z3 constraints.
|
||||||
|
*/
|
||||||
|
implicit def toBoolExpr(approx: RApprox): List[BoolExpr] =
|
||||||
|
List(approx.numApprox, approx.referenceApprox, approx.taintApprox)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract heap.
|
||||||
|
*/
|
||||||
|
object H {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the heap that maps references to objects.
|
||||||
|
*/
|
||||||
|
def obj(): FuncDecl = {
|
||||||
|
val z3Obj = new Z3Object(ctx, domains)
|
||||||
|
ctx.mkFuncDecl(
|
||||||
|
"H",
|
||||||
|
Array[Sort](ctx.mkStringSort()),
|
||||||
|
z3Obj.sort
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract static heap.
|
||||||
|
*/
|
||||||
|
object SH {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the class field value.
|
||||||
|
* @param dexClass the class.
|
||||||
|
* @param dexField the field.
|
||||||
|
* @return a tuple where the first element is the value and the second the taint.
|
||||||
|
*/
|
||||||
|
def apply(dexClass: DexClass, dexField: DexField): (Expr, Expr) = {
|
||||||
|
(
|
||||||
|
dexField.dexType match {
|
||||||
|
case PrimitiveType() =>
|
||||||
|
const(dexClass, dexField, domains.primitiveDomain.sort, "SH-prim")
|
||||||
|
case ObjectType(_) =>
|
||||||
|
const(dexClass, dexField, domains.referenceDomain.sort, "SH-ref")
|
||||||
|
},
|
||||||
|
const(dexClass, dexField, domains.taintDomain.sort, "SH-taint")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the constant that represent the class field.
|
||||||
|
* @param dexClass the class.
|
||||||
|
* @param dexField the field.
|
||||||
|
* @param sort the type of the constant domain.
|
||||||
|
* @param prefix prefix to distinguish between the same constant in different domains.
|
||||||
|
* @return the constant.
|
||||||
|
*/
|
||||||
|
private def const(
|
||||||
|
dexClass: DexClass,
|
||||||
|
dexField: DexField,
|
||||||
|
sort: Sort,
|
||||||
|
prefix: String
|
||||||
|
): Expr = {
|
||||||
|
ctx.mkConst(s"$prefix-${dexClass.name}-${dexField.name}", sort)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method return value.
|
||||||
|
*/
|
||||||
|
object M {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value returned by the given class method.
|
||||||
|
* @param dexClass the class.
|
||||||
|
* @param dexMethod the method.
|
||||||
|
* @return the value.
|
||||||
|
*/
|
||||||
|
def apply(dexClass: DexClass, dexMethod: DexMethod): Option[Expr] = {
|
||||||
|
dexMethod.returnType match {
|
||||||
|
case PrimitiveType() =>
|
||||||
|
Option(
|
||||||
|
typeReturn(
|
||||||
|
dexClass,
|
||||||
|
dexMethod,
|
||||||
|
domains.primitiveDomain.sort,
|
||||||
|
"M-prim"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case ObjectType(_) =>
|
||||||
|
Option(
|
||||||
|
typeReturn(
|
||||||
|
dexClass,
|
||||||
|
dexMethod,
|
||||||
|
domains.referenceDomain.sort,
|
||||||
|
"M-ref"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case VoidType() => Option.empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method result in the given domain.
|
||||||
|
*/
|
||||||
|
private def typeReturn(
|
||||||
|
dexClass: DexClass,
|
||||||
|
dexMethod: DexMethod,
|
||||||
|
sort: Sort,
|
||||||
|
prefix: String
|
||||||
|
): Expr = {
|
||||||
|
ctx.mkConst(
|
||||||
|
s"$prefix-${dexClass.name}-${dexMethod.name}",
|
||||||
|
sort
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method invocation helper.
|
||||||
|
*/
|
||||||
|
object Dispatch {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a map where the key is the class that has the given
|
||||||
|
* method, either because it implements it or because it is inherited,
|
||||||
|
* and the value is the corresponding class with the method implementation.
|
||||||
|
*/
|
||||||
|
def apply(dexMethod: String,
|
||||||
|
definingClass: String): Map[DexClass, DexClass] = {
|
||||||
|
dispatchAux(dexMethod, definingClass).toMap
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tail recursive search of method implementations.
|
||||||
|
*/
|
||||||
|
private def dispatchAux(
|
||||||
|
dexMethod: String,
|
||||||
|
definingClass: String): Seq[(DexClass, DexClass)] = {
|
||||||
|
val defClass = app.classes.find(c => c.name == definingClass)
|
||||||
|
val impl: Option[DexClass] =
|
||||||
|
defClass match {
|
||||||
|
case Some(c) => firstImpl(dexMethod, c)
|
||||||
|
case None => Option.empty
|
||||||
|
}
|
||||||
|
val impls = app.classes
|
||||||
|
.filter(c => c.superClass == definingClass)
|
||||||
|
.flatMap(
|
||||||
|
c => dispatchAux(dexMethod, c.name)
|
||||||
|
)
|
||||||
|
.toList
|
||||||
|
impl match {
|
||||||
|
case Some(c) => (defClass.get -> c) :: impls
|
||||||
|
case None => impls
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first implementation (if present) of the given method when invoked to the given class.
|
||||||
|
*/
|
||||||
|
private def firstImpl(dexMethod: String,
|
||||||
|
dexClass: DexClass): Option[DexClass] = {
|
||||||
|
dexClass.methods.find(m => m.name == dexMethod) match {
|
||||||
|
case Some(_) => Option(dexClass)
|
||||||
|
case None =>
|
||||||
|
app.classes.find(c => c.name == dexClass.superClass) match {
|
||||||
|
case Some(c) => firstImpl(dexMethod, c)
|
||||||
|
case None => Option.empty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
842
src/main/scala/analysis/InstructionAnalyzer.scala
Normal file
842
src/main/scala/analysis/InstructionAnalyzer.scala
Normal file
|
@ -0,0 +1,842 @@
|
||||||
|
package analysis
|
||||||
|
|
||||||
|
import com.microsoft.z3._
|
||||||
|
import com.typesafe.scalalogging.StrictLogging
|
||||||
|
import constraints.Z3Constraint
|
||||||
|
import dex.DexType.{PrimitiveType, ObjectType, VoidType}
|
||||||
|
import dex.{DexClass, DexMethod, DexType}
|
||||||
|
import org.jf.dexlib2.dexbacked.reference.{
|
||||||
|
DexBackedStringReference,
|
||||||
|
DexBackedTypeReference
|
||||||
|
}
|
||||||
|
import org.jf.dexlib2.iface.instruction.Instruction
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats._
|
||||||
|
import org.jf.dexlib2.iface.reference.MethodReference
|
||||||
|
import utils.Utils
|
||||||
|
|
||||||
|
object InstructionAnalyzer extends StrictLogging {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do nothing. All registers at pp+ approximates the registers at pp.
|
||||||
|
*/
|
||||||
|
def nop(i: Instruction, pp: PP, env: Environment): Seq[Z3Constraint] = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
val rPlus = env.R(pp +)
|
||||||
|
Seq(
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(r <= rPlus: _*),
|
||||||
|
s"R($pp) <= R(${pp +})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def const4(i: Instruction11n, pp: PP, env: Environment): Seq[Z3Constraint] = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
val rPlus = env.R(pp +)
|
||||||
|
val rApprox = r.<=(i.getRegisterA)(rPlus)
|
||||||
|
Seq(
|
||||||
|
Z3Constraint(
|
||||||
|
env.domains.primitiveDomain.approx(
|
||||||
|
env.domains.primitiveDomain.toElement(i.getWideLiteral.toInt),
|
||||||
|
rPlus.numbers(i.getRegisterA)
|
||||||
|
),
|
||||||
|
s"${i.getWideLiteral} <= R(${pp +})(${i.getRegisterA})",
|
||||||
|
pp
|
||||||
|
),
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(rApprox: _*),
|
||||||
|
s"R($pp) <={${i.getRegisterA}} R(${pp +})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def constString(i: Instruction21c,
|
||||||
|
pp: PP,
|
||||||
|
env: Environment): Seq[Z3Constraint] = {
|
||||||
|
constString(
|
||||||
|
i.getRegisterA,
|
||||||
|
i.getReference.asInstanceOf[DexBackedStringReference].getString,
|
||||||
|
pp,
|
||||||
|
env
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def constStringJumbo(i: Instruction31c,
|
||||||
|
pp: PP,
|
||||||
|
env: Environment): Seq[Z3Constraint] = {
|
||||||
|
constString(
|
||||||
|
i.getRegisterA,
|
||||||
|
i.getReference.asInstanceOf[DexBackedStringReference].getString,
|
||||||
|
pp,
|
||||||
|
env
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make the register A approximates str.
|
||||||
|
* Constant strings are treated like instances of Ljava/lang/String;
|
||||||
|
*/
|
||||||
|
private def constString(registerA: Int,
|
||||||
|
str: String,
|
||||||
|
pp: PP,
|
||||||
|
env: Environment): Seq[Z3Constraint] = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
val rPlus = env.R(pp +)
|
||||||
|
Seq(
|
||||||
|
Z3Constraint(
|
||||||
|
env.domains.referenceDomain.approx(
|
||||||
|
env.domains.referenceDomain.toElement(pp.toString),
|
||||||
|
rPlus.references(registerA)
|
||||||
|
),
|
||||||
|
s"($pp) <= R(${pp +})($registerA)",
|
||||||
|
pp
|
||||||
|
),
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(r.<=(registerA)(rPlus): _*),
|
||||||
|
s"R($pp) <={$registerA} R(${pp +})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def iput(i: Instruction22c, pp: PP, env: Environment): Seq[Z3Constraint] = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
val rPlus = env.R(pp +)
|
||||||
|
val rApprox = r.<=(rPlus)
|
||||||
|
val fieldRef = i.getReference.toString
|
||||||
|
val cName = Utils.classPrefix(fieldRef)
|
||||||
|
val fieldName = Utils.removeClassPrefix(fieldRef)
|
||||||
|
val fieldType = DexType(fieldName.substring(fieldName.indexOf(":") + 1))
|
||||||
|
val classes = classAndSubclasses(env)(cName)
|
||||||
|
val obj: FuncDecl = env.H.obj()
|
||||||
|
val objApp = env.ctx.mkApp(obj, env.ctx.mkString(pp.toString))
|
||||||
|
val accessor: FuncDecl = fieldType match {
|
||||||
|
case PrimitiveType() =>
|
||||||
|
env.z3Obj.primAccessor
|
||||||
|
}
|
||||||
|
val objFields =
|
||||||
|
env.ctx.mkApp(accessor, objApp).asInstanceOf[ArrayExpr]
|
||||||
|
val constraints: Seq[Z3Constraint] = classes.flatMap(
|
||||||
|
c =>
|
||||||
|
env.preAnalysisResult.objectCreation
|
||||||
|
.filter(p => p._2 == c.name)
|
||||||
|
.map(objPP => {
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkImplies(
|
||||||
|
env.ctx.mkAnd(
|
||||||
|
env.domains.referenceDomain.approx(
|
||||||
|
env.domains.referenceDomain.toElement(objPP._1.toString),
|
||||||
|
r.references(i.getRegisterB)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
fieldType match {
|
||||||
|
case PrimitiveType() =>
|
||||||
|
env.domains.primitiveDomain.approx(
|
||||||
|
r.numbers(i.getRegisterA),
|
||||||
|
env.ctx
|
||||||
|
.mkSelect(objFields, env.ctx.mkString(fieldName))
|
||||||
|
.asInstanceOf[env.domains.primitiveDomain.Element]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
s"${objPP._1.toString} <= R($pp)(${i.getRegisterB}) => " +
|
||||||
|
s"R($pp)(${i.getRegisterA}) <= H($objPP).$fieldName",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
constraints ++ Seq(
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(rApprox: _*),
|
||||||
|
s"R($pp) <= R(${pp +})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def sgetObject(i: Instruction21c,
|
||||||
|
pp: PP,
|
||||||
|
env: Environment): Seq[Z3Constraint] = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
val rPlus = env.R(pp +)
|
||||||
|
val sFieldRef = i.getReference.toString
|
||||||
|
val cName = Utils.classPrefix(sFieldRef)
|
||||||
|
val sFieldName = Utils.removeClassPrefix(sFieldRef)
|
||||||
|
val sFieldType = DexType(sFieldName.substring(sFieldName.indexOf(":") + 1))
|
||||||
|
val dexClass = env.app.classes.find(c => c.name == cName)
|
||||||
|
val (sFieldVal, sFieldTaint): (Expr, Expr) = dexClass match {
|
||||||
|
case Some(c) =>
|
||||||
|
val f = c.fields.find(dexField => dexField.name == sFieldName).get
|
||||||
|
env.SH(c, f)
|
||||||
|
case None =>
|
||||||
|
// Unknown class, use top as value.
|
||||||
|
sFieldType match {
|
||||||
|
case PrimitiveType() =>
|
||||||
|
(env.domains.primitiveDomain.top, env.domains.taintDomain.top)
|
||||||
|
case ObjectType(_) =>
|
||||||
|
(env.domains.referenceDomain.top, env.domains.taintDomain.top)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Seq(
|
||||||
|
Z3Constraint(
|
||||||
|
sFieldType match {
|
||||||
|
case PrimitiveType() =>
|
||||||
|
env.domains.primitiveDomain.approx(
|
||||||
|
sFieldVal.asInstanceOf[env.domains.primitiveDomain.Element],
|
||||||
|
rPlus.numbers(i.getRegisterA)
|
||||||
|
)
|
||||||
|
case ObjectType(_) =>
|
||||||
|
env.domains.referenceDomain.approx(
|
||||||
|
sFieldVal.asInstanceOf[env.domains.referenceDomain.Element],
|
||||||
|
rPlus.references(i.getRegisterA)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
s"SH($cName,$sFieldName) <= R(${pp +})(${i.getRegisterA})",
|
||||||
|
pp
|
||||||
|
),
|
||||||
|
Z3Constraint(
|
||||||
|
env.domains.taintDomain.approx(
|
||||||
|
sFieldTaint.asInstanceOf[env.domains.taintDomain.Element],
|
||||||
|
rPlus.taints(i.getRegisterA)
|
||||||
|
),
|
||||||
|
s"Taint R($pp)(${i.getRegisterA}) if SH($cName,$sFieldName) is tainted",
|
||||||
|
pp
|
||||||
|
),
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(r.<=(i.getRegisterA)(rPlus): _*),
|
||||||
|
s"R($pp) <={${i.getRegisterA}} R(${pp +})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def sputObject(i: Instruction21c,
|
||||||
|
pp: PP,
|
||||||
|
env: Environment): Seq[Z3Constraint] = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
val rPlus = env.R(pp +)
|
||||||
|
val sFieldRef = i.getReference.toString
|
||||||
|
val cName = Utils.classPrefix(sFieldRef)
|
||||||
|
val sFieldName = Utils.removeClassPrefix(sFieldRef)
|
||||||
|
val sFieldType = DexType(sFieldName.substring(sFieldName.indexOf(":") + 1))
|
||||||
|
val dexClass = env.app.classes.find(c => c.name == cName)
|
||||||
|
val (sFieldVal, sFieldTaint): (Expr, Expr) = dexClass match {
|
||||||
|
case Some(c) =>
|
||||||
|
val f = c.fields.find(dexField => dexField.name == sFieldName).get
|
||||||
|
env.SH(c, f)
|
||||||
|
case None =>
|
||||||
|
// Unknown class, use top as value.
|
||||||
|
sFieldType match {
|
||||||
|
case PrimitiveType() =>
|
||||||
|
(env.domains.primitiveDomain.top, env.domains.taintDomain.top)
|
||||||
|
case ObjectType(_) =>
|
||||||
|
(env.domains.referenceDomain.top, env.domains.taintDomain.top)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Seq(
|
||||||
|
Z3Constraint(
|
||||||
|
sFieldType match {
|
||||||
|
case PrimitiveType() =>
|
||||||
|
env.domains.primitiveDomain.approx(
|
||||||
|
r.numbers(i.getRegisterA),
|
||||||
|
sFieldVal.asInstanceOf[env.domains.primitiveDomain.Element]
|
||||||
|
)
|
||||||
|
case ObjectType(_) =>
|
||||||
|
env.domains.referenceDomain.approx(
|
||||||
|
r.references(i.getRegisterA),
|
||||||
|
sFieldVal.asInstanceOf[env.domains.referenceDomain.Element]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
s"R($pp)(${i.getRegisterA}) <= SH($cName,$sFieldName)",
|
||||||
|
pp
|
||||||
|
),
|
||||||
|
Z3Constraint(
|
||||||
|
env.domains.taintDomain.approx(
|
||||||
|
r.taints(i.getRegisterA),
|
||||||
|
sFieldTaint.asInstanceOf[env.domains.taintDomain.Element]
|
||||||
|
),
|
||||||
|
s"Taint SH($cName,$sFieldName) if R($pp)(${i.getRegisterA}) is tainted",
|
||||||
|
pp
|
||||||
|
),
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(r <= rPlus: _*),
|
||||||
|
s"R($pp) <= R(${pp +})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def move(i: Instruction12x, pp: PP, env: Environment): Seq[Z3Constraint] = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
val rPlus = env.R(pp +)
|
||||||
|
val rApprox = r.<=(i.getRegisterA)(rPlus)
|
||||||
|
Seq(
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(
|
||||||
|
r.<=(i.getRegisterB, i.getRegisterA)(rPlus): _*
|
||||||
|
),
|
||||||
|
s"R($pp)(${i.getRegisterB}) <= R(${pp +})(${i.getRegisterA})",
|
||||||
|
pp
|
||||||
|
),
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(rApprox: _*),
|
||||||
|
s"R($pp) <={${i.getRegisterA}} R(${pp +})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def moveResult(i: Instruction11x,
|
||||||
|
pp: PP,
|
||||||
|
env: Environment): Seq[Z3Constraint] = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
val rPlus = env.R(pp +)
|
||||||
|
val rApprox = r.<=(i.getRegisterA)(rPlus)
|
||||||
|
Seq(
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(
|
||||||
|
r.<=(env.R.retIdx, i.getRegisterA)(rPlus): _*
|
||||||
|
),
|
||||||
|
s"R($pp)(ret) <= R(${pp +})(${i.getRegisterA})",
|
||||||
|
pp
|
||||||
|
),
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(rApprox: _*),
|
||||||
|
s"R($pp) <={${i.getRegisterA}} R(${pp +})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def goto(i: Instruction10t, pp: PP, env: Environment): Seq[Z3Constraint] = {
|
||||||
|
val ppGoto = pp.copy(pc = pp.pc + i.getCodeOffset)
|
||||||
|
Seq(
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(env.R(pp) <= env.R(ppGoto): _*),
|
||||||
|
s"$pp: R($pp) <= R($ppGoto)",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def retVoid(i: Instruction10x,
|
||||||
|
pp: PP,
|
||||||
|
env: Environment): Seq[Z3Constraint] = {
|
||||||
|
Seq()
|
||||||
|
}
|
||||||
|
|
||||||
|
def ret(i: Instruction11x, pp: PP, env: Environment): Seq[Z3Constraint] = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
val mResult = env.M(pp.c, pp.m).get
|
||||||
|
pp.m.returnType match {
|
||||||
|
case PrimitiveType() =>
|
||||||
|
Seq(
|
||||||
|
Z3Constraint(
|
||||||
|
env.domains.primitiveDomain.approx(
|
||||||
|
r.numbers(i.getRegisterA),
|
||||||
|
mResult.asInstanceOf[env.domains.primitiveDomain.Element]
|
||||||
|
),
|
||||||
|
s"$pp: R($pp)(${i.getRegisterA}) <= M(${pp.c.name}, ${pp.m.name})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def ifEq(i: Instruction22t, pp: PP, env: Environment): Seq[Z3Constraint] = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
val ppGoto = pp.copy(pc = pp.pc + i.getCodeOffset)
|
||||||
|
Seq(
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(r <= env.R(ppGoto): _*),
|
||||||
|
s"$pp: R($pp) <= R(${pp +})",
|
||||||
|
pp
|
||||||
|
),
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(r <= env.R(pp +): _*),
|
||||||
|
s"$pp: R($pp) <= R($ppGoto)",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def newInstance(i: Instruction21c,
|
||||||
|
pp: PP,
|
||||||
|
env: Environment): Seq[Z3Constraint] = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
val objType = i.getReference.asInstanceOf[DexBackedTypeReference].getType
|
||||||
|
val obj: FuncDecl = env.H.obj()
|
||||||
|
val objApp = env.ctx.mkApp(obj, env.ctx.mkString(pp.toString))
|
||||||
|
val objName =
|
||||||
|
env.ctx.mkApp(env.z3Obj.nameAccessor, objApp).asInstanceOf[SeqExpr]
|
||||||
|
val primFields =
|
||||||
|
env.ctx.mkApp(env.z3Obj.primAccessor, objApp).asInstanceOf[ArrayExpr]
|
||||||
|
val fieldsInitialization: List[Z3Constraint] =
|
||||||
|
env.app.classes.find(c => c.name == objType) match {
|
||||||
|
case Some(dexClass) =>
|
||||||
|
dexClass.fields
|
||||||
|
.map(dexField =>
|
||||||
|
dexField.dexType match {
|
||||||
|
case PrimitiveType() =>
|
||||||
|
Z3Constraint(
|
||||||
|
env.domains.primitiveDomain.approx(
|
||||||
|
env.domains.primitiveDomain.toElement(0),
|
||||||
|
env.ctx
|
||||||
|
.mkSelect(primFields, env.ctx.mkString(dexField.name))
|
||||||
|
.asInstanceOf[env.domains.primitiveDomain.Element]
|
||||||
|
),
|
||||||
|
s"0 <= H($pp).${dexField.name}",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
case ObjectType(_) =>
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkTrue(),
|
||||||
|
"true",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.toList
|
||||||
|
case None => List()
|
||||||
|
}
|
||||||
|
val instanceClass = Z3Constraint(
|
||||||
|
env.ctx.mkEq(objName, env.ctx.mkString(objType)),
|
||||||
|
s"H($pp) = {'$objType',(f->_)^*}",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
val rPlus = env.R(pp +)
|
||||||
|
// Register A of pp+ contains the object pointer (pp).
|
||||||
|
val storeReference =
|
||||||
|
Z3Constraint(
|
||||||
|
env.domains.referenceDomain.approx(
|
||||||
|
env.domains.referenceDomain.toElement(pp.toString),
|
||||||
|
rPlus.references(i.getRegisterA)
|
||||||
|
),
|
||||||
|
s"($pp) <= R(${pp +})(${i.getRegisterA})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
val regApprox = Z3Constraint(
|
||||||
|
env.ctx.mkAnd(r.<=(i.getRegisterA)(rPlus): _*),
|
||||||
|
s"R($pp) <={${i.getRegisterA}} R(${pp +})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
Seq(
|
||||||
|
instanceClass,
|
||||||
|
storeReference,
|
||||||
|
regApprox
|
||||||
|
) ++ fieldsInitialization
|
||||||
|
}
|
||||||
|
|
||||||
|
private def approxParam(env: Environment)(
|
||||||
|
pp: PP,
|
||||||
|
register: Int,
|
||||||
|
dexClass: DexClass,
|
||||||
|
dexMethod: DexMethod,
|
||||||
|
paramIdx: Int
|
||||||
|
): Z3Constraint = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
val ppMethod = PP(dexClass, dexMethod, 0)
|
||||||
|
val rMethod = env.R(ppMethod)
|
||||||
|
val methodParamIdx = dexMethod.localRegistersCount + paramIdx
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(
|
||||||
|
r.<=(register, methodParamIdx)(rMethod): _*
|
||||||
|
),
|
||||||
|
s"R($pp)($register) <= R($ppMethod)($methodParamIdx)",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def approxParameters(env: Environment)(
|
||||||
|
i: Instruction35c,
|
||||||
|
pp: PP,
|
||||||
|
dexClass: DexClass,
|
||||||
|
dexMethod: DexMethod): List[Z3Constraint] = {
|
||||||
|
var paramApprox: List[Z3Constraint] = List()
|
||||||
|
val regCount = i.getRegisterCount
|
||||||
|
if (regCount > 1) {
|
||||||
|
paramApprox = approxParam(env)(pp, i.getRegisterD, dexClass, dexMethod, 0) :: paramApprox
|
||||||
|
if (regCount > 2) {
|
||||||
|
paramApprox = approxParam(env)(
|
||||||
|
pp,
|
||||||
|
i.getRegisterE,
|
||||||
|
dexClass,
|
||||||
|
dexMethod,
|
||||||
|
1
|
||||||
|
) :: paramApprox
|
||||||
|
if (regCount > 3) {
|
||||||
|
paramApprox = approxParam(env)(
|
||||||
|
pp,
|
||||||
|
i.getRegisterF,
|
||||||
|
dexClass,
|
||||||
|
dexMethod,
|
||||||
|
2
|
||||||
|
) :: paramApprox
|
||||||
|
if (regCount > 4) {
|
||||||
|
paramApprox = approxParam(env)(
|
||||||
|
pp,
|
||||||
|
i.getRegisterG,
|
||||||
|
dexClass,
|
||||||
|
dexMethod,
|
||||||
|
3
|
||||||
|
) :: paramApprox
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
paramApprox.reverse
|
||||||
|
}
|
||||||
|
|
||||||
|
private def checkParamLeak(env: Environment)(pp: PP,
|
||||||
|
register: Int): Z3Constraint = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkNot(
|
||||||
|
env.domains.taintDomain.approx(
|
||||||
|
env.domains.taintDomain.top,
|
||||||
|
r.taints(register)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
s"Check R($pp)($register) LEAK",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def checkParametersLeak(env: Environment)(
|
||||||
|
i: Instruction35c,
|
||||||
|
pp: PP
|
||||||
|
): List[Z3Constraint] = {
|
||||||
|
var paramsLeak: List[Z3Constraint] = List()
|
||||||
|
val regCount = i.getRegisterCount
|
||||||
|
if (regCount > 1) {
|
||||||
|
paramsLeak = checkParamLeak(env)(pp, i.getRegisterD) :: paramsLeak
|
||||||
|
if (regCount > 2) {
|
||||||
|
paramsLeak = checkParamLeak(env)(pp, i.getRegisterE) :: paramsLeak
|
||||||
|
if (regCount > 3) {
|
||||||
|
paramsLeak = checkParamLeak(env)(pp, i.getRegisterF) :: paramsLeak
|
||||||
|
if (regCount > 4) {
|
||||||
|
paramsLeak = checkParamLeak(env)(pp, i.getRegisterG) :: paramsLeak
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
paramsLeak.reverse
|
||||||
|
}
|
||||||
|
|
||||||
|
private def taintRegisterBasedOnParam(env: Environment)(
|
||||||
|
pp: PP,
|
||||||
|
register: Int,
|
||||||
|
paramRegister: Int): Z3Constraint = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
val rPlus = env.R(pp +)
|
||||||
|
Z3Constraint(
|
||||||
|
env.domains.taintDomain.approx(
|
||||||
|
r.taints(paramRegister),
|
||||||
|
rPlus.taints(register)
|
||||||
|
),
|
||||||
|
s"Taint R(${pp +})($register) if R($pp)($paramRegister) is tainted",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def taintRegisterBasedOnParameters(env: Environment)(
|
||||||
|
i: Instruction35c,
|
||||||
|
pp: PP,
|
||||||
|
register: Int
|
||||||
|
): List[Z3Constraint] = {
|
||||||
|
var registerTaint: List[Z3Constraint] = List()
|
||||||
|
val paramsCount = i.getRegisterCount
|
||||||
|
if (paramsCount > 1) {
|
||||||
|
registerTaint = taintRegisterBasedOnParam(env)(
|
||||||
|
pp,
|
||||||
|
register,
|
||||||
|
i.getRegisterD) :: registerTaint
|
||||||
|
if (paramsCount > 2) {
|
||||||
|
registerTaint = taintRegisterBasedOnParam(env)(
|
||||||
|
pp,
|
||||||
|
register,
|
||||||
|
i.getRegisterE) :: registerTaint
|
||||||
|
if (paramsCount > 3) {
|
||||||
|
registerTaint = taintRegisterBasedOnParam(env)(
|
||||||
|
pp,
|
||||||
|
register,
|
||||||
|
i.getRegisterF) :: registerTaint
|
||||||
|
if (paramsCount > 4) {
|
||||||
|
registerTaint = taintRegisterBasedOnParam(env)(
|
||||||
|
pp,
|
||||||
|
register,
|
||||||
|
i.getRegisterG) :: registerTaint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
registerTaint.reverse
|
||||||
|
}
|
||||||
|
|
||||||
|
private def taintResBasedOnParam(
|
||||||
|
env: Environment)(pp: PP, register: Int): Z3Constraint = {
|
||||||
|
val r = env.R(pp)
|
||||||
|
val rPlus = env.R(pp +)
|
||||||
|
Z3Constraint(
|
||||||
|
env.domains.taintDomain.approx(
|
||||||
|
r.taints(register),
|
||||||
|
rPlus.taints(env.R.retIdx)
|
||||||
|
),
|
||||||
|
s"Taint R(${pp +})(ret) if R($pp)($register) is tainted",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def taintResultBasedOnParameters(env: Environment)(
|
||||||
|
i: Instruction35c,
|
||||||
|
pp: PP
|
||||||
|
): List[Z3Constraint] = {
|
||||||
|
var resultTaint: List[Z3Constraint] = List()
|
||||||
|
val paramsCount = i.getRegisterCount
|
||||||
|
if (paramsCount > 1) {
|
||||||
|
resultTaint = taintResBasedOnParam(env)(pp, i.getRegisterD) :: resultTaint
|
||||||
|
if (paramsCount > 2) {
|
||||||
|
resultTaint = taintResBasedOnParam(env)(pp, i.getRegisterE) :: resultTaint
|
||||||
|
if (paramsCount > 3) {
|
||||||
|
resultTaint = taintResBasedOnParam(env)(pp, i.getRegisterF) :: resultTaint
|
||||||
|
if (paramsCount > 4) {
|
||||||
|
resultTaint = taintResBasedOnParam(env)(pp, i.getRegisterG) :: resultTaint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resultTaint.reverse
|
||||||
|
}
|
||||||
|
|
||||||
|
def invokeDirect(
|
||||||
|
i: Instruction35c,
|
||||||
|
pp: PP,
|
||||||
|
env: Environment,
|
||||||
|
addMethodToAnalyze: (DexClass, DexMethod) => Unit): Seq[Z3Constraint] = {
|
||||||
|
val mRef = i.getReference.asInstanceOf[MethodReference]
|
||||||
|
// RegisterD: parameter
|
||||||
|
val r = env.R(pp)
|
||||||
|
val rPlus = env.R(pp +)
|
||||||
|
val constraints: Seq[Z3Constraint] =
|
||||||
|
env.app.classes.find(c => c.name == mRef.getDefiningClass) match {
|
||||||
|
case Some(dexClass) =>
|
||||||
|
val method =
|
||||||
|
dexClass.methods
|
||||||
|
.find(m => m.name == Utils.removeClassPrefix(mRef.toString))
|
||||||
|
.get
|
||||||
|
addMethodToAnalyze(dexClass, method)
|
||||||
|
val ppMethod = PP(dexClass, method, 0)
|
||||||
|
val thisApprox = Z3Constraint(
|
||||||
|
env.ctx.mkAnd(
|
||||||
|
r.<=(
|
||||||
|
i.getRegisterC,
|
||||||
|
method.registersCount
|
||||||
|
)(env.R(ppMethod)): _*
|
||||||
|
),
|
||||||
|
s"R($pp)(${i.getRegisterC}) <= R($ppMethod)(${method.registersCount})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
val paramApprox = approxParameters(env)(i, pp, dexClass, method)
|
||||||
|
val returnApprox: Z3Constraint =
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(
|
||||||
|
env
|
||||||
|
.M(dexClass, method)
|
||||||
|
.map(mResult => {
|
||||||
|
method.returnType match {
|
||||||
|
case PrimitiveType() =>
|
||||||
|
env.domains.primitiveDomain.approx(
|
||||||
|
mResult.asInstanceOf[env.domains.primitiveDomain.Element],
|
||||||
|
rPlus
|
||||||
|
.numbers(env.R.retIdx)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.getOrElse(env.ctx.mkTrue())
|
||||||
|
),
|
||||||
|
s"M(${dexClass.name},${method.name}) <= R(${pp +})(ret)",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
(thisApprox :: paramApprox) ++ Seq(returnApprox)
|
||||||
|
case None =>
|
||||||
|
taintRegisterBasedOnParameters(env)(i, pp, i.getRegisterC) ++
|
||||||
|
taintResultBasedOnParameters(env)(i, pp)
|
||||||
|
}
|
||||||
|
constraints ++ Seq(
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(
|
||||||
|
r.<=(env.R.retIdx)(rPlus): _*
|
||||||
|
),
|
||||||
|
s"R($pp) <={ret} R(${pp +})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def invokeVirtual(
|
||||||
|
i: Instruction35c,
|
||||||
|
pp: PP,
|
||||||
|
env: Environment,
|
||||||
|
addMethodToAnalyze: (DexClass, DexMethod) => Unit): Seq[Z3Constraint] = {
|
||||||
|
val mRef = i.getReference.asInstanceOf[MethodReference]
|
||||||
|
val r = env.R(pp)
|
||||||
|
val rPlus = env.R(pp +)
|
||||||
|
val cName = Utils.classPrefix(mRef.toString)
|
||||||
|
val mName = Utils.removeClassPrefix(mRef.toString)
|
||||||
|
val impls: Map[DexClass, DexClass] =
|
||||||
|
env.Dispatch(mName, mRef.getDefiningClass)
|
||||||
|
val constraints: Seq[Z3Constraint] =
|
||||||
|
if (impls.isEmpty) {
|
||||||
|
val returnApprox: Z3Constraint =
|
||||||
|
Z3Constraint(
|
||||||
|
DexType(mName.substring(mName.indexOf(")") + 1)) match {
|
||||||
|
case PrimitiveType() =>
|
||||||
|
env.domains.primitiveDomain.approx(
|
||||||
|
env.domains.primitiveDomain.top,
|
||||||
|
rPlus
|
||||||
|
.numbers(env.R.retIdx)
|
||||||
|
.asInstanceOf[env.domains.primitiveDomain.Element]
|
||||||
|
)
|
||||||
|
case ObjectType(_) =>
|
||||||
|
env.domains.referenceDomain.approx(
|
||||||
|
env.domains.referenceDomain.top,
|
||||||
|
rPlus
|
||||||
|
.references(env.R.retIdx)
|
||||||
|
.asInstanceOf[env.domains.referenceDomain.Element]
|
||||||
|
)
|
||||||
|
case VoidType() =>
|
||||||
|
env.ctx.mkTrue()
|
||||||
|
},
|
||||||
|
s"T <= R(${pp +})(ret)",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
Seq(returnApprox) ++ taintResultBasedOnParameters(env)(i, pp)
|
||||||
|
} else
|
||||||
|
impls
|
||||||
|
.flatMap(impl => {
|
||||||
|
val dexClass = impl._2
|
||||||
|
env.preAnalysisResult.objectCreation
|
||||||
|
.filter(p => p._2 == impl._1.name)
|
||||||
|
.map(objPP => {
|
||||||
|
val method = dexClass.methods
|
||||||
|
.find(m => m.name == Utils.removeClassPrefix(mRef.toString))
|
||||||
|
.get
|
||||||
|
addMethodToAnalyze(dexClass, method)
|
||||||
|
val ppMethod = PP(dexClass, method, 0)
|
||||||
|
val thisApprox = Z3Constraint(
|
||||||
|
env.ctx.mkAnd(
|
||||||
|
r.<=(
|
||||||
|
i.getRegisterC,
|
||||||
|
method.registersCount
|
||||||
|
)(env.R(ppMethod)): _*
|
||||||
|
),
|
||||||
|
s"R($pp)(${i.getRegisterC}) <= R($ppMethod)(${method.registersCount})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
val paramApprox = approxParameters(env)(i, pp, dexClass, method)
|
||||||
|
val returnApprox: Z3Constraint =
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(
|
||||||
|
env
|
||||||
|
.M(dexClass, method)
|
||||||
|
.map(mResult => {
|
||||||
|
method.returnType match {
|
||||||
|
case PrimitiveType() =>
|
||||||
|
env.domains.primitiveDomain.approx(
|
||||||
|
mResult.asInstanceOf[env.domains.primitiveDomain.Element],
|
||||||
|
rPlus
|
||||||
|
.numbers(env.R.retIdx)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.getOrElse(env.ctx.mkTrue())
|
||||||
|
),
|
||||||
|
s"M(${dexClass.name},${method.name}) <= R(${pp +})(ret)",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkImplies(
|
||||||
|
env.ctx.mkAnd(
|
||||||
|
env.domains.referenceDomain.approx(
|
||||||
|
env.domains.referenceDomain.toElement(
|
||||||
|
objPP._1.toString),
|
||||||
|
r.references(i.getRegisterC)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
env.ctx.mkAnd(
|
||||||
|
env.ctx.mkAnd(
|
||||||
|
paramApprox.map(c => c.boolExpr): _*
|
||||||
|
),
|
||||||
|
env.ctx.mkAnd(
|
||||||
|
thisApprox.boolExpr
|
||||||
|
),
|
||||||
|
returnApprox.boolExpr
|
||||||
|
)
|
||||||
|
),
|
||||||
|
s"${objPP._1.toString} <= R($pp)(${i.getRegisterC}) => " +
|
||||||
|
(if (paramApprox.isEmpty) ""
|
||||||
|
else
|
||||||
|
paramApprox
|
||||||
|
.map(c => c.description)
|
||||||
|
.mkString(" ∧ ") + " ∧ ") + thisApprox.description +
|
||||||
|
" ∧ " + returnApprox.description,
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.toList
|
||||||
|
var taintConstraints = List[Z3Constraint]()
|
||||||
|
if (env.app.sourcesSinks.isSource(cName, mName)) {
|
||||||
|
taintConstraints = Z3Constraint(
|
||||||
|
env.domains.taintDomain.approx(
|
||||||
|
env.domains.taintDomain.top,
|
||||||
|
rPlus.taints(env.R.retIdx)
|
||||||
|
),
|
||||||
|
s"Taint R(${pp +})(ret)",
|
||||||
|
pp
|
||||||
|
) :: taintConstraints
|
||||||
|
}
|
||||||
|
if (env.app.sourcesSinks.isSink(cName, mName)) {
|
||||||
|
taintConstraints = taintConstraints ++ (
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkNot(
|
||||||
|
env.domains.taintDomain.approx(
|
||||||
|
env.domains.taintDomain.top,
|
||||||
|
r.taints(i.getRegisterC)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
s"Check R($pp)(${i.getRegisterC}) LEAK",
|
||||||
|
pp
|
||||||
|
) :: checkParametersLeak(env)(i, pp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
constraints ++ taintConstraints ++ Seq(
|
||||||
|
Z3Constraint(
|
||||||
|
env.ctx.mkAnd(
|
||||||
|
r.<=(env.R.retIdx)(rPlus): _*
|
||||||
|
),
|
||||||
|
s"R($pp) <={ret} R(${pp +})",
|
||||||
|
pp
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private def classAndSubclasses(env: Environment)(
|
||||||
|
definingClass: String): List[DexClass] = {
|
||||||
|
env.app.classes.find(c => c.name == definingClass) match {
|
||||||
|
case Some(dexClass) => dexClass :: subclasses(env)(dexClass)
|
||||||
|
case None => List()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def subclasses(env: Environment)(
|
||||||
|
dexClass: DexClass): List[DexClass] = {
|
||||||
|
env.app.classes
|
||||||
|
.filter(c => c.superClass == dexClass.name)
|
||||||
|
.flatMap(c => subclasses(env)(c))
|
||||||
|
.toList
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
20
src/main/scala/analysis/PP.scala
Normal file
20
src/main/scala/analysis/PP.scala
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package analysis
|
||||||
|
|
||||||
|
import dex.{DexClass, DexMethod}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Program point.
|
||||||
|
*/
|
||||||
|
case class PP(c: DexClass, m: DexMethod, pc: Int) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new program point with the program counter increased by 1.
|
||||||
|
*/
|
||||||
|
def + : PP = {
|
||||||
|
this.copy(pc = this.pc + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def toString: String =
|
||||||
|
s"${c.name},${m.name},$pc"
|
||||||
|
|
||||||
|
}
|
8
src/main/scala/analysis/PreAnalysisResult.scala
Normal file
8
src/main/scala/analysis/PreAnalysisResult.scala
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package analysis
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of the pre analysis.
|
||||||
|
* @param objectCreation map where the key is the program point where the object is created,
|
||||||
|
* and the key is the class name.
|
||||||
|
*/
|
||||||
|
case class PreAnalysisResult(objectCreation: Map[PP, String]) {}
|
39
src/main/scala/analysis/PreAnalyzer.scala
Normal file
39
src/main/scala/analysis/PreAnalyzer.scala
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package analysis
|
||||||
|
|
||||||
|
import dex.DexClass
|
||||||
|
import org.jf.dexlib2.Opcode
|
||||||
|
import org.jf.dexlib2.dexbacked.reference.DexBackedTypeReference
|
||||||
|
import org.jf.dexlib2.iface.instruction.{Instruction, ReferenceInstruction}
|
||||||
|
|
||||||
|
class PreAnalyzer(classes: Seq[DexClass]) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre analysis.
|
||||||
|
*/
|
||||||
|
def analyze: PreAnalysisResult = {
|
||||||
|
/*
|
||||||
|
Find all the NEW_INSTANCE instructions and create a map
|
||||||
|
where the key is the program point and the key the class name.
|
||||||
|
*/
|
||||||
|
val objectCreation: Map[PP, String] = classes
|
||||||
|
.flatMap(c => {
|
||||||
|
c.methods.flatMap(m => {
|
||||||
|
m.instructions
|
||||||
|
.filter(i => i.getOpcode == Opcode.NEW_INSTANCE)
|
||||||
|
.zipWithIndex
|
||||||
|
.map {
|
||||||
|
case (i: Instruction, pc: Int) =>
|
||||||
|
val objType = i
|
||||||
|
.asInstanceOf[ReferenceInstruction]
|
||||||
|
.getReference
|
||||||
|
.asInstanceOf[DexBackedTypeReference]
|
||||||
|
.getType
|
||||||
|
(PP(c, m, pc), objType)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.toMap
|
||||||
|
PreAnalysisResult(objectCreation)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
src/main/scala/analysis/Z3Object.scala
Normal file
39
src/main/scala/analysis/Z3Object.scala
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package analysis
|
||||||
|
|
||||||
|
import com.microsoft.z3._
|
||||||
|
import domain.AbstractDomainsCollection
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract object Z3 representation.
|
||||||
|
*/
|
||||||
|
class Z3Object(ctx: Context, domains: AbstractDomainsCollection) {
|
||||||
|
|
||||||
|
private val constructor: Constructor =
|
||||||
|
ctx.mkConstructor(
|
||||||
|
"mk-object",
|
||||||
|
"is-object",
|
||||||
|
Array("name", "prim", "ref", "taint"),
|
||||||
|
Array[Sort](
|
||||||
|
ctx.mkStringSort(),
|
||||||
|
ctx.mkArraySort(ctx.mkStringSort(), domains.primitiveDomain.sort),
|
||||||
|
ctx.mkArraySort(ctx.mkStringSort(), domains.referenceDomain.sort),
|
||||||
|
ctx.mkArraySort(ctx.mkStringSort(), domains.taintDomain.sort)
|
||||||
|
),
|
||||||
|
Array(0, 0, 0, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
val sort: DatatypeSort =
|
||||||
|
ctx.mkDatatypeSort(
|
||||||
|
"Object",
|
||||||
|
Array(
|
||||||
|
constructor
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
private val accessors = constructor.getAccessorDecls
|
||||||
|
|
||||||
|
val nameAccessor: FuncDecl = accessors(0)
|
||||||
|
val primAccessor: FuncDecl = accessors(1)
|
||||||
|
val refAccessor: FuncDecl = accessors(2)
|
||||||
|
val taintAccessor: FuncDecl = accessors(3)
|
||||||
|
}
|
12
src/main/scala/constraints/Z3Constraint.scala
Normal file
12
src/main/scala/constraints/Z3Constraint.scala
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package constraints
|
||||||
|
|
||||||
|
import analysis.PP
|
||||||
|
import com.microsoft.z3.BoolExpr
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constraint to give to Z3.
|
||||||
|
* @param boolExpr the Z3 boolean expression.
|
||||||
|
* @param description text description of the rule.
|
||||||
|
* @param pp the program point where the rule has been generated.
|
||||||
|
*/
|
||||||
|
case class Z3Constraint(boolExpr: BoolExpr, description: String, pp: PP) {}
|
11
src/main/scala/dex/DexApp.scala
Normal file
11
src/main/scala/dex/DexApp.scala
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package dex
|
||||||
|
|
||||||
|
import taint.SourcesSinks
|
||||||
|
|
||||||
|
class DexApp(
|
||||||
|
val manifest: Manifest,
|
||||||
|
val classes: Seq[DexClass],
|
||||||
|
val entryPoints: Seq[(DexClass, DexMethod)],
|
||||||
|
val sourcesSinks: SourcesSinks
|
||||||
|
) {
|
||||||
|
}
|
25
src/main/scala/dex/DexClass.scala
Normal file
25
src/main/scala/dex/DexClass.scala
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
package dex
|
||||||
|
|
||||||
|
import org.jf.dexlib2.iface.instruction.Instruction
|
||||||
|
|
||||||
|
case class DexClass(
|
||||||
|
name: String,
|
||||||
|
superClass: String,
|
||||||
|
interfaces: Seq[String],
|
||||||
|
fields: Seq[DexField],
|
||||||
|
methods: Seq[DexMethod]
|
||||||
|
) {}
|
||||||
|
|
||||||
|
case class DexField(shortName: String, name: String, dexType: DexType) {}
|
||||||
|
|
||||||
|
case class DexMethod(
|
||||||
|
shortName: String,
|
||||||
|
name: String,
|
||||||
|
paramTypes: Seq[DexType],
|
||||||
|
returnType: DexType,
|
||||||
|
registersCount: Int,
|
||||||
|
paramsCount: Int,
|
||||||
|
instructions: Seq[Instruction]
|
||||||
|
) {
|
||||||
|
val localRegistersCount: Int = registersCount - paramsCount
|
||||||
|
}
|
33
src/main/scala/dex/DexType.scala
Normal file
33
src/main/scala/dex/DexType.scala
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package dex
|
||||||
|
|
||||||
|
sealed trait DexType {}
|
||||||
|
|
||||||
|
object DexType {
|
||||||
|
|
||||||
|
case class VoidType private[DexType] () extends DexType {}
|
||||||
|
|
||||||
|
case class PrimitiveType private[DexType] () extends DexType {}
|
||||||
|
|
||||||
|
case class ObjectType private[DexType] (className: String) extends DexType {}
|
||||||
|
|
||||||
|
case class ArrayType private[DexType] (dexType: DexType) extends DexType {}
|
||||||
|
|
||||||
|
def apply(param: String): DexType = {
|
||||||
|
param match {
|
||||||
|
case "V" => VoidType()
|
||||||
|
case "Z" // Boolean
|
||||||
|
| "B" // Byte
|
||||||
|
| "S" // Short
|
||||||
|
| "C" // Char
|
||||||
|
| "I" // Integer
|
||||||
|
| "J" // Long
|
||||||
|
| "F" // Float
|
||||||
|
| "D" // Double
|
||||||
|
=>
|
||||||
|
PrimitiveType()
|
||||||
|
case className if className.startsWith("L") => ObjectType(className)
|
||||||
|
case arrayType if arrayType.startsWith("[") =>
|
||||||
|
ArrayType(apply(arrayType.substring(1)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
6
src/main/scala/dex/Manifest.scala
Normal file
6
src/main/scala/dex/Manifest.scala
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package dex
|
||||||
|
|
||||||
|
class Manifest(
|
||||||
|
val entryPoints: Seq[(DexClass, DexMethod)],
|
||||||
|
val activities: Seq[DexClass]
|
||||||
|
) {}
|
24
src/main/scala/domain/AbstractDomain.scala
Normal file
24
src/main/scala/domain/AbstractDomain.scala
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import com.microsoft.z3.{BoolExpr, Expr, Sort}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lattice.
|
||||||
|
*/
|
||||||
|
trait AbstractDomain {
|
||||||
|
|
||||||
|
type Element <: Expr
|
||||||
|
|
||||||
|
def sort: Sort
|
||||||
|
|
||||||
|
def top: Element
|
||||||
|
|
||||||
|
def bottom: Element
|
||||||
|
|
||||||
|
def meet(e1: Element, e2: Element): Element
|
||||||
|
|
||||||
|
def join(e1: Element, e2: Element): Element
|
||||||
|
|
||||||
|
def approx(e1: Element, e2: Element): BoolExpr
|
||||||
|
|
||||||
|
}
|
7
src/main/scala/domain/AbstractDomainsCollection.scala
Normal file
7
src/main/scala/domain/AbstractDomainsCollection.scala
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
case class AbstractDomainsCollection(
|
||||||
|
primitiveDomain: PrimitiveDomain,
|
||||||
|
referenceDomain: ReferenceDomain,
|
||||||
|
taintDomain: TaintDomain
|
||||||
|
) {}
|
59
src/main/scala/domain/BitVectorSetDomain.scala
Normal file
59
src/main/scala/domain/BitVectorSetDomain.scala
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import com.microsoft.z3._
|
||||||
|
|
||||||
|
class BitVectorSetDomain(ctx: Context) extends PrimitiveDomain {
|
||||||
|
|
||||||
|
override type Element = ArrayExpr
|
||||||
|
|
||||||
|
private val size: Int = 32
|
||||||
|
private val bv: BitVecSort = ctx.mkBitVecSort(size)
|
||||||
|
private val bvSet: SetSort = ctx.mkSetSort(ctx.mkBitVecSort(size))
|
||||||
|
|
||||||
|
override def sort: Sort = bvSet
|
||||||
|
|
||||||
|
override def top: Element = ctx.mkFullSet(bv)
|
||||||
|
|
||||||
|
override def bottom: Element = ctx.mkEmptySet(bv)
|
||||||
|
|
||||||
|
override def meet(e1: Element, e2: Element): Element =
|
||||||
|
ctx.mkSetIntersection(e1, e2)
|
||||||
|
|
||||||
|
override def join(e1: Element, e2: Element): Element =
|
||||||
|
ctx.mkSetUnion(e1, e2)
|
||||||
|
|
||||||
|
override def approx(e1: Element, e2: Element): BoolExpr =
|
||||||
|
ctx.mkSetSubset(
|
||||||
|
e1,
|
||||||
|
e2
|
||||||
|
)
|
||||||
|
|
||||||
|
override def toElement(n: Long): Element =
|
||||||
|
ctx.mkSetAdd(
|
||||||
|
ctx.mkEmptySet(bv),
|
||||||
|
ctx.mkBV(n, size)
|
||||||
|
)
|
||||||
|
|
||||||
|
override def toElement(n: Double): ArrayExpr =
|
||||||
|
ctx.mkSetAdd(
|
||||||
|
ctx.mkEmptySet(bv),
|
||||||
|
ctx.mkFPToIEEEBV(
|
||||||
|
ctx.mkFP(n, ctx.mkFPSort32())
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
override def sum(e1: ArrayExpr, e2: ArrayExpr): ArrayExpr =
|
||||||
|
top
|
||||||
|
|
||||||
|
override def neg(e: ArrayExpr): ArrayExpr =
|
||||||
|
top
|
||||||
|
|
||||||
|
override def div(e1: ArrayExpr, e2: ArrayExpr): ArrayExpr =
|
||||||
|
top
|
||||||
|
|
||||||
|
override def mult(e1: ArrayExpr, e2: ArrayExpr): ArrayExpr =
|
||||||
|
top
|
||||||
|
|
||||||
|
override def rem(e1: ArrayExpr, e2: ArrayExpr): ArrayExpr =
|
||||||
|
top
|
||||||
|
}
|
21
src/main/scala/domain/PrimitiveDomain.scala
Normal file
21
src/main/scala/domain/PrimitiveDomain.scala
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract domain for primitive values.
|
||||||
|
*/
|
||||||
|
trait PrimitiveDomain extends AbstractDomain {
|
||||||
|
|
||||||
|
def toElement(n: Long): Element
|
||||||
|
|
||||||
|
def toElement(n: Double): Element
|
||||||
|
|
||||||
|
def neg(e: Element): Element
|
||||||
|
|
||||||
|
def sum(e1: Element, e2: Element): Element
|
||||||
|
|
||||||
|
def div(e1: Element, e2: Element): Element
|
||||||
|
|
||||||
|
def mult(e1: Element, e2: Element): Element
|
||||||
|
|
||||||
|
def rem(e1: Element, e2: Element): Element
|
||||||
|
}
|
39
src/main/scala/domain/ReferenceDomain.scala
Normal file
39
src/main/scala/domain/ReferenceDomain.scala
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import com.microsoft.z3._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract domain for references.
|
||||||
|
*/
|
||||||
|
class ReferenceDomain(ctx: Context) extends AbstractDomain {
|
||||||
|
|
||||||
|
override type Element = ArrayExpr
|
||||||
|
|
||||||
|
private val stringSort: SeqSort = ctx.mkStringSort()
|
||||||
|
private val stringSet: SetSort = ctx.mkSetSort(stringSort)
|
||||||
|
|
||||||
|
override def sort: Sort = stringSet
|
||||||
|
|
||||||
|
override def top: Element = ctx.mkFullSet(stringSort)
|
||||||
|
|
||||||
|
override def bottom: Element = ctx.mkEmptySet(stringSort)
|
||||||
|
|
||||||
|
override def meet(e1: Element, e2: Element): Element =
|
||||||
|
ctx.mkSetIntersection(e1, e2)
|
||||||
|
|
||||||
|
override def join(e1: Element, e2: Element): Element =
|
||||||
|
ctx.mkSetUnion(e1, e2)
|
||||||
|
|
||||||
|
override def approx(e1: Element, e2: Element): BoolExpr =
|
||||||
|
ctx.mkSetSubset(
|
||||||
|
e1,
|
||||||
|
e2
|
||||||
|
)
|
||||||
|
|
||||||
|
def toElement(pp: String): Element =
|
||||||
|
ctx.mkSetAdd(
|
||||||
|
ctx.mkEmptySet(stringSort),
|
||||||
|
ctx.mkString(pp)
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
34
src/main/scala/domain/TaintDomain.scala
Normal file
34
src/main/scala/domain/TaintDomain.scala
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import com.microsoft.z3._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Domain to track taint information.
|
||||||
|
*/
|
||||||
|
class TaintDomain(ctx: Context) extends AbstractDomain {
|
||||||
|
|
||||||
|
override type Element = BoolExpr
|
||||||
|
|
||||||
|
override def sort: Sort = ctx.mkBoolSort()
|
||||||
|
|
||||||
|
override def top: Element = ctx.mkTrue()
|
||||||
|
|
||||||
|
override def bottom: Element = ctx.mkFalse()
|
||||||
|
|
||||||
|
override def meet(e1: Element, e2: Element): Element =
|
||||||
|
ctx.mkNot(
|
||||||
|
ctx.mkXor(e1, e2)
|
||||||
|
)
|
||||||
|
|
||||||
|
override def join(e1: Element, e2: Element): Element =
|
||||||
|
ctx.mkNot(
|
||||||
|
ctx.mkXor(e1, e2)
|
||||||
|
)
|
||||||
|
|
||||||
|
override def approx(e1: Element, e2: Element): BoolExpr =
|
||||||
|
ctx.mkOr(
|
||||||
|
ctx.mkNot(e1),
|
||||||
|
e2
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
47
src/main/scala/domain/TopDomain.scala
Normal file
47
src/main/scala/domain/TopDomain.scala
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import com.microsoft.z3._
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Domain that maps everything to top.
|
||||||
|
*/
|
||||||
|
class TopDomain(ctx: Context) extends PrimitiveDomain {
|
||||||
|
|
||||||
|
override type Element = BoolExpr
|
||||||
|
|
||||||
|
override def sort: Sort = ctx.mkBoolSort()
|
||||||
|
|
||||||
|
override def top: Element = ctx.mkTrue()
|
||||||
|
|
||||||
|
override def bottom: Element = ctx.mkTrue()
|
||||||
|
|
||||||
|
override def meet(e1: Element, e2: Element): Element =
|
||||||
|
ctx.mkTrue()
|
||||||
|
|
||||||
|
override def join(e1: Element, e2: Element): Element =
|
||||||
|
ctx.mkTrue()
|
||||||
|
|
||||||
|
override def approx(e1: Element, e2: Element): BoolExpr =
|
||||||
|
ctx.mkTrue()
|
||||||
|
|
||||||
|
override def toElement(n: Long): Element =
|
||||||
|
ctx.mkTrue()
|
||||||
|
|
||||||
|
override def toElement(n: Double): Element =
|
||||||
|
ctx.mkTrue
|
||||||
|
|
||||||
|
override def sum(e1: Element, e2: Element): Element =
|
||||||
|
ctx.mkTrue()
|
||||||
|
|
||||||
|
override def neg(e: Element): Element =
|
||||||
|
ctx.mkTrue()
|
||||||
|
|
||||||
|
override def div(e1: Element, e2: Element): Element =
|
||||||
|
ctx.mkTrue()
|
||||||
|
|
||||||
|
override def mult(e1: Element, e2: Element): Element =
|
||||||
|
ctx.mkTrue()
|
||||||
|
|
||||||
|
override def rem(e1: Element, e2: Element): Element =
|
||||||
|
ctx.mkTrue()
|
||||||
|
}
|
19
src/main/scala/parser/AndroidXmlParser.scala
Normal file
19
src/main/scala/parser/AndroidXmlParser.scala
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import dex.{DexClass, Manifest}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to parse the manifest and the resources xmls.
|
||||||
|
*/
|
||||||
|
// TODO: parse xml files.
|
||||||
|
class AndroidXmlParser(apkLocation: String, classes: Seq[DexClass]) {
|
||||||
|
|
||||||
|
def parseManifest(): Manifest = {
|
||||||
|
val mainActivity =
|
||||||
|
classes.filter(c => c.name.contains("MyActivity;")).head
|
||||||
|
new Manifest(
|
||||||
|
Seq(),
|
||||||
|
Seq(mainActivity)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
18
src/main/scala/parser/EntryPointsProvider.scala
Normal file
18
src/main/scala/parser/EntryPointsProvider.scala
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import dex.{DexClass, DexMethod}
|
||||||
|
|
||||||
|
// TODO: read from EntryPoints.txt file
|
||||||
|
class EntryPointsProvider(classes: Seq[DexClass]) {
|
||||||
|
|
||||||
|
def entryPoints(): Seq[(DexClass, DexMethod)] = {
|
||||||
|
classes.flatMap(
|
||||||
|
c =>
|
||||||
|
c.methods
|
||||||
|
.filter(m =>
|
||||||
|
m.name == "onCreate(Landroid/os/Bundle;)V"
|
||||||
|
|| m.name == "onStart()V")
|
||||||
|
.map(m => (c, m))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
79
src/main/scala/parser/Parser.scala
Normal file
79
src/main/scala/parser/Parser.scala
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
import dex._
|
||||||
|
import org.jf.dexlib2.dexbacked.{
|
||||||
|
DexBackedClassDef,
|
||||||
|
DexBackedDexFile,
|
||||||
|
DexBackedField,
|
||||||
|
DexBackedMethod
|
||||||
|
}
|
||||||
|
import org.jf.dexlib2.{DexFileFactory, Opcodes}
|
||||||
|
import utils.Utils
|
||||||
|
|
||||||
|
import scala.collection.JavaConverters._
|
||||||
|
|
||||||
|
class Parser(apkLocation: String) {
|
||||||
|
|
||||||
|
val apkFile = new File(apkLocation)
|
||||||
|
val dexFile: DexBackedDexFile =
|
||||||
|
DexFileFactory.loadDexFile(apkFile, Opcodes.forApi(21))
|
||||||
|
|
||||||
|
def dexApp: DexApp = {
|
||||||
|
val classes = parseClasses
|
||||||
|
new DexApp(
|
||||||
|
new AndroidXmlParser(apkLocation, classes).parseManifest(),
|
||||||
|
classes,
|
||||||
|
new EntryPointsProvider(classes).entryPoints(),
|
||||||
|
new SourcesSinksProvider().sourcesSinks()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def parseClasses: Seq[DexClass] = {
|
||||||
|
dexFile.getClasses.asScala.toSeq
|
||||||
|
.filter(classFilter())
|
||||||
|
.map(
|
||||||
|
c =>
|
||||||
|
DexClass(
|
||||||
|
c.getType,
|
||||||
|
c.getSuperclass,
|
||||||
|
c.getInterfaces.asScala,
|
||||||
|
classFields(c.getFields.asScala.toSeq),
|
||||||
|
classMethods(c.getMethods.asScala.toSeq)
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
private def classFilter(): DexBackedClassDef => Boolean =
|
||||||
|
(t: DexBackedClassDef) => !t.getType.startsWith("Landroid/support")
|
||||||
|
|
||||||
|
private def methodFilter(): DexBackedMethod => Boolean =
|
||||||
|
(m: DexBackedMethod) => m.getImplementation != null
|
||||||
|
|
||||||
|
private def classFields(fields: Seq[DexBackedField]): Seq[DexField] =
|
||||||
|
fields
|
||||||
|
.map(
|
||||||
|
f =>
|
||||||
|
DexField(
|
||||||
|
f.getName,
|
||||||
|
Utils.removeClassPrefix(f.toString),
|
||||||
|
DexType(f.getType)
|
||||||
|
))
|
||||||
|
|
||||||
|
private def classMethods(methods: Seq[DexBackedMethod]): Seq[DexMethod] = {
|
||||||
|
methods
|
||||||
|
.filter(methodFilter())
|
||||||
|
.map(
|
||||||
|
m =>
|
||||||
|
DexMethod(
|
||||||
|
m.getName,
|
||||||
|
Utils.removeClassPrefix(m.toString),
|
||||||
|
m.getParameterTypes.asScala.map(t => DexType(t)),
|
||||||
|
DexType(m.getReturnType),
|
||||||
|
m.getImplementation.getRegisterCount,
|
||||||
|
m.getParameters.size(),
|
||||||
|
m.getImplementation.getInstructions.asScala.toSeq
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
27
src/main/scala/parser/SourcesSinksProvider.scala
Normal file
27
src/main/scala/parser/SourcesSinksProvider.scala
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package parser
|
||||||
|
|
||||||
|
import taint.{Sink, Source, SourcesSinks}
|
||||||
|
|
||||||
|
// TODO: read from SourcesAndSinks.txt file
|
||||||
|
class SourcesSinksProvider() {
|
||||||
|
|
||||||
|
def sourcesSinks(): SourcesSinks =
|
||||||
|
new SourcesSinks(
|
||||||
|
Seq(
|
||||||
|
Source(
|
||||||
|
"Landroid/telephony/TelephonyManager;",
|
||||||
|
"getDeviceId()Ljava/lang/String;"
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Seq(
|
||||||
|
Sink(
|
||||||
|
"Ljava/net/URL;",
|
||||||
|
"openConnection()Ljava/net/URLConnection;"
|
||||||
|
),
|
||||||
|
Sink(
|
||||||
|
"Landroid/telephony/SmsManager;",
|
||||||
|
"sendTextMessage(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/app/PendingIntent;Landroid/app/PendingIntent;)V"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
16
src/main/scala/taint/SourcesSinks.scala
Normal file
16
src/main/scala/taint/SourcesSinks.scala
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package taint
|
||||||
|
|
||||||
|
class SourcesSinks(val sources: Seq[Source], val sinks: Seq[Sink]) {
|
||||||
|
|
||||||
|
def isSource(className: String, methodName: String): Boolean = {
|
||||||
|
sources.exists(s => s.className == className && s.methodName == methodName)
|
||||||
|
}
|
||||||
|
|
||||||
|
def isSink(className: String, methodName: String): Boolean = {
|
||||||
|
sinks.exists(s => s.className == className && s.methodName == methodName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class Source(className: String, methodName: String) {}
|
||||||
|
|
||||||
|
case class Sink(className: String, methodName: String) {}
|
12
src/main/scala/utils/Utils.scala
Normal file
12
src/main/scala/utils/Utils.scala
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
object Utils {
|
||||||
|
|
||||||
|
def classPrefix(str: String): String = {
|
||||||
|
str.substring(0, str.indexOf("->"))
|
||||||
|
}
|
||||||
|
|
||||||
|
def removeClassPrefix(str: String): String = {
|
||||||
|
str.substring(str.indexOf("->") + 2)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user