Aplood-Static / dex-parser.js
Hosdroid's picture
Upload folder using huggingface_hub
f604f37 verified
Raw History Blame Contribute Delete
20.4 kB
/**
* Advanced DEX Parser and Transpiler
* Converts Android DEX bytecode to JavaScript
*
* This module provides deep analysis and conversion of DEX files
*/
class DEXParser {
constructor() {
this.dexData = null;
this.header = null;
this.stringIds = [];
this.typeIds = [];
this.protoIds = [];
this.fieldIds = [];
this.methodIds = [];
this.classDefs = [];
this.codeItems = [];
}
async parse(data) {
if (data instanceof ArrayBuffer) {
this.dexData = new Uint8Array(data);
} else if (data instanceof Uint8Array) {
this.dexData = data;
} else {
throw new Error('Invalid DEX data format');
}
await this.parseHeader();
await this.parseStringIds();
await this.parseTypeIds();
await this.parseProtoIds();
await this.parseFieldIds();
await this.parseMethodIds();
await this.parseClassDefs();
return this.generateJavaScript();
}
async parseHeader() {
const view = new DataView(this.dexData.buffer);
// Verify magic number
const magic = new TextDecoder().decode(this.dexData.slice(0, 8));
if (!magic.startsWith('dex\n')) {
throw new Error('Invalid DEX file: bad magic number');
}
this.header = {
magic: magic,
version: view.getUint32(4, true),
fileSize: view.getUint32(32, true),
endianTag: view.getUint32(40, true),
linkSize: view.getUint32(44, true),
linkOff: view.getUint32(48, true),
mapOff: view.getUint32(52, true),
stringIdsSize: view.getUint32(56, true),
stringIdsOff: view.getUint32(60, true),
typeIdsSize: view.getUint32(64, true),
typeIdsOff: view.getUint32(68, true),
protoIdsSize: view.getUint32(72, true),
protoIdsOff: view.getUint32(76, true),
fieldIdsSize: view.getUint32(80, true),
fieldIdsOff: view.getUint32(84, true),
methodIdsSize: view.getUint32(88, true),
methodIdsOff: view.getUint32(92, true),
classDefsSize: view.getUint32(96, true),
classDefsOff: view.getUint32(100, true),
dataSize: view.getUint32(104, true),
dataOff: view.getUint32(108, true)
};
console.log('DEX Header parsed:', this.header);
}
async parseStringIds() {
const view = new DataView(this.dexData.buffer);
const offset = this.header.stringIdsOff;
const size = this.header.stringIdsSize;
for (let i = 0; i < size; i++) {
const stringOffset = view.getUint32(offset + (i * 4), true);
const string = this.readString(stringOffset);
this.stringIds.push(string);
}
console.log(`Parsed ${this.stringIds.length} strings`);
}
readString(offset) {
// Read MUTF-8 encoded string
let index = offset;
const len = this.readULEB128(index);
index += this.getULEB128Size(len);
const bytes = [];
for (let i = 0; i < len; i++) {
bytes.push(this.dexData[index + i]);
}
try {
return new TextDecoder().decode(new Uint8Array(bytes));
} catch (e) {
return '';
}
}
readULEB128(offset) {
let result = 0;
let shift = 0;
let byte;
do {
byte = this.dexData[offset++];
result |= (byte & 0x7f) << shift;
shift += 7;
} while (byte & 0x80);
return result;
}
getULEB128Size(value) {
let size = 0;
do {
value >>= 7;
size++;
} while (value !== 0);
return size;
}
async parseTypeIds() {
const view = new DataView(this.dexData.buffer);
const offset = this.header.typeIdsOff;
const size = this.header.typeIdsSize;
for (let i = 0; i < size; i++) {
const descriptorIdx = view.getUint32(offset + (i * 4), true);
const descriptor = this.stringIds[descriptorIdx];
this.typeIds.push({
descriptorIdx,
descriptor,
className: this.descriptorToClassName(descriptor)
});
}
console.log(`Parsed ${this.typeIds.length} types`);
}
descriptorToClassName(descriptor) {
if (!descriptor) return 'unknown';
// Convert Ljava/lang/Object; to java.lang.Object
if (descriptor.startsWith('L') && descriptor.endsWith(';')) {
return descriptor.substring(1, descriptor.length - 1).replace(/\//g, '.');
}
// Array types
if (descriptor.startsWith('[')) {
return descriptor.replace(/\//g, '.');
}
// Primitive types
const primitives = {
'V': 'void',
'Z': 'boolean',
'B': 'byte',
'S': 'short',
'C': 'char',
'I': 'int',
'J': 'long',
'F': 'float',
'D': 'double'
};
return primitives[descriptor] || descriptor;
}
async parseProtoIds() {
const view = new DataView(this.dexData.buffer);
const offset = this.header.protoIdsOff;
const size = this.header.protoIdsSize;
for (let i = 0; i < size; i++) {
const baseOffset = offset + (i * 12);
const shortyIdx = view.getUint32(baseOffset, true);
const returnTypeIdx = view.getUint32(baseOffset + 4, true);
const parametersOff = view.getUint32(baseOffset + 8, true);
const returnType = this.typeIds[returnTypeIdx];
const shorty = this.stringIds[shortyIdx];
this.protoIds.push({
shorty,
returnType,
parametersOff,
parameters: []
});
}
console.log(`Parsed ${this.protoIds.length} prototypes`);
}
async parseFieldIds() {
const view = new DataView(this.dexData.buffer);
const offset = this.header.fieldIdsOff;
const size = this.header.fieldIdsSize;
for (let i = 0; i < size; i++) {
const baseOffset = offset + (i * 6);
const classIdx = view.getUint16(baseOffset, true);
const typeIdx = view.getUint16(baseOffset + 2, true);
const nameIdx = view.getUint16(baseOffset + 4, true);
this.fieldIds.push({
classIdx,
typeIdx,
nameIdx,
className: this.typeIds[classIdx]?.className || 'unknown',
typeName: this.typeIds[typeIdx]?.className || 'unknown',
name: this.stringIds[nameIdx] || 'unknown'
});
}
console.log(`Parsed ${this.fieldIds.length} fields`);
}
async parseMethodIds() {
const view = new DataView(this.dexData.buffer);
const offset = this.header.methodIdsOff;
const size = this.header.methodIdsSize;
for (let i = 0; i < size; i++) {
const baseOffset = offset + (i * 8);
const classIdx = view.getUint16(baseOffset, true);
const protoIdx = view.getUint16(baseOffset + 2, true);
const nameIdx = view.getUint32(baseOffset + 4, true);
this.methodIds.push({
classIdx,
protoIdx,
nameIdx,
className: this.typeIds[classIdx]?.className || 'unknown',
proto: this.protoIds[protoIdx],
name: this.stringIds[nameIdx] || 'unknown'
});
}
console.log(`Parsed ${this.methodIds.length} methods`);
}
async parseClassDefs() {
const view = new DataView(this.dexData.buffer);
const offset = this.header.classDefsOff;
const size = this.header.classDefsSize;
for (let i = 0; i < size; i++) {
const baseOffset = offset + (i * 32);
const classIdx = view.getUint32(baseOffset, true);
const accessFlags = view.getUint32(baseOffset + 4, true);
const superclassIdx = view.getUint32(baseOffset + 8, true);
const interfacesOff = view.getUint32(baseOffset + 12, true);
const sourceFileIdx = view.getUint32(baseOffset + 16, true);
const annotationsOff = view.getUint32(baseOffset + 20, true);
const classDataOff = view.getUint32(baseOffset + 24, true);
const staticValuesOff = view.getUint32(baseOffset + 28, true);
this.classDefs.push({
classIdx,
accessFlags,
superclassIdx,
interfacesOff,
sourceFileIdx,
annotationsOff,
classDataOff,
staticValuesOff,
className: this.typeIds[classIdx]?.className || 'unknown',
superClass: this.typeIds[superclassIdx]?.className || 'unknown'
});
}
console.log(`Parsed ${this.classDefs.length} class definitions`);
}
generateJavaScript() {
let jsCode = '// Auto-generated JavaScript from DEX bytecode\n\n';
jsCode += '// DEX Information\n';
jsCode += `// Version: ${this.header.version}\n`;
jsCode += `// File Size: ${this.header.fileSize} bytes\n`;
jsCode += `// Strings: ${this.header.stringIdsSize}\n`;
jsCode += `// Types: ${this.header.typeIdsSize}\n`;
jsCode += `// Methods: ${this.header.methodIdsSize}\n`;
jsCode += `// Classes: ${this.header.classDefsSize}\n\n`;
// Generate class stubs
jsCode += '// Class Definitions\n';
for (const classDef of this.classDefs) {
jsCode += this.generateClassStub(classDef);
}
// Generate method stubs
jsCode += '\n// Method Stubs\n';
for (const method of this.methodIds.slice(0, 100)) { // Limit to first 100 methods
jsCode += this.generateMethodStub(method);
}
// Generate Android framework simulation
jsCode += this.generateAndroidFramework();
console.log('JavaScript code generated successfully');
return jsCode;
}
generateClassStub(classDef) {
const className = classDef.className.split('.').pop() || 'UnknownClass';
const packageName = classDef.className.substring(0, classDef.className.lastIndexOf('.')) || 'default';
let code = `// Class: ${classDef.className}\n`;
code += `class ${className} {\n`;
code += ` constructor() {\n`;
code += ` // Access flags: ${classDef.accessFlags}\n`;
code += ` // Superclass: ${classDef.superClass}\n`;
code += ` console.log('${className} instantiated');\n`;
code += ` }\n`;
code += `}\n\n`;
return code;
}
generateMethodStub(method) {
const methodName = method.name;
const returnType = method.proto?.returnType?.className || 'void';
let code = `// Method: ${method.className}.${methodName}()\n`;
code += `${methodName}: function() {\n`;
code += ` // Return type: ${returnType}\n`;
code += ` console.log('Method ${methodName} called');\n`;
code += ` return null;\n`;
code += `},\n\n`;
return code;
}
generateAndroidFramework() {
return `
// Android Framework Simulation
const android = {
// Core classes
Context: class {
constructor() {
this.resources = {};
this.preferences = {};
}
getSystemService(name) {
console.log('Getting system service:', name);
return null;
}
startActivity(intent) {
console.log('Starting activity:', intent);
}
getResources() {
return this.resources;
}
getSharedPreferences(name, mode) {
return this.preferences;
}
},
Activity: class extends android.Context {
constructor() {
super();
this.intent = null;
this.view = null;
}
onCreate(savedInstanceState) {
console.log('Activity created');
}
onStart() {
console.log('Activity started');
}
onResume() {
console.log('Activity resumed');
}
onPause() {
console.log('Activity paused');
}
onStop() {
console.log('Activity stopped');
}
onDestroy() {
console.log('Activity destroyed');
}
setContentView(layoutId) {
console.log('Setting content view:', layoutId);
}
findViewById(id) {
console.log('Finding view by ID:', id);
return null;
}
},
Service: class extends android.Context {
onCreate() {
console.log('Service created');
}
onStartCommand(intent, flags, startId) {
console.log('Service started');
return this.START_NOT_STICKY;
}
onDestroy() {
console.log('Service destroyed');
}
},
BroadcastReceiver: class {
onReceive(context, intent) {
console.log('Broadcast received:', intent);
}
},
ContentProvider: class {
onCreate() {
console.log('Content provider created');
return true;
}
query(uri, projection, selection, selectionArgs, sortOrder) {
console.log('Querying:', uri);
return null;
}
insert(uri, values) {
console.log('Inserting into:', uri);
return null;
}
update(uri, values, selection, selectionArgs) {
console.log('Updating:', uri);
return 0;
}
delete(uri, selection, selectionArgs) {
console.log('Deleting from:', uri);
return 0;
}
},
// UI Components
View: class {
constructor(context) {
this.context = context;
this.visibility = 0; // VISIBLE
this.alpha = 1.0;
}
setVisibility(visibility) {
this.visibility = visibility;
}
setAlpha(alpha) {
this.alpha = alpha;
}
invalidate() {
console.log('View invalidated');
}
},
TextView: class extends android.View {
constructor(context) {
super(context);
this.text = '';
}
setText(text) {
this.text = text;
console.log('TextView set to:', text);
}
getText() {
return this.text;
}
},
Button: class extends android.TextView {
constructor(context) {
super(context);
this.onClick = null;
}
setOnClickListener(listener) {
this.onClick = listener;
}
performClick() {
if (this.onClick) {
this.onClick(this);
}
}
},
ImageView: class extends android.View {
constructor(context) {
super(context);
this.imageResource = 0;
}
setImageResource(resId) {
this.imageResource = resId;
console.log('ImageView set to resource:', resId);
}
setImageBitmap(bitmap) {
console.log('ImageView set to bitmap');
}
},
LinearLayout: class extends android.View {
constructor(context) {
super(context);
this.orientation = 1; // VERTICAL
this.children = [];
}
setOrientation(orientation) {
this.orientation = orientation;
}
addView(view) {
this.children.push(view);
console.log('View added to LinearLayout');
}
removeView(view) {
const index = this.children.indexOf(view);
if (index > -1) {
this.children.splice(index, 1);
}
}
},
RelativeLayout: class extends android.View {
constructor(context) {
super(context);
this.children = [];
}
addView(view, params) {
this.children.push(view);
console.log('View added to RelativeLayout');
}
},
// Common utilities
Intent: class {
constructor(context, component) {
this.context = context;
this.component = component;
this.extras = {};
this.action = null;
this.data = null;
}
putExtra(key, value) {
this.extras[key] = value;
}
getExtra(key) {
return this.extras[key];
}
setAction(action) {
this.action = action;
}
setData(uri) {
this.data = uri;
}
},
Bundle: class {
constructor() {
this.data = {};
}
putString(key, value) {
this.data[key] = value;
}
getString(key, defaultValue) {
return this.data[key] || defaultValue;
}
putInt(key, value) {
this.data[key] = value;
}
getInt(key, defaultValue) {
return this.data[key] || defaultValue;
}
putBoolean(key, value) {
this.data[key] = value;
}
getBoolean(key, defaultValue) {
return this.data[key] !== undefined ? this.data[key] : defaultValue;
}
},
SharedPreferences: class {
constructor() {
this.data = {};
}
edit() {
return {
putString: (key, value) => { this.data[key] = value; return this; },
putInt: (key, value) => { this.data[key] = value; return this; },
putBoolean: (key, value) => { this.data[key] = value; return this; },
commit: () => { console.log('Preferences committed'); return true; },
apply: () => { console.log('Preferences applied'); }
};
}
getString(key, defValue) {
return this.data[key] || defValue;
}
getInt(key, defValue) {
return this.data[key] !== undefined ? this.data[key] : defValue;
}
getBoolean(key, defValue) {
return this.data[key] !== undefined ? this.data[key] : defValue;
}
},
// Constants
View: {
VISIBLE: 0,
INVISIBLE: 1,
GONE: 8
},
LinearLayout: {
VERTICAL: 1,
HORIZONTAL: 0
},
Service: {
START_NOT_STICKY: 2,
START_STICKY: 1,
START_REDELIVER_INTENT: 3
},
// Logging
Log: {
d: (tag, msg) => console.log('[DEBUG]', tag, ':', msg),
i: (tag, msg) => console.log('[INFO]', tag, ':', msg),
w: (tag, msg) => console.warn('[WARN]', tag, ':', msg),
e: (tag, msg) => console.error('[ERROR]', tag, ':', msg),
v: (tag, msg) => console.log('[VERBOSE]', tag, ':', msg)
}
};
// Initialize application
console.log('Android framework initialized');
console.log('Ready to run transpiled DEX code');
`;
}
}
// Export for usage
if (typeof module !== 'undefined' && module.exports) {
module.exports = DEXParser;
}