Skip to content

Commit dbe758f

Browse files
committed
bytecode post processing
1 parent 06335e3 commit dbe758f

File tree

9 files changed

+221
-49
lines changed

9 files changed

+221
-49
lines changed

src/api/bytecode/ewasm/FuncType.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,16 @@
11
import { WasmValueType } from "./wasmTypes";
22

33
export interface FuncType {
4-
index: number
54
params: WasmValueType[]
65
results: WasmValueType[]
76
}
7+
8+
export const printSignature = (index: number, funcType: FuncType): string => {
9+
let signature = `func_${index}(`
10+
signature += funcType.params.map(p => p.toString()).join(',')
11+
signature += ')'
12+
signature += ':'
13+
const results = funcType.results.length > 0 ? funcType.results.map(r => r.toString()).join(',') : 'void'
14+
signature += results
15+
return signature
16+
}

src/api/bytecode/ewasm/FunctionBody.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,43 @@
11
import { WasmValueType } from "./wasmTypes";
22
import { WasmOpcode, WasmOpcodes } from "./WasmOpcodes";
3+
import { WasmImportSectionPayload, WasmCodeSectionPayload } from "./WasmSection";
34

45
export interface FunctionBody {
56
locals: FunctionLocal[]
67
bytecodeHex: string
7-
opcodes: WasmOpcode[]
8-
formattedOpcodes: string
8+
opcodes?: WasmOpcode[]
9+
formattedOpcodes?: string
10+
functionSignature?: string
11+
name?: string
12+
exportedName?: string
913
}
1014

1115
export interface FunctionLocal {
1216
count: number
1317
valueType: WasmValueType
1418
}
1519

