#include "main.hpp"
void patch_class_name(FILE *fp, char *name, unsigned short len, unsigned short offset, unsigned short size) {
// Переведём имя в верхний регистр
for (unsigned short i = 0; i < size; ++i)
name[offset + i] = toupper(name[offset + i]);
fseek(fp, ftell(fp) - (len - offset), SEEK_SET);
fwrite(name + offset, 1, size, fp);
if (offset) fseek(fp, ftell(fp) + offset, SEEK_SET);
}
int process_string_constant(FILE *fp, char *name, unsigned short len) {
char *p;
if (len == sizeof("platformRequest") - 1 && strcmp(name, "platformRequest") == 0) {
// Nothing todo...
return CONST_HACK_KEYWORD;
} else if ((p = strstr(name, MESSAGE_CONNECTION))) {
patch_class_name(fp, name, len, p - name, sizeof(MESSAGE_CONNECTION) - 1);
return (CONST_HACK_REPLACED | CONST_HACK_KEYWORD);
} else if ((p = strstr(name, TEXT_MESSAGE))) {
patch_class_name(fp, name, len, p - name, sizeof(TEXT_MESSAGE) - 1);
return (CONST_HACK_REPLACED | CONST_HACK_KEYWORD);
} else if ((p = strstr(name, MIDLET))) {
patch_class_name(fp, name, len, p - name, sizeof(MIDLET) - 1);
return CONST_HACK_REPLACED;
}
return CONST_HACK_NONE;
}
int hack_class(FILE *fp, bool *need_repack, bool *class_changed) {
unsigned int magic_number;
unsigned short minor_version, major_version, constant_count;
// Прочитаем header класса
if (
!read_int(&magic_number, fp) ||
!read_short(&minor_version, fp) ||
!read_short(&major_version, fp) ||
!read_short(&constant_count, fp)
) {
fprintf(stderr, "Can't read class header: %s\n", strerror(errno));
return E_IO_ERROR;
}
// Проверим MAGIC
if (magic_number != 0xCAFEBABE) {
fprintf(stderr, "Invalid magic! Is not class!\n");
return E_CLASS_MAGIC;
}
// Парсим Constant Pool
int ret;
unsigned char cp_type = 0;
for (unsigned short i = 0; i < constant_count - 1; ++i) {
if (!read_byte(&cp_type, fp) == 1) {
fprintf(stderr, "Can't read const type: %s\n", strerror(errno));
return E_IO_ERROR;
}
switch (cp_type) {
// Пропустим 2 байта
case CONST_CLASS:
case CONST_STRING:
fseek(fp, ftell(fp) + 2, SEEK_SET);
break;
// Пропустим 4 байта
case CONST_FIELD_REF:
case CONST_METHOD_REF:
case CONST_INTERFACE_METHOD_REF:
case CONST_INTEGER:
case CONST_FLOAT:
case CONST_NAME_AND_TYPE:
fseek(fp, ftell(fp) + 4, SEEK_SET);
break;
// Пропустим 8 байт
case CONST_LONG:
case CONST_DOUBLE:
fseek(fp, ftell(fp) + 8, SEEK_SET);
break;
// Строки. То, что нам нужно.
case CONST_UTF8:
{
unsigned short len = 0;
char *value;
if (read_short(&len, fp)) {
value = new char[len + 1];
if (fread(value, 1, len, fp) == len) {
value[len] = 0;
// printf("CONST_UTF8 [%d] = \"%s\"\n", len, value);
ret = process_string_constant(fp, value, len);
if ((ret & CONST_HACK_REPLACED) && !*class_changed)
*class_changed = true;
if ((ret & CONST_HACK_KEYWORD) && !*need_repack)
*need_repack = true;
delete[] value;
} else {
delete[] value;
fprintf(stderr, "Can't read string data!\n");
return -1;
}
} else {
fprintf(stderr, "Can't read string length!\n");
return -1;
}
}
break;
default:
fprintf(stderr, "Unknown constant type %04X!!!\n", cp_type);
// Ебанитовые обфускаторы или компиляторы любят пихать сюда разную хуитку
// Поэтому не будем падать. А только ругаться.
// return E_UNKNOWN_CONST_TYPE;
break;
}
}
return E_OK;
}
bool zip_add_files_from_dir(zip *zip, const char *dir_path, unsigned int offset = 0) {
struct dirent *file;
bool state = true;
DIR *dir = opendir(dir_path);
char path[PATH_MAX];
struct stat tmp_stat;
struct zip_stat zstat;
if (dir) {
while ((file = readdir(dir))) {
if (!strcmp(".", file -> d_name) || !strcmp("..", file -> d_name))
continue;
snprintf(path, PATH_MAX, "%s/%s", dir_path, file -> d_name);
if (stat(path, &tmp_stat) != 0) {
fprintf(stderr, "Can't stat(%s): %s\n", path, strerror(errno));
continue;
}
// Если уже существует
bool need_create = true;
if (zip_stat(zip, path + offset, 0, &zstat) == 0) {
if (
// Если в архиве папка, но добавляемый объект не папка
(zstat.crc == 0 && zstat.size == 0 && !S_ISDIR(tmp_stat.st_mode)) ||
// Если в архиве файл, но добавляемый объект не регулярный файл
(zstat.crc != 0 && !S_ISREG(tmp_stat.st_mode))
) {
// Удаляем
if (zip_delete(zip, zstat.index) != 0) {
fprintf (
stderr, "Can't delete #%ld (%s; %08X; %ld): %s\n",
zstat.index, zstat.name, zstat.crc, zstat.size,
zip_strerror(zip)
);
state = false;
break;
}
} else
need_create = false;
}
if (S_ISDIR(tmp_stat.st_mode)) {
if (need_create && zip_add_dir(zip, path + offset) < 0) {
fprintf(stderr, "Can't create dir %s: %s\n", path + offset, zip_strerror(zip));
state = false;
break;
}
if (!zip_add_files_from_dir(zip, path, (!offset ? strlen(dir_path) : offset)) && state) {
state = false;
break;
}
} else if (S_ISREG(tmp_stat.st_mode)) {
zip_source *src = zip_source_file(zip, path, 0, tmp_stat.st_size);
if (src) {
if (zip_add(zip, path + offset, src) < 0) {
fprintf(stderr, "Can't add file %s: %s\n", path, zip_strerror(zip));
state = false;
}
} else {
fprintf(stderr, "Can't source from file %s: %s\n", path, zip_strerror(zip));
state = false;
break;
}
}
printf("%s\n", path);
}
state = true;
} else {
fprintf(stderr, "Can't open directory (%s): %s\n", dir_path, strerror(errno));
state = false;
}
return state;
}
int process_jar_file(const char *filename, const char *tmp_dir_path, const char *bootstrap_dir) {
int error = 0, ret;
zip *zip = NULL;
size_t total, name_len;
struct zip_stat zstat;
char tmp_file_name[PATH_MAX];
bool need_repack = false;
unsigned char buff[FILE_BUFFER_SIZE];
Errors return_value = E_OK;
try {
zip = zip_open(filename, 0, &error);
if (error) {
fprintf(stderr, "Can't open jar file %s: %s\n", filename, zip_strerror(zip));
throw E_OPEN_ZIP;
}
total = zip_get_num_entries(zip, 0);
for (size_t i = 0; i < total; ++i) {
if (zip_stat_index(zip, i, 0, &zstat) != 0) {
fprintf(stderr, "Can't stat file #%ld in %s: %s\n", i, filename, zip_strerror(zip));
throw E_ZIP_STAT_FILE;
}
// Нам нужны только *.class
name_len = strlen(zstat.name);
if (name_len < 7 || strcasecmp(zstat.name + (name_len - 6), ".class") != 0)
continue;
// Запишем .class во временный файл для модификации
zip_file *fp = zip_fopen_index(zip, i, 0);
if (!fp) {
fprintf(stderr, "Can't open file %s (%ld) in %s: %s\n", zstat.name, i, filename, zip_strerror(zip));
throw E_ZIP_OPEN_FILE;
}
sprintf(tmp_file_name, "%s/class.%08X.%ld.%d", tmp_dir_path, zstat.crc, zstat.index, getpid());
printf("tmp_file_name = %s\n", tmp_file_name);
int readed; size_t writed = 0;
FILE *fp_tmp = fopen(tmp_file_name, "w+");
if (fp_tmp) {
while (writed < zstat.size && (readed = zip_fread(fp, &buff, FILE_BUFFER_SIZE)) != -1) {
if (fwrite(buff, 1, readed, fp_tmp) != (size_t)readed)
break; // io error
writed += readed;
}
if (writed != zstat.size) {
fprintf(stderr, "Can't write data (file: %s): %s\n", tmp_file_name, strerror(ferror(fp_tmp)));
fclose(fp_tmp);
unlink(tmp_file_name);
throw E_IO_ERROR;
}
} else {
fprintf(stderr, "Can't open file %s: %s\n", tmp_file_name, strerror(errno));
throw E_IO_ERROR;
}
Errors err = E_OK;
fseek(fp_tmp, 0, SEEK_SET);
bool class_changed = false;
// Если похачили класс успешно и он изменён - заменяем его в архиве
if ((ret = hack_class(fp_tmp, &need_repack, &class_changed)) == E_OK) {
if (class_changed) {
printf("%s\n", zstat.name);
fseek(fp_tmp, 0, SEEK_SET);
zip_source *src = zip_source_file(zip, tmp_file_name, 0, zstat.size);
if (src) {
if (zip_replace(zip, i, src) != 0) {
fprintf(stderr, "Can't update file %s from %s: %s\n", tmp_file_name, filename, zip_strerror(zip));
err = E_ZIP_REPLACE_FILE;
}
zip_close(zip);
} else {
fprintf(stderr, "Can't create zip source file: %s\n", zip_strerror(zip));
err = E_ZIP_CREATE_SRC;
}
}
} else {
fprintf(stderr, "Can't process class %s from %s (error=%d)\n", zstat.name, filename, ret);
// err = E_INVALID_CLASS; // падать из-за одного кривого класса не будем
}
fclose(fp_tmp);
unlink(tmp_file_name);
if (err != E_OK)
throw err;
}
// Добавляем наши классы
if (need_repack)
zip_add_files_from_dir(zip, bootstrap_dir);
} catch (Errors err_no) {
return_value = err_no;
}
if (zip) {
zip_close(zip); printf("%p\n", zip);
zip = NULL;
}
if (return_value == E_OK && need_repack)
return_value = E_OK_CHANGED;
return return_value;
}
int main() {
system("cp /home/azq2/проекты/java_antialarm/Midlet.class2 /home/azq2/проекты/java_antialarm/Midlet.class");
system("cp /home/azq2/Загрузки/test2.jar /home/azq2/Загрузки/test.jar");
process_jar_file("/home/azq2/Загрузки/test.jar", "/tmp", "../bootstrap");
return 0;
}