jodelstats
jodelstats copied to clipboard
Jodel auth
Has anyone had any success in authenticating with Jodel via HMAC? I digged into the Jodel apk and found the following:
private String calculateHMac(Request var1, String var2) {
String var7;
try {
URI var4 = var1.DB();
StringBuilder var5 = new StringBuilder(var1.Ee());
var5.append("%").append(var4.getHost()).append("%").append(String.valueOf(this.port)).append("%").append(var4.getPath());
var5.append("%");
String var3 = var1.dJ("Authorization");
if(!TextUtils.isEmpty(var3)) {
var5.append(var3.split(" ")[1]);
}
var5.append("%").append(var2);
appendQuery(var5, var4.getQuery());
appendBody(var5, var1);
var3 = var5.toString();
Mac var8 = Mac.getInstance("HmacSHA1");
SecretKeySpec var9 = new SecretKeySpec(this.secret, "HmacSHA1");
var8.init(var9);
var7 = hex(var8.doFinal(var3.getBytes("UTF-8")));
} catch (Throwable var6) {
Crashlytics.logException(var6);
var7 = "";
}
return var7;
}
private native String generate(String var1);
generate() is called from a native file (~~C~~ C++, libhmac.so) which decompiled leads to the following:
//
// This file was generated by the Retargetable Decompiler
// Website: https://retdec.com
// Copyright (c) 2016 Retargetable Decompiler <[email protected]>
//
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
// ------------------------ Structures ------------------------
struct struct_0 {
int32_t e0;
int32_t e1;
char e2[4];
int32_t e3;
int32_t e4;
};
// ------------------- Function Prototypes --------------------
int32_t ___cxa_finalize(int32_t a1);
int32_t _ftext(int32_t a1, int32_t a2);
char * client_secret(void);
int32_t function_568(char * a1, int32_t a2);
void function_758(void);
int32_t get_generated_key(void);
int32_t get_random_key(void);
int32_t Java_com_jodelapp_jodelandroidv3_api_HmacInterceptor_generate(struct struct_0 * a1, int32_t a2, int32_t a3);
// --------------------- Global Variables ---------------------
int32_t g1 = 0; // $a0
int32_t g2 = 0; // $a3
int32_t g3 = 0; // $gp
int32_t g4 = 0; // $ra
int32_t g5 = 0; // $t9
int32_t g6 = 0; // $v0
int32_t g7 = 0x11000; // 0x11000
int32_t g8 = 0xa002000;
// ------------------------ Functions -------------------------
// Address range: 0x530 - 0x567
int32_t _ftext(int32_t a1, int32_t a2) {
int32_t v1 = g5; // bp+538
g3 = v1 + 0x18b00;
___cxa_finalize(g7);
return *(int32_t *)(v1 + 0x10b18);
}
// Address range: 0x568 - 0x58f
int32_t function_568(char * a1, int32_t a2) {
int32_t v1 = *(int32_t *)(g5 + 0x10ae4); // bp+580
return __cxa_atexit((void (*)())v1, a1, (char *)&g7);
}
// Address range: 0x590 - 0x5a7
int32_t get_random_key(void) {
// 0x590
g3 = g5 + 0x18aa0;
g6 = *(int32_t *)(g5 + 0x10ac0);
return (int32_t)"\x18\x6e\x2e\x76\x09\x7d\x08\x42\x23\x76\x4a\x45\x45\x4e\x38\x7f\x30\x5e\x47\x42\x49\x5e\x29\x6c\x67\x4b\x30\x35\x5a\x29\x33\x75\x7c\x47\x6f\x73\x5f\x3f\x0b\x53";
}
// Address range: 0x5a8 - 0x61f
int32_t get_generated_key(void) {
int32_t v1 = g5;
g5 = get_random_key;
int32_t v2 = g1; // $s0
get_random_key();
g3 = v1 + 0x18a88;
int32_t * v3 = (int32_t *)(v1 + 0x10aa8);
int32_t result = *v3 + 0x1080;
int32_t v4 = result;
*(char *)v4 = *(char *)v2 ^ *(char *)g6;
int32_t v5 = v4 + 1; // bp+604
int32_t v6 = g6 + 1; // bp+604
g6 = v6;
// branch -> 0x5ec
while (v5 != *v3 + 0x10a8) {
// 0x5ec
v4 = v5;
v2++;
*(char *)v4 = *(char *)v2 ^ *(char *)v6;
v5 = v4 + 1;
v6 = g6 + 1;
g6 = v6;
// continue -> 0x5ec
}
// 0x608
return result;
}
// Address range: 0x620 - 0x6ab
char * client_secret(void) {
int32_t v1 = g5; // bp+628
g5 = get_generated_key;
int32_t v2 = get_generated_key(); // bp+640
g3 = v1 + 0x18a10;
char * mem = malloc(g8); // bp+654
int32_t v3 = (int32_t)mem;
int32_t v4 = 0;
// branch -> 0x664
while (true) {
int32_t v5 = v4 - v4 % 4; // bp+668
int32_t v6 = (-1 - v4) % 4; // bp+674
uint32_t v7 = *(int32_t *)(v5 + v2); // bp+678
int32_t v8 = 8 * v6; // bp+680
g2 = v8;
int32_t v9 = v4 + 1;
*(char *)(v5 + v3 + v6) = (char)(v7 >> v8);
if (v9 == 40) {
// 0x694
*(char *)(v3 + 40) = 0;
return mem;
}
// 0x664
v4 = v9;
// branch -> 0x664
}
}
// Address range: 0x6ac - 0x757
int32_t Java_com_jodelapp_jodelandroidv3_api_HmacInterceptor_generate(struct struct_0 * a1, int32_t a2, int32_t a3) {
int32_t v1 = g5 + 0x18984;
g3 = v1;
int32_t v2 = a1->e0;
g6 = v2;
int32_t v3 = *(int32_t *)(v2 + 676);
g5 = v3;
g4 = 1776;
int32_t v4 = (int32_t)a1; // $s0
((int32_t (*)(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t))v3)(g1, a3, 0, g2, v1, 0);
g5 = (int32_t)client_secret;
int32_t v5 = *(int32_t *)(*(int32_t *)v4 + 668); // $s3
g1 = g6;
char * v6 = client_secret(); // bp+704
g5 = v5;
g4 = 1820;
g1 = v4;
((int32_t (*)())v5)();
int32_t v7 = *(int32_t *)(*(int32_t *)v4 + 680); // bp+724
g5 = v7;
g1 = v4;
g4 = 1848;
((int32_t (*)(int32_t))v7)(v4);
return (int32_t)v6;
}
// Address range: 0x758 - 0x75f
void function_758(void) {
// 0x758
return;
}
// Address range: 0x760 - 0x79f
int32_t ___cxa_finalize(int32_t a1) {
int32_t v1 = *(int32_t *)(g3 - 0x7ff0); // bp+760
g5 = v1;
g4 = 1904;
((int32_t (*)())v1)();
int32_t v2 = *(int32_t *)(g3 - 0x7ff0); // bp+770
g5 = v2;
g4 = 1920;
((int32_t (*)())v2)();
int32_t v3 = *(int32_t *)(g3 - 0x7ff0); // bp+780
g5 = v3;
g4 = 1936;
((int32_t (*)())v3)();
return g5;
}
// --------------- Dynamically Linked Functions ---------------
// int __cxa_atexit(void(*func)(void *), void * arg, void * dso_handle);
// void * malloc(size_t);
// --------------------- Meta-Information ---------------------
// Detected compiler/packer: gcc (4.9)
// Detected functions: 8
// Decompiler release: v2.1.2 (2016-01-27)
// Decompilation date: 2016-06-13 22:47:23
I tried to call some functions in it, but I always get segmentation faults (for example one at 0x0000555555554933 in get_generated_key () at main.cpp:75
)
Did you try to modify the android app with smali?
A good resource for smali: http://androidcracking.blogspot.de
I think they don't want let us build our own bots 😁
The problem is that, like for Pokémon Go they created a string that has to be sent on every request.
This string is created with the app signature and with a key generated by an ~~arm7 library~~ multi-architecture shared object called libhmac.so
, which is then called by the Java app itself (IIRC there is also a check to ensure that the lib is called from com.tellm.android.app)
I debugged this some time ago. Pretty easy actually.
- attach a debugger to the android app
- capture the output of the native
generate()
function to get the secret for the hash function (No need to reverse it lol) - rebuild the
calculateHmac()
function
I ended up with this (This is outdated. It doesn't seem to work for creating new accounts, but still does for re-authenticating old accounts.):
def _sign_request(self, method, url, headers, payload=None):
timestamp = datetime.datetime.utcnow().isoformat()[:-7] + "Z"
req = [method,
urlparse(url).netloc,
"443",
urlparse(url).path,
self.access_token if self.access_token else "",
timestamp]
req.extend(sorted(urlparse(url).query.replace("=", "%").split("&")))
req.append(payload if payload else "")
secret = bytearray([108, 67, 89, 78, 86, 75, 110, 104, 71, 120, 118, 111, 120, 101, 111, 119, 104, 108, 82, 80,
69, 99, 76, 82, 87, 120, 75, 106, 76, 77, 69, 101, 105, 116, 113, 98, 114, 110, 97, 82])
signature = hmac.new(secret, "%".join(req).encode("utf-8"), sha1).hexdigest().upper()
headers['X-Authorization'] = 'HMAC ' + signature
headers['X-Client-Type'] = 'android_4.5.11'
headers['X-Timestamp'] = timestamp
headers['X-Api-Version'] = '0.1'
For making this work again, you probably only need to get the new secret from the generate()
function.