16 Commits
linux ... main

Author SHA1 Message Date
ab287a2b69 fix: Dockerfile make buildable 2025-03-29 21:40:32 +09:00
4459548fb3 update 2025-01-17 10:12:44 +08:00
6ffd3585b6 update 2024-12-20 17:46:15 +08:00
3b3a73bb60 add arm 2024-12-01 19:13:30 +08:00
ca1f26a950 Update wrapper-qemu.yml 2024-09-19 09:00:15 +08:00
639f50bf4d add auto make qemu 2024-09-19 08:55:04 +08:00
57dd342fd7 Update README.md 2024-09-06 21:53:58 +08:00
1077660bcc V2 2024-09-06 21:47:00 +08:00
976118d948 set INADDR_ANY 2024-08-21 02:12:39 +08:00
86d1c1a507 change dns 2024-08-20 18:34:03 +08:00
aa1886e487 fix get-m3u8 2024-08-20 18:13:32 +08:00
b96afbbd3a support get-m3u8 2024-08-20 12:51:47 +08:00
c37d47c2ec auto make 2024-06-30 22:15:30 +08:00
4fdc3aabce Update README.md 2024-06-27 20:39:11 +08:00
5e687bed4f Update README.md 2024-06-27 20:25:11 +08:00
cfd4ad5fd1 Update README.md 2024-06-27 20:24:48 +08:00
16 changed files with 1413 additions and 51 deletions

49
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,49 @@
name: Build
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install aria2
run: sudo apt install aria2 -y
- name: Install LLVM
run: sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"
- name: Set up Android NDK r23b
run: |
aria2c -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
- name: Build
run: |
mkdir build
cd build
cmake ..
make
- name: Set outputs
id: vars
run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: Wrapper.x86_64.${{ steps.vars.outputs.sha_short }}
include-hidden-files: true
path: |
rootfs
wrapper
Dockerfile

47
.github/workflows/wrapper-qemu.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: wrapper-qemu
on:
workflow_run:
workflows: ["Build"]
types: [completed]
jobs:
on-success:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- uses: actions/checkout@v4
- name: Download wrapper-qemu basic image
run: wget https://github.com/zhaarey/wrapper/releases/download/wrapper-qemu/wrapper.qcow2
- name: Mount image
run: |
sudo apt-get install -y qemu-utils
sudo modprobe nbd max_part=8
sudo qemu-nbd --connect=/dev/nbd0 wrapper.qcow2
sudo mkdir /mnt/wrapper
sudo mount /dev/nbd0p3 /mnt/wrapper/
- name: Download latest artifact
uses: dawidd6/action-download-artifact@v6
with:
github_token: ${{secrets.ACTION_TOKEN}}
run_id: ${{ github.event.workflow_run.id }}
- name: Copy wrapper to image
run: |
mv Wrapper.x86_64.* wrapper
sudo mv wrapper /mnt/wrapper/root/wrapper
sudo chmod +x /mnt/wrapper/root/wrapper/wrapper
- name: Unmount image
run: |
sudo umount /mnt/wrapper/
sudo qemu-nbd --disconnect /dev/nbd0
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: wrapper-qemu
path: wrapper.qcow2

92
.gitignore vendored Normal file
View File

@ -0,0 +1,92 @@
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
rootfs/
.vscode/
wrapper
rootfs/system/bin/linker64
build/

58
CMakeLists.txt Normal file
View File

@ -0,0 +1,58 @@
cmake_minimum_required(VERSION 3.25)
project(wrapper)
include(ExternalProject)
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 11)
set(ANDROID_NDK_PATH "$ENV{HOME}/android-ndk-r23b")
set(TOOLCHAIN "${ANDROID_NDK_PATH}/toolchains/llvm/prebuilt/linux-x86_64")
set(CMAKE_C_COMPILER "${TOOLCHAIN}/bin/x86_64-linux-android22-clang")
set(CMAKE_CXX_COMPILER "${TOOLCHAIN}/bin/x86_64-linux-android22-clang++")
set(C_COMPILER "${TOOLCHAIN}/bin/clang")
set(CMAKE_C_FLAGS "-Wall -Werror -O3")
set(CMAKE_CXX_FLAGS "-Wall -Werror -O3")
set(CMDLINE_SOURCE cmdline.c)
set(HANDLE_SOURCE main.cpp)
set(MAIN_SOURCE main.c)
set(WRAPPER_SOURCE wrapper.c)
add_library(cmdline_object OBJECT ${CMDLINE_SOURCE})
add_library(handle_object OBJECT ${HANDLE_SOURCE})
add_executable(main ${MAIN_SOURCE} $<TARGET_OBJECTS:cmdline_object> $<TARGET_OBJECTS:handle_object>)
set_target_properties(main PROPERTIES
COMPILE_DEFINITIONS "MyRelease"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/rootfs/system/bin"
)
find_library(ANDROIDAPPMUSIC_LIB androidappmusic PATHS ${CMAKE_SOURCE_DIR}/rootfs/system/lib64)
find_library(STORESERVICESCORE_LIB storeservicescore PATHS ${CMAKE_SOURCE_DIR}/rootfs/system/lib64)
find_library(MEDIAPLATFORM_LIB mediaplatform PATHS ${CMAKE_SOURCE_DIR}/rootfs/system/lib64)
find_library(CXX_SHARED_LIB c++_shared PATHS ${CMAKE_SOURCE_DIR}/rootfs/system/lib64)
# Link libraries
target_link_libraries(main
${CXX_SHARED_LIB}
${ANDROIDAPPMUSIC_LIB}
${STORESERVICESCORE_LIB}
${MEDIAPLATFORM_LIB}
)
link_directories(${CMAKE_SOURCE_DIR}/rootfs/system/lib64)
ExternalProject_Add(
wrapper
PREFIX ${CMAKE_BINARY_DIR}
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}
CONFIGURE_COMMAND ""
BUILD_COMMAND ${C_COMPILER} -O3 -Wall -o wrapper ${WRAPPER_SOURCE}
BUILD_IN_SOURCE 1
INSTALL_COMMAND ""
DEPENDS main
)

