Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
FROM ubuntu:latest
FROM debian:stable-slim

ENV args=""

COPY ./rootfs /app/rootfs
COPY ./wrapper /app
WORKDIR /app
#COPY --from=builder /app /app
COPY . /app
ENV args ""

CMD ["bash", "-c", "./wrapper ${args}"]
CMD ["bash", "-c", "/app/wrapper $args"]

EXPOSE 10020 20020
EXPOSE 10020 20020 30020
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,46 @@ Usage: wrapper [OPTION]...
-H, --host=STRING (default=`127.0.0.1')
-D, --decrypt-port=INT (default=`10020')
-M, --m3u8-port=INT (default=`20020')
-A, --account-port=INT (default=`30020')
-P, --proxy=STRING (default=`')
-L, --login=STRING (username:password)
-F, --code-from-file (default=off)
```

# Build from source

1. Install dependencies:

- Build tools:

```
sudo apt install build-essential cmake wget unzip git
```

- LLVM:

```
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)
```

- Android NDK r23b:
```
wget -O android-ndk-r23b-linux.zip https://dl.google.com/android/repository/android-ndk-r23b-linux.zip
unzip -q -d ~ android-ndk-r23b-linux.zip
```

2. Build:

```
git clone https://github.com/WorldObservationLog/wrapper
cd wrapper
mkdir build
cd build
cmake ..
make -j$(nproc)
```


# Special thanks
- Anonymous, for providing the original version of this project and the legacy Frida decryption method.
- chocomint, for providing support for arm64 arch.
33 changes: 27 additions & 6 deletions cmdline.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const char *gengetopt_args_info_help[] = {
" -H, --host=STRING (default=`127.0.0.1')",
" -D, --decrypt-port=INT (default=`10020')",
" -M, --m3u8-port=INT (default=`20020')",
" -A, --account-port=INT (default=`30020')",
" -P, --proxy=STRING (default=`')",
" -L, --login=STRING username:password",
" -F, --code-from-file (default=off)",
Expand Down Expand Up @@ -74,6 +75,7 @@ void clear_given (struct gengetopt_args_info *args_info)
args_info->host_given = 0 ;
args_info->decrypt_port_given = 0 ;
args_info->m3u8_port_given = 0 ;
args_info->account_port_given = 0 ;
args_info->proxy_given = 0 ;
args_info->login_given = 0 ;
args_info->code_from_file_given = 0 ;
Expand All @@ -91,6 +93,8 @@ void clear_args (struct gengetopt_args_info *args_info)
args_info->decrypt_port_orig = NULL;
args_info->m3u8_port_arg = 20020;
args_info->m3u8_port_orig = NULL;
args_info->account_port_arg = 30020;
args_info->account_port_orig = NULL;
args_info->proxy_arg = gengetopt_strdup ("");
args_info->proxy_orig = NULL;
args_info->login_arg = NULL;
Expand All @@ -113,11 +117,12 @@ void init_args_info(struct gengetopt_args_info *args_info)
args_info->host_help = gengetopt_args_info_help[2] ;
args_info->decrypt_port_help = gengetopt_args_info_help[3] ;
args_info->m3u8_port_help = gengetopt_args_info_help[4] ;
args_info->proxy_help = gengetopt_args_info_help[5] ;
args_info->login_help = gengetopt_args_info_help[6] ;
args_info->code_from_file_help = gengetopt_args_info_help[7] ;
args_info->base_dir_help = gengetopt_args_info_help[8] ;
args_info->device_info_help = gengetopt_args_info_help[9] ;
args_info->account_port_help = gengetopt_args_info_help[5] ;
args_info->proxy_help = gengetopt_args_info_help[6] ;
args_info->login_help = gengetopt_args_info_help[7] ;
args_info->code_from_file_help = gengetopt_args_info_help[8] ;
args_info->base_dir_help = gengetopt_args_info_help[9] ;
args_info->device_info_help = gengetopt_args_info_help[10] ;

}

Expand Down Expand Up @@ -211,6 +216,7 @@ cmdline_parser_release (struct gengetopt_args_info *args_info)
free_string_field (&(args_info->host_orig));
free_string_field (&(args_info->decrypt_port_orig));
free_string_field (&(args_info->m3u8_port_orig));
free_string_field (&(args_info->account_port_orig));
free_string_field (&(args_info->proxy_arg));
free_string_field (&(args_info->proxy_orig));
free_string_field (&(args_info->login_arg));
Expand Down Expand Up @@ -259,6 +265,8 @@ cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info)
write_into_file(outfile, "decrypt-port", args_info->decrypt_port_orig, 0);
if (args_info->m3u8_port_given)
write_into_file(outfile, "m3u8-port", args_info->m3u8_port_orig, 0);
if (args_info->account_port_given)
write_into_file(outfile, "account-port", args_info->account_port_orig, 0);
if (args_info->proxy_given)
write_into_file(outfile, "proxy", args_info->proxy_orig, 0);
if (args_info->login_given)
Expand Down Expand Up @@ -534,6 +542,7 @@ cmdline_parser_internal (
{ "host", 1, NULL, 'H' },
{ "decrypt-port", 1, NULL, 'D' },
{ "m3u8-port", 1, NULL, 'M' },
{ "account-port", 1, NULL, 'A' },
{ "proxy", 1, NULL, 'P' },
{ "login", 1, NULL, 'L' },
{ "code-from-file", 0, NULL, 'F' },
Expand All @@ -542,7 +551,7 @@ cmdline_parser_internal (
{ 0, 0, 0, 0 }
};

c = getopt_long (argc, argv, "hVH:D:M:P:L:FB:I:", long_options, &option_index);
c = getopt_long (argc, argv, "hVH:D:M:A:P:L:FB:I:", long_options, &option_index);

if (c == -1) break; /* Exit from `while (1)' loop. */

Expand Down Expand Up @@ -593,6 +602,18 @@ cmdline_parser_internal (
additional_error))
goto failure;

break;
case 'A': /* . */


if (update_arg( (void *)&(args_info->account_port_arg),
&(args_info->account_port_orig), &(args_info->account_port_given),
&(local_args_info.account_port_given), optarg, 0, "30020", ARG_INT,
check_ambiguity, override, 0, 0,
"account-port", 'A',
additional_error))
goto failure;

break;
case 'P': /* . */

Expand Down
4 changes: 4 additions & 0 deletions cmdline.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ struct gengetopt_args_info
int m3u8_port_arg; /**< @brief (default='20020'). */
char * m3u8_port_orig; /**< @brief original value given at command line. */
const char *m3u8_port_help; /**< @brief help description. */
int account_port_arg; /**< @brief (default='30020'). */
char * account_port_orig; /**< @brief original value given at command line. */
const char *account_port_help; /**< @brief help description. */
char * proxy_arg; /**< @brief (default=''). */
char * proxy_orig; /**< @brief original value given at command line. */
const char *proxy_help; /**< @brief help description. */
Expand All @@ -68,6 +71,7 @@ struct gengetopt_args_info
unsigned int host_given ; /**< @brief Whether host was given. */
unsigned int decrypt_port_given ; /**< @brief Whether decrypt-port was given. */
unsigned int m3u8_port_given ; /**< @brief Whether m3u8-port was given. */
unsigned int account_port_given ; /**< @brief Whether account-port was given. */
unsigned int proxy_given ; /**< @brief Whether proxy was given. */
unsigned int login_given ; /**< @brief Whether login was given. */
unsigned int code_from_file_given ; /**< @brief Whether code-from-file was given. */
Expand Down
143 changes: 131 additions & 12 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ struct shared_ptr GUID;
int decryptCount = 1000;
char *device_infos[9];

// Account info cache
static char *g_storefront_id = NULL;
static char *g_dev_token = NULL;
static char *g_music_token = NULL;

#ifndef MyRelease
int32_t CURLOPT_SSL_VERIFYPEER = 64;
int32_t CURLOPT_SSL_VERIFYHOST = 81;
Expand Down Expand Up @@ -714,6 +719,114 @@ static inline void *new_socket_m3u8(void *args) {
}
}

void handle_account(const int connfd)
{
char buffer[4096];
ssize_t n = read(connfd, buffer, sizeof(buffer) - 1);
if (n <= 0) {
return;
}
buffer[n] = '\0';

// Parse HTTP request (simple check for GET)
if (strncmp(buffer, "GET", 3) != 0 && strncmp(buffer, "POST", 4) != 0) {
const char *error_response = "HTTP/1.1 400 Bad Request\r\nContent-Type: application/json\r\nContent-Length: 0\r\n\r\n";
writefull(connfd, (void *)error_response, strlen(error_response));
return;
}

// Format JSON response body
size_t json_size = 1024;
char *json_body = (char *)malloc(json_size);
if (json_body == NULL)
{
fprintf(stderr, "[.] failed to allocate memory for account response\n");
const char *error_response = "HTTP/1.1 500 Internal Server Error\r\nContent-Type: application/json\r\nContent-Length: 0\r\n\r\n";
writefull(connfd, (void *)error_response, strlen(error_response));
return;
}

snprintf(json_body, json_size, "{\"storefront_id\":\"%s\",\"dev_token\":\"%s\",\"music_token\":\"%s\"}",
g_storefront_id, g_dev_token, g_music_token);

int json_len = strlen(json_body);

// Format HTTP response with headers
size_t response_size = 512;
char *http_response = (char *)malloc(response_size);
if (http_response == NULL)
{
fprintf(stderr, "[.] failed to allocate memory for HTTP response\n");
free(json_body);
const char *error_response = "HTTP/1.1 500 Internal Server Error\r\nContent-Type: application/json\r\nContent-Length: 0\r\n\r\n";
writefull(connfd, (void *)error_response, strlen(error_response));
return;
}

snprintf(http_response, response_size, "HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nContent-Length: %d\r\nConnection: close\r\n\r\n",
json_len);

fprintf(stderr, "[.] returning account info, storefront: %s\n", g_storefront_id);
writefull(connfd, http_response, strlen(http_response));
writefull(connfd, json_body, json_len);

free(http_response);
free(json_body);
}

static inline void *new_socket_account(void *args)
{
const int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
if (fd == -1)
{
perror("socket");
return NULL;
}
const int optval = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));

