/*----------------------------------------------------------------------------+ *| | *| Android's Hooker | *| | *+---------------------------------------------------------------------------+ *| Copyright (C) 2011 Georges Bossert and Dimitri Kirchner | *| This program is free software: you can redistribute it and/or modify | *| it under the terms of the GNU General Public License as published by | *| the Free Software Foundation, either version 3 of the License, or | *| (at your option) any later version. | *| | *| This program is distributed in the hope that it will be useful, | *| but WITHOUT ANY WARRANTY; without even the implied warranty of | *| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | *| GNU General Public License for more details. | *| | *| You should have received a copy of the GNU General Public License | *| along with this program. If not, see <http://www.gnu.org/licenses/>. | *+---------------------------------------------------------------------------+ *| @url : http://www.amossys.fr | *| @contact : android-hooker@amossys.fr | *| @sponsors : Amossys, http://www.amossys.fr | *+---------------------------------------------------------------------------+ */ package com.amossys.hooker.service; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.HashSet; import java.util.LinkedList; import java.util.Queue; import java.util.Set; import android.app.Service; import android.content.Intent; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; import com.amossys.hooker.ApkInstrumenterActivity; import com.amossys.hooker.SubstrateMain; import com.amossys.hooker.common.INIParser; import com.amossys.hooker.common.InterceptEvent; import com.amossys.hooker.reporting.AbstractReporter; import com.amossys.hooker.reporting.FileEventReporter; import com.amossys.hooker.reporting.NetworkEventSender; public class InstrumentationService extends Service { /** * Sets of event reporters */ private Set<AbstractReporter> eventReporters = new HashSet<AbstractReporter>(); private Queue<InterceptEvent> localCacheOfEvents = new LinkedList<InterceptEvent>(); private boolean parsingInProgess = false; private static boolean monitoring; // Elements to communicate with the service public static final int ConnectToService = 1; public static final int Event = 2; public static final int GetConfiguration = 3; // Target we publish for clients to send messages to IncomingHandler final IncomingHandler inHandler = new IncomingHandler(); final Messenger mMessenger = new Messenger(inHandler); // Configuration parameters String idXP = "0"; boolean fileMode = false; boolean networkMode = false; String esIp = null; int esPort = 0; int esNbThread = 0; String esIndex = null; String esDoctype = null; String fileName = null; /** * Handler of incoming messages from clients. */ class IncomingHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { case ConnectToService: SubstrateMain.log("New client is connected to instrumentation service"); //Create reporters, only if this is our first connection if (!parsingInProgess && eventReporters.size()==0) { createReportersFromConfigFile(); } break; case Event: msg.getData().setClassLoader(InstrumentationServiceConnection.class.getClassLoader()); InterceptEvent event = (InterceptEvent) msg.getData().getParcelable("eventkey"); if (event != null) { reportEvent(event); } break; case GetConfiguration: SubstrateMain.log("Configuration message has been received, sending current configuration to APK Instrumenter"); Messenger m = msg.replyTo; Message response = Message.obtain(null, ApkInstrumenterActivity.Configuration); //Construct Bundle from our attributes Bundle configuration = new Bundle(); configuration.putString("idxp", idXP); configuration.putBoolean("fileMode", fileMode); configuration.putBoolean("networkMode", networkMode); configuration.putString("esIp", esIp); configuration.putInt("esPort", esPort); configuration.putInt("esNbThread", esNbThread); configuration.putString("esIndex", esIndex); configuration.putString("esDoctype", esDoctype); configuration.putString("fileName", fileName); response.setData(configuration); try { m.send(response); } catch (RemoteException e) { SubstrateMain.log("Service has crashed"); e.printStackTrace(); } break; default: SubstrateMain.log("Unknown Message received: " + msg.toString()); super.handleMessage(msg); } } /** * Report the event to all the reporters * * @param event */ private void reportEvent(InterceptEvent event) { if (event != null) { SubstrateMain.log("Collecting Service received an event to report."); if (eventReporters.size() == 0) { SubstrateMain.log("No reporter available, will try to parse the configuration file..."); if (!parsingInProgess) { // No reporter available, we try to parse their configuration createReportersFromConfigFile(); } if (eventReporters.size() == 0) { localCacheOfEvents.add(event); } else { while (localCacheOfEvents.size() > 0) { SubstrateMain.log("Collecting service emptying its local cache"); InterceptEvent previousEvent = localCacheOfEvents.poll(); for (AbstractReporter reporter : eventReporters) { reporter.reportEvent(previousEvent); } } } } for (AbstractReporter reporter : eventReporters) { SubstrateMain.log("Collecting service send received event to reporter "+reporter); reporter.reportEvent(event); } } } } public static boolean isMonitoring() { return monitoring; } @Override public int onStartCommand(Intent intent, int flags, int startId) { // service will not stop if no clients are connected return START_STICKY; } /** * When binding to the service, we return an interface to our messenger for sending messages to * the service. */ @Override public IBinder onBind(Intent intent) { return mMessenger.getBinder(); } @Override public void onCreate() { super.onCreate(); SubstrateMain.log("Service has started"); if (!parsingInProgess && eventReporters.size()==0) { // No reporter available, we try to parse their configuration createReportersFromConfigFile(); } } public void createReportersFromConfigFile() { parsingInProgess = true; SubstrateMain.log("Try to create reporters based on configuration file"); // does a property file is available and override these values ? File sdcard = Environment.getExternalStorageDirectory(); File configurationFile = new File(sdcard, "/hooker/" + SubstrateMain.CONFIGURATION_FILENAME); if (configurationFile.exists()) { try { SubstrateMain.log("Parsing the configuration file '" + configurationFile.getAbsolutePath() + "'"); INIParser iniParser = new INIParser(configurationFile); // First we try to determine if elasticsearch is active if (iniParser.getString("elasticsearch", "elasticsearch_mode") != null) { networkMode = Boolean.parseBoolean(iniParser.getString("elasticsearch", "elasticsearch_mode")); esIp = iniParser.getString("elasticsearch", "elasticsearch_ip"); esPort = Integer.parseInt(iniParser.getString("elasticsearch", "elasticsearch_port")); esNbThread = Integer.parseInt(iniParser.getString("elasticsearch", "elasticsearch_nb_thread")); esIndex = iniParser.getString("elasticsearch", "elasticsearch_index"); esDoctype = iniParser.getString("elasticsearch", "elasticsearch_doctype"); SubstrateMain.log("Eslaticsearch mode'" + networkMode + "' (esIP='" + esIp + "', esPort='" + esPort + "', esIndex='"+esIndex+"', esDoctype='"+esDoctype+"') extracted from the configuration file"); } // And what about file mode if (iniParser.getString("file", "file_mode") != null) { fileMode = Boolean.parseBoolean(iniParser.getString("file", "file_mode")); fileName = iniParser.getString("file", "file_name"); SubstrateMain.log("Filemode '" + fileMode + "' (fileName='" + fileName + "') extracted from the configuration file"); } // finally, we look for the IdXP if (iniParser.getString("analysis", "idXP") != null) { idXP = iniParser.getString("analysis", "idXP"); SubstrateMain.log("IdXP '" + idXP + "' extracted from the configuration file"); } // initialize the event reporters if (fileMode && fileName !=null && idXP!=null) { SubstrateMain.log("Create a file reporter (filename='" + fileName + "', idXp='" + idXP + "')."); FileEventReporter fileReporter = new FileEventReporter(fileName); fileReporter.setIdXp(idXP); this.eventReporters.add(fileReporter); } if (networkMode && esIp != null && esPort > 0 && esNbThread>0 && esIndex != null && esDoctype !=null && idXP!=null) { SubstrateMain .log("Create a network reporter (esIP='" + esIp + "', idXp='" + idXP + "')."); NetworkEventSender networkReporter = new NetworkEventSender(esIp, esPort, esNbThread, esIndex, esDoctype); networkReporter.setIdXp(idXP); this.eventReporters.add(networkReporter); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } else { SubstrateMain.log("No configuration file found at '" + configurationFile.getAbsolutePath() + "'."); } parsingInProgess = false; } @Override public void onDestroy() { super.onDestroy(); InstrumentationService.monitoring = false; } }