26
Dockerfile Normal file
View File

@ -0,0 +1,26 @@
FROM ubuntu:latest AS builder
WORKDIR /app
RUN apt update && \
apt install aria2 lsb-release wget software-properties-common gnupg unzip build-essential cmake -y
RUN bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"
RUN aria2c -o android-ndk-r23b-linux.zip https://dl.google.com/android/repository/android-ndk-r23b-linux.zip
RUN unzip -q -d ~ android-ndk-r23b-linux.zip
COPY . /app
RUN mkdir /app/build && cd /app/build && cmake .. && make
FROM ubuntu:latest
WORKDIR /app
COPY --from=builder /app/rootfs/ /app/rootfs/
COPY --from=builder /app/wrapper /app/
ENV args ""
CMD ["bash", "-c", "./wrapper ${args}"]
EXPOSE 10020 10020

View File

@ -1,13 +0,0 @@
all: handle.o main wrapper
handle.o: main.cpp
~/android-ndk-r23b/standalone/bin/clang++ -Wall -Werror -nostdlib -c -O3 -o handle.o main.cpp
main: handle.o test.c
~/android-ndk-r23b/standalone/bin/clang -DMyRelease -Wall -Werror -L ./rootfs/system/lib64 -landroidappmusic -lstoreservicescore -lmediaplatform -lc++_shared -O3 -Wall -o rootfs/system/bin/main handle.o test.c
wrapper: wrapper.c
clang -O3 -Wall -o wrapper wrapper.c
clean:
rm handle.o wrapper rootfs/system/bin/main

View File

@ -1,2 +1,71 @@
# wrapper
All files from anonymous, No need for an Android emulator to decrypt alac
## Wrapper
No need for an Android emulator to decrypt ALAC files. All files from anonymous.
### Recommended Environment
#### Only support Linux x86_64 and arm64.
For best results, it's recommended to use **Windows Subsystem for Linux (WSL)**.
# Special thanks
- Anonymous, for providing the original version of this project and the legacy Frida decryption method.
- chocomint, for providing support for arm64 arch.
---
### Version 2 Docker
Available for x86_64 and arm64. Need to download prebuilt version from releases or actions.
Build image: `docker build --tag wrapper .`
Login: `docker run -v ./rootfs/data:/app/rootfs/data -p 10020:10020 -e args="-L username:password -F -H 0.0.0.0" wrapper`
Run: `docker run -v ./rootfs/data:/app/rootfs/data -p 10020:10020 -e args="-H 0.0.0.0" wrapper`
### Version 2
#### Usage:
```shell
./wrapper [OPTION]...
-h, --help Print help and exit
-V, --version Print version and exit
-H, --host=STRING (default: `127.0.0.1`)
-D, --decrypt-port=INT (default: `10020`)
-M, --m3u8-port=INT (default: `20020`)
-P, --proxy=STRING (default: `''`)
-L, --login=STRING ([username]:[password])
```
#### Installation x86_64
```shell
sudo -i
wget "https://github.com/zhaarey/wrapper/releases/download/linux.V2/wrapper.x86_64.tar.gz"
mkdir wrapper
tar -xzf wrapper.x86_64.tar.gz -C wrapper
cd wrapper
./wrapper
```
#### Installation arm64
```shell
sudo -i
wget "https://github.com/zhaarey/wrapper/releases/download/arm64/wrapper.arm64.tar.gz"
mkdir wrapper
tar -xzf wrapper.arm64.tar.gz -C wrapper
cd wrapper
./wrapper
```
---
### Version 1
#### Usage:
`./wrapper [port] ([username] [password])`
#### Installation only x86_64
```shell
sudo -i
wget "https://github.com/zhaarey/wrapper/releases/download/linux/wrapper.linux.x86_64.tar.gz"
mkdir wrapper
tar -xzf wrapper.linux.x86_64.tar.gz -C wrapper
cd wrapper
./wrapper
```

639
cmdline.c Normal file
View File