static struct sockaddr_in serv_addr = {.sin_family = AF_INET};
inet_pton(AF_INET, args_info.host_arg, &serv_addr.sin_addr);
serv_addr.sin_port = htons(args_info.account_port_arg);
if (bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
{
perror("bind");
return NULL;
}

if (listen(fd, 5) == -1)
{
perror("listen");
return NULL;
}

fprintf(stderr, "[!] listening account info request on %s:%d\n", args_info.host_arg, args_info.account_port_arg);

static struct sockaddr_in peer_addr;
static socklen_t peer_addr_size = sizeof(peer_addr);
while (1)
{
const int connfd = accept4(fd, (struct sockaddr *)&peer_addr,
&peer_addr_size, SOCK_CLOEXEC);
if (connfd == -1)
{
if (errno == ENETDOWN || errno == EPROTO || errno == ENOPROTOOPT ||
errno == EHOSTDOWN || errno == ENONET ||
errno == EHOSTUNREACH || errno == EOPNOTSUPP ||
errno == ENETUNREACH)
continue;
perror("accept4");
}

handle_account(connfd);

if (close(connfd) == -1)
{
perror("close");
}
}
}

char* get_account_storefront_id(struct shared_ptr reqCtx) {
union std_string *region = malloc(sizeof(union std_string));
struct shared_ptr urlbag = {.obj = 0x0, .ctrl_blk = 0x0};
Expand All @@ -727,11 +840,10 @@ char* get_account_storefront_id(struct shared_ptr reqCtx) {
return NULL;
}

void write_storefront_id(struct shared_ptr reqCtx) {
void write_storefront_id(void) {
FILE *fp = fopen(strcat_b(args_info.base_dir_arg, "/STOREFRONT_ID"), "w");
char *storefront_id = get_account_storefront_id(reqCtx);
printf("[+] StoreFront ID: %s\n", storefront_id);
fprintf(fp, "%s", get_account_storefront_id(reqCtx));
printf("[+] StoreFront ID: %s\n", g_storefront_id);
fprintf(fp, "%s", g_storefront_id);
fclose(fp);
}

Expand Down Expand Up @@ -836,7 +948,7 @@ char* get_dev_token(struct shared_ptr reqCtx) {
return result;
}

void write_music_token(struct shared_ptr reqCtx) {
void write_music_token(void) {
int token_file_available = 0;
if (file_exists(strcat_b(args_info.base_dir_arg, "/MUSIC_TOKEN"))) {
FILE *fp = fopen(strcat_b(args_info.base_dir_arg, "/MUSIC_TOKEN"), "r");
Expand All @@ -857,11 +969,8 @@ void write_music_token(struct shared_ptr reqCtx) {
return;
}
FILE *fp = fopen(strcat_b(args_info.base_dir_arg, "/MUSIC_TOKEN"), "w");
char *guid = get_guid();
char *dev_token = get_dev_token(reqCtx);
char *token = get_music_user_token(guid, dev_token, reqCtx);
printf("[+] Music-Token: %.14s...\n", token);
fprintf(fp, "%s", token);
printf("[+] Music-Token: %.14s...\n", g_music_token);
fprintf(fp, "%s", g_music_token);
fclose(fp);
}

Expand Down Expand Up @@ -895,12 +1004,22 @@ int main(int argc, char *argv[]) {
_ZN22SVPlaybackLeaseManager12requestLeaseERKb(leaseMgr, &autom);
FHinstance = _ZN21SVFootHillSessionCtrl8instanceEv();

write_storefront_id(ctx);
write_music_token(ctx);
// Cache account info
g_storefront_id = get_account_storefront_id(ctx);
g_dev_token = get_dev_token(ctx);
g_music_token = get_music_user_token(get_guid(), g_dev_token, ctx);
fprintf(stderr, "[+] account info cached successfully\n");

write_storefront_id();
write_music_token();

pthread_t m3u8_thread;
pthread_create(&m3u8_thread, NULL, &new_socket_m3u8, NULL);
pthread_detach(m3u8_thread);

pthread_t account_thread;
pthread_create(&account_thread, NULL, &new_socket_account, NULL);
pthread_detach(account_thread);

return new_socket();
}
1 change: 1 addition & 0 deletions wrapper.ggo
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ version "1.2.0"
option "host" H "" string optional default="127.0.0.1"
option "decrypt-port" D "" int optional default="10020"
option "m3u8-port" M "" int optional default="20020"
option "account-port" A "" int optional default="30020"
option "proxy" P "" string optional default=""
option "login" L "username:password" string optional
option "code-from-file" F "" flag off
Expand Down