INTRODUÇÃO
Dando continuidade aos artigos sobre pentesting mobile, como já vimos sobre elementos que fazem parte deste universo, agora vamos entender um pouco sobre o básico que compõe um aplicativo Android.
COMPONENTES DE UMA APLICAÇÃO ANDROID
Assim como qualquer programa, feito para qualquer sistema operacional, uma aplicação Android tem uma arquitetura específica que contém componentes específicos. Esta padronização permite que o desenvolvimento seja feito de forma a atender as especificações do SO.
Um aplicativo Android, ou um APK, nada mais é do que um arquivo compactado contendo toda a estrutura da aplicação, inclusive com um descompactador como unzip
é possível descompactar o pacote e ter acesso a sua estrutura, porém, desta forma, a maior parte das informações estará criptografada.
Alguns compentes padrões em aplicações Android, são possíveis de se encontrar no AndroidManigest.xml
que será abordado mais adiante. Porém, é de extrema importância que estes componetes sejam explanados.
Activities
As activities
nada mais são do que os pontos de interação de uma aplicação com o usuário, ou seja, toda tela da qual o usuário pode interagir, seja com botões, caixas de texto e demais interações.
Um exemplo abstrato seria de uma aplicação de mensagens, onde existe uma activiy para listar as mensagens recabidas, uma para abrir uma conversa, uma para digitar um texto. Sendo basicamente uma interface com o usuário.
Services
Os services
, ou serviços, tem uma funcionalidade parecida com as activities, porém, não tem uma interface com o usuário, sendo executado geralmente em background.
Um exemplo abstrato seria em uma aplicação de monitoramento, como monitor de energia. Ele está sempre em execução, porém na maior parte do tempo sendo executado em background utilizando services
. Quando é aberto para interação com o usuário, ele chama as activities
.
Content Providers
Os content providers
são espaços de armazenamento que podem ser acessados como recuros de linguagem, semelhante a uma base de dados. Este componente é muito sensível em uma aplicação e será detalhado mais a frente.
Broadcast Receivers
Os broadcast receivers
são elementos de uma aplicação que fazem a comunicação com o próprio sistema e outras aplicações. Geralmente quando uma aplicação é instalada, ela solicita permissão para utilizar recursos como a câmera, telefone, armazenamento, entre outros. Esta comunicação com os demais recursos de um dispositivo é feita fia broadcast receiver
.
Android Manifest
O AndroindManifest.xml
é o arquivo principal dentro de um APK. Sendo armazenado na raíz do pacote, este arquivo contém basicamente tudo que uma aplicação está programada para fazer ou usar.
Este arquivo, também pode ser separado em partes:
Application information
Esta parte do AndroidManifest.xml contém os dados sobre a própria aplicação, como seu nome, a versão do SDK para qual foi feita, a versão mínima do SDK em que pode ser executado.
User permissions
Esta parte especifica todos os recursos do sistema que a aplicação precisa ter acesso, como internet, câmera, GPS.
Caso uma aplicação utilize um recurso que não esteja explícito nesta sessão do AndroidManifest.xml, o Google simplesmente não terá acesso a esta informação e este recurso não poderá ser utilizado. Pois o Android, utiliza esta sessão para demonstrar para o usuário, todos os recursos solicitados pela aplicação e solicitar sua permissão.
Components
Esta é a parte do AndroindManifest.xml que contém todos os componentes citados anteriormente, como as activities, services content providers e broadcast receivers.
Components resources
Esta parte contém o direcionamento para recursos da aplicação como arquivos de imagem, xml contendo bibliotecas para a aplicação.
ANATOMIA DO MANIFEST
Por se tratar de um arquivo xml
o AndroidManifest.xml trabalha com tags
. A tag principal, se chama <manifest ..>
e um exemplo de sua sintaxe pode ser vista abaixo.
1
2
3
4
5
6
7
8
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="string"
android:sharedUserId="string"
android:sharedUserLabel="string resource"
android:versionCode="integer"
android:versionName="string"
android:installLocation=["auto" | "internalOnly" | "preferExternal"] >
</manifest>
É importante salientar que o parâmetro package
dentro da tag principal, contém o nome da aplicação utilizado para referenciar os processos dentro do Android.
Outra tag utilizada é a <user-permission ...>
que contém as permissões que a aplicação precisa para solicitar recursos do sistema. Esta tag fica dentro da tag manifest
, alguns exemplos de sintaxe de permissão são:
- android:name=”android.permission.CAMERA”
- android:name=”android.permission.ACCESS_FINE_LOCATION”
- android:name=”android.permission.READ_CONTACTS”
Sua sintaxe é desta forma:
1
2
<user-permission android:name="string"
android:maxSdkVersion="integer" />
Outra tag que está abaixo da tag manifest
é a <application ...>
. Esta tag contém muitas informações sobre o funcionamento da aplicação. Sua sintaxe é desta forma:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<application android:allowTaskReparenting=["true" | "false"]
android:allowBackup=["true" | "false"]
android:allowClearUserData=["true" | "false"]
android:allowNativeHeapPointerTagging=["true" | "false"]
android:backupAgent="string"
android:backupInForeground=["true" | "false"]
android:banner="drawable resource"
android:dataExtractionRules="string resource"
android:debuggable=["true" | "false"]
android:description="string resource"
android:enabled=["true" | "false"]
android:extractNativeLibs=["true" | "false"]
android:fullBackupContent="string"
android:fullBackupOnly=["true" | "false"]
android:gwpAsanMode=["always" | "never"]
android:hasCode=["true" | "false"]
android:hasFragileUserData=["true" | "false"]
android:hardwareAccelerated=["true" | "false"]
android:icon="drawable resource"
android:isGame=["true" | "false"]
android:killAfterRestore=["true" | "false"]
android:largeHeap=["true" | "false"]
android:label="string resource"
android:logo="drawable resource"
android:manageSpaceActivity="string"
android:name="string"
android:networkSecurityConfig="xml resource"
android:permission="string"
android:persistent=["true" | "false"]
android:process="string"
android:restoreAnyVersion=["true" | "false"]
android:requestLegacyExternalStorage=["true" | "false"]
android:requiredAccountType="string"
android:resizeableActivity=["true" | "false"]
android:restrictedAccountType="string"
android:supportsRtl=["true" | "false"]
android:taskAffinity="string"
android:testOnly=["true" | "false"]
android:theme="resource or theme"
android:uiOptions=["none" | "splitActionBarWhenNarrow"]
android:usesCleartextTraffic=["true" | "false"]
android:vmSafeMode=["true" | "false"] >
. . .
</application>
Entre estas informações, duas são muito importantes de se observar:
- android:allowBackup
- android:debuggable
A tag activity
contém, conforme explicado anteriormente, contém todas as interfaces com o usuário. Sua sintaxe dentro do AndroidManifest.xml é desta formaa;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<activity android:allowEmbedded=["true" | "false"]
android:allowTaskReparenting=["true" | "false"]
android:alwaysRetainTaskState=["true" | "false"]
android:autoRemoveFromRecents=["true" | "false"]
android:banner="drawable resource"
android:clearTaskOnLaunch=["true" | "false"]
android:colorMode=[ "hdr" | "wideColorGamut"]
android:configChanges=["mcc", "mnc", "locale",
"touchscreen", "keyboard", "keyboardHidden",
"navigation", "screenLayout", "fontScale",
"uiMode", "orientation", "density",
"screenSize", "smallestScreenSize"]
android:directBootAware=["true" | "false"]
android:documentLaunchMode=["intoExisting" | "always" |
"none" | "never"]
android:enabled=["true" | "false"]
android:excludeFromRecents=["true" | "false"]
android:exported=["true" | "false"]
android:finishOnTaskLaunch=["true" | "false"]
android:hardwareAccelerated=["true" | "false"]
android:icon="drawable resource"
android:immersive=["true" | "false"]
android:label="string resource"
android:launchMode=["standard" | "singleTop" |
"singleTask" | "singleInstance" | "singleInstancePerTask"]
android:lockTaskMode=["normal" | "never" |
"if_whitelisted" | "always"]
android:maxRecents="integer"
android:maxAspectRatio="float"
android:multiprocess=["true" | "false"]
android:name="string"
android:noHistory=["true" | "false"]
android:parentActivityName="string"
android:persistableMode=["persistRootOnly" |
"persistAcrossReboots" | "persistNever"]
android:permission="string"
android:process="string"
android:relinquishTaskIdentity=["true" | "false"]
android:resizeableActivity=["true" | "false"]
android:screenOrientation=["unspecified" | "behind" |
"landscape" | "portrait" |
"reverseLandscape" | "reversePortrait" |
"sensorLandscape" | "sensorPortrait" |
"userLandscape" | "userPortrait" |
"sensor" | "fullSensor" | "nosensor" |
"user" | "fullUser" | "locked"]
android:showForAllUsers=["true" | "false"]
android:stateNotNeeded=["true" | "false"]
android:supportsPictureInPicture=["true" | "false"]
android:taskAffinity="string"
android:theme="resource or theme"
android:uiOptions=["none" | "splitActionBarWhenNarrow"]
android:windowSoftInputMode=["stateUnspecified",
"stateUnchanged", "stateHidden",
"stateAlwaysHidden", "stateVisible",
"stateAlwaysVisible", "adjustUnspecified",
"adjustResize", "adjustPan"] >
. . .
</activity>
A tag <service ...>
assim como já dito, contém os services da aplicação. Sua sintaxe é desta forma:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<service android:description="string resource"
android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:foregroundServiceType=["camera" | "connectedDevice" |
"dataSync" | "location" | "mediaPlayback" |
"mediaProjection" | "microphone" | "phoneCall"]
android:icon="drawable resource"
android:isolatedProcess=["true" | "false"]
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
. . .
</service>
A tag <provider ...>
contém os content providers. Um ponto interessante desta tag, é o parâmetro exported
, pois se estiver marcado como true
, isso significa que aplicações externas podem interagir com ele. Sua sintaxe é desta forma:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<provider android:authorities="list"
android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:grantUriPermissions=["true" | "false"]
android:icon="drawable resource"
android:initOrder="integer"
android:label="string resource"
android:multiprocess=["true" | "false"]
android:name="string"
android:permission="string"
android:process="string"
android:readPermission="string"
android:syncable=["true" | "false"]
android:writePermission="string" >
. . .
</provider>
A tag receiver contém os broadcast receivers, esta tag mostra todas as permissões que a applicação tem para receber informações sistêmicas e de outras aplicações. Sua sintaxe é:
1
2
3
4
5
6
7
8
9
10
<receiver android:directBootAware=["true" | "false"]
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
. . .
</receiver>
INTENTS E INTENT FILTERS
Os intents
são um recurso da aplicação que fornece um meio de comunicação com outros recursos do dispsitivo. De forma análoga ao nome, o intent expressa uma “intenção” de utilização de recursos do dispositivo.
Uma intent pode ser usada para chamar uma outra activity, um serviço pra rodar em background ou para entregar um recurso via broadcast.
Para realizar sua tarefa, os intents pordem utilizar vários mecanismos, por exemplo, chamar outra activity pode ser feito com startActivity()
ou startActivityForResult()
passando como argumento o intent, fazendo com que a aplicação abra uma novo recurso para interagir com o usuário. Pode iniciar um serviço com startService()
ou bindService()
passando como argumento a intent para iniciar um novo serviço. Ou entregar uma mensagem via broadcast, passando a intent como argumento para sendBroadcast()
.
Existem dois tipos de intents:
- Explicit Intents: São intents que especificam qual aplicação irá receber o intent, esta aplicação alvo pode ser a própria aplicação que está chamando o intent ou aplicações deiferentes. Geralmente utilizado para chamar uma nova activity na própria aplicação.
- Implicit Intents: São intents das quais não se especifica qual aplicação irá receber este intent, ao invés disso a ação é declarada. Desta forma, a aplicação vai passar este intent para o Android, e o próprio Android vai procurar um candidato a receber. Como exemplo, um aplicativo que vai compartilhar uma foto, inicia um Implicit Intent e o Android vai procurar todas as oitras aplicações que aceitam receber este compartilhamento.
A imagem abaixo mostra o fluxo de comunicação de uma intent:
Neste fluxo, a Activity A
de uma aplicação está passando sua intent através do startActivity()
para o Android. O Android por sua vez, vai verificar se esta intent é específica para uma plicação, no caso de explicit intent
ou se a intent é uma ação implicit intent
, neste caso o Android vai varrer o sistema por aplicações que possam satisfazer a requisição feita e enviar o onCreate()
para ela.
Uma intent possui as seguintes propriedades:
- Component Name: O nome do componente que será acessado, pode ser o nome do pacote de uma aplicação ou uma activity específica;
- Action: Representa qual a intenção enviada, pode ser enviar uma mensagem, ou ver uma tela, entre outros;
- Data: Contém a URI dos dados que serão passados para o componente que receberá este intent;
- Category: Fornece informações adicionais para o componente que receberá este intent;
- Extras: Algumas Activities precisam de parâmetros extras para seu funcionamento, e esta propriedade armazena tais informações.
O código abaixo mostra uma Explicit Intent em JAVA
.
1
2
3
Intent downloadIntent = new.Intent(this, DownloadService.class);
downloadIntent.setData(Uri.parse(fileUrl));
startService(downloadIntent);
O objetivo deste intent de exemplo, é fazer o download de um contúdo. Para isso é criado um objeto do tipo Intent, é passado como Data a URI do conteúdo a ser baixado e por fim chama o startService()
chamando este objeto.
O código abaixo, mostra uma Implicit Intent em JAVA
1
2
3
4
5
6
7
8
9
10
// Cria a mensagem como uma string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_TEXT, textMessage);
sendIntent.setType("text/plain");
// Verifica se a intent será resolvida para uma aplicação
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
Neste caso, quando o startActivity()
é chamado, o Android vai varrer o sistema por uma aplicação que exporte uma intent do tipo ACTION_SEND
e devolver para o usuário escolher para qual aplicação quer exportar o texto.
Este processo mostrou como uma intent é enviada e como se comunica com o sistema, porém o outro lado da comunicação precisa declarar
que pode receber uma intent.
Uma aplicação define quais os tipos de intent pode receber, através dos intent filters
que fica dentro dos componentes no AndroidManifest.xml.
Esta flag leva em consideração os elementos action
, data
e category
que já vimos anteriormente. Sia sintaxe segue desta forma:
1
2
3
4
5
<intent-filter android:icon="drawable resource"
android:label="string resource"
android:priority="integer" >
. . .
</intent-filter>
CONCLUSÃO
Neste artigo, foi possível obter um overview da anatomia de uma aplicação Android, nos próximos artigos exploraremos algumas ferramentas e técnicas pera o pentest nestas aplicações.