@ -0,0 +1,639 @@
/*
File autogenerated by gengetopt version 2.23
generated with the following command:
gengetopt
The developers of gengetopt consider the fixed text that goes in all
gengetopt output files to be in the public domain:
we make no copyright claims on it.
*/
/* If we use autoconf. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef FIX_UNUSED
#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */
#endif
#include <getopt.h>
#include "cmdline.h"
const char *gengetopt_args_info_purpose = "";
const char *gengetopt_args_info_usage = "Usage: wrapper [OPTION]...";
const char *gengetopt_args_info_versiontext = "";
const char *gengetopt_args_info_description = "";
const char *gengetopt_args_info_help[] = {
" -h, --help Print help and exit",
" -V, --version Print version and exit",
" -H, --host=STRING (default=`127.0.0.1')",
" -D, --decrypt-port=INT (default=`10020')",
" -M, --m3u8-port=INT (default=`20020')",
" -P, --proxy=STRING (default=`')",
" -L, --login=STRING username:password",
" -F, --code-from-file (default=off)",
0
};
typedef enum {ARG_NO
, ARG_FLAG
, ARG_STRING
, ARG_INT
} cmdline_parser_arg_type;
static
void clear_given (struct gengetopt_args_info *args_info);
static
void clear_args (struct gengetopt_args_info *args_info);
static int
cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info,
struct cmdline_parser_params *params, const char *additional_error);
static char *
gengetopt_strdup (const char *s);
static
void clear_given (struct gengetopt_args_info *args_info)
{
args_info->help_given = 0 ;
args_info->version_given = 0 ;
args_info->host_given = 0 ;
args_info->decrypt_port_given = 0 ;
args_info->m3u8_port_given = 0 ;
args_info->proxy_given = 0 ;
args_info->login_given = 0 ;
args_info->code_from_file_given = 0 ;
}
static
void clear_args (struct gengetopt_args_info *args_info)
{
FIX_UNUSED (args_info);
args_info->host_arg = gengetopt_strdup ("127.0.0.1");
args_info->host_orig = NULL;
args_info->decrypt_port_arg = 10020;
args_info->decrypt_port_orig = NULL;
args_info->m3u8_port_arg = 20020;
args_info->m3u8_port_orig = NULL;
args_info->proxy_arg = gengetopt_strdup ("");
args_info->proxy_orig = NULL;
args_info->login_arg = NULL;
args_info->login_orig = NULL;
args_info->code_from_file_flag = 0;
}
static
void init_args_info(struct gengetopt_args_info *args_info)
{
args_info->help_help = gengetopt_args_info_help[0] ;
args_info->version_help = gengetopt_args_info_help[1] ;
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] ;
}
void
cmdline_parser_print_version (void)
{
printf ("%s %s\n",
(strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE),
CMDLINE_PARSER_VERSION);
if (strlen(gengetopt_args_info_versiontext) > 0)
printf("\n%s\n", gengetopt_args_info_versiontext);
}
static void print_help_common(void)
{
size_t len_purpose = strlen(gengetopt_args_info_purpose);
size_t len_usage = strlen(gengetopt_args_info_usage);
if (len_usage > 0) {
printf("%s\n", gengetopt_args_info_usage);
}
if (len_purpose > 0) {
printf("%s\n", gengetopt_args_info_purpose);
}
if (len_usage || len_purpose) {
printf("\n");
}
if (strlen(gengetopt_args_info_description) > 0) {
printf("%s\n\n", gengetopt_args_info_description);
}
}
void
cmdline_parser_print_help (void)
{
int i = 0;
print_help_common();
while (gengetopt_args_info_help[i])
printf("%s\n", gengetopt_args_info_help[i++]);
}
void
cmdline_parser_init (struct gengetopt_args_info *args_info)
{
clear_given (args_info);
clear_args (args_info);
init_args_info (args_info);
}
void
cmdline_parser_params_init(struct cmdline_parser_params *params)
{
if (params)
{
params->override = 0;
params->initialize = 1;
params->check_required = 1;
params->check_ambiguity = 0;
params->print_errors = 1;
}
}
struct cmdline_parser_params *
cmdline_parser_params_create(void)
{
struct cmdline_parser_params *params =
(struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params));
cmdline_parser_params_init(params);
return params;
}
static void
free_string_field (char **s)
{
if (*s)
{
free (*s);
*s = 0;
}
}
static void
cmdline_parser_release (struct gengetopt_args_info *args_info)
{
free_string_field (&(args_info->host_arg));
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->proxy_arg));
free_string_field (&(args_info->proxy_orig));
free_string_field (&(args_info->login_arg));
free_string_field (&(args_info->login_orig));
clear_given (args_info);
}
static void
write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[])
{
FIX_UNUSED (values);
if (arg) {
fprintf(outfile, "%s=\"%s\"\n", opt, arg);
} else {
fprintf(outfile, "%s\n", opt);
}
}
int
cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info)
{
int i = 0;
if (!outfile)
{
fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE);
return EXIT_FAILURE;
}
if (args_info->help_given)
write_into_file(outfile, "help", 0, 0 );
if (args_info->version_given)
write_into_file(outfile, "version", 0, 0 );
if (args_info->host_given)
write_into_file(outfile, "host", args_info->host_orig, 0);
if (args_info->decrypt_port_given)
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->proxy_given)
write_into_file(outfile, "proxy", args_info->proxy_orig, 0);
if (args_info->login_given)
write_into_file(outfile, "login", args_info->login_orig, 0);
if (args_info->code_from_file_given)
write_into_file(outfile, "code-from-file", 0, 0 );
i = EXIT_SUCCESS;
return i;
}
int
cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info)
{
FILE *outfile;
int i = 0;
outfile = fopen(filename, "w");
if (!outfile)
{
fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename);
return EXIT_FAILURE;
}
i = cmdline_parser_dump(outfile, args_info);
fclose (outfile);
return i;
}
void
cmdline_parser_free (struct gengetopt_args_info *args_info)
{
cmdline_parser_release (args_info);
}
/** @brief replacement of strdup, which is not standard */
char *
gengetopt_strdup (const char *s)
{
char *result = 0;
if (!s)
return result;
result = (char*)malloc(strlen(s) + 1);
if (result == (char*)0)
return (char*)0;
strcpy(result, s);
return result;
}
int
cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info)
{
return cmdline_parser2 (argc, argv, args_info, 0, 1, 1);
}
int
cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info,
struct cmdline_parser_params *params)
{
int result;
result = cmdline_parser_internal (argc, argv, args_info, params, 0);
if (result == EXIT_FAILURE)
{
cmdline_parser_free (args_info);
exit (EXIT_FAILURE);
}
return result;
}
int
cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required)
{
int result;
struct cmdline_parser_params params;
params.override = override;
params.initialize = initialize;
params.check_required = check_required;
params.check_ambiguity = 0;
params.print_errors = 1;
result = cmdline_parser_internal (argc, argv, args_info, &params, 0);
if (result == EXIT_FAILURE)
{
cmdline_parser_free (args_info);
exit (EXIT_FAILURE);
}
return result;
}
int
cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name)
{
FIX_UNUSED (args_info);
FIX_UNUSED (prog_name);
return EXIT_SUCCESS;
}
static char *package_name = 0;
/**
* @brief updates an option
* @param field the generic pointer to the field to update
* @param orig_field the pointer to the orig field
* @param field_given the pointer to the number of occurrence of this option
* @param prev_given the pointer to the number of occurrence already seen
* @param value the argument for this option (if null no arg was specified)
* @param possible_values the possible values for this option (if specified)
* @param default_value the default value (in case the option only accepts fixed values)
* @param arg_type the type of this option
* @param check_ambiguity @see cmdline_parser_params.check_ambiguity
* @param override @see cmdline_parser_params.override
* @param no_free whether to free a possible previous value
* @param multiple_option whether this is a multiple option
* @param long_opt the corresponding long option
* @param short_opt the corresponding short option (or '-' if none)
* @param additional_error possible further error specification
*/
static
int update_arg(void *field, char **orig_field,
unsigned int *field_given, unsigned int *prev_given,
char *value, const char *possible_values[],
const char *default_value,
cmdline_parser_arg_type arg_type,
int check_ambiguity, int override,
int no_free, int multiple_option,
const char *long_opt, char short_opt,
const char *additional_error)
{
char *stop_char = 0;
const char *val = value;
int found;
char **string_field;
FIX_UNUSED (field);
stop_char = 0;
found = 0;
if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given)))
{
if (short_opt != '-')
fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n",
package_name, long_opt, short_opt,
(additional_error ? additional_error : ""));
else
fprintf (stderr, "%s: `--%s' option given more than once%s\n",
package_name, long_opt,
(additional_error ? additional_error : ""));
return 1; /* failure */
}
FIX_UNUSED (default_value);
if (field_given && *field_given && ! override)
return 0;
if (prev_given)
(*prev_given)++;
if (field_given)
(*field_given)++;
if (possible_values)
val = possible_values[found];
switch(arg_type) {
case ARG_FLAG:
*((int *)field) = !*((int *)field);
break;
case ARG_INT:
if (val) *((int *)field) = strtol (val, &stop_char, 0);
break;
case ARG_STRING:
if (val) {
string_field = (char **)field;
if (!no_free && *string_field)
free (*string_field); /* free previous string */
*string_field = gengetopt_strdup (val);
}
break;
default:
break;
};
/* check numeric conversion */
switch(arg_type) {
case ARG_INT:
if (val && !(stop_char && *stop_char == '\0')) {
fprintf(stderr, "%s: invalid numeric value: %s\n", package_name, val);
return 1; /* failure */
}
break;
default:
;
};
/* store the original value */
switch(arg_type) {
case ARG_NO:
case ARG_FLAG:
break;
default:
if (value && orig_field) {
if (no_free) {
*orig_field = value;
} else {
if (*orig_field)
free (*orig_field); /* free previous string */
*orig_field = gengetopt_strdup (value);
}
}
};
return 0; /* OK */
}
int
cmdline_parser_internal (
int argc, char **argv, struct gengetopt_args_info *args_info,
struct cmdline_parser_params *params, const char *additional_error)
{
int c; /* Character of the parsed option. */
int error_occurred = 0;
struct gengetopt_args_info local_args_info;
int override;
int initialize;
int check_required;
int check_ambiguity;
package_name = argv[0];
/* TODO: Why is this here? It is not used anywhere. */
override = params->override;
FIX_UNUSED(override);
initialize = params->initialize;
check_required = params->check_required;
/* TODO: Why is this here? It is not used anywhere. */
check_ambiguity = params->check_ambiguity;
FIX_UNUSED(check_ambiguity);
if (initialize)
cmdline_parser_init (args_info);
cmdline_parser_init (&local_args_info);
optarg = 0;
optind = 0;
opterr = params->print_errors;
optopt = '?';
while (1)
{
int option_index = 0;
static struct option long_options[] = {
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
{ "host", 1, NULL, 'H' },
{ "decrypt-port", 1, NULL, 'D' },
{ "m3u8-port", 1, NULL, 'M' },
{ "proxy", 1, NULL, 'P' },
{ "login", 1, NULL, 'L' },
{ "code-from-file", 0, NULL, 'F' },
{ 0, 0, 0, 0 }
};
c = getopt_long (argc, argv, "hVH:D:M:P:L:F", long_options, &option_index);
if (c == -1) break; /* Exit from `while (1)' loop. */
switch (c)
{
case 'h': /* Print help and exit. */
cmdline_parser_print_help ();
cmdline_parser_free (&local_args_info);
exit (EXIT_SUCCESS);
case 'V': /* Print version and exit. */
cmdline_parser_print_version ();
cmdline_parser_free (&local_args_info);
exit (EXIT_SUCCESS);
case 'H': /* . */
if (update_arg( (void *)&(args_info->host_arg),
&(args_info->host_orig), &(args_info->host_given),
&(local_args_info.host_given), optarg, 0, "127.0.0.1", ARG_STRING,
check_ambiguity, override, 0, 0,
"host", 'H',
additional_error))
goto failure;
break;
case 'D': /* . */
if (update_arg( (void *)&(args_info->decrypt_port_arg),
&(args_info->decrypt_port_orig), &(args_info->decrypt_port_given),
&(local_args_info.decrypt_port_given), optarg, 0, "10020", ARG_INT,
check_ambiguity, override, 0, 0,
"decrypt-port", 'D',
additional_error))
goto failure;
break;
case 'M': /* . */
if (update_arg( (void *)&(args_info->m3u8_port_arg),
&(args_info->m3u8_port_orig), &(args_info->m3u8_port_given),
&(local_args_info.m3u8_port_given), optarg, 0, "20020", ARG_INT,
check_ambiguity, override, 0, 0,
"m3u8-port", 'M',
additional_error))
goto failure;
break;
case 'P': /* . */
if (update_arg( (void *)&(args_info->proxy_arg),
&(args_info->proxy_orig), &(args_info->proxy_given),
&(local_args_info.proxy_given), optarg, 0, "", ARG_STRING,
check_ambiguity, override, 0, 0,
"proxy", 'P',
additional_error))
goto failure;
break;
case 'L': /* username:password. */
if (update_arg( (void *)&(args_info->login_arg),
&(args_info->login_orig), &(args_info->login_given),
&(local_args_info.login_given), optarg, 0, 0, ARG_STRING,
check_ambiguity, override, 0, 0,
"login", 'L',
additional_error))
goto failure;
break;
case 'F': /* . */
if (update_arg((void *)&(args_info->code_from_file_flag), 0, &(args_info->code_from_file_given),
&(local_args_info.code_from_file_given), optarg, 0, 0, ARG_FLAG,
check_ambiguity, override, 1, 0, "code-from-file", 'F',
additional_error))
goto failure;
break;
case 0: /* Long option with no short option */
case '?': /* Invalid option. */
/* `getopt_long' already printed an error message. */
goto failure;
default: /* bug: option not considered. */
fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : ""));
abort ();
} /* switch */
} /* while */
FIX_UNUSED(check_required);
cmdline_parser_release (&local_args_info);
if ( error_occurred )
return (EXIT_FAILURE);
return 0;
failure:
cmdline_parser_release (&local_args_info);
return (EXIT_FAILURE);
}
/* vim: set ft=c noet ts=8 sts=8 sw=8 tw=80 nojs spell : */

