Browse Source

Email deobfuscation optimizations

pull/61/merge
Ezerous 6 years ago
parent
commit
eeb5757a88
No known key found for this signature in database GPG Key ID: 262B2954BBA319E3
  1. 4
      app/src/main/java/gr/thmmy/mthmmy/activities/create_content/NewTopicTask.java
  2. 6
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java
  3. 7
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java
  4. 6
      app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java
  5. 4
      app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java
  6. 48
      app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java

4
app/src/main/java/gr/thmmy/mthmmy/activities/create_content/NewTopicTask.java

@ -2,12 +2,12 @@ package gr.thmmy.mthmmy.activities.create_content;
import android.os.AsyncTask; import android.os.AsyncTask;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import java.io.IOException; import java.io.IOException;
import gr.thmmy.mthmmy.base.BaseApplication; import gr.thmmy.mthmmy.base.BaseApplication;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
@ -44,7 +44,7 @@ public class NewTopicTask extends AsyncTask<String, Void, Boolean> {
String seqnum, sc, topic, createTopicUrl; String seqnum, sc, topic, createTopicUrl;
try { try {
Response response = client.newCall(request).execute(); Response response = client.newCall(request).execute();
document = ParseHelpers.parse(response.body().string()); document = Jsoup.parse(response.body().string());
seqnum = document.select("input[name=seqnum]").first().attr("value"); seqnum = document.select("input[name=seqnum]").first().attr("value");
sc = document.select("input[name=sc]").first().attr("value"); sc = document.select("input[name=sc]").first().attr("value");

6
app/src/main/java/gr/thmmy/mthmmy/activities/profile/latestPosts/LatestPostsFragment.java

@ -8,6 +8,7 @@ import android.view.ViewGroup;
import android.widget.ProgressBar; import android.widget.ProgressBar;
import android.widget.Toast; import android.widget.Toast;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
@ -29,6 +30,8 @@ import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import timber.log.Timber; import timber.log.Timber;
import static gr.thmmy.mthmmy.utils.parsing.ParseHelpers.deobfuscateElements;
/** /**
* Use the {@link LatestPostsFragment#newInstance} factory method to create an instance of this fragment. * Use the {@link LatestPostsFragment#newInstance} factory method to create an instance of this fragment.
*/ */
@ -163,7 +166,7 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
.build(); .build();
try { try {
Response response = BaseActivity.getClient().newCall(request).execute(); Response response = BaseActivity.getClient().newCall(request).execute();
return parseLatestPosts(ParseHelpers.parse(response.body().string())); return parseLatestPosts(Jsoup.parse(response.body().string()));
} catch (SSLHandshakeException e) { } catch (SSLHandshakeException e) {
Timber.w("Certificate problem (please switch to unsafe connection)."); Timber.w("Certificate problem (please switch to unsafe connection).");
} catch (Exception e) { } catch (Exception e) {
@ -206,6 +209,7 @@ public class LatestPostsFragment extends BaseFragment implements LatestPostsAdap
return true; return true;
} }
deobfuscateElements(latestPostsRows, false);
for (Element row : latestPostsRows) { for (Element row : latestPostsRows) {
String pTopicUrl, pTopicTitle, pDateTime, pPost; String pTopicUrl, pTopicTitle, pDateTime, pPost;
if (Integer.parseInt(row.attr("cellpadding")) == 4) { if (Integer.parseInt(row.attr("cellpadding")) == 4) {

7
app/src/main/java/gr/thmmy/mthmmy/activities/profile/stats/StatsFragment.java

@ -26,6 +26,7 @@ import com.github.mikephil.charting.data.LineDataSet;
import com.github.mikephil.charting.formatter.IAxisValueFormatter; import com.github.mikephil.charting.formatter.IAxisValueFormatter;
import com.github.mikephil.charting.formatter.PercentFormatter; import com.github.mikephil.charting.formatter.PercentFormatter;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
@ -39,12 +40,13 @@ import javax.net.ssl.SSLHandshakeException;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import gr.thmmy.mthmmy.R; import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.base.BaseActivity; import gr.thmmy.mthmmy.base.BaseActivity;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import me.zhanghai.android.materialprogressbar.MaterialProgressBar; import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
import okhttp3.Request; import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import timber.log.Timber; import timber.log.Timber;
import static gr.thmmy.mthmmy.utils.parsing.ParseHelpers.deobfuscateElements;
public class StatsFragment extends Fragment { public class StatsFragment extends Fragment {
/** /**
* The key to use when putting profile's url String to {@link StatsFragment}'s Bundle. * The key to use when putting profile's url String to {@link StatsFragment}'s Bundle.
@ -138,7 +140,7 @@ public class StatsFragment extends Fragment {
.build(); .build();
try { try {
Response response = BaseActivity.getClient().newCall(request).execute(); Response response = BaseActivity.getClient().newCall(request).execute();
return parseStats(ParseHelpers.parse(response.body().string())); return parseStats(Jsoup.parse(response.body().string()));
} catch (SSLHandshakeException e) { } catch (SSLHandshakeException e) {
Timber.w("Certificate problem (please switch to unsafe connection)."); Timber.w("Certificate problem (please switch to unsafe connection).");
} catch (Exception e) { } catch (Exception e) {
@ -173,6 +175,7 @@ public class StatsFragment extends Fragment {
return false; return false;
{ {
Elements titleRows = statsPage.select("table.bordercolor[align]>tbody>tr.titlebg"); Elements titleRows = statsPage.select("table.bordercolor[align]>tbody>tr.titlebg");
deobfuscateElements(titleRows, false);
generalStatisticsTitle = titleRows.first().text(); generalStatisticsTitle = titleRows.first().text();
if (userHasPosts) { if (userHasPosts) {
postingActivityByTimeTitle = titleRows.get(1).text(); postingActivityByTimeTitle = titleRows.get(1).text();

6
app/src/main/java/gr/thmmy/mthmmy/activities/profile/summary/SummaryFragment.java

@ -13,6 +13,7 @@ import android.webkit.WebView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements; import org.jsoup.select.Elements;
@ -25,6 +26,8 @@ import gr.thmmy.mthmmy.R;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers; import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import timber.log.Timber; import timber.log.Timber;
import static gr.thmmy.mthmmy.utils.parsing.ParseHelpers.deobfuscateElements;
/** /**
* Use the {@link SummaryFragment#newInstance} factory method to create an instance of this fragment. * Use the {@link SummaryFragment#newInstance} factory method to create an instance of this fragment.
@ -68,7 +71,7 @@ public class SummaryFragment extends Fragment {
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
profileSummaryDocument = ParseHelpers.parse(getArguments().getString(PROFILE_DOCUMENT)); profileSummaryDocument = Jsoup.parse(getArguments().getString(PROFILE_DOCUMENT));
parsedProfileSummaryData = new ArrayList<>(); parsedProfileSummaryData = new ArrayList<>();
} }
@ -131,6 +134,7 @@ public class SummaryFragment extends Fragment {
//Contains all summary's rows //Contains all summary's rows
Elements summaryRows = profile.select(".bordercolor > tbody:nth-child(1) > tr:nth-child(2) tr"); Elements summaryRows = profile.select(".bordercolor > tbody:nth-child(1) > tr:nth-child(2) tr");
deobfuscateElements(summaryRows, false);
for (Element summaryRow : summaryRows) { for (Element summaryRow : summaryRows) {
String rowText = summaryRow.text(), pHtml = ""; String rowText = summaryRow.text(), pHtml = "";

4
app/src/main/java/gr/thmmy/mthmmy/activities/topic/Posting.java

@ -1,10 +1,10 @@
package gr.thmmy.mthmmy.activities.topic; package gr.thmmy.mthmmy.activities.topic;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document; import org.jsoup.nodes.Document;
import java.io.IOException; import java.io.IOException;
import gr.thmmy.mthmmy.utils.parsing.ParseHelpers;
import okhttp3.Response; import okhttp3.Response;
import timber.log.Timber; import timber.log.Timber;
@ -59,7 +59,7 @@ public class Posting {
if (response.code() < 200 || response.code() >= 400) return REPLY_STATUS.OTHER_ERROR; if (response.code() < 200 || response.code() >= 400) return REPLY_STATUS.OTHER_ERROR;
String finalUrl = response.request().url().toString(); String finalUrl = response.request().url().toString();
if (finalUrl.contains("action=post")) { if (finalUrl.contains("action=post")) {
Document postErrorPage = ParseHelpers.parse(response.body().string()); Document postErrorPage = Jsoup.parse(response.body().string());
String[] errors = postErrorPage.select("tr[id=errors] div[id=error_list]").first() String[] errors = postErrorPage.select("tr[id=errors] div[id=error_list]").first()
.toString().split("<br>"); .toString().split("<br>");
for (int i = 0; i < errors.length; ++i) { //TODO test for (int i = 0; i < errors.length; ++i) { //TODO test

48
app/src/main/java/gr/thmmy/mthmmy/utils/parsing/ParseHelpers.java

@ -10,6 +10,8 @@ import java.util.ArrayList;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import timber.log.Timber;
/** /**
* This class consists exclusively of static classes (enums) and methods (excluding methods of inner * This class consists exclusively of static classes (enums) and methods (excluding methods of inner
* classes). It can be used to resolve a page's language and state or fix embedded videos html code * classes). It can be used to resolve a page's language and state or fix embedded videos html code
@ -190,7 +192,7 @@ public class ParseHelpers {
} }
/** /**
* Method that adds email deobfuscation functionality to Jsoup.parse. * Method that replaces CloudFlare-obfuscated emails with deobfuscated ones
* Replace Jsoup.parse with this wherever needed * Replace Jsoup.parse with this wherever needed
* *
* @param html html to parse * @param html html to parse
@ -198,25 +200,41 @@ public class ParseHelpers {
*/ */
public static Document parse(String html){ public static Document parse(String html){
Document document = Jsoup.parse(html); Document document = Jsoup.parse(html);
Elements obfuscatedEmails = document.select("span.__cf_email__"); deobfuscateElements(document.select("span.__cf_email__"), true);
for (Element obfuscatedEmail : obfuscatedEmails) { return document;
String obfuscatedEmailStr = obfuscatedEmail.attr("data-cfemail");
//Deobfuscate
final StringBuilder stringBuilder = new StringBuilder();
final int r = Integer.parseInt(obfuscatedEmailStr.substring(0, 2), 16);
for (int n = 2; n < obfuscatedEmailStr.length(); n += 2) {
final int i = Integer.parseInt(obfuscatedEmailStr.substring(n, n + 2), 16) ^ r;
stringBuilder.append(Character.toString((char) i));
} }
String deobfuscatedEmail = stringBuilder.toString(); /**
* Use this method instead of parse() if you are targetting specific elements
*/
public static void deobfuscateElements(Elements elements, boolean found){
if(!found)
elements = elements.select("span.__cf_email__");
Element parent = obfuscatedEmail.parent(); for (Element obfuscatedElement : elements) {
String deobfuscatedEmail = deobfuscateEmail(obfuscatedElement.attr("data-cfemail"));
Element parent = obfuscatedElement.parent();
if (parent.is("a")&&parent.attr("href").contains("email-protection")) if (parent.is("a")&&parent.attr("href").contains("email-protection"))
parent.attr("href", "mailto:"+deobfuscatedEmail); parent.attr("href", "mailto:"+deobfuscatedEmail);
obfuscatedEmail.replaceWith(new TextNode(deobfuscatedEmail, "")); obfuscatedElement.replaceWith(new TextNode(deobfuscatedEmail, ""));
} }
return document; }
/**
* @param obfuscatedEmail CloudFlare-obfuscated email
* @return deobfuscated email
*/
private static String deobfuscateEmail(String obfuscatedEmail){
//Deobfuscate
final StringBuilder stringBuilder = new StringBuilder();
final int r = Integer.parseInt(obfuscatedEmail.substring(0, 2), 16);
for (int n = 2; n < obfuscatedEmail.length(); n += 2) {
final int i = Integer.parseInt(obfuscatedEmail.substring(n, n + 2), 16) ^ r;
stringBuilder.append(Character.toString((char) i));
}
Timber.i("Email deobfuscated.");
return stringBuilder.toString();
} }
} }

Loading…
Cancel
Save