16-
export const formatOpcodes = (opcodes: WasmOpcode[]): string => {
20+
export const formatOpcodes = (opcodes: WasmOpcode[], importsPayload: WasmImportSectionPayload, codePayload: WasmCodeSectionPayload): string => {
1721
const newLine = '\n'
1822
const tab = ' '
1923
let formattedCode = ''
2024
let indentation = 0
2125
for (const op of opcodes) {
2226
formattedCode += `${tab.repeat(indentation)}${op.opcode.name} ${op.immediates}`
27+
if (WasmOpcodes.isCall(op)) {
28+
const callIdx: number = parseInt(op.immediates[0], 16)
29+
let functionCalledName = ''
30+
if (callIdx < importsPayload.imports.length) {
31+
const imp = importsPayload.imports[callIdx];
32+
functionCalledName = `${imp.moduleName}.${imp.fieldName}`
33+
} else {
34+
const fun = codePayload.functions[callIdx]
35+
if (fun) {
36+
functionCalledName = `${fun.exportedName? fun.exportedName: fun.name}`
37+
}
38+
}
39+
formattedCode += ` [${functionCalledName}]`
40+
}
2341
formattedCode += newLine
2442
if (WasmOpcodes.isBlockStart(op)) {
2543
indentation++

src/api/bytecode/ewasm/WasmBinaryParser.ts

Lines changed: 82 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { injectable } from "inversify";
22
import { WasmBinary } from "./WasmBinary";
33
import { BytesReader } from "./BytesReader";
4-
import { WasmSection, WasmTypeSectionPayload, WasmSectionPayload, WasmExportSectionPayload, WasmCodeSectionPayload, WasmImportSectionPayload } from "./WasmSection";
4+
import { WasmSection, WasmTypeSectionPayload, WasmSectionPayload, WasmExportSectionPayload, WasmCodeSectionPayload, WasmImportSectionPayload, WasmFunctionSectionPayload, findSection } from "./WasmSection";
55
import { WasmSectionType, WasmType, WasmValueType, getWasmValueType, getExternalType, WasmExternalKind } from "./wasmTypes";
6-
import { FuncType } from "./FuncType";
6+
import { FuncType, printSignature } from "./FuncType";
77
import { FunctionBody, FunctionLocal, formatOpcodes } from "./FunctionBody";
88
import { WasmOpcode, WasmOpcodeDefinition, WasmOpcodes, Immediate } from "./WasmOpcodes";
99
import { OpcodeImmediateType } from "./OpcodeImmediateType";
@@ -22,6 +22,7 @@ export class WasmBinaryParser {
2222
this.sectionParsers.set(WasmSectionType.Export, this.parseExportSection)
2323
this.sectionParsers.set(WasmSectionType.Code, this.parseCodeSection)
2424
this.sectionParsers.set(WasmSectionType.Import, this.parseImportSection)
25+
this.sectionParsers.set(WasmSectionType.Function, this.parseFunctionSection)
2526
}
2627

2728
parse(binary: Buffer): WasmBinary {
@@ -30,7 +31,7 @@ export class WasmBinaryParser {
3031
const magicNumberRead: string = reader.readBytesToHex(4)
3132

3233
if(magicNumberRead !== this.WASM_MAGIC_NUMBER) {
33-
throw new Error(`WASM Magic number not found`)
34+
throw new Error(`WASM Magic number not found: ${magicNumberRead}`)
3435
}
3536
const version = reader.readBytesToHex(4)
3637

@@ -53,24 +54,55 @@ export class WasmBinaryParser {
5354
})
5455
}
5556

56-
// removeme
57-
const sec = sections.find(section => {
58-
return section.sectionType.toString() == WasmSectionType[WasmSectionType.Code.toString()]
59-
});
60-
61-
const imp = sections.find(section => {
62-
return section.sectionType.toString() == WasmSectionType[WasmSectionType.Import.toString()]
63-
});
64-
console.log(JSON.stringify(imp))
65-
// const outp: WasmCodeSectionPayload = sec.payload as WasmCodeSectionPayload;
66-
// console.log(JSON.stringify(outp.functions[1]))
67-
// const mapp = outp.functions[3].opcodes.map(p => `[0x${p.opcode.code.toString(16)}] ${p.opcode.name} ${p.immediates}`)
68-
// console.log(mapp)
69-
// console.log(outp.functions[2].formattedOpcodes)
70-
// console.log(JSON.stringify(wasmSections))
71-
return {
57+
const wasmBinary: WasmBinary = {
7258
sections
59+
};
60+
const wasmPostProcessed = this.postProcess(wasmBinary);
61+
// console.log(JSON.stringify(wasmPostProcessed))
62+
return wasmPostProcessed
63+
}
64+
65+
postProcess(wasmBinary: WasmBinary): WasmBinary {
66+
const functionSection = findSection(wasmBinary.sections, WasmSectionType.Function)
67+
const functionSectionPayload: WasmFunctionSectionPayload = functionSection.payload as WasmFunctionSectionPayload
68+
69+
const typeSection = findSection(wasmBinary.sections, WasmSectionType.Type)
70+
const typeSectionPayload: WasmTypeSectionPayload = typeSection.payload as WasmTypeSectionPayload
71+
const codeSection = findSection(wasmBinary.sections, WasmSectionType.Code)
72+
const codeSectionPayload: WasmCodeSectionPayload = codeSection.payload as WasmCodeSectionPayload
73+
const exportSection = findSection(wasmBinary.sections, WasmSectionType.Export)
74+
const exportSectionPayload: WasmExportSectionPayload = exportSection.payload as WasmExportSectionPayload
75+
76+
const importSection = findSection(wasmBinary.sections, WasmSectionType.Import)
77+
const importSectionPayload: WasmImportSectionPayload = importSection.payload as WasmImportSectionPayload
78+
79+
// adding function signatures & name
80+
for (let i = 0; i < functionSectionPayload.functionsTypes.length; i++) {
81+
const fun = functionSectionPayload.functionsTypes[i]
82+
const funCode = codeSectionPayload.functions[i]
83+
const typ = typeSectionPayload.functions[fun]
84+
const signature = printSignature(i, typ)
85+
funCode.functionSignature = signature
86+
funCode.name = `func_${i}`
87+
}
88+
const numOfImports = importSectionPayload.imports.length
89+
// adding export names to functions
90+
for(const exp of exportSectionPayload.exports) {
91+
if(exp.kind !== getExternalType(WasmExternalKind.Function.toString())) {
92+
continue
93+
}
94+
const index = exp.index - numOfImports
95+
const fun = codeSectionPayload.functions[index]
96+
fun.exportedName = exp.name
7397
}
98+
// formatting opcodes
99+
for(const fun of codeSectionPayload.functions) {
100+
const formattedOpcodes = formatOpcodes(fun.opcodes, importSectionPayload, codeSectionPayload)
101+
fun.formattedOpcodes = formattedOpcodes
102+
// deleting opcodes for now, not really needed in the response, maybe in the future
103+
delete fun.opcodes
104+
}
105+
return wasmBinary
74106
}
75107

76108
parseSectionPayload(payload: Buffer, sectionId: number): WasmSectionPayload {
@@ -91,7 +123,7 @@ export class WasmBinaryParser {
91123
const numberOfElements = reader.readVarUint32()
92124
let index = 0;
93125
while(index < numberOfElements) {
94-
const funcType: FuncType = self.parseFuncType(reader, index)
126+
const funcType: FuncType = self.parseFuncType(reader)
95127
sectionPayload.functions.push(funcType)
96128
index++
97129
}
@@ -185,13 +217,27 @@ export class WasmBinaryParser {
185217
return limits
186218
}
187219

220+
parseFunctionSection(payload: Buffer, self: any): WasmFunctionSectionPayload {
221+
const reader = new BytesReader(payload)
222+
const numberOfElements = reader.readVarUint32()
223+
const functionSection: WasmFunctionSectionPayload = {
224+
functionsTypes: []
225+
}
226+
let functionsCounter = 0
227+
while(functionsCounter < numberOfElements) {
228+
const functionTypeIndex = reader.readVarUint32()
229+
functionSection.functionsTypes.push(functionTypeIndex)
230+
functionsCounter++
231+
}
232+
return functionSection
233+
}
234+
188235
parseCodeSection(payload: Buffer, self: any): WasmCodeSectionPayload {
189236
const reader = new BytesReader(payload)
190237
const numberOfElements = reader.readVarUint32()
191238
const codeSection: WasmCodeSectionPayload = {
192239
functions: []
193240
}
194-
console.log(`ParsingCodeSection, noElements=${numberOfElements}`)
195241
let bodiesCounter = 0
196242
while(bodiesCounter < numberOfElements) {
197243
const functionBody = self.parseFunctionCode(reader)
@@ -226,12 +272,10 @@ export class WasmBinaryParser {
226272
const bytecodeBuffer: Buffer = reader.readBytes(body.length - reader.getPointer())
227273
const opcodes: WasmOpcode[] = this.parseFunctionBytecode(bytecodeBuffer)
228274
const bytecodeHex: string = bytecodeBuffer.toString('hex')
229-
const formattedOpcodes = formatOpcodes(opcodes)
230275
return {
231276
bytecodeHex,
232277
locals,
233-
opcodes,
234-
formattedOpcodes
278+
opcodes
235279
}
236280
}
237281

@@ -246,7 +290,7 @@ export class WasmBinaryParser {
246290
throw new Error(`Opcode not implemented: ${opcodeByte} [${opcodeByte.toString(16)}]`)
247291
}
248292
for(const immediate of opcodeDefinition.immediates) {
249-
// TODO refactor
293+
// TODO refactor this shit
250294
if (immediate.type === OpcodeImmediateType.U32) {
251295
const immediateValue = reader.readVarUint32()
252296
const valueFormatted = immediateValue < 0? `-0x${(immediateValue * -1).toString(16)}`: `0x${immediateValue.toString(16)}`
@@ -263,6 +307,18 @@ export class WasmBinaryParser {
263307
const immediateValue = reader.readBytesToNumber(1)
264308
const valueFormatted = immediateValue < 0? `-0x${(immediateValue * -1).toString(16)}`: `0x${immediateValue.toString(16)}`
265309
immediates.push(valueFormatted)
310+
} else if (immediate.type === OpcodeImmediateType.VECTOR_U32) {
311+
const vectorLength = reader.readVarUint32()
312+
let vectorCounter = 0
313+
while(vectorCounter < vectorLength) {
314+
const immediateValue = reader.readVarUint32()
315+
const valueFormatted = immediateValue < 0? `-0x${(immediateValue * -1).toString(16)}`: `0x${immediateValue.toString(16)}`
316+
immediates.push(valueFormatted)
317+
vectorCounter++
318+
}
319+
const defaultTarget = reader.readVarUint32()
320+
const valueFormatted = defaultTarget < 0? `-0x${(defaultTarget * -1).toString(16)}`: `0x${defaultTarget.toString(16)}`
321+
immediates.push(valueFormatted)
266322
}
267323
}
268324
opcodes.push({
@@ -273,7 +329,7 @@ export class WasmBinaryParser {
273329
return opcodes
274330
}
275331

276-
parseFuncType(reader: BytesReader, index: number): FuncType {
332+
parseFuncType(reader: BytesReader): FuncType {
277333
const typeByte = reader.readBytesToNumber(1)
278334
if(typeByte !== WasmType.FunctionType) {
279335
throw new Error(`Error parsing FuncType - type=${typeByte} not function `)
@@ -297,7 +353,6 @@ export class WasmBinaryParser {
297353
resultsCounter++
298354
}
299355
return {
300-
index,
301356
params,
302357
results
303358
}

src/api/bytecode/ewasm/WasmOpcodes.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export class WasmOpcodes {
1313

1414
this.opcodes.set(0xc, {code: 0xc, name: 'br', immediates: [{type: OpcodeImmediateType.U32}]})
1515
this.opcodes.set(0xd, {code: 0xd, name: 'br_if', immediates: [{type: OpcodeImmediateType.U32}]})
16+
this.opcodes.set(0xe, {code: 0xe, name: 'br_table', immediates: [{type: OpcodeImmediateType.VECTOR_U32},{type: OpcodeImmediateType.U32}]})
1617

1718
this.opcodes.set(0xf, {code: 0xf, name: 'return', immediates: []})
1819
this.opcodes.set(0x10, {code: 0x10, name: 'call', immediates: [{type: OpcodeImmediateType.U32}]})
@@ -210,6 +211,10 @@ export class WasmOpcodes {
210211
static isBlockEnd(opcode: WasmOpcode): boolean {
211212
return opcode.opcode.name === 'end'
212213
}
214+
215+
static isCall(opcode: WasmOpcode): boolean {
216+
return opcode.opcode.name === 'call'
217+
}
213218
}
214219

215220
export interface WasmOpcodeDefinition {

src/api/bytecode/ewasm/WasmSection.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,13 @@ export interface WasmImportSectionPayload extends WasmSectionPayload {
2929
export interface WasmCodeSectionPayload extends WasmSectionPayload {
3030
functions: FunctionBody[]
3131
}
32+
33+
export interface WasmFunctionSectionPayload extends WasmSectionPayload {
34+
functionsTypes: number[]
35+
}
36+
37+
export const findSection = (sections: WasmSection[], sectionType: WasmSectionType): WasmSection => {
38+
return sections.find(section => {
39+
return section.sectionType.toString() == WasmSectionType[sectionType.toString()]
40+
});
41+
}

src/api/service/controller/DebuggerController.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { StringBodyRequest } from '../request/StringBodyRequest';
1515
@Route('debug')
1616
@provideSingleton(DebuggerController)
1717
export class DebuggerController extends Controller {
18+
1819
constructor(
1920
@inject(TYPES.CFGService) private cfgService: CFGService,
2021
@inject(TYPES.GraphVizService) private graphVizService: GraphVizService,
Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import { Route, Controller, Post, Body } from "tsoa";
1+
import { Route, Controller, Post, Body, Get, Query, Path } from "tsoa";
22
import { provideSingleton, inject } from "../../../inversify/ioc";
33
import { StringBodyRequest } from "../request/StringBodyRequest";
44
import { TYPES } from "../../../inversify/types";
55
import { EwasmService } from "../service/EwasmService";
66
import { WasmBinary } from "../../bytecode/ewasm/WasmBinary";
7+
import { Web3Configuration } from "../../blockchain/Web3Configuration";
8+
import { logger } from '../../../Logger'
79

810
@Route('ewasm')
911
@provideSingleton(EwasmController)
@@ -15,16 +17,53 @@ export class EwasmController extends Controller {
1517

1618
@Post('toWat')
1719
async wasmToWat(@Body() source: StringBodyRequest ): Promise<string> {
18-
return this.ewasmService.wasmToWat(source.request)
20+
try {
21+
return this.ewasmService.wasmToWat(source.request)
22+
} catch(error) {
23+
logger.error(error)
24+
throw new Error(error.message)
25+
}
1926
}
2027

2128
@Post('decompile')
2229
async decompile(@Body() source: StringBodyRequest ): Promise<string> {
23-
return this.ewasmService.decompile(source.request)
30+
try {
31+
return this.ewasmService.decompile(source.request)
32+
} catch(error) {
33+
logger.error(error)
34+
throw new Error(error.message)
35+
}
2436
}
2537

2638
@Post('analyze')
2739
async analyze(@Body() source: StringBodyRequest ): Promise<WasmBinary> {
28-
return this.ewasmService.analyze(source.request)
40+
try {
41+
return this.ewasmService.analyze(source.request)
42+
} catch(error) {
43+
logger.error(error)
44+
throw new Error(error.message)
45+
}
46+
}
47+
48+
@Get('analyze/{address}')
49+
async analyzeAddress(
50+
@Path() address: string,
51+
@Query('blockchainHost') blockchainHost?: string,
52+
@Query('blockchainProtocol') blockchainProtocol?: string,
53+
@Query('blockchainBasicAuthUsername') blockchainBasicAuthUsername?: string,
54+
@Query('blockchainBasicAuthPassword') blockchainBasicAuthPassword?: string
55+
): Promise<WasmBinary> {
56+
try {
57+
const config = {
58+
blockchainHost,
59+
blockchainProtocol,
60+
blockchainBasicAuthUsername,
61+
blockchainBasicAuthPassword
62+
} as Web3Configuration
63+
return this.ewasmService.analyzeAddress(address, config)
64+
} catch(error) {
65+
logger.error(error)
66+
throw new Error(error.message)
67+
}
2968
}
3069
}

0 commit comments

Comments
 (0)