196
cmdline.h Normal file
View File

@ -0,0 +1,196 @@
/** @file cmdline.h
* @brief The header file for the command line option parser
* generated by GNU Gengetopt version 2.23
* http://www.gnu.org/software/gengetopt.
* DO NOT modify this file, since it can be overwritten
* @author GNU Gengetopt */
#ifndef CMDLINE_H
#define CMDLINE_H
/* If we use autoconf. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h> /* for FILE */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#ifndef CMDLINE_PARSER_PACKAGE
/** @brief the program name (used for printing errors) */
#define CMDLINE_PARSER_PACKAGE "wrapper"
#endif
#ifndef CMDLINE_PARSER_PACKAGE_NAME
/** @brief the complete program name (used for help and version) */
#define CMDLINE_PARSER_PACKAGE_NAME "wrapper"
#endif
#ifndef CMDLINE_PARSER_VERSION
/** @brief the program version */
#define CMDLINE_PARSER_VERSION "1.2.0"
#endif
/** @brief Where the command line options are stored */
struct gengetopt_args_info
{
const char *help_help; /**< @brief Print help and exit help description. */
const char *version_help; /**< @brief Print version and exit help description. */
char * host_arg; /**< @brief (default='127.0.0.1'). */
char * host_orig; /**< @brief original value given at command line. */
const char *host_help; /**< @brief help description. */
int decrypt_port_arg; /**< @brief (default='10020'). */
char * decrypt_port_orig; /**< @brief original value given at command line. */
const char *decrypt_port_help; /**< @brief help description. */
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. */
char * proxy_arg; /**< @brief (default=''). */
char * proxy_orig; /**< @brief original value given at command line. */
const char *proxy_help; /**< @brief help description. */
char * login_arg; /**< @brief username:password. */
char * login_orig; /**< @brief username:password original value given at command line. */
const char *login_help; /**< @brief username:password help description. */
int code_from_file_flag; /**< @brief (default=off). */
const char *code_from_file_help; /**< @brief help description. */
unsigned int help_given ; /**< @brief Whether help was given. */
unsigned int version_given ; /**< @brief Whether version was given. */
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 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. */
} ;
/** @brief The additional parameters to pass to parser functions */
struct cmdline_parser_params
{
int override; /**< @brief whether to override possibly already present options (default 0) */
int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */
int check_required; /**< @brief whether to check that all required options were provided (default 1) */
int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */
int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */
} ;
/** @brief the purpose string of the program */
extern const char *gengetopt_args_info_purpose;
/** @brief the usage string of the program */
extern const char *gengetopt_args_info_usage;
/** @brief the description string of the program */
extern const char *gengetopt_args_info_description;
/** @brief all the lines making the help output */
extern const char *gengetopt_args_info_help[];
/**
* The command line parser
* @param argc the number of command line options
* @param argv the command line options
* @param args_info the structure where option information will be stored
* @return 0 if everything went fine, NON 0 if an error took place
*/
int cmdline_parser (int argc, char **argv,
struct gengetopt_args_info *args_info);
/**
* The command line parser (version with additional parameters - deprecated)
* @param argc the number of command line options
* @param argv the command line options
* @param args_info the structure where option information will be stored
* @param override whether to override possibly already present options
* @param initialize whether to initialize the option structure my_args_info
* @param check_required whether to check that all required options were provided
* @return 0 if everything went fine, NON 0 if an error took place
* @deprecated use cmdline_parser_ext() instead
*/
int cmdline_parser2 (int argc, char **argv,
struct gengetopt_args_info *args_info,
int override, int initialize, int check_required);
/**
* The command line parser (version with additional parameters)
* @param argc the number of command line options
* @param argv the command line options
* @param args_info the structure where option information will be stored
* @param params additional parameters for the parser
* @return 0 if everything went fine, NON 0 if an error took place
*/
int cmdline_parser_ext (int argc, char **argv,
struct gengetopt_args_info *args_info,
struct cmdline_parser_params *params);
/**
* Save the contents of the option struct into an already open FILE stream.
* @param outfile the stream where to dump options
* @param args_info the option struct to dump
* @return 0 if everything went fine, NON 0 if an error took place
*/
int cmdline_parser_dump(FILE *outfile,
struct gengetopt_args_info *args_info);
/**
* Save the contents of the option struct into a (text) file.
* This file can be read by the config file parser (if generated by gengetopt)
* @param filename the file where to save
* @param args_info the option struct to save
* @return 0 if everything went fine, NON 0 if an error took place
*/
int cmdline_parser_file_save(const char *filename,
struct gengetopt_args_info *args_info);
/**
* Print the help
*/
void cmdline_parser_print_help(void);
/**
* Print the version
*/
void cmdline_parser_print_version(void);
/**
* Initializes all the fields a cmdline_parser_params structure
* to their default values
* @param params the structure to initialize
*/
void cmdline_parser_params_init(struct cmdline_parser_params *params);
/**
* Allocates dynamically a cmdline_parser_params structure and initializes
* all its fields to their default values
* @return the created and initialized cmdline_parser_params structure
*/
struct cmdline_parser_params *cmdline_parser_params_create(void);
/**
* Initializes the passed gengetopt_args_info structure's fields
* (also set default values for options that have a default)
* @param args_info the structure to initialize
*/
void cmdline_parser_init (struct gengetopt_args_info *args_info);
/**
* Deallocates the string fields of the gengetopt_args_info structure
* (but does not deallocate the structure itself)
* @param args_info the structure to deallocate
*/
void cmdline_parser_free (struct gengetopt_args_info *args_info);
/**
* Checks that all the required options were specified
* @param args_info the structure to check
* @param prog_name the name of the program that will be used to print
* possible errors
* @return
*/
int cmdline_parser_required (struct gengetopt_args_info *args_info,
const char *prog_name);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* CMDLINE_H */

