Hy
my professor asked how you can print the contents of the variables within a class
he provided us with an apk to be launched and analyzed with frida:
package com.test_uni_apk.lib.proftest;
public class ProfApi{
public static class StateReady
extends ProfApi.CallState
{
public CallStateReady() {}
public CallStateReady(ProfApi.CallProc paramCallProc,
ProfApi.CallConnection[] paramArrayOfCallConnection, String
paramString, byte[] paramArrayOfByte, String[] paramArrayOfString)
{
this.printthis = paramArrayOfCallConnection;
}
}
}
I read that with frida you can hook a class but I do not understand how to print the value of printthis.
I will assume CallStateReady is an inner class of com.test_uni_apk.lib.proftest.ProfApi and you want to hook the c'tor and print the second parameter #PleaseSubmitElegantCode
function printParamArrayOfCallConnection() {
var ArrayList = Java.use("java.util.ArrayList");
var CallConnection = Java.use("com.test_uni_apk.lib.proftest.ProfApi$CallConnection");
Java.use("com.test_uni_apk.lib.proftest.ProfApi$CallStateReady") // dollar sign for inner class
.$init // init represent the constructor
// list of arguments are passed in byte code style, [B represents byte array
// when you try to hook Frida will provide an informative error with all the possible arguments for overloading
// copy & paste the right one which will look like this:
.overload("Lcom..ProfApi.CallProc;", "Lcom...ProfApi.CallConnection;", "java.lang.String", "[B", "Ljava.lang.String;")
.implementation = function(paramCallProc, paramArrayOfCallConnection, paramString, paramArrayOfByte, paramArrayOfString) {
// first we cast to list
var list = Java.cast(paramArrayOfCallConnection, ArrayList);
// iterating the list
for (var i = 0, l = list.size(); i < l; i++) {
// casting each element to the object we created earlier
var currentElement = Java.cast(list.get(i), CallConnection);
// printing to terminal
console.log(i, currentElement);
}
// executing original c'tor
this.$init(paramCallProc, paramArrayOfCallConnection, paramString, paramArrayOfByte, paramArrayOfString);
}
}
Java.perform(printParamArrayOfCallConnection);
Related
I'm adding three different objects to an ArrayList, but the list contains three copies of the last object I added.
For example:
for (Foo f : list) {
System.out.println(f.getValue());
}
Expected:
0
1
2
Actual:
2
2
2
What mistake have I made?
Note: this is designed to be a canonical Q&A for the numerous similar issues that arise on this site.
This problem has two typical causes:
Static fields used by the objects you stored in the list
Accidentally adding the same object to the list
Static Fields
If the objects in your list store data in static fields, each object in your list will appear to be the same because they hold the same values. Consider the class below:
public class Foo {
private static int value;
// ^^^^^^------------ - Here's the problem!
public Foo(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
In that example, there is only one int value which is shared between all instances of Foo because it is declared static. (See "Understanding Class Members" tutorial.)
If you add multiple Foo objects to a list using the code below, each instance will return 3 from a call to getValue():
for (int i = 0; i < 4; i++) {
list.add(new Foo(i));
}
The solution is simple - don't use the static keywords for fields in your class unless you actually want the values shared between every instance of that class.
Adding the Same Object
If you add a temporary variable to a list, you must create a new instance of the object you are adding, each time you loop. Consider the following erroneous code snippet:
List<Foo> list = new ArrayList<Foo>();
Foo tmp = new Foo();
for (int i = 0; i < 3; i++) {
tmp.setValue(i);
list.add(tmp);
}
Here, the tmp object was constructed outside the loop. As a result, the same object instance is being added to the list three times. The instance will hold the value 2, because that was the value passed during the last call to setValue().
To fix this, just move the object construction inside the loop:
List<Foo> list = new ArrayList<Foo>();
for (int i = 0; i < 3; i++) {
Foo tmp = new Foo(); // <-- fresh instance!
tmp.setValue(i);
list.add(tmp);
}
Your problem is with the type static which requires a new initialization every time a loop is iterated. If you are in a loop it is better to keep the concrete initialization inside the loop.
List<Object> objects = new ArrayList<>();
for (int i = 0; i < length_you_want; i++) {
SomeStaticClass myStaticObject = new SomeStaticClass();
myStaticObject.tag = i;
// Do stuff with myStaticObject
objects.add(myStaticClass);
}
Instead of:
List<Object> objects = new ArrayList<>();
SomeStaticClass myStaticObject = new SomeStaticClass();
for (int i = 0; i < length; i++) {
myStaticObject.tag = i;
// Do stuff with myStaticObject
objects.add(myStaticClass);
// This will duplicate the last item "length" times
}
Here tag is a variable in SomeStaticClass to check the validity of the above snippet; you can have some other implementation based on your use case.
Had the same trouble with the calendar instance.
Wrong code:
Calendar myCalendar = Calendar.getInstance();
for (int days = 0; days < daysPerWeek; days++) {
myCalendar.add(Calendar.DAY_OF_YEAR, 1);
// In the next line lies the error
Calendar newCal = myCalendar;
calendarList.add(newCal);
}
You have to create a NEW object of the calendar, which can be done with calendar.clone();
Calendar myCalendar = Calendar.getInstance();
for (int days = 0; days < daysPerWeek; days++) {
myCalendar.add(Calendar.DAY_OF_YEAR, 1);
// RIGHT WAY
Calendar newCal = (Calendar) myCalendar.clone();
calendarList.add(newCal);
}
Every time you add an object to an ArrayList, make sure you add a new object and not already used object. What is happening is that when you add the same 1 copy of object, that same object is added to different positions in an ArrayList. And when you make change to one, because the same copy is added over and over again, all the copies get affected.
For example,
Say you have an ArrayList like this:
ArrayList<Card> list = new ArrayList<Card>();
Card c = new Card();
Now if you add this Card c to list, it will be added no problem. It will be saved at location 0. But, when you save the same Card c in the list, it will be saved at location 1. So remember that you added same 1 object to two different locations in a list. Now if you make a change that Card object c, the objects in a list at location 0 and 1 will also reflect that change, because they are the same object.
One solution would be to make a constructor in Card class, that accepts another Card object. Then in that constructor, you can set the properties like this:
public Card(Card c){
this.property1 = c.getProperty1();
this.property2 = c.getProperty2();
... //add all the properties that you have in this class Card this way
}
And lets say you have the same 1 copy of Card, so at the time of adding a new object, you can do this:
list.add(new Card(nameOfTheCardObjectThatYouWantADifferentCopyOf));
It can also consequence of using the same reference instead of using a new one.
List<Foo> list = new ArrayList<Foo>();
setdata();
......
public void setdata(int i) {
Foo temp = new Foo();
tmp.setValue(i);
list.add(tmp);
}
Instead of:
List<Foo> list = new ArrayList<Foo>();
Foo temp = new Foo();
setdata();
......
public void setdata(int i) {
tmp.setValue(i);
list.add(tmp);
}
I searched for related questions but didn´t find a solution (at least i don´t know if i named it correctly)
So, i have two ArrayLists and i would like to randomize all of them to get a value:
public class ListBox {
public static ArrayList listOne(){
ArrayList<Lists> listOne = new ArrayList<>();
listOne.add(new Item("Text One"));
listOne.add(new Item("Text Two"));
return listOne;
}
public static ArrayList listTwo(){
ArrayList<Lists> listTwo = new ArrayList<>();
listTwo.add(new Item("Text Three"));
listTwo.add(new Item("Text Four"));
return listTwo;
}
}
in other activity:
public void buttonClick(View view){
ArrayList<Lists> algumasListas = ListBox.listOne();
...
}
This is where i shuffle it
public class ListMixer extends ListBox{
public ArrayList<Lists> listas = null;
public ListMixer(ArrayList<Lists> listas ) {
this.listas = listas;
}
protected String mixList(){
Double randomNumber = new Double(Math.random() * listas.size());
int randomNum = randomNumber.intValue();
Lista lista= listas.get(randomNum);
String listaString2 = String.valueOf(lista);
String message = ("This is your list: " + listas);
return message;
}
}
my desired output would be one of the four listItems.
Appreciate the help!
Merge arrays into single one of size N.
Choose a random number in range 0..N-1.
Choose an element by index.
The first bug I'm seeing in your code is that listOne() returns object listTwo when called, which doesn't exist. It probably shouldn't even compile, unless something funky is going on with global scope variables.
The following code should do what you want by merging the two lists into one and then returning a random object from them.
public Object randomFromList(List<Object> listOne, List<Object> listTwo){
List<Object> bigList = new ArrayList<Object>(listOne.size() + listTwo.size());
bigList.addAll(listOne);
bigList.addAll(listTwo);
return bigList.get(new Random().nextInt(bigList.size()));
}
For optimization, if you call this a lot, I would save the Random() object outside of the method to avoid instantiating it every time you make the call.
I am using Retrofit and SimpleXMLConverter to get and deserialize an XML file. I am converting an attribute to type long. It would work fine if the attribute wasn´t sometimes "", aka empty.
I tried using
#Attribute(empty="-1")
for the cases this attribute should be empty, so it should return "-1" but it does not do so. Is the usage of this empty attribute correct?
It would work fine if the attribute wasn´t sometimes "", aka empty.
This is not correct here: The attributes value - seen as string - is not empty in the meaning of missing or "nothing", it's an empty string.
Here's an example:
XML:
<root value=""></root>
Java class:
#Root(name = "root")
public class Example
{
#Attribute(name = "value", empty = "-1", required = false)
private long value;
#Override
public String toString()
{
return "Example{" + "value=" + value + '}';
}
}
This throws - and that's reasonable - a NumberFormatException. If you replace the type of value with String you wont catch an exception, value is set as an empty string (""). On the other hand, keeping string type but removing the attribute in XML will set "-1" as value (that's why required = false is used). Now the Serializer can't find any value and therefore sets the default one.
You could handle this in your class internally, like let the corresponding getter-method return -1 in case of an empty string:
public long getValue()
{
if( value == null || value.isEmpty() == true )
{
return -1;
}
return Long.valueOf(value);
}
(Don't forget to change your code according this - in my example you have to change toString()-method)
But there's a better solution: Simple allows you to implement custom Transformer for any types (don't mix with Converter!). With these you can implement type -> String (write) and String -> type (read) as you need.
Based on my example above, here's an implementation:
public class LongTransformer implements Transform<Long>
{
// Default value which is set if no / empty input is available
private final long defaultValue;
public LongTransformer(long defaultValue)
{
this.defaultValue = defaultValue;
}
public LongTransformer()
{
this(-1); // Just in case you always have -1 as default
}
#Override
public Long read(String value) throws Exception
{
// If there's no or an empty String the default value is returned
if( value == null || value.isEmpty() == true )
{
return defaultValue; //
}
return Long.valueOf(value); // Return the value
}
#Override
public String write(Long value) throws Exception
{
/*
* Nothing special here. In case you you need a empty string if
* value = -1, you can do it here.
*/
return value.toString();
}
}
And finally a example how to use. The key parts are between the two lines:
final String xml = "<root value=\"\">\n"
+ "</root>";
// ---------------------------------------------------------------------
final LongTransformer numberTransformer = new LongTransformer(-1);
RegistryMatcher m = new RegistryMatcher();
m.bind(long.class, numberTransformer);
Serializer ser = new Persister(m);
// ---------------------------------------------------------------------
Example root = ser.read(Example.class, xml);
System.out.println(root);
Output:
Example{value=-1}
#vandus, here is what I did so far:
I created an adapter in the Main onCreate class, the Model that I pass in as a List is the root node of the XML file:http://www.w3schools.com//xml/simple.xml
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("http://www.w3schools.com")
.setConverter(new SimpleXMLConverter())
.build();
GitHubService foodService = restAdapter.create(GitHubService.class);
List<BreakfastMenu> repos = foodService.getFile();
repos.toString();
public interface GitHubService
{
#GET("/xml/simple.xml")
List<BreakfastMenu> getFile();
}
Currently working on an app that takes results from a search, parses the JSON object returned, and then adds the resulting pieces into a few ArrayLists within a class created called VenueList.
Here is the method that receives the results from the service and parses the JSON:
private static List<String> getResultsFromJson(String json) {
ArrayList<String> resultList = new ArrayList<String>();
try {
JSONObject resultsWrapper = (JSONObject) new JSONTokener(json).nextValue();
JSONArray results = resultsWrapper.getJSONArray("results");
for (int i = 0; i < results.length(); i++) {
JSONObject result = results.getJSONObject(i);
resultList.add(result.getString("text"));
}
}
catch (JSONException e) {
Log.e(TAG, "Failed to parse JSON.", e);
}
return resultList;
}
What results of this becomes a List variable call mResults (to clarify: mResults = getResultsFromJson(restResult);. That is then used, among other places, in the following loop that puts the results into an ArrayAdapter that is used for displaying them in a ListFragment:
for (String result : mResults) {
VenueList.addVenue(result, "HELLO WORLD");
adapter.add(result);
}
I also add the result to a class called VenueList that manages the results and makes them accessible for multiple views. It essentially just holds multiple ArrayLists that hold different types of details for each venue returned in the search. The method I use to add a venue to VenueList is below (and you can see it used in the for loop above):
public static void addVenue(String name, String geo) {
venueNames.add(name);
venueGeos.add(geo);
}
I want the addVenue method to be able to take multiple arguments and update the VenueList class. Yet, when I call the addVenue method in the for loop, I can only pass it String result (from the parameters of the loop) and can't figure out how to pass it a second argument (which should also come from the JSON parsed by getResultsFromJson) so I've used "HELLO WORLD" as a placeholder for now.
I realize getResultsFromJson only has one list returned. I need to be able to take multiple elements from the JSON object that I parse, and then add them to VenueList in the right order.
So my questions are:
1) Given the getResultsFromJson method and the for loop, how can I use the addVenue() method as designed? How do I parse multiple elements from the JSON, and then add them to the VenueList at the same time? I plan on adding more arguments to it later on, but I assume if I can make it work with two, I can make it work with four or five.
2) If that's not possible, how should the getResultsFromJson, the for loop, and the addVenue method be redesigned to work properly together?
Please let me know if you need more detail or code - happy to provide. Thank you!
EDIT - Full VenueList class:
public class VenueList {
private static ArrayList<String> venueNames;
private static ArrayList<String> venueGeos;
public VenueList() {
venueNames = new ArrayList<String>();
venueGeos = new ArrayList<String>();
}
public static void addVenue(String name, String geo) {
venueNames.add(name);
venueGeos.add(geo);
}
public static String getVenueName(int position) {
return venueNames.get(position);
}
public static String getVenueGeo(int position) {
return venueGeos.get(position);
}
public static void clearList() {
venueNames.clear();
venueGeos.clear();
}
}
Clarification: I will have additional ArrayLists for each element of data that I want to store about a venue (phone number, address, etc etc)
1) I don't think methods getResultsFromJson(String json) and addVenue(String name, String geo) fit your needs.
2) I would consider rewriting method getResultsFromJson(String json) to something like this:
private static SortedMap<Integer, List<String>> getResultsFromJson(String json) {
Map<Integer, String> resultMap = new TreeMap<Integer, String>();
//...
return resultMap;
}
where the number of keys of your map should be equal to the number of objects you're extracting info, and each one of them will properly have their own list of items just in the right order you extract them.
With this approach you can certainly change your logic to something like this:
// grab your retuned map and get an entrySet, the just iterate trough it
SortedMap<Integer, String> result = returnedMap.entrySet();
for (Entry<Integer, String> result : entrySet) {
Integer key = result.getKey(); // use it if you need it
List<String> yourDesiredItems = result.getValue(); // explicitly shown to know how to get it
VenueList.addVenue(yourDesiredItems);
}
public static void addVenue(List<String> yourDesiredItems) {
// refactor here to iterate the items trough the list and save properly
//....
}
EDIT -- as you wish to avoid the go-between map i'm assuming you need nothing to return from the method
First i'm providing you with a solution to your requirements, then i'll provide you with some tips cause i see some things that could smplify your design.
To save VenueList things directly from getResultsFromJSON do something like this:
private static void getResultsFromJson(String json) {
try {
JSONObject resultsWrapper = (JSONObject) new JSONTokener(json).nextValue();
JSONArray results = resultsWrapper.getJSONArray("results");
for (int i = 0; i < results.length(); i++) {
JSONObject result = results.getJSONObject(i);
//FOR EXAMPLE HERE IS WHERE YOU NEED TO EXTRACT INFO
String name = result.getString("name");
String geo = result.getString("geo");
// and then...
VenueList.addVenue(name, geo, ..., etc);
}
} catch (JSONException e) {
Log.e(TAG, "Failed to parse JSON.", e);
}
}
This implies that your addVenue method should know receive all params needed; as you can see this is just a way (that you can consider a workaround to your needs), however as i don't know all requirements that lead you to code this model, i will point to a few things you might consider:
1. If there's a reason for VenueList class to use everything static, consider doing this:
static{
venueNames = new ArrayList<String>();
venueGeos = new ArrayList<String>();
//....
}
private VenueList(){
}
This way you won't need to get an instance every time and also will avoid null pointer exceptions when doing VenueList.addVenue(...) without previous instantiation.
2. Instead of having an ArrayList for every characteristic in VenueList class consider defining a model object for a Venue like this:
public class Venue{
String name;
String geo;
//... etc
public Venue(){
}
// ... getters & setters
}
then if you need that VenueList class you will just have a list o Venue objects (List<Venue>), this means that instead of calling the method addVenue, you will first create a brand new instance of Venue class and will call the setter method of each characteristic, as an example of the refactored for loop from the workaround i provided you you'd be using something like this:
List<Venue> myListOfVenues = new ArrayList<Venue>();
for (int i = 0; i < results.length(); i++) {
JSONObject result = results.getJSONObject(i);
// THIS WOULD REMAIN THE SAME TO EXTRACT INFO
String name = result.getString("name");
String geo = result.getString("geo");
// and then instead of calling VenueList.addVenue(name, geo, ..., etc)...
Venue v = new Venue();
v.setName(name);
v.setGeo(geo);
// ...etc
myListOfVenues.add(v);
}
// Once you're done, set that List to VenueList class
VenueList.setVenueList(myListOfVenues);
So VenueList class would now have a single property List<Venue> venueList; and would suffer minor tweeks on methods getVenueName, etc... and everything would be more readable... i hope this helps you to get another approach to solve your problem, if i still don't make my point let me know and i'll try to help you out...
I'm writing an app for android that needs to parse data from an XML file. I've never come across an error like this that is so impossibly hard to track down. Or maybe my brain just stopped working. That happens. XML file is of the form:
<?xml version="1.0" encoding="iso-8859-1"?>
<memberRoster>
<agent>
<agentInfo1>...</agentInfo1>
<agentInfo2>...</agentInfo2>
...
</agent>
<agent>
...
</agent>
...
</memberRoster>
So far it's working well, except for some random bits of fun!
Every now and then it will throw a NullPointerException. I did some more digging and found out that there are THREE "agents" (out of 800) with "supposedly" null data. I checked the XML file and the data is there, there are no illegal characters, etc. It is the same three "agents" every time. The program parses other entries before and after these "null" "agents". Also of note is that not all "agentInfo" fields in the ArrayList come up null; example, one of the entries has 7 of the 8 entries as null, with the 8th one non-null, another has only one null with the last 7 non-null.
I'm parsing the data in to an ArrayList from the XML file, and like I mentioned before, it works flawlessly until it comes to those three specific entries in the XML file.
I'm sorry I can't give much more info than that, the data is sensitive to our members.
EDIT:
Sorry! I knew I was forgetting something! :)
Some code from my XMLHandler.java class:
public void characters(char[] ch, int start, int length)
if(this.in_mr_agentNrdsId) {
agent[0] = ch.toString();
}
else if(this.in_mr_agentFirstName) {
agent[1] = ch.toString();
}
else if(this.in_mr_agentLastName) {
agent[2] = ch.toString();
}
else if(this.in_mr_agentPhone) {
agent[3] = ch.toString();
}
else if(this.in_mr_agentEmail) {
agent[4] = ch.toString();
}
else if(this.in_mr_agentOfficeName) {
agent[5] = ch.toString();
}
else if(this.in_mr_agentOfficePhone) {
agent[6] = ch.toString();
}
else if(this.in_mr_agentType) {
agent[7] = ch.toString();
pds.setMemberRoster(agent);
agent = new String[8];
}
PDS is an object of type ParsedDataSet, which is just a simple class containing the ArrayList objects and a few getter and setter methods:
public class ParsedDataSet {
private ArrayList agentOpenHouses = new ArrayList();
private ArrayList calendarOfEvents = new ArrayList();
private ArrayList latestStatistics = new ArrayList();
private ArrayList memberRoster = new ArrayList();
public ArrayList<String[]> getAgentOpenHouses() {
return agentOpenHouses;
}
public ArrayList<String[]> getCalendarOfEvents() {
return calendarOfEvents;
}
public ArrayList<String[]> getLatestStatistics() {
return latestStatistics;
}
public ArrayList<String[]> getMemberRoster() {
return memberRoster;
}
public void setAgentOpenHouses(String[] agentOpenHousesItem) {
this.agentOpenHouses.add(agentOpenHousesItem);
}
public void setCalendarOfEvents(String[] calendarOfEventsItem) {
this.calendarOfEvents.add(calendarOfEventsItem);
}
public void setLatestStatistics(String[] latestStatisticsItem) {
this.latestStatistics.add(latestStatisticsItem);
}
public void setMemberRoster(String[] memberRosterItem) {
this.memberRoster.add(memberRosterItem);
}
} // end class ParsedDataSet
You could throw an if statement into your assignements and reassign any caught 'NULL' or empty strings into a zero value or just reassign as variable = "" in your code.
For example:
if (agentInfo1 == NULL) {
agentInfo1 = "" || agentInfo1 = 0; //Depending on what your variables are
}
Try putting try catch loop in code to find where the error is happening, then, pinpoint the exact part of code that is giving this error, there do null checks before proceeding. This is based on best practices of software development, rather than a fix for you.
Alternatively, you can makes sure on server side that there are no "null" values, maybe by giving dummy value like "EMPTY_STRING". This is especially relevant if your app is already shipped and you cant make any client code changes.