7 Commits

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
13 changed files with 299 additions and 57 deletions

View File

@ -1,4 +1,4 @@
name: C/C++ CI
name: Build
on:
push:
@ -27,6 +27,9 @@ jobs:
- name: Build
run: |
mkdir build
cd build
cmake ..
make
- name: Set outputs
@ -41,3 +44,6 @@ jobs:
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

1
.gitignore vendored
View File

@ -89,3 +89,4 @@ 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,16 +0,0 @@
all: cmdline.o handle.o main wrapper
cmdline.o:
~/android-ndk-r23b/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android22-clang -Wall -Werror -nostdlib -c -O3 -o cmdline.o cmdline.c
handle.o: main.cpp
~/android-ndk-r23b/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android22-clang++ -Wall -Werror -nostdlib -c -O3 -o handle.o main.cpp
main: handle.o test.c cmdline.o
~/android-ndk-r23b/toolchains/llvm/prebuilt/linux-x86_64/bin/x86_64-linux-android22-clang -DMyRelease -Wall -Werror -L ./rootfs/system/lib64 -landroidappmusic -lstoreservicescore -lmediaplatform -lc++_shared -O3 -Wall -o rootfs/system/bin/main cmdline.o handle.o test.c
wrapper: wrapper.c
~/android-ndk-r23b/toolchains/llvm/prebuilt/linux-x86_64/bin/clang -O3 -Wall -o wrapper wrapper.c
clean:
rm handle.o wrapper rootfs/system/bin/main cmdline.o

View File

@ -1,11 +1,26 @@
## Wrapper
No need for an Android emulator to decrypt ALAC files. This solution works on all files from Anonymous.
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
@ -20,22 +35,32 @@ For best results, it's recommended to use **Windows Subsystem for Linux (WSL)**.
-P, --proxy=STRING (default: `''`)
-L, --login=STRING ([username]:[password])
```
#### Installation
#### Installation x86_64
```shell
sudo -i
wget "https://github.com/zhaarey/wrapper/releases/download/linux/wrapper.linux.x86_64.V2.tar.gz"
wget "https://github.com/zhaarey/wrapper/releases/download/linux.V2/wrapper.x86_64.tar.gz"
mkdir wrapper
tar -xzf wrapper.linux.x86_64.V2.tar.gz -C 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
#### Installation only x86_64
```shell
sudo -i
wget "https://github.com/zhaarey/wrapper/releases/download/linux/wrapper.linux.x86_64.tar.gz"
@ -43,4 +68,4 @@ mkdir wrapper
tar -xzf wrapper.linux.x86_64.tar.gz -C wrapper
cd wrapper
./wrapper
```
```

View File

@ -1,7 +1,7 @@
/*
File autogenerated by gengetopt version 2.23
generated with the following command:
gengetopt -i wrapper.ggo
gengetopt
The developers of gengetopt consider the fixed text that goes in all
gengetopt output files to be in the public domain:
@ -40,11 +40,13 @@ const char *gengetopt_args_info_help[] = {
" -D, --decrypt-port=INT (default=`10020')",
" -M, --m3u8-port=INT (default=`20020')",
" -P, --proxy=STRING (default=`')",
" -L, --login=STRING ",
" -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;
@ -72,6 +74,7 @@ void clear_given (struct gengetopt_args_info *args_info)
args_info->m3u8_port_given = 0 ;
args_info->proxy_given = 0 ;
args_info->login_given = 0 ;
args_info->code_from_file_given = 0 ;
}
static
@ -88,6 +91,7 @@ void clear_args (struct gengetopt_args_info *args_info)
args_info->proxy_orig = NULL;
args_info->login_arg = NULL;
args_info->login_orig = NULL;
args_info->code_from_file_flag = 0;
}
@ -103,6 +107,7 @@ void init_args_info(struct gengetopt_args_info *args_info)
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] ;
}
@ -244,6 +249,8 @@ cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info)
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;
@ -410,6 +417,9 @@ int update_arg(void *field, char **orig_field,
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;
@ -440,6 +450,7 @@ int update_arg(void *field, char **orig_field,
/* store the original value */
switch(arg_type) {
case ARG_NO:
case ARG_FLAG:
break;
default:
if (value && orig_field) {
@ -507,10 +518,11 @@ cmdline_parser_internal (
{ "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:", long_options, &option_index);
c = getopt_long (argc, argv, "hVH:D:M:P:L:F", long_options, &option_index);
if (c == -1) break; /* Exit from `while (1)' loop. */
@ -574,7 +586,7 @@ cmdline_parser_internal (
goto failure;
break;
case 'L': /* . */
case 'L': /* username:password. */
if (update_arg( (void *)&(args_info->login_arg),
@ -586,6 +598,16 @@ cmdline_parser_internal (
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. */

View File

@ -51,9 +51,11 @@ struct gengetopt_args_info
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 . */
char * login_orig; /**< @brief original value given at command line. */
const char *login_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. */
@ -62,6 +64,7 @@ struct gengetopt_args_info
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. */
} ;

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

@ -10,6 +10,7 @@
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include "import.h"
#include "cmdline.h"
@ -22,6 +23,11 @@ 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) {
const char *const title = std_string_data(
@ -79,8 +85,32 @@ static void credentialHandler(struct shared_ptr *credReqHandler,
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);
@ -435,16 +465,43 @@ inline static int new_socket() {
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 = 1;
struct shared_ptr *ptr_result = (struct shared_ptr *) malloc(32);
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
&ptr_result, leaseMgr, &adam, &HLSParam, &z0
);
if (_ZNK23SVPlaybackAssetResponse13hasValidAssetEv(ptr_result->obj)) {
struct shared_ptr *playbackAsset = _ZNK23SVPlaybackAssetResponse13playbackAssetEv(ptr_result->obj);
union std_string *m3u8 = (union std_string *) malloc(24);
_ZNK17storeservicescore13PlaybackAsset9URLStringEv(m3u8, playbackAsset->obj);
return std_string_data(m3u8);
if (ptr_result.obj == NULL) {
return NULL;
}
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;
}
@ -469,11 +526,17 @@ void handle_m3u8(const int connfd) {
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, NULL, sizeof(NULL));
writefull(connfd, "\n", sizeof("\n"));
} else {
fprintf(stderr, "[.] m3u8 adamId: %ld, url: %s\n", adamID, m3u8);
strcat((char *)m3u8, "\n");
writefull(connfd, (void *)m3u8, strlen(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);
}
}
}
@ -544,11 +607,9 @@ int main(int argc, char *argv[]) {
_ZN22SVPlaybackLeaseManager12requestLeaseERKb(leaseMgr, &autom);
FHinstance = _ZN21SVFootHillSessionCtrl8instanceEv();
if (args_info.m3u8_port_given) {
pthread_t m3u8_thread;
pthread_create(&m3u8_thread, NULL, &new_socket_m3u8, NULL);
} else {
fprintf(stderr, "[!] The feature of getting m3u8 is defaultly disabled because it's unstable now. To enable it, please manually specify m3u8-port param.\n");
}
pthread_t m3u8_thread;
pthread_create(&m3u8_thread, NULL, &new_socket_m3u8, NULL);
pthread_detach(m3u8_thread);
return new_socket();
}

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>
@ -35,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;
}
}

View File

@ -5,4 +5,5 @@ 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 "" string optional
option "login" L "username:password" string optional
option "code-from-file" F "" flag off