10
compose.yaml Normal file
View File

@ -0,0 +1,10 @@
services:
wrapper:
image: sim1222/wrapper:latest
build: .
volumes:
- ./rootfs/data:/app/rootfs/data
ports:
- 10020:10020
environment:
- args="-H 0.0.0.0"

View File

@ -32,6 +32,24 @@ static inline union std_string new_std_string(const char *s) {
return str;
}
static inline struct std_vector new_std_vector(void *begin) {
struct std_vector vector = {
.begin = begin,
.end = begin + 1,
};
vector.end_capacity = vector.end;
return vector;
}
static inline union std_string new_std_string_short_mode(const char *str) {
short str_size = strlen(str);
union std_string std_str = {
.mark = str_size << 1,
};
strcpy(std_str.str, str);
return std_str;
}
static inline const char *std_string_data(union std_string *str) {
if ((str->mark & 1) == 0) {
return str->str;
@ -215,6 +233,13 @@ extern void _ZN8FootHill24defaultContextIdentifierEv(void *);
extern void _ZN21RequestContextManager9configureERKNSt6__ndk110shared_ptrIN17storeservicescore14RequestContextEEE(
struct shared_ptr *);
extern struct shared_ptr *_ZN22SVPlaybackLeaseManager12requestAssetERKmRKNSt6__ndk16vectorINS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEENS7_IS9_EEEERKb(
void *, void *, unsigned long *, struct std_vector *, uint8_t *
);
extern int _ZNK23SVPlaybackAssetResponse13hasValidAssetEv(void *);
extern struct shared_ptr *_ZNK23SVPlaybackAssetResponse13playbackAssetEv(void *);
extern union std_string *_ZNK17storeservicescore13PlaybackAsset9URLStringEv(void *, uint8_t *);
const char *const android_id = "dc28071e981c439e";
const char *const fairplayCert = "MIIEzjCCA7agAwIBAgIIAXAVjHFZDjgwDQYJKoZIhvcNAQEFBQAwfzELMAkGA1UEBhMCVVMxEz"
"ARBgNVBAoMCkFwcGxlIEluYy4xJjAkBgNVBAsMHUFwcGxlIENlcnRpZmljYXRpb24gQXV0aG9y"

View File

@ -5,20 +5,28 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include "import.h"
#include "cmdline.h"
#ifndef MyRelease
#include "subhook/subhook.c"
#endif
static struct shared_ptr apInf;
static size_t passLen;
static char *amUsername, *amPassword;
static uint16_t port;
static uint8_t leaseMgr[16];
struct gengetopt_args_info args_info;
char *amUsername, *amPassword;
int file_exists(char *filename) {
struct stat buffer;
return (stat (filename, &buffer) == 0);
}
static void dialogHandler(long j, struct shared_ptr *protoDialogPtr,
struct shared_ptr *respHandler) {
@ -74,9 +82,35 @@ static void credentialHandler(struct shared_ptr *credReqHandler,
credReqHandler->obj)),
need2FA ? "true" : "false");
int passLen = strlen(amPassword);
if (need2FA) {
printf("2FA code: ");
scanf("%6s", amPassword + passLen);
if (args_info.code_from_file_flag) {
fprintf(stderr, "[!] Enter your 2FA code into rootfs/data/code.txt\n");
fprintf(stderr, "[!] Example command: echo -n 114514 > rootfs/data/2fa.txt\n");
fprintf(stderr, "[!] Waiting for input...\n");
int count = 0;
while (1)
{
if (count >= 20) {
fprintf(stderr, "[!] Failed to get 2FA Code in 60s. Exiting...\n");
exit(0);
}
if (file_exists("/data/2fa.txt")) {
FILE *fp = fopen("/data/2fa.txt", "r");
fscanf(fp, "%6s", amPassword + passLen);
remove("/data/2fa.txt");
fprintf(stderr, "[!] Code file detected! Logging in...\n");
break;
} else {
sleep(3);
count++;
}
}
} else {
printf("2FA code: ");
scanf("%6s", amPassword + passLen);
}
}
uint8_t *const ptr = malloc(80);
@ -112,6 +146,12 @@ static inline void init() {
// raise(SIGSTOP);
fprintf(stderr, "[+] starting...\n");
setenv("ANDROID_DNS_MODE", "local", 1);
if (args_info.proxy_given) {
fprintf(stderr, "[+] Using proxy %s", args_info.proxy_arg);
setenv("http_proxy", args_info.proxy_arg, 1);
setenv("https_proxy", args_info.proxy_arg, 1);
}
static const char *resolvers[2] = {"1.1.1.1", "1.0.0.1"};
_resolv_set_nameservers_for_net(0, resolvers, 2, ".");
#ifndef MyRelease
@ -363,8 +403,6 @@ void handle(const int connfd) {
}
extern uint8_t handle_cpp(int);
static char *selfPath;
static char *portStr;
inline static int new_socket() {
const int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
@ -376,8 +414,8 @@ inline static int new_socket() {
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval));
static struct sockaddr_in serv_addr = {.sin_family = AF_INET};
serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
serv_addr.sin_port = htons(port);
inet_pton(AF_INET, args_info.host_arg, &serv_addr.sin_addr);
serv_addr.sin_port = htons(args_info.decrypt_port_arg);
if (bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
perror("bind");
return EXIT_FAILURE;
@ -388,8 +426,8 @@ inline static int new_socket() {
return EXIT_FAILURE;
}
fprintf(stderr, "[!] listening 127.0.0.1:%d\n", port);
close(STDOUT_FILENO);
fprintf(stderr, "[!] listening %s:%d\n", args_info.host_arg, args_info.decrypt_port_arg);
// close(STDOUT_FILENO);
static struct sockaddr_in peer_addr;
static socklen_t peer_addr_size = sizeof(peer_addr);
@ -423,24 +461,141 @@ inline static int new_socket() {
}
}
int main(int argc, char *argv[]) {
selfPath = argv[0];
if (argc != 2) {
if (argc != 4) {
fprintf(stderr, "usage: %s [port] ([username] [password])\n",
argv[0]);
return EXIT_FAILURE;
}
amUsername = argv[2];
passLen = strlen(argv[3]);
amPassword = malloc(passLen + 7);
strcpy(amPassword, argv[3]);
const char* get_m3u8_method_play(uint8_t leaseMgr[16], unsigned long adam) {
union std_string HLS = new_std_string_short_mode("HLS");
struct std_vector HLSParam = new_std_vector(&HLS);
static uint8_t z0 = 0;
struct shared_ptr ptr_result;
_ZN22SVPlaybackLeaseManager12requestAssetERKmRKNSt6__ndk16vectorINS2_12basic_stringIcNS2_11char_traitsIcEENS2_9allocatorIcEEEENS7_IS9_EEEERKb(
&ptr_result, leaseMgr, &adam, &HLSParam, &z0
);
if (ptr_result.obj == NULL) {
return NULL;
}
port = atoi(portStr = argv[1]);
if (_ZNK23SVPlaybackAssetResponse13hasValidAssetEv(ptr_result.obj)) {
struct shared_ptr *playbackAsset = _ZNK23SVPlaybackAssetResponse13playbackAssetEv(ptr_result.obj);
if (playbackAsset == NULL || playbackAsset->obj == NULL) {
return NULL;
}
union std_string *m3u8 = malloc(sizeof(union std_string));
if (m3u8 == NULL) {
return NULL;
}
void *playbackObj = playbackAsset->obj;
_ZNK17storeservicescore13PlaybackAsset9URLStringEv(m3u8, playbackObj);
if (m3u8 == NULL || std_string_data(m3u8) == NULL) {
free(m3u8);
return NULL;
}
const char *m3u8_str = std_string_data(m3u8);
if (m3u8_str) {
char *result = strdup(m3u8_str); // Make a copy
free(m3u8);
return result;
} else {
return NULL;
}
} else {
return NULL;
}
}
void handle_m3u8(const int connfd) {
while (1)
{
uint8_t adamSize;
if (!readfull(connfd, &adamSize, sizeof(uint8_t))) {
return;
}
if (adamSize <= 0) {
return;
}
char adam[adamSize];
for (int i=0; i<adamSize; i=i+1) {
readfull(connfd, &adam[i], sizeof(uint8_t));
}
char *ptr;
unsigned long adamID = strtoul(adam, &ptr, 10);
const char *m3u8 = get_m3u8_method_play(leaseMgr, adamID);
if (m3u8 == NULL) {
fprintf(stderr, "[.] failed to get m3u8 of adamId: %ld\n", adamID);
writefull(connfd, "\n", sizeof("\n"));
} else {
fprintf(stderr, "[.] m3u8 adamId: %ld, url: %s\n", adamID, m3u8);
char *with_newline = malloc(strlen(m3u8) + 2);
if (with_newline) {
strcpy(with_newline, m3u8);
strcat(with_newline, "\n");
writefull(connfd, with_newline, strlen(with_newline));
free(with_newline);
}
free((void *)m3u8);
}
}
}
static inline void *new_socket_m3u8(void *args) {
const int fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
if (fd == -1) {
perror("socket");
}
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.m3u8_port_arg);
if (bind(fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1) {
perror("bind");
}
if (listen(fd, 5) == -1) {
perror("listen");
}
fprintf(stderr, "[!] listening m3u8 request on %s:%d\n", args_info.host_arg, args_info.m3u8_port_arg);
// close(STDOUT_FILENO);
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_m3u8(connfd);
if (close(connfd) == -1) {
perror("close");
}
}
}
int main(int argc, char *argv[]) {
cmdline_parser(argc, argv, &args_info);
init();
const struct shared_ptr ctx = init_ctx();
if (argc == 4 && !login(ctx)) {
if (args_info.login_given) {
amUsername = strtok(args_info.login_arg, ":");
amPassword = strtok(NULL, ":");
}
if (args_info.login_given && !login(ctx)) {
fprintf(stderr, "[!] login failed\n");
return EXIT_FAILURE;
}
@ -452,5 +607,9 @@ int main(int argc, char *argv[]) {
_ZN22SVPlaybackLeaseManager12requestLeaseERKb(leaseMgr, &autom);
FHinstance = _ZN21SVFootHillSessionCtrl8instanceEv();
pthread_t m3u8_thread;
pthread_create(&m3u8_thread, NULL, &new_socket_m3u8, NULL);
pthread_detach(m3u8_thread);
return new_socket();
}

View File

@ -0,0 +1 @@
GITKEEP

View File

@ -0,0 +1 @@
GITKEEP

View File

@ -1,6 +1,6 @@
#define _GNU_SOURCE
#include <errno.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
@ -23,10 +23,6 @@ int main(int argc, char *argv[], char *envp[]) {
return 1;
}
if (argc > 2) {
system("rm -r ./rootfs/data/data/com.apple.android.music/files/");
}
if (chdir("./rootfs") != 0) {
perror("chdir");
return 1;
@ -39,24 +35,22 @@ int main(int argc, char *argv[], char *envp[]) {
chmod("/system/bin/linker64", 0755);
chmod("/system/bin/main", 0755);
if (unshare(CLONE_NEWPID)) {
perror("unshare");
return 1;
}
child_proc = fork();
if (child_proc == -1) {
perror("fork");
return 1;
}
if (child_proc > 0) {
close(STDOUT_FILENO);
wait(NULL);
wait(NULL); // Parent waits for the child process to terminate
return 0;
}
// Child process logic
mkdir("/data/data/com.apple.android.music/files", 0777);
mkdir("/data/data/com.apple.android.music/files/mpl_db", 0777);
execve("/system/bin/main", argv, envp);
perror("execve");
return 1;
}
}

9
wrapper.ggo Normal file
View File

@ -0,0 +1,9 @@
package "wrapper"
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 "proxy" P "" string optional default=""
option "login" L "username:password" string optional
option "code-from-file" F "" flag off