diff --git a/.github/workflows/publish_docker.yml b/.github/workflows/publish_docker.yml index 25db2724909..b5ed42f700c 100644 --- a/.github/workflows/publish_docker.yml +++ b/.github/workflows/publish_docker.yml @@ -46,7 +46,7 @@ jobs: - name: Build package working-directory: panda/debian - run: ./setup.sh Ubuntu ${{ matrix.ubuntu_version }} + run: ./setup.sh Ubuntu ${{ matrix.ubuntu_version }} ${{ needs.create_release.outputs.v-version }} - name: Upload wheel and debian packages to release uses: softprops/action-gh-release@v2 diff --git a/hw/avatar/configurable_machine.c b/hw/avatar/configurable_machine.c index d77cb8d6e1b..b63d1f12cfd 100644 --- a/hw/avatar/configurable_machine.c +++ b/hw/avatar/configurable_machine.c @@ -438,8 +438,9 @@ static void set_entry_point(QDict *conf, THISCPU *cpuu) cpuu->env.active_tc.PC = entry; #elif defined(TARGET_PPC) + cpuu->env.nip = entry; //Not implemented yet - fprintf(stderr, "Not yet implemented- can't start execution at 0x%x\n", entry); + //fprintf(stderr, "Not yet implemented- can't start execution at 0x%x\n", entry); #endif } diff --git a/panda/debian/.gitignore b/panda/debian/.gitignore index 7bbf39a4c32..b6c92bba57e 100644 --- a/panda/debian/.gitignore +++ b/panda/debian/.gitignore @@ -1 +1,2 @@ -panda.deb +*.deb +*.whl diff --git a/panda/debian/Dockerfile b/panda/debian/Dockerfile index 8d45e844161..6e4deb86459 100644 --- a/panda/debian/Dockerfile +++ b/panda/debian/Dockerfile @@ -1,7 +1,9 @@ +ARG PACKAGE_VERSION="" + # First run the main Dockerfile to build the base image and name it panda. Then we run here # to generate a debian package -FROM debian:buster-slim +FROM debian:bookworm-slim # Install necessary tools for packaging RUN apt-get -qq update && \ @@ -13,14 +15,28 @@ COPY --from=panda /tmp/base_dep.txt /tmp COPY --from=panda /tmp/build_dep.txt /tmp # Set up /package-root with files from panda we'll package -COPY --from=panda /usr/local/bin/panda* /usr/local/bin/libpanda* /usr/local/bin/qemu-img /package-root/usr/local/bin/ -COPY --from=panda /usr/local/etc/panda /package-root/usr/local/etc/panda -COPY --from=panda /usr/local/lib/panda /package-root/usr/local/lib/panda -COPY --from=panda /usr/local/share/panda /package-root/usr/local/share/panda +COPY --from=panda /usr/local/bin/panda* /usr/bin/libpanda* /usr/bin/qemu-img /package-root/usr/bin/ +COPY --from=panda /usr/local/etc/panda /package-root/etc/ +COPY --from=panda /usr/local/lib/panda /package-root/usr/lib/ +COPY --from=panda /usr/local/share/panda /package-root/usr/share/ + +# Copy documentation over, we should have a better Changelog if we go for official release? +COPY ./LICENSE /package-root/usr/share/doc/panda +COPY ./README.md /package-root/usr/share/doc/panda # Create DEBIAN directory and control file COPY control /package-root/DEBIAN/control +# Generate MD5 checksums for all files and save to DEBIAN/md5sums +RUN cd /package-root && \ + find . -type f ! -path './DEBIAN/*' -exec md5sum {} + | sed 's| \./| |' > /package-root/DEBIAN/md5sums + +# Update control file with the correct version, and place installed size +ARG PACKAGE_VERSION +RUN INSTALLED_SIZE=$(du -sk /package-root | cut -f1) && \ + sed -i "s/^Installed-Size:.*/Installed-Size: ${INSTALLED_SIZE}/" /package-root/DEBIAN/control +RUN sed -i "s/^Version:.*/Version: ${PACKAGE_VERSION}/" /package-root/DEBIAN/control + # Update control file with dependencies # Build time. We only select dependencies that are not commented out or blank RUN dependencies=$(grep '^[a-zA-Z]' /tmp/build_dep.txt | tr '\n' ',' | sed 's/,,\+/,/g'| sed 's/,$//') && \ diff --git a/panda/debian/control b/panda/debian/control index c5457a4457d..948f2ba2aef 100644 --- a/panda/debian/control +++ b/panda/debian/control @@ -1,9 +1,15 @@ -Package: pandare -Version: 3.1.0 -Architecture: all +Package: panda +Source: MITLL +Version: +Architecture: amd64 BUILD_DEPENDS_LIST DEPENDS_LIST -Maintainer: Andrew Fasano +Maintainer: Luke Craig +Installed-Size: +Section: devel +Priority: optional +Multi-Arch: same +Homepage: https://panda.re/ Description: dynamic analysis platform Platform for Architecture Neutral Dynamic Analysis (PANDA) is a processor emulator designed to support analyses of guest code. PANDA supports record- diff --git a/panda/debian/setup.sh b/panda/debian/setup.sh index b28dc85c7e2..58fd78cfa19 100755 --- a/panda/debian/setup.sh +++ b/panda/debian/setup.sh @@ -25,17 +25,33 @@ if [[ $# -eq 1 ]]; then echo " To build a package for current Ubuntu version:" echo " $0" echo " To build a package for a specific OS/version (only Ubuntu supported for now):" - echo " $0 " + echo " $0 " exit 1 fi if [[ $# -eq 2 ]]; then version=$2 - else version=$(lsb_release -r | awk '{print $2}') fi +if [[ $# -eq 3 ]]; then + tag_version=$3 +else + tag_version='v3.1.0' +fi + +# Remove leading 'v' if present, e. g. v1.5.1 -> 1.5.1 +if [[ "$tag_version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + tag_version=${tag_version:1} +fi + +# Check if the version follows the format X.Y.Z, e. g. 1.5.1 or 1.9.1 +if [[ ! "$tag_version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "ERROR: Version must be in the format X.Y.Z, provided tag version: $tag_version" + exit 1 +fi + # Check if the given version is supported if [[ ! -f "../dependencies/ubuntu_${version}_base.txt" ]]; then echo "ERROR: Ubuntu ${version} is not supported, no dependencies file found" @@ -53,7 +69,7 @@ docker run --rm -v $(pwd):/out panda bash -c "cp /panda/panda/python/core/dist/* DOCKER_BUILDKIT=1 docker build --target panda -t panda --build-arg BASE_IMAGE="ubuntu:${version}" ../.. # Now build the packager container from that -docker build -t packager . +docker build -t packager --build-arg PACKAGE_VERSION="${tag_version}" . # Copy deb file out of container to host docker run --rm -v $(pwd):/out packager bash -c "cp /pandare.deb /out" diff --git a/panda/plugins/osi_linux/osi_linux.h b/panda/plugins/osi_linux/osi_linux.h index e9cf0706cd3..ab38eb4f7b8 100644 --- a/panda/plugins/osi_linux/osi_linux.h +++ b/panda/plugins/osi_linux/osi_linux.h @@ -476,7 +476,7 @@ static inline char *read_dentry_name(CPUState *env, target_ptr_t dentry) { #endif OG_printf("Pcomp length %d\n", pcomp_length); if (pcomp_length == (uint32_t)-1) { // Not sure why this happens, but it does - printf("Warning: OSI_linux Unhandled pcomp value, ignoring\n"); + OG_printf("Warning: OSI_linux Unhandled pcomp value, ignoring\n"); break; } diff --git a/panda/python/core/pandare/arch.py b/panda/python/core/pandare/arch.py index 589912f5c8c..381f18b01c4 100644 --- a/panda/python/core/pandare/arch.py +++ b/panda/python/core/pandare/arch.py @@ -62,7 +62,7 @@ def _determine_bits(self): endianness = "big" elif self.panda.arch_name == "mips64el": bits = 64 - endianness = "big" + endianness = "little" assert (bits is not None), f"Missing num_bits logic for {self.panda.arch_name}" assert (endianness is not None), f"Missing endianness logic for {self.panda.arch_name}" @@ -369,7 +369,7 @@ def __init__(self, panda): regnames = ["X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", "XR", "X9", "X10", "X11", "X12", "X13", "X14", "X15", "IP0", "IP1", "PR", "X19", "X20", "X21", - "X22", "X23", "X24", "X25", "X26", "X27", "X27", + "X22", "X23", "X24", "X25", "X26", "X27", "X28", "FP", "LR", "SP"] self.reg_sp = regnames.index("SP") @@ -601,6 +601,7 @@ def set_retval(self, cpu, val, convention='default', failure=False): return super().set_retval(cpu, val, convention) + class Mips64Arch(MipsArch): ''' Register names and accessors for MIPS64. Inherits from MipsArch for everything @@ -629,67 +630,71 @@ def __init__(self, panda): # note names must be stored uppercase for get/set reg to work case-insensitively self.registers = {regnames[idx].upper(): idx for idx in range(len(regnames)) } -class X86Arch(PandaArch): +class PowerPCArch(PandaArch): ''' - Register names and accessors for x86 + Register names and accessors for ppc ''' - - def __init__(self, panda): + def __init__(self, panda): super().__init__(panda) - regnames = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI'] - # XXX Note order is A C D B, because that's how qemu does it . See target/i386/cpu.h - - # Note we don't set self.call_conventions because stack-based arg get/set is - # not yet supported - self.reg_retval = {"default": "EAX", - "syscall": "EAX", - "linux_kernel": "EAX"} - - self.call_conventions = {"cdecl": [f"stack_{x}" for x in range(20)], # 20: arbitrary but big - "syscall": ["EAX", "EBX", "ECX", "EDX", "ESI", "EDI", "EBP"], - "linux_kernel": ["EAX", "EDX", "ECX", "stack_3", "stack_4", "stack_5", "stack_6"]} - self.call_conventions['default'] = self.call_conventions['cdecl'] - - self.reg_sp = regnames.index('ESP') - self.registers = {regnames[idx]: idx for idx in range(len(regnames)) } - + regnames = ["r0", "sp", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", + "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", "r21", "r22", + "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31"] + self.reg_sp = regnames.index('sp') + self.registers = {regnames[idx].upper(): idx for idx in range(len(regnames)) } + self.registers_crf = ["CR0", "CR1", "CR2", "CR3", "CR4", "CR5", "CR6", "CR7"] def get_pc(self, cpu): ''' - Overloaded function to return the x86 current program counter + Overloaded function to return the ppc current program counter ''' - return cpu.env_ptr.eip + return cpu.env_ptr.nip def set_pc(self, cpu, val): ''' - Overloaded function to set the x86 program counter + Overloaded function to set the ppc program counter ''' - cpu.env_ptr.eip = val + cpu.env_ptr.nip = val def _get_reg_val(self, cpu, reg): ''' - Return an x86 register + Return a ppc register ''' - return cpu.env_ptr.regs[reg] + return cpu.env_ptr.gpr[reg] def _set_reg_val(self, cpu, reg, val): ''' - Set an x86 register + Set an x86_64 register ''' - cpu.env_ptr.regs[reg] = val + cpu.env_ptr.gpr[reg] = val + + def get_reg(self, cpu, reg): + + reg = reg.upper() + env = cpu.env_ptr + if reg == "LR": + return env.lr + elif reg == "CTR": + return env.ctr + elif reg in self.registers_crf: + return env.crf[self.registers_crf.index(reg)] + else: + return super().get_reg(cpu, reg) + + + def set_reg(self, cpu, reg, val): + reg = reg.upper() + env = cpu.env_ptr + + if reg == "LR": + env.lr = val + elif reg == "CTR": + env.ctr = val + elif reg in self.registers_crf: + env.crf[self.registers_crf.index(reg)] = val + else: + super().set_reg(cpu, reg, val) - def get_return_value(self, cpu): - ''' - .. Deprecated:: use get_retval - ''' - return self.get_retval(cpu) - def get_return_address(self,cpu): - ''' - looks up where ret will go - ''' - esp = self.get_reg(cpu,"ESP") - return self.panda.virtual_memory_read(cpu,esp,4,fmt='int') class X86_64Arch(PandaArch): ''' @@ -722,6 +727,7 @@ def __init__(self, panda): self.reg_names_short = ['AX', 'CX', 'DX', 'BX', 'SP', 'BP', 'SI', 'DI'] self.reg_names_byte = ['AL', 'CL', 'DL', 'BL', 'AH', 'CH', 'DH', 'BH'] self.seg_names = ['ES', 'CS', 'SS', 'DS', 'FS', 'GS'] + self.reg_names_mmr = ['LDT', 'TR', 'GDT', 'IDT'] def _get_segment_register(self, env, seg_name): seg_idx = self.seg_names.index(seg_name) @@ -765,6 +771,20 @@ def set_pc(self, cpu, val): ''' cpu.env_ptr.eip = val + def _get_mmr_val(self, cpu, reg): + reg = reg.lower() + sc = getattr(cpu.env_ptr, reg) + return (sc.selector, sc.base, sc.limit, sc.flags) + + def _set_mmr_val(self, cpu, reg, val): + reg = reg.lower() + selector, base, limit, flags = val + sc = getattr(cpu.env_ptr, reg) + sc.selector = selector + sc.base = base + sc.limit = limit + sc.flags = flags + def _get_reg_val(self, cpu, reg): ''' Return an x86_64 register @@ -802,8 +822,12 @@ def get_reg(self, cpu, reg): reg = reg.upper() env = cpu.env_ptr + if reg in self.reg_names_mmr: + return self._get_mmr_val(cpu, reg) if reg in self.seg_names: return self._get_segment_register(env, reg) + elif reg in ['EFLAGS', 'RFLAGS']: + return env.eflags elif reg in ['RIP', 'PC', 'EIP']: pc = self.get_pc(cpu) # changes reg to 'IP' and re-calls this if reg == 'EIP': @@ -856,13 +880,17 @@ def set_reg(self, cpu, reg, val): reg = reg.upper() env = cpu.env_ptr - if reg in ['ES', 'CS', 'SS', 'DS', 'FS', 'GS']: + if reg in self.reg_names_mmr: + return self._set_mmr_val(cpu, reg, val) + elif reg in self.seg_names: self._set_segment_register(env, reg, val) + elif reg in ['EFLAGS', 'RFLAGS']: + env.eflags = val elif reg in ['RIP', 'PC']: return self.set_pc(cpu, val) # changes reg to 'IP' and re-calls this elif reg.startswith('XMM'): - #env.xmm_regs[int(reg[3:])] = val - raise NotImplementedError("XMM registers unsupported") + env.xmm_regs[int(reg[3:])] = val + #raise NotImplementedError("XMM registers unsupported") elif reg.startswith('MM'): raise NotImplementedError("MM registers unsupported") elif reg.startswith('YMM'): @@ -890,3 +918,28 @@ def set_reg(self, cpu, reg, val): self._set_general_purpose_register(env, reg, val, mask) else: super().set_reg(cpu, reg, val) + + +class X86Arch(X86_64Arch): + ''' + Register names and accessors for x86 + ''' + + def __init__(self, panda): + super().__init__(panda) + regnames = ['EAX', 'ECX', 'EDX', 'EBX', 'ESP', 'EBP', 'ESI', 'EDI'] + # XXX Note order is A C D B, because that's how qemu does it . See target/i386/cpu.h + + # Note we don't set self.call_conventions because stack-based arg get/set is + # not yet supported + self.reg_retval = {"default": "EAX", + "syscall": "EAX", + "linux_kernel": "EAX"} + + self.call_conventions = {"cdecl": [f"stack_{x}" for x in range(20)], # 20: arbitrary but big + "syscall": ["EAX", "EBX", "ECX", "EDX", "ESI", "EDI", "EBP"], + "linux_kernel": ["EAX", "EDX", "ECX", "stack_3", "stack_4", "stack_5", "stack_6"]} + self.call_conventions['default'] = self.call_conventions['cdecl'] + + self.reg_sp = regnames.index('ESP') + self.registers = {regnames[idx]: idx for idx in range(len(regnames)) } diff --git a/panda/python/core/pandare/panda.py b/panda/python/core/pandare/panda.py index 713e47082bb..01307a3029c 100644 --- a/panda/python/core/pandare/panda.py +++ b/panda/python/core/pandare/panda.py @@ -37,7 +37,7 @@ from .asyncthread import AsyncThread from .qcows_internal import Qcows from .qemu_logging import QEMU_Log_Manager -from .arch import ArmArch, Aarch64Arch, MipsArch, Mips64Arch, X86Arch, X86_64Arch +from .arch import ArmArch, Aarch64Arch, MipsArch, Mips64Arch, X86Arch, X86_64Arch, PowerPCArch from .cosi import Cosi from dataclasses import dataclass @@ -150,6 +150,8 @@ def __init__(self, arch="i386", mem="128M", self.arch = MipsArch(self) elif self.arch_name in ["mips64", "mips64el"]: self.arch = Mips64Arch(self) + elif self.arch_name in ["ppc"]: + self.arch = PowerPCArch(self) else: raise ValueError(f"Unsupported architecture {self.arch_name}") self.bits, self.endianness, self.register_size = self.arch._determine_bits() diff --git a/target/ppc/translate_init.c b/target/ppc/translate_init.c index dd47b101a78..aae36a7e1ae 100644 --- a/target/ppc/translate_init.c +++ b/target/ppc/translate_init.c @@ -9814,6 +9814,7 @@ static void ppc_cpu_realizefn(DeviceState *dev, Error **errp) error_propagate(errp, local_err); return; } + cpu_reset(cs); #if !defined(CONFIG_USER_ONLY) cpu->cpu_dt_id = (cs->cpu_index / smp_threads) * max_smt @@ -10247,7 +10248,19 @@ const char *ppc_cpu_lookup_alias(const char *alias) PowerPCCPU *cpu_ppc_init(const char *cpu_model) { - return POWERPC_CPU(cpu_generic_init(TYPE_POWERPC_CPU, cpu_model)); + ObjectClass *cpu_oc; + Object *cpuobj; + + cpu_oc = cpu_class_by_name(TYPE_POWERPC_CPU, cpu_model); + if (cpu_oc == NULL) { + error_report("Unable to find CPU definition: %s", cpu_model); + exit(1); + } + cpuobj = object_new(object_class_get_name(cpu_oc)); + object_property_set_bool(cpuobj, true, "realized", &error_fatal); + return POWERPC_CPU(cpuobj); + +//return POWERPC_CPU(cpu_generic_init(TYPE_POWERPC_CPU, cpu_model)); } /* Sort by PVR, ordering special case "host" last. */