3
0
mirror of https://github.com/CrazyRedMachine/popnhax.git synced 2024-11-23 22:00:57 +01:00

patch_db: generate patch file

This commit is contained in:
CrazyRedMachine 2024-06-16 20:44:13 +02:00
parent eea48f2432
commit 4c92b4fda7
53 changed files with 12701 additions and 35 deletions

View File

@ -6,6 +6,7 @@ avsvers_64 := 1700 1509
imps += avs avs-ea3
include util/Module.mk
include libdisasm/Module.mk
include minhook/Module.mk
include popnhax/Module.mk

View File

@ -140,12 +140,12 @@
<fps_uncap __type="bool">0</fps_uncap>
<!-- Song db patches (requires patch_db) -->
<!-- Auto select patch file from data_mods folder based on music limit, or datecode otherwise (will detect datecode from ea3-config or force_datecode option) -->
<patch_xml_auto __type="bool">1</patch_xml_auto>
<!-- Manually set XML file containing patches (requires patch_xml_auto to be disabled) -->
<patch_xml_filename __type="str"></patch_xml_filename>
<!-- Disable patch file detection/generation and manually set the XML file containing patches instead -->
<force_patch_xml __type="str"></force_patch_xml>
<!-- Do not perform music limit checks for patch_xml_auto (not recommended) -->
<ignore_music_limit __type="bool">0</ignore_music_limit>
<!-- Dump the generated patch file to data_mods\patches_<datecode>.xml (in case generated patches require some fixes) -->
<patch_xml_dump __type="bool">0</patch_xml_dump>
<!-- Custom category options (requires patch_db) -->
<!-- Also exclude omnimix (song id < 3000) tracks from version/level (requires custom_exclude_from_level or custom_exclude_from_version) -->

43
libdisasm/Makefile.am Normal file
View File

@ -0,0 +1,43 @@
include_HEADERS = libdis.h
lib_LTLIBRARIES = libdisasm.la
libdisasm_la_SOURCES = \
ia32_implicit.c \
ia32_implicit.h \
ia32_insn.c \
ia32_insn.h \
ia32_invariant.c \
ia32_invariant.h \
ia32_modrm.c \
ia32_modrm.h \
ia32_opcode_tables.c \
ia32_opcode_tables.h \
ia32_operand.c \
ia32_operand.h \
ia32_reg.c \
ia32_reg.h \
ia32_settings.c \
ia32_settings.h \
libdis.h \
qword.h \
x86_disasm.c \
x86_format.c \
x86_imm.c \
x86_imm.h \
x86_insn.c \
x86_misc.c \
x86_operand_list.c \
x86_operand_list.h
# Cheat to get non-autoconf swig into tarball,
# even if it doesn't build by default.
EXTRA_DIST = \
swig/Makefile \
swig/libdisasm.i \
swig/libdisasm_oop.i \
swig/python/Makefile-swig \
swig/perl/Makefile-swig \
swig/perl/Makefile.PL \
swig/ruby/Makefile-swig \
swig/ruby/extconf.rb \
swig/tcl/Makefile-swig \
swig/README

544
libdisasm/Makefile.in Normal file
View File

@ -0,0 +1,544 @@
# Makefile.in generated by automake 1.10 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
# 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
subdir = libdisasm
DIST_COMMON = $(include_HEADERS) $(srcdir)/Makefile.am \
$(srcdir)/Makefile.in TODO
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
libLTLIBRARIES_INSTALL = $(INSTALL)
LTLIBRARIES = $(lib_LTLIBRARIES)
libdisasm_la_LIBADD =
am_libdisasm_la_OBJECTS = ia32_implicit.lo ia32_insn.lo \
ia32_invariant.lo ia32_modrm.lo ia32_opcode_tables.lo \
ia32_operand.lo ia32_reg.lo ia32_settings.lo x86_disasm.lo \
x86_format.lo x86_imm.lo x86_insn.lo x86_misc.lo \
x86_operand_list.lo
libdisasm_la_OBJECTS = $(am_libdisasm_la_OBJECTS)
DEFAULT_INCLUDES = -I. -I$(top_builddir)@am__isrc@
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
--mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
CCLD = $(CC)
LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
$(LDFLAGS) -o $@
SOURCES = $(libdisasm_la_SOURCES)
DIST_SOURCES = $(libdisasm_la_SOURCES)
includeHEADERS_INSTALL = $(INSTALL_HEADER)
HEADERS = $(include_HEADERS)
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AR = @AR@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CXX = @CXX@
CXXCPP = @CXXCPP@
CXXDEPMODE = @CXXDEPMODE@
CXXFLAGS = @CXXFLAGS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
ECHO = @ECHO@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
EXEEXT = @EXEEXT@
F77 = @F77@
FFLAGS = @FFLAGS@
GREP = @GREP@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
LDFLAGS = @LDFLAGS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MKDIR_P = @MKDIR_P@
OBJEXT = @OBJEXT@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
RANLIB = @RANLIB@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
STRIP = @STRIP@
VERSION = @VERSION@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_CC = @ac_ct_CC@
ac_ct_CXX = @ac_ct_CXX@
ac_ct_F77 = @ac_ct_F77@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
include_HEADERS = libdis.h
lib_LTLIBRARIES = libdisasm.la
libdisasm_la_SOURCES = \
ia32_implicit.c \
ia32_implicit.h \
ia32_insn.c \
ia32_insn.h \
ia32_invariant.c \
ia32_invariant.h \
ia32_modrm.c \
ia32_modrm.h \
ia32_opcode_tables.c \
ia32_opcode_tables.h \
ia32_operand.c \
ia32_operand.h \
ia32_reg.c \
ia32_reg.h \
ia32_settings.c \
ia32_settings.h \
libdis.h \
qword.h \
x86_disasm.c \
x86_format.c \
x86_imm.c \
x86_imm.h \
x86_insn.c \
x86_misc.c \
x86_operand_list.c \
x86_operand_list.h
# Cheat to get non-autoconf swig into tarball,
# even if it doesn't build by default.
EXTRA_DIST = \
swig/Makefile \
swig/libdisasm.i \
swig/libdisasm_oop.i \
swig/python/Makefile-swig \
swig/perl/Makefile-swig \
swig/perl/Makefile.PL \
swig/ruby/Makefile-swig \
swig/ruby/extconf.rb \
swig/tcl/Makefile-swig \
swig/README
all: all-am
.SUFFIXES:
.SUFFIXES: .c .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
&& exit 0; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu libdisasm/Makefile'; \
cd $(top_srcdir) && \
$(AUTOMAKE) --gnu libdisasm/Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
if test -f $$p; then \
f=$(am__strip_dir) \
echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) '$$p' '$(DESTDIR)$(libdir)/$$f'"; \
$(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) "$$p" "$(DESTDIR)$(libdir)/$$f"; \
else :; fi; \
done
uninstall-libLTLIBRARIES:
@$(NORMAL_UNINSTALL)
@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
p=$(am__strip_dir) \
echo " $(LIBTOOL) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$p'"; \
$(LIBTOOL) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$p"; \
done
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
@list='$(lib_LTLIBRARIES)'; for p in $$list; do \
dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
test "$$dir" != "$$p" || dir=.; \
echo "rm -f \"$${dir}/so_locations\""; \
rm -f "$${dir}/so_locations"; \
done
libdisasm.la: $(libdisasm_la_OBJECTS) $(libdisasm_la_DEPENDENCIES)
$(LINK) -rpath $(libdir) $(libdisasm_la_OBJECTS) $(libdisasm_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ia32_implicit.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ia32_insn.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ia32_invariant.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ia32_modrm.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ia32_opcode_tables.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ia32_operand.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ia32_reg.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ia32_settings.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x86_disasm.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x86_format.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x86_imm.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x86_insn.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x86_misc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x86_operand_list.Plo@am__quote@
.c.o:
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(COMPILE) -c $<
.c.obj:
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
install-includeHEADERS: $(include_HEADERS)
@$(NORMAL_INSTALL)
test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(include_HEADERS)'; for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
f=$(am__strip_dir) \
echo " $(includeHEADERS_INSTALL) '$$d$$p' '$(DESTDIR)$(includedir)/$$f'"; \
$(includeHEADERS_INSTALL) "$$d$$p" "$(DESTDIR)$(includedir)/$$f"; \
done
uninstall-includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(include_HEADERS)'; for p in $$list; do \
f=$(am__strip_dir) \
echo " rm -f '$(DESTDIR)$(includedir)/$$f'"; \
rm -f "$(DESTDIR)$(includedir)/$$f"; \
done
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
mkid -fID $$unique
tags: TAGS
TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$tags $$unique; \
fi
ctags: CTAGS
CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$(TAGS_FILES) $(LISP)
tags=; \
here=`pwd`; \
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | \
$(AWK) ' { files[$$0] = 1; } \
END { for (i in files) print i; }'`; \
test -z "$(CTAGS_ARGS)$$tags$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$tags $$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& cd $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) $$here
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
fi; \
cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
else \
test -f $(distdir)/$$file \
|| cp -p $$d/$$file $(distdir)/$$file \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
all-am: Makefile $(LTLIBRARIES) $(HEADERS)
installdirs:
for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
`test -z '$(STRIP)' || \
echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
mostlyclean-am
distclean: distclean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
info: info-am
info-am:
install-data-am: install-includeHEADERS
install-dvi: install-dvi-am
install-exec-am: install-libLTLIBRARIES
install-html: install-html-am
install-info: install-info-am
install-man:
install-pdf: install-pdf-am
install-ps: install-ps-am
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -rf ./$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-compile mostlyclean-generic \
mostlyclean-libtool
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
clean-libLTLIBRARIES clean-libtool ctags distclean \
distclean-compile distclean-generic distclean-libtool \
distclean-tags distdir dvi dvi-am html html-am info info-am \
install install-am install-data install-data-am install-dvi \
install-dvi-am install-exec install-exec-am install-html \
install-html-am install-includeHEADERS install-info \
install-info-am install-libLTLIBRARIES install-man install-pdf \
install-pdf-am install-ps install-ps-am install-strip \
installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
tags uninstall uninstall-am uninstall-includeHEADERS \
uninstall-libLTLIBRARIES
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

19
libdisasm/Module.mk Normal file
View File

@ -0,0 +1,19 @@
libs += libdisasm
cflags += -w
src_libdisasm := \
ia32_implicit.c \
ia32_insn.c \
ia32_invariant.c \
ia32_modrm.c \
ia32_opcode_tables.c \
ia32_operand.c \
ia32_reg.c \
ia32_settings.c \
x86_disasm.c \
x86_format.c \
x86_imm.c \
x86_insn.c \
x86_misc.c \
x86_operand_list.c

43
libdisasm/TODO Normal file
View File

@ -0,0 +1,43 @@
x86_format.c
------------
intel: jmpf -> jmp, callf -> call
att: jmpf -> ljmp, callf -> lcall
opcode table
------------
finish typing instructions
fix flag clear/set/toggle types
ix64 stuff
----------
document output file formats in web page
features doc: register aliases, implicit operands, stack mods,
ring0 flags, eflags, cpu model/isa
ia32_handle_* implementation
fix operand 0F C2
CMPPS
* sysenter, sysexit as CALL types -- preceded by MSR writes
* SYSENTER/SYSEXIT stack : overwrites SS, ESP
* stos, cmps, scas, movs, ins, outs, lods -> OP_PTR
* OP_SIZE in implicit operands
* use OP_SIZE to choose reg sizes!
DONE?? :
implicit operands: provide action ?
e.g. add/inc for stach, write, etc
replace table numbers in opcodes.dat with
#defines for table names
replace 0 with INSN_INVALID [or maybe FF for imnvalid and 00 for Not Applicable */
no wait that is only for prefix tables -- n/p
if ( prefx) only use if insn != invalid
these should cover all the wacky disasm exceptions
for the rep one we can chet, match only a 0x90
todo: privilege | ring

422
libdisasm/ia32_implicit.c Normal file
View File

@ -0,0 +1,422 @@
#include <stdlib.h>
#include "ia32_implicit.h"
#include "ia32_insn.h"
#include "ia32_reg.h"
#include "x86_operand_list.h"
/* Conventions: Register operands which are aliases of another register
* operand (e.g. AX in one operand and AL in another) assume that the
* operands are different registers and that alias tracking will resolve
* data flow. This means that something like
* mov ax, al
* would have 'write only' access for AX and 'read only' access for AL,
* even though both AL and AX are read and written */
typedef struct {
uint32_t type;
uint32_t operand;
} op_implicit_list_t;
static op_implicit_list_t list_aaa[] =
/* 37 : AAA : rw AL */
/* 3F : AAS : rw AL */
{{ OP_R | OP_W, REG_BYTE_OFFSET }, {0}}; /* aaa */
static op_implicit_list_t list_aad[] =
/* D5 0A, D5 (ib) : AAD : rw AX */
/* D4 0A, D4 (ib) : AAM : rw AX */
{{ OP_R | OP_W, REG_WORD_OFFSET }, {0}}; /* aad */
static op_implicit_list_t list_call[] =
/* E8, FF, 9A, FF : CALL : rw ESP, rw EIP */
/* C2, C3, CA, CB : RET : rw ESP, rw EIP */
{{ OP_R | OP_W, REG_EIP_INDEX },
{ OP_R | OP_W, REG_ESP_INDEX }, {0}}; /* call, ret */
static op_implicit_list_t list_cbw[] =
/* 98 : CBW : r AL, rw AX */
{{ OP_R | OP_W, REG_WORD_OFFSET },
{ OP_R, REG_BYTE_OFFSET}, {0}}; /* cbw */
static op_implicit_list_t list_cwde[] =
/* 98 : CWDE : r AX, rw EAX */
{{ OP_R | OP_W, REG_DWORD_OFFSET },
{ OP_R, REG_WORD_OFFSET }, {0}}; /* cwde */
static op_implicit_list_t list_clts[] =
/* 0F 06 : CLTS : rw CR0 */
{{ OP_R | OP_W, REG_CTRL_OFFSET}, {0}}; /* clts */
static op_implicit_list_t list_cmpxchg[] =
/* 0F B0 : CMPXCHG : rw AL */
{{ OP_R | OP_W, REG_BYTE_OFFSET }, {0}}; /* cmpxchg */
static op_implicit_list_t list_cmpxchgb[] =
/* 0F B1 : CMPXCHG : rw EAX */
{{ OP_R | OP_W, REG_DWORD_OFFSET }, {0}}; /* cmpxchg */
static op_implicit_list_t list_cmpxchg8b[] =
/* 0F C7 : CMPXCHG8B : rw EDX, rw EAX, r ECX, r EBX */
{{ OP_R | OP_W, REG_DWORD_OFFSET },
{ OP_R | OP_W, REG_DWORD_OFFSET + 2 },
{ OP_R, REG_DWORD_OFFSET + 1 },
{ OP_R, REG_DWORD_OFFSET + 3 }, {0}}; /* cmpxchg8b */
static op_implicit_list_t list_cpuid[] =
/* 0F A2 : CPUID : rw EAX, w EBX, w ECX, w EDX */
{{ OP_R | OP_W, REG_DWORD_OFFSET },
{ OP_W, REG_DWORD_OFFSET + 1 },
{ OP_W, REG_DWORD_OFFSET + 2 },
{ OP_W, REG_DWORD_OFFSET + 3 }, {0}}; /* cpuid */
static op_implicit_list_t list_cwd[] =
/* 99 : CWD/CWQ : rw EAX, w EDX */
{{ OP_R | OP_W, REG_DWORD_OFFSET },
{ OP_W, REG_DWORD_OFFSET + 2 }, {0}}; /* cwd */
static op_implicit_list_t list_daa[] =
/* 27 : DAA : rw AL */
/* 2F : DAS : rw AL */
{{ OP_R | OP_W, REG_BYTE_OFFSET }, {0}}; /* daa */
static op_implicit_list_t list_idiv[] =
/* F6 : DIV, IDIV : r AX, w AL, w AH */
/* FIXED: first op was EAX, not Aw. TODO: verify! */
{{ OP_R, REG_WORD_OFFSET },
{ OP_W, REG_BYTE_OFFSET },
{ OP_W, REG_BYTE_OFFSET + 4 }, {0}}; /* div */
static op_implicit_list_t list_div[] =
/* F7 : DIV, IDIV : rw EDX, rw EAX */
{{ OP_R | OP_W, REG_DWORD_OFFSET + 2 },
{ OP_R | OP_W, REG_DWORD_OFFSET }, {0}}; /* div */
static op_implicit_list_t list_enter[] =
/* C8 : ENTER : rw ESP w EBP */
{{ OP_R | OP_W, REG_DWORD_OFFSET + 4 },
{ OP_R, REG_DWORD_OFFSET + 5 }, {0}}; /* enter */
static op_implicit_list_t list_f2xm1[] =
/* D9 F0 : F2XM1 : rw ST(0) */
/* D9 E1 : FABS : rw ST(0) */
/* D9 E0 : FCHS : rw ST(0) */
/* D9 FF : FCOS : rw ST(0)*/
/* D8, DA : FDIV : rw ST(0) */
/* D8, DA : FDIVR : rw ST(0) */
/* D9 F2 : FPTAN : rw ST(0) */
/* D9 FC : FRNDINT : rw ST(0) */
/* D9 FB : FSINCOS : rw ST(0) */
/* D9 FE : FSIN : rw ST(0) */
/* D9 FA : FSQRT : rw ST(0) */
/* D9 F4 : FXTRACT : rw ST(0) */
{{ OP_R | OP_W, REG_FPU_OFFSET }, {0}}; /* f2xm1 */
static op_implicit_list_t list_fcom[] =
/* D8, DC, DE D9 : FCOM : r ST(0) */
/* DE, DA : FICOM : r ST(0) */
/* DF, D8 : FIST : r ST(0) */
/* D9 E4 : FTST : r ST(0) */
/* D9 E5 : FXAM : r ST(0) */
{{ OP_R, REG_FPU_OFFSET }, {0}}; /* fcom */
static op_implicit_list_t list_fpatan[] =
/* D9 F3 : FPATAN : r ST(0), rw ST(1) */
{{ OP_R, REG_FPU_OFFSET }, {0}}; /* fpatan */
static op_implicit_list_t list_fprem[] =
/* D9 F8, D9 F5 : FPREM : rw ST(0) r ST(1) */
/* D9 FD : FSCALE : rw ST(0), r ST(1) */
{{ OP_R | OP_W, REG_FPU_OFFSET },
{ OP_R, REG_FPU_OFFSET + 1 }, {0}}; /* fprem */
static op_implicit_list_t list_faddp[] =
/* DE C1 : FADDP : r ST(0), rw ST(1) */
/* DE E9 : FSUBP : r ST(0), rw ST(1) */
/* D9 F1 : FYL2X : r ST(0), rw ST(1) */
/* D9 F9 : FYL2XP1 : r ST(0), rw ST(1) */
{{ OP_R, REG_FPU_OFFSET },
{ OP_R | OP_W, REG_FPU_OFFSET + 1 }, {0}}; /* faddp */
static op_implicit_list_t list_fucompp[] =
/* DA E9 : FUCOMPP : r ST(0), r ST(1) */
{{ OP_R, REG_FPU_OFFSET },
{ OP_R, REG_FPU_OFFSET + 1 }, {0}}; /* fucompp */
static op_implicit_list_t list_imul[] =
/* F6 : IMUL : r AL, w AX */
/* F6 : MUL : r AL, w AX */
{{ OP_R, REG_BYTE_OFFSET },
{ OP_W, REG_WORD_OFFSET }, {0}}; /* imul */
static op_implicit_list_t list_mul[] =
/* F7 : IMUL : rw EAX, w EDX */
/* F7 : MUL : rw EAX, w EDX */
{{ OP_R | OP_W, REG_DWORD_OFFSET },
{ OP_W, REG_DWORD_OFFSET + 2 }, {0}}; /* imul */
static op_implicit_list_t list_lahf[] =
/* 9F : LAHF : r EFLAGS, w AH */
{{ OP_R, REG_FLAGS_INDEX },
{ OP_W, REG_BYTE_OFFSET + 4 }, {0}}; /* lahf */
static op_implicit_list_t list_ldmxcsr[] =
/* 0F AE : LDMXCSR : w MXCSR SSE Control Status Reg */
{{ OP_W, REG_MXCSG_INDEX }, {0}}; /* ldmxcsr */
static op_implicit_list_t list_leave[] =
/* C9 : LEAVE : rw ESP, w EBP */
{{ OP_R | OP_W, REG_ESP_INDEX },
{ OP_W, REG_DWORD_OFFSET + 5 }, {0}}; /* leave */
static op_implicit_list_t list_lgdt[] =
/* 0F 01 : LGDT : w GDTR */
{{ OP_W, REG_GDTR_INDEX }, {0}}; /* lgdt */
static op_implicit_list_t list_lidt[] =
/* 0F 01 : LIDT : w IDTR */
{{ OP_W, REG_IDTR_INDEX }, {0}}; /* lidt */
static op_implicit_list_t list_lldt[] =
/* 0F 00 : LLDT : w LDTR */
{{ OP_W, REG_LDTR_INDEX }, {0}}; /* lldt */
static op_implicit_list_t list_lmsw[] =
/* 0F 01 : LMSW : w CR0 */
{{ OP_W, REG_CTRL_OFFSET }, {0}}; /* lmsw */
static op_implicit_list_t list_loop[] =
/* E0, E1, E2 : LOOP : rw ECX */
{{ OP_R | OP_W, REG_DWORD_OFFSET + 1 }, {0}};/* loop */
static op_implicit_list_t list_ltr[] =
/* 0F 00 : LTR : w Task Register */
{{ OP_W, REG_TR_INDEX }, {0}}; /* ltr */
static op_implicit_list_t list_pop[] =
/* 8F, 58, 1F, 07, 17, 0F A1, 0F A9 : POP : rw ESP */
/* FF, 50, 6A, 68, 0E, 16, 1E, 06, 0F A0, 0F A8 : PUSH : rw ESP */
{{ OP_R | OP_W, REG_ESP_INDEX }, {0}}; /* pop, push */
static op_implicit_list_t list_popad[] =
/* 61 : POPAD : rw esp, w edi esi ebp ebx edx ecx eax */
{{ OP_R | OP_W, REG_ESP_INDEX },
{ OP_W, REG_DWORD_OFFSET + 7 },
{ OP_W, REG_DWORD_OFFSET + 6 },
{ OP_W, REG_DWORD_OFFSET + 5 },
{ OP_W, REG_DWORD_OFFSET + 3 },
{ OP_W, REG_DWORD_OFFSET + 2 },
{ OP_W, REG_DWORD_OFFSET + 1 },
{ OP_W, REG_DWORD_OFFSET }, {0}}; /* popad */
static op_implicit_list_t list_popfd[] =
/* 9D : POPFD : rw esp, w eflags */
{{ OP_R | OP_W, REG_ESP_INDEX },
{ OP_W, REG_FLAGS_INDEX }, {0}}; /* popfd */
static op_implicit_list_t list_pushad[] =
/* FF, 50, 6A, 68, 0E, 16, 1E, 06, 0F A0, 0F A8 : PUSH : rw ESP */
/* 60 : PUSHAD : rw esp, r eax ecx edx ebx esp ebp esi edi */
{{ OP_R | OP_W, REG_ESP_INDEX },
{ OP_R, REG_DWORD_OFFSET },
{ OP_R, REG_DWORD_OFFSET + 1 },
{ OP_R, REG_DWORD_OFFSET + 2 },
{ OP_R, REG_DWORD_OFFSET + 3 },
{ OP_R, REG_DWORD_OFFSET + 5 },
{ OP_R, REG_DWORD_OFFSET + 6 },
{ OP_R, REG_DWORD_OFFSET + 7 }, {0}}; /* pushad */
static op_implicit_list_t list_pushfd[] =
/* 9C : PUSHFD : rw esp, r eflags */
{{ OP_R | OP_W, REG_ESP_INDEX },
{ OP_R, REG_FLAGS_INDEX }, {0}}; /* pushfd */
static op_implicit_list_t list_rdmsr[] =
/* 0F 32 : RDMSR : r ECX, w EDX, w EAX */
{{ OP_R, REG_DWORD_OFFSET + 1 },
{ OP_W, REG_DWORD_OFFSET + 2 },
{ OP_W, REG_DWORD_OFFSET }, {0}}; /* rdmsr */
static op_implicit_list_t list_rdpmc[] =
/* 0F 33 : RDPMC : r ECX, w EDX, w EAX */
{{ OP_R, REG_DWORD_OFFSET + 1 },
{ OP_W, REG_DWORD_OFFSET + 2 },
{ OP_W, REG_DWORD_OFFSET }, {0}}; /* rdpmc */
static op_implicit_list_t list_rdtsc[] =
/* 0F 31 : RDTSC : rw EDX, rw EAX */
{{ OP_R | OP_W, REG_DWORD_OFFSET + 2 },
{ OP_R | OP_W, REG_DWORD_OFFSET }, {0}}; /* rdtsc */
static op_implicit_list_t list_rep[] =
/* F3, F2 ... : REP : rw ECX */
{{ OP_R | OP_W, REG_DWORD_OFFSET + 1 }, {0}};/* rep */
static op_implicit_list_t list_rsm[] =
/* 0F AA : RSM : r CR4, r CR0 */
{{ OP_R, REG_CTRL_OFFSET + 4 },
{ OP_R, REG_CTRL_OFFSET }, {0}}; /* rsm */
static op_implicit_list_t list_sahf[] =
/* 9E : SAHF : r ah, rw eflags (set SF ZF AF PF CF) */
{{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sahf */
static op_implicit_list_t list_sgdt[] =
/* 0F : SGDT : r gdtr */
/* TODO: finish this! */
{{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sgdt */
static op_implicit_list_t list_sidt[] =
/* 0F : SIDT : r idtr */
/* TODO: finish this! */
{{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sidt */
static op_implicit_list_t list_sldt[] =
/* 0F : SLDT : r ldtr */
/* TODO: finish this! */
{{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sldt */
static op_implicit_list_t list_smsw[] =
/* 0F : SMSW : r CR0 */
/* TODO: finish this! */
{{ OP_R, REG_DWORD_OFFSET }, {0}}; /* smsw */
static op_implicit_list_t list_stmxcsr[] =
/* 0F AE : STMXCSR : r MXCSR */
/* TODO: finish this! */
{{ OP_R, REG_DWORD_OFFSET }, {0}}; /* stmxcsr */
static op_implicit_list_t list_str[] =
/* 0F 00 : STR : r TR (task register) */
/* TODO: finish this! */
{{ OP_R, REG_DWORD_OFFSET }, {0}}; /* str */
static op_implicit_list_t list_sysenter[] =
/* 0F 34 : SYSENTER : w cs, w eip, w ss, w esp, r CR0, w eflags
* r sysenter_cs_msr, sysenter_esp_msr, sysenter_eip_msr */
/* TODO: finish this! */
{{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sysenter */
static op_implicit_list_t list_sysexit[] =
/* 0F 35 : SYSEXIT : r edx, r ecx, w cs, w eip, w ss, w esp
* r sysenter_cs_msr */
/* TODO: finish this! */
{{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sysexit */
static op_implicit_list_t list_wrmsr[] =
/* 0F 30 : WRMST : r edx, r eax, r ecx */
/* TODO: finish this! */
{{ OP_R, REG_DWORD_OFFSET }, {0}}; /* wrmsr */
static op_implicit_list_t list_xlat[] =
/* D7 : XLAT : rw al r ebx (ptr) */
/* TODO: finish this! */
{{ OP_R, REG_DWORD_OFFSET }, {0}}; /* xlat */
/* TODO:
* monitor 0f 01 c8 eax OP_R ecx OP_R edx OP_R
* mwait 0f 01 c9 eax OP_R ecx OP_R
*/
static op_implicit_list_t list_monitor[] =
{{ OP_R, REG_DWORD_OFFSET }, {0}}; /* monitor */
static op_implicit_list_t list_mwait[] =
{{ OP_R, REG_DWORD_OFFSET }, {0}}; /* mwait */
op_implicit_list_t *op_implicit_list[] = {
/* This is a list of implicit operands which are read/written by
* various x86 instructions. Note that modifications to the stack
* register are mentioned here, but that additional information on
* the effect an instruction has on the stack is contained in the
* x86_insn_t 'stack_mod' and 'stack_mod_val' fields. Use of the
* eflags register, i.e. setting, clearing, and testing flags, is
* not recorded here but rather in the flags_set and flags_tested
* fields of the x86_insn_t.*/
NULL,
list_aaa, list_aad, list_call, list_cbw, /* 1 - 4 */
list_cwde, list_clts, list_cmpxchg, list_cmpxchgb, /* 5 - 8 */
list_cmpxchg8b, list_cpuid, list_cwd, list_daa, /* 9 - 12 */
list_idiv, list_div, list_enter, list_f2xm1, /* 13 - 16 */
list_fcom, list_fpatan, list_fprem, list_faddp, /* 17 - 20 */
list_fucompp, list_imul, list_mul, list_lahf, /* 21 - 24 */
list_ldmxcsr, list_leave, list_lgdt, list_lidt, /* 25 - 28 */
list_lldt, list_lmsw, list_loop, list_ltr, /* 29 - 32 */
list_pop, list_popad, list_popfd, list_pushad, /* 33 - 36 */
list_pushfd, list_rdmsr, list_rdpmc, list_rdtsc, /* 37 - 40 */
/* NOTE: 'REP' is a hack since it is a prefix: if its position
* in the table changes, then change IDX_IMPLICIT_REP in the .h */
list_rep, list_rsm, list_sahf, list_sgdt, /* 41 - 44 */
list_sidt, list_sldt, list_smsw, list_stmxcsr, /* 45 - 48 */
list_str, list_sysenter, list_sysexit, list_wrmsr, /* 49 - 52 */
list_xlat, list_monitor, list_mwait, /* 53 - 55*/
NULL /* end of list */
};
#define LAST_IMPL_IDX 55
static void handle_impl_reg( x86_op_t *op, uint32_t val ) {
x86_reg_t *reg = &op->data.reg;
op->type = op_register;
ia32_handle_register( reg, (unsigned int) val );
switch (reg->size) {
case 1:
op->datatype = op_byte; break;
case 2:
op->datatype = op_word; break;
case 4:
op->datatype = op_dword; break;
case 8:
op->datatype = op_qword; break;
case 10:
op->datatype = op_extreal; break;
case 16:
op->datatype = op_dqword; break;
}
return;
}
/* 'impl_idx' is the value from the opcode table: between 1 and LAST_IMPL_IDX */
/* returns number of operands added */
unsigned int ia32_insn_implicit_ops( x86_insn_t *insn, unsigned int impl_idx ) {
op_implicit_list_t *list;
x86_op_t *op;
unsigned int num = 0;
if (! impl_idx || impl_idx > LAST_IMPL_IDX ) {
return 0;
}
for ( list = op_implicit_list[impl_idx]; list->type; list++, num++ ) {
enum x86_op_access access = (enum x86_op_access) OP_PERM(list->type);
enum x86_op_flags flags = (enum x86_op_flags) (OP_FLAGS(list->type) >> 12);
op = NULL;
/* In some cases (MUL), EAX is an implicit operand hardcoded in
* the instruction without being explicitly listed in assembly.
* For this situation, find the hardcoded operand and add the
* implied flag rather than adding a new implicit operand. */
x86_oplist_t * existing;
if (ia32_true_register_id(list->operand) == REG_DWORD_OFFSET) {
for ( existing = insn->operands; existing; existing = existing->next ) {
if (existing->op.type == op_register &&
existing->op.data.reg.id == list->operand) {
op = &existing->op;
break;
}
}
}
if (!op) {
op = x86_operand_new( insn );
/* all implicit operands are registers */
handle_impl_reg( op, list->operand );
/* decrement the 'explicit count' incremented by default in
* x86_operand_new */
insn->explicit_count = insn->explicit_count -1;
}
if (!op) {
return num; /* gah! return early */
}
op->access |= access;
op->flags |= flags;
op->flags |= op_implied;
}
return num;
}

13
libdisasm/ia32_implicit.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef IA32_IMPLICIT_H
#define IA32_IMPLICIT_H
#include "libdis.h"
/* OK, this is a hack to deal with prefixes having implicit operands...
* thought I had removed all the old hackishness ;( */
#define IDX_IMPLICIT_REP 41 /* change this if the table changes! */
unsigned int ia32_insn_implicit_ops( x86_insn_t *insn, unsigned int impl_idx );
#endif

625
libdisasm/ia32_insn.c Normal file
View File

@ -0,0 +1,625 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "qword.h"
#include "ia32_insn.h"
#include "ia32_opcode_tables.h"
#include "ia32_reg.h"
#include "ia32_operand.h"
#include "ia32_implicit.h"
#include "ia32_settings.h"
#include "libdis.h"
extern ia32_table_desc_t ia32_tables[];
extern ia32_settings_t ia32_settings;
#define IS_SP( op ) (op->type == op_register && \
(op->data.reg.id == REG_ESP_INDEX || \
op->data.reg.alias == REG_ESP_INDEX) )
#define IS_IMM( op ) (op->type == op_immediate )
#ifdef WIN32
# define INLINE
#else
# define INLINE inline
#endif
/* for calculating stack modification based on an operand */
static INLINE int32_t long_from_operand( x86_op_t *op ) {
if (! IS_IMM(op) ) {
return 0L;
}
switch ( op->datatype ) {
case op_byte:
return (int32_t) op->data.sbyte;
case op_word:
return (int32_t) op->data.sword;
case op_qword:
return (int32_t) op->data.sqword;
case op_dword:
return op->data.sdword;
default:
/* these are not used in stack insn */
break;
}
return 0L;
}
/* determine what this insn does to the stack */
static void ia32_stack_mod(x86_insn_t *insn) {
x86_op_t *dest, *src = NULL;
if (! insn || ! insn->operands ) {
return;
}
dest = &insn->operands->op;
if ( dest ) {
src = &insn->operands->next->op;
}
insn->stack_mod = 0;
insn->stack_mod_val = 0;
switch ( insn->type ) {
case insn_call:
case insn_callcc:
insn->stack_mod = 1;
insn->stack_mod_val = insn->addr_size * -1;
break;
case insn_push:
insn->stack_mod = 1;
insn->stack_mod_val = insn->addr_size * -1;
break;
case insn_return:
insn->stack_mod = 1;
insn->stack_mod_val = insn->addr_size;
case insn_int: case insn_intcc:
case insn_iret:
break;
case insn_pop:
insn->stack_mod = 1;
if (! IS_SP( dest ) ) {
insn->stack_mod_val = insn->op_size;
} /* else we don't know the stack change in a pop esp */
break;
case insn_enter:
insn->stack_mod = 1;
insn->stack_mod_val = 0; /* TODO : FIX */
break;
case insn_leave:
insn->stack_mod = 1;
insn->stack_mod_val = 0; /* TODO : FIX */
break;
case insn_pushregs:
insn->stack_mod = 1;
insn->stack_mod_val = 0; /* TODO : FIX */
break;
case insn_popregs:
insn->stack_mod = 1;
insn->stack_mod_val = 0; /* TODO : FIX */
break;
case insn_pushflags:
insn->stack_mod = 1;
insn->stack_mod_val = 0; /* TODO : FIX */
break;
case insn_popflags:
insn->stack_mod = 1;
insn->stack_mod_val = 0; /* TODO : FIX */
break;
case insn_add:
if ( IS_SP( dest ) ) {
insn->stack_mod = 1;
insn->stack_mod_val = long_from_operand( src );
}
break;
case insn_sub:
if ( IS_SP( dest ) ) {
insn->stack_mod = 1;
insn->stack_mod_val = long_from_operand( src );
insn->stack_mod_val *= -1;
}
break;
case insn_inc:
if ( IS_SP( dest ) ) {
insn->stack_mod = 1;
insn->stack_mod_val = 1;
}
break;
case insn_dec:
if ( IS_SP( dest ) ) {
insn->stack_mod = 1;
insn->stack_mod_val = 1;
}
break;
case insn_mov: case insn_movcc:
case insn_xchg: case insn_xchgcc:
case insn_mul: case insn_div:
case insn_shl: case insn_shr:
case insn_rol: case insn_ror:
case insn_and: case insn_or:
case insn_not: case insn_neg:
case insn_xor:
if ( IS_SP( dest ) ) {
insn->stack_mod = 1;
}
break;
default:
break;
}
if (! strcmp("enter", insn->mnemonic) ) {
insn->stack_mod = 1;
} else if (! strcmp("leave", insn->mnemonic) ) {
insn->stack_mod = 1;
}
/* for mov, etc we return 0 -- unknown stack mod */
return;
}
/* get the cpu details for this insn from cpu flags int */
static void ia32_handle_cpu( x86_insn_t *insn, unsigned int cpu ) {
insn->cpu = (enum x86_insn_cpu) CPU_MODEL(cpu);
insn->isa = (enum x86_insn_isa) (ISA_SUBSET(cpu)) >> 16;
return;
}
/* handle mnemonic type and group */
static void ia32_handle_mnemtype(x86_insn_t *insn, unsigned int mnemtype) {
unsigned int type = mnemtype & ~INS_FLAG_MASK;
insn->group = (enum x86_insn_group) (INS_GROUP(type)) >> 12;
insn->type = (enum x86_insn_type) INS_TYPE(type);
return;
}
static void ia32_handle_notes(x86_insn_t *insn, unsigned int notes) {
insn->note = (enum x86_insn_note) notes;
return;
}
static void ia32_handle_eflags( x86_insn_t *insn, unsigned int eflags) {
unsigned int flags;
/* handle flags effected */
flags = INS_FLAGS_TEST(eflags);
/* handle weird OR cases */
/* these are either JLE (ZF | SF<>OF) or JBE (CF | ZF) */
if (flags & INS_TEST_OR) {
flags &= ~INS_TEST_OR;
if ( flags & INS_TEST_ZERO ) {
flags &= ~INS_TEST_ZERO;
if ( flags & INS_TEST_CARRY ) {
flags &= ~INS_TEST_CARRY ;
flags |= (int)insn_carry_or_zero_set;
} else if ( flags & INS_TEST_SFNEOF ) {
flags &= ~INS_TEST_SFNEOF;
flags |= (int)insn_zero_set_or_sign_ne_oflow;
}
}
}
insn->flags_tested = (enum x86_flag_status) flags;
insn->flags_set = (enum x86_flag_status) INS_FLAGS_SET(eflags) >> 16;
return;
}
static void ia32_handle_prefix( x86_insn_t *insn, unsigned int prefixes ) {
insn->prefix = (enum x86_insn_prefix) prefixes & PREFIX_MASK; // >> 20;
if (! (insn->prefix & PREFIX_PRINT_MASK) ) {
/* no printable prefixes */
insn->prefix = insn_no_prefix;
}
/* concat all prefix strings */
if ( (unsigned int)insn->prefix & PREFIX_LOCK ) {
strncat(insn->prefix_string, "lock ", 32 -
strlen(insn->prefix_string));
}
if ( (unsigned int)insn->prefix & PREFIX_REPNZ ) {
strncat(insn->prefix_string, "repnz ", 32 -
strlen(insn->prefix_string));
} else if ( (unsigned int)insn->prefix & PREFIX_REPZ ) {
strncat(insn->prefix_string, "repz ", 32 -
strlen(insn->prefix_string));
}
return;
}
static void reg_32_to_16( x86_op_t *op, x86_insn_t *insn, void *arg ) {
/* if this is a 32-bit register and it is a general register ... */
if ( op->type == op_register && op->data.reg.size == 4 &&
(op->data.reg.type & reg_gen) ) {
/* WORD registers are 8 indices off from DWORD registers */
ia32_handle_register( &(op->data.reg),
op->data.reg.id + 8 );
}
}
static void handle_insn_metadata( x86_insn_t *insn, ia32_insn_t *raw_insn ) {
ia32_handle_mnemtype( insn, raw_insn->mnem_flag );
ia32_handle_notes( insn, raw_insn->notes );
ia32_handle_eflags( insn, raw_insn->flags_effected );
ia32_handle_cpu( insn, raw_insn->cpu );
ia32_stack_mod( insn );
}
static size_t ia32_decode_insn( unsigned char *buf, size_t buf_len,
ia32_insn_t *raw_insn, x86_insn_t *insn,
unsigned int prefixes ) {
size_t size, op_size;
unsigned char modrm;
/* this should never happen, but just in case... */
if ( raw_insn->mnem_flag == INS_INVALID ) {
return 0;
}
if (ia32_settings.options & opt_16_bit) {
insn->op_size = ( prefixes & PREFIX_OP_SIZE ) ? 4 : 2;
insn->addr_size = ( prefixes & PREFIX_ADDR_SIZE ) ? 4 : 2;
} else {
insn->op_size = ( prefixes & PREFIX_OP_SIZE ) ? 2 : 4;
insn->addr_size = ( prefixes & PREFIX_ADDR_SIZE ) ? 2 : 4;
}
/* ++++ 1. Copy mnemonic and mnemonic-flags to CODE struct */
if ((ia32_settings.options & opt_att_mnemonics) && raw_insn->mnemonic_att[0]) {
strncpy( insn->mnemonic, raw_insn->mnemonic_att, 16 );
}
else {
strncpy( insn->mnemonic, raw_insn->mnemonic, 16 );
}
ia32_handle_prefix( insn, prefixes );
handle_insn_metadata( insn, raw_insn );
/* prefetch the next byte in case it is a modr/m byte -- saves
* worrying about whether the 'mod/rm' operand or the 'reg' operand
* occurs first */
modrm = GET_BYTE( buf, buf_len );
/* ++++ 2. Decode Explicit Operands */
/* Intel uses up to 3 explicit operands in its instructions;
* the first is 'dest', the second is 'src', and the third
* is an additional source value (usually an immediate value,
* e.g. in the MUL instructions). These three explicit operands
* are encoded in the opcode tables, even if they are not used
* by the instruction. Additional implicit operands are stored
* in a supplemental table and are handled later. */
op_size = ia32_decode_operand( buf, buf_len, insn, raw_insn->dest,
raw_insn->dest_flag, prefixes, modrm );
/* advance buffer, increase size if necessary */
buf += op_size;
buf_len -= op_size;
size = op_size;
op_size = ia32_decode_operand( buf, buf_len, insn, raw_insn->src,
raw_insn->src_flag, prefixes, modrm );
buf += op_size;
buf_len -= op_size;
size += op_size;
op_size = ia32_decode_operand( buf, buf_len, insn, raw_insn->aux,
raw_insn->aux_flag, prefixes, modrm );
size += op_size;
/* ++++ 3. Decode Implicit Operands */
/* apply implicit operands */
ia32_insn_implicit_ops( insn, raw_insn->implicit_ops );
/* we have one small inelegant hack here, to deal with
* the two prefixes that have implicit operands. If Intel
* adds more, we'll change the algorithm to suit :) */
if ( (prefixes & PREFIX_REPZ) || (prefixes & PREFIX_REPNZ) ) {
ia32_insn_implicit_ops( insn, IDX_IMPLICIT_REP );
}
/* 16-bit hack: foreach operand, if 32-bit reg, make 16-bit reg */
if ( insn->op_size == 2 ) {
x86_operand_foreach( insn, reg_32_to_16, NULL, op_any );
}
return size;
}
/* convenience routine */
#define USES_MOD_RM(flag) \
(flag == ADDRMETH_E || flag == ADDRMETH_M || flag == ADDRMETH_Q || \
flag == ADDRMETH_W || flag == ADDRMETH_R)
static int uses_modrm_flag( unsigned int flag ) {
unsigned int meth;
if ( flag == ARG_NONE ) {
return 0;
}
meth = (flag & ADDRMETH_MASK);
if ( USES_MOD_RM(meth) ) {
return 1;
}
return 0;
}
/* This routine performs the actual byte-by-byte opcode table lookup.
* Originally it was pretty simple: get a byte, adjust it to a proper
* index into the table, then check the table row at that index to
* determine what to do next. But is anything that simple with Intel?
* This is now a huge, convoluted mess, mostly of bitter comments. */
/* buf: pointer to next byte to read from stream
* buf_len: length of buf
* table: index of table to use for lookups
* raw_insn: output pointer that receives opcode definition
* prefixes: output integer that is encoded with prefixes in insn
* returns : number of bytes consumed from stream during lookup */
size_t ia32_table_lookup( unsigned char *buf, size_t buf_len,
unsigned int table, ia32_insn_t **raw_insn,
unsigned int *prefixes ) {
unsigned char *next, op = buf[0]; /* byte value -- 'opcode' */
size_t size = 1, sub_size = 0, next_len;
ia32_table_desc_t *table_desc;
unsigned int subtable, prefix = 0, recurse_table = 0;
table_desc = &ia32_tables[table];
op = GET_BYTE( buf, buf_len );
if ( table_desc->type == tbl_fpu && op > table_desc->maxlim) {
/* one of the fucking FPU tables out of the 00-BH range */
/* OK,. this is a bit of a hack -- the proper way would
* have been to use subtables in the 00-BF FPU opcode tables,
* but that is rather wasteful of space... */
table_desc = &ia32_tables[table +1];
}
/* PERFORM TABLE LOOKUP */
/* ModR/M trick: shift extension bits into lowest bits of byte */
/* Note: non-ModR/M tables have a shift value of 0 */
op >>= table_desc->shift;
/* ModR/M trick: mask out high bits to turn extension into an index */
/* Note: non-ModR/M tables have a mask value of 0xFF */
op &= table_desc->mask;
/* Sparse table trick: check that byte is <= max value */
/* Note: full (256-entry) tables have a maxlim of 155 */
if ( op > table_desc->maxlim ) {
/* this is a partial table, truncated at the tail,
and op is out of range! */
return INVALID_INSN;
}
/* Sparse table trick: check that byte is >= min value */
/* Note: full (256-entry) tables have a minlim of 0 */
if ( table_desc->minlim > op ) {
/* this is a partial table, truncated at the head,
and op is out of range! */
return INVALID_INSN;
}
/* adjust op to be an offset from table index 0 */
op -= table_desc->minlim;
/* Yay! 'op' is now fully adjusted to be an index into 'table' */
*raw_insn = &(table_desc->table[op]);
//printf("BYTE %X TABLE %d OP %X\n", buf[0], table, op );
if ( (*raw_insn)->mnem_flag & INS_FLAG_PREFIX ) {
prefix = (*raw_insn)->mnem_flag & PREFIX_MASK;
}
/* handle escape to a multibyte/coproc/extension/etc table */
/* NOTE: if insn is a prefix and has a subtable, then we
* only recurse if this is the first prefix byte --
* that is, if *prefixes is 0.
* NOTE also that suffix tables are handled later */
subtable = (*raw_insn)->table;
if ( subtable && ia32_tables[subtable].type != tbl_suffix &&
(! prefix || ! *prefixes) ) {
if ( ia32_tables[subtable].type == tbl_ext_ext ||
ia32_tables[subtable].type == tbl_fpu_ext ) {
/* opcode extension: reuse current byte in buffer */
next = buf;
next_len = buf_len;
} else {
/* "normal" opcode: advance to next byte in buffer */
if ( buf_len > 1 ) {
next = &buf[1];
next_len = buf_len - 1;
}
else {
// buffer is truncated
return INVALID_INSN;
}
}
/* we encountered a multibyte opcode: recurse using the
* table specified in the opcode definition */
sub_size = ia32_table_lookup( next, next_len, subtable,
raw_insn, prefixes );
/* SSE/prefix hack: if the original opcode def was a
* prefix that specified a subtable, and the subtable
* lookup returned a valid insn, then we have encountered
* an SSE opcode definition; otherwise, we pretend we
* never did the subtable lookup, and deal with the
* prefix normally later */
if ( prefix && ( sub_size == INVALID_INSN ||
INS_TYPE((*raw_insn)->mnem_flag) == INS_INVALID ) ) {
/* this is a prefix, not an SSE insn :
* lookup next byte in main table,
* subsize will be reset during the
* main table lookup */
recurse_table = 1;
} else {
/* this is either a subtable (two-byte) insn
* or an invalid insn: either way, set prefix
* to NULL and end the opcode lookup */
prefix = 0;
// short-circuit lookup on invalid insn
if (sub_size == INVALID_INSN) return INVALID_INSN;
}
} else if ( prefix ) {
recurse_table = 1;
}
/* by default, we assume that we have the opcode definition,
* and there is no need to recurse on the same table, but
* if we do then a prefix was encountered... */
if ( recurse_table ) {
/* this must have been a prefix: use the same table for
* lookup of the next byte */
sub_size = ia32_table_lookup( &buf[1], buf_len - 1, table,
raw_insn, prefixes );
// short-circuit lookup on invalid insn
if (sub_size == INVALID_INSN) return INVALID_INSN;
/* a bit of a hack for branch hints */
if ( prefix & BRANCH_HINT_MASK ) {
if ( INS_GROUP((*raw_insn)->mnem_flag) == INS_EXEC ) {
/* segment override prefixes are invalid for
* all branch instructions, so delete them */
prefix &= ~PREFIX_REG_MASK;
} else {
prefix &= ~BRANCH_HINT_MASK;
}
}
/* apply prefix to instruction */
/* TODO: implement something enforcing prefix groups */
(*prefixes) |= prefix;
}
/* if this lookup was in a ModR/M table, then an opcode byte is
* NOT consumed: subtract accordingly. NOTE that if none of the
* operands used the ModR/M, then we need to consume the byte
* here, but ONLY in the 'top-level' opcode extension table */
if ( table_desc->type == tbl_ext_ext ) {
/* extensions-to-extensions never consume a byte */
--size;
} else if ( (table_desc->type == tbl_extension ||
table_desc->type == tbl_fpu ||
table_desc->type == tbl_fpu_ext ) &&
/* extensions that have an operand encoded in ModR/M
* never consume a byte */
(uses_modrm_flag((*raw_insn)->dest_flag) ||
uses_modrm_flag((*raw_insn)->src_flag) ) ) {
--size;
}
size += sub_size;
return size;
}
static size_t handle_insn_suffix( unsigned char *buf, size_t buf_len,
ia32_insn_t *raw_insn, x86_insn_t * insn ) {
ia32_table_desc_t *table_desc;
ia32_insn_t *sfx_insn;
size_t size;
unsigned int prefixes = 0;
table_desc = &ia32_tables[raw_insn->table];
size = ia32_table_lookup( buf, buf_len, raw_insn->table, &sfx_insn,
&prefixes );
if (size == INVALID_INSN || sfx_insn->mnem_flag == INS_INVALID ) {
return 0;
}
strncpy( insn->mnemonic, sfx_insn->mnemonic, 16 );
handle_insn_metadata( insn, sfx_insn );
return 1;
}
/* invalid instructions are handled by returning 0 [error] from the
* function, setting the size of the insn to 1 byte, and copying
* the byte at the start of the invalid insn into the x86_insn_t.
* if the caller is saving the x86_insn_t for invalid instructions,
* instead of discarding them, this will maintain a consistent
* address space in the x86_insn_ts */
/* this function is called by the controlling disassembler, so its name and
* calling convention cannot be changed */
/* buf points to the loc of the current opcode (start of the
* instruction) in the instruction stream. The instruction
* stream is assumed to be a buffer of bytes read directly
* from the file for the purpose of disassembly; a mem-mapped
* file is ideal for * this.
* insn points to a code structure to be filled by instr_decode
* returns the size of the decoded instruction in bytes */
size_t ia32_disasm_addr( unsigned char * buf, size_t buf_len,
x86_insn_t *insn ) {
ia32_insn_t *raw_insn = NULL;
unsigned int prefixes = 0;
size_t size, sfx_size;
if ( (ia32_settings.options & opt_ignore_nulls) && buf_len > 3 &&
!buf[0] && !buf[1] && !buf[2] && !buf[3]) {
/* IF IGNORE_NULLS is set AND
* first 4 bytes in the intruction stream are NULL
* THEN return 0 (END_OF_DISASSEMBLY) */
/* TODO: set errno */
MAKE_INVALID( insn, buf );
return 0; /* 4 00 bytes in a row? This isn't code! */
}
/* Perform recursive table lookup starting with main table (0) */
size = ia32_table_lookup(buf, buf_len, idx_Main, &raw_insn, &prefixes);
if ( size == INVALID_INSN || size > buf_len || raw_insn->mnem_flag == INS_INVALID ) {
MAKE_INVALID( insn, buf );
/* TODO: set errno */
return 0;
}
/* We now have the opcode itself figured out: we can decode
* the rest of the instruction. */
size += ia32_decode_insn( &buf[size], buf_len - size, raw_insn, insn,
prefixes );
if ( raw_insn->mnem_flag & INS_FLAG_SUFFIX ) {
/* AMD 3DNow! suffix -- get proper operand type here */
sfx_size = handle_insn_suffix( &buf[size], buf_len - size,
raw_insn, insn );
if (! sfx_size ) {
/* TODO: set errno */
MAKE_INVALID( insn, buf );
return 0;
}
size += sfx_size;
}
if (! size ) {
/* invalid insn */
MAKE_INVALID( insn, buf );
return 0;
}
insn->size = size;
return size; /* return size of instruction in bytes */
}

506
libdisasm/ia32_insn.h Normal file
View File

@ -0,0 +1,506 @@
#ifndef IA32_INSN_H
#define IA32_INSN_H
/* this file contains the structure of opcode definitions and the
* constants they use */
#include <sys/types.h>
#include "libdis.h"
#define GET_BYTE( buf, buf_len ) buf_len ? *buf : 0
#define OP_SIZE_16 1
#define OP_SIZE_32 2
#define ADDR_SIZE_16 4
#define ADDR_SIZE_32 8
#define MAX_INSTRUCTION_SIZE 20
/* invalid instructions are handled by returning 0 [error] from the
* function, setting the size of the insn to 1 byte, and copying
* the byte at the start of the invalid insn into the x86_insn_t.
* if the caller is saving the x86_insn_t for invalid instructions,
* instead of discarding them, this will maintain a consistent
* address space in the x86_insn_ts */
#define INVALID_INSN ((size_t) -1) /* return value for invalid insn */
#define MAKE_INVALID( i, buf ) \
strcpy( i->mnemonic, "invalid" ); \
x86_oplist_free( i ); \
i->size = 1; \
i->group = insn_none; \
i->type = insn_invalid; \
memcpy( i->bytes, buf, 1 );
size_t ia32_disasm_addr( unsigned char * buf, size_t buf_len,
x86_insn_t *insn);
/* --------------------------------------------------------- Table Lookup */
/* IA32 Instruction defintion for ia32_opcodes.c */
typedef struct {
unsigned int table; /* escape to this sub-table */
unsigned int mnem_flag; /* Flags referring to mnemonic */
unsigned int notes; /* Notes for this instruction */
unsigned int dest_flag, src_flag, aux_flag; /* and for specific operands */
unsigned int cpu; /* minimumCPU [AND with clocks?? */
char mnemonic[16]; /* buffers for building instruction */
char mnemonic_att[16]; /* at&t style mnemonic name */
int32_t dest;
int32_t src;
int32_t aux;
unsigned int flags_effected;
unsigned int implicit_ops; /* implicit operands */
} ia32_insn_t;
/* --------------------------------------------------------- Prefixes */
/* Prefix Flags */
/* Prefixes, same order as in the manual */
/* had to reverse the values of the first three as they were entered into
* libdis.h incorrectly. */
#define PREFIX_LOCK 0x0004
#define PREFIX_REPNZ 0x0002
#define PREFIX_REPZ 0x0001
#define PREFIX_OP_SIZE 0x0010
#define PREFIX_ADDR_SIZE 0x0020
#define PREFIX_CS 0x0100
#define PREFIX_SS 0x0200
#define PREFIX_DS 0x0300
#define PREFIX_ES 0x0400
#define PREFIX_FS 0x0500
#define PREFIX_GS 0x0600
#define PREFIX_TAKEN 0x1000 /* branch taken */
#define PREFIX_NOTTAKEN 0x2000 /* branch not taken */
#define PREFIX_REG_MASK 0x0F00
#define BRANCH_HINT_MASK 0x3000
#define PREFIX_PRINT_MASK 0x000F /* printable prefixes */
#define PREFIX_MASK 0xFFFF
/* ---------------------------------------------------------- CPU Type */
#define cpu_8086 0x0001
#define cpu_80286 0x0002
#define cpu_80386 0x0003
#define cpu_80387 0x0004 /* originally these were a co-proc */
#define cpu_80486 0x0005
#define cpu_PENTIUM 0x0006
#define cpu_PENTPRO 0x0007
#define cpu_PENTIUM2 0x0008
#define cpu_PENTIUM3 0x0009
#define cpu_PENTIUM4 0x000A
#define cpu_K6 0x0010
#define cpu_K7 0x0020
#define cpu_ATHLON 0x0030
#define CPU_MODEL_MASK 0xFFFF
#define CPU_MODEL(cpu) (cpu & CPU_MODEL_MASK)
/* intel instruction subsets */
#define isa_GP 0x10000 /* General Purpose Instructions */
#define isa_FPU 0x20000 /* FPU instructions */
#define isa_FPUMGT 0x30000 /* FPU/SIMD Management */
#define isa_MMX 0x40000 /* MMX */
#define isa_SSE1 0x50000 /* SSE */
#define isa_SSE2 0x60000 /* SSE 2 */
#define isa_SSE3 0x70000 /* SSE 3 */
#define isa_3DNOW 0x80000 /* AMD 3d Now */
#define isa_SYS 0x90000 /* System Instructions */
#define ISA_SUBSET_MASK 0xFFFF0000
#define ISA_SUBSET(isa) (isa & ISA_SUBSET_MASK)
/* ------------------------------------------------------ Operand Decoding */
#define ARG_NONE 0
/* Using a mask allows us to store info such as OP_SIGNED in the
* operand flags field */
#define OPFLAGS_MASK 0x0000FFFF
/* Operand Addressing Methods, per intel manual */
#define ADDRMETH_MASK 0x00FF0000
/* note: for instructions with implied operands, use no ADDRMETH */
#define ADDRMETH_A 0x00010000
#define ADDRMETH_C 0x00020000
#define ADDRMETH_D 0x00030000
#define ADDRMETH_E 0x00040000
#define ADDRMETH_F 0x00050000
#define ADDRMETH_G 0x00060000
#define ADDRMETH_I 0x00070000
#define ADDRMETH_J 0x00080000
#define ADDRMETH_M 0x00090000
#define ADDRMETH_O 0x000A0000
#define ADDRMETH_P 0x000B0000
#define ADDRMETH_Q 0x000C0000
#define ADDRMETH_R 0x000D0000
#define ADDRMETH_S 0x000E0000
#define ADDRMETH_T 0x000F0000
#define ADDRMETH_V 0x00100000
#define ADDRMETH_W 0x00110000
#define ADDRMETH_X 0x00120000
#define ADDRMETH_Y 0x00130000
#define ADDRMETH_RR 0x00140000 /* gen reg hard-coded in opcode */
#define ADDRMETH_RS 0x00150000 /* seg reg hard-coded in opcode */
#define ADDRMETH_RT 0x00160000 /* test reg hard-coded in opcode */
#define ADDRMETH_RF 0x00170000 /* fpu reg hard-coded in opcode */
#define ADDRMETH_II 0x00180000 /* immediate hard-coded in opcode */
#define ADDRMETH_PP 0x00190000 /* mm reg ONLY in modr/m field */
#define ADDRMETH_VV 0x001A0000 /* xmm reg ONLY in mod/rm field */
/* Operand Types, per intel manual */
#define OPTYPE_MASK 0xFF000000
#define OPTYPE_a 0x01000000 /* BOUND: h:h or w:w */
#define OPTYPE_b 0x02000000 /* byte */
#define OPTYPE_c 0x03000000 /* byte or word */
#define OPTYPE_d 0x04000000 /* word */
#define OPTYPE_dq 0x05000000 /* qword */
#define OPTYPE_p 0x06000000 /* 16:16 or 16:32 pointer */
#define OPTYPE_pi 0x07000000 /* dword MMX reg */
#define OPTYPE_ps 0x08000000 /* 128-bit single fp */
#define OPTYPE_q 0x09000000 /* dword */
#define OPTYPE_s 0x0A000000 /* 6-byte descriptor */
#define OPTYPE_ss 0x0B000000 /* scalar of 128-bit single fp */
#define OPTYPE_si 0x0C000000 /* word general register */
#define OPTYPE_v 0x0D000000 /* hword or word */
#define OPTYPE_w 0x0E000000 /* hword */
#define OPTYPE_m 0x0F000000 /* to handle LEA */
#define OPTYPE_none 0xFF000000 /* no valid operand size, INVLPG */
/* custom ones for FPU instructions */
#define OPTYPE_fs 0x10000000 /* pointer to single-real*/
#define OPTYPE_fd 0x20000000 /* pointer to double real */
#define OPTYPE_fe 0x30000000 /* pointer to extended real */
#define OPTYPE_fb 0x40000000 /* pointer to packed BCD */
#define OPTYPE_fv 0x50000000 /* pointer to FPU env: 14|28-bytes */
#define OPTYPE_ft 0x60000000 /* pointer to FPU state: 94|108-bytes */
#define OPTYPE_fx 0x70000000 /* pointer to FPU regs: 512 bites */
#define OPTYPE_fp 0x80000000 /* general fpu register: dbl ext */
/* SSE2 operand types */
#define OPTYPE_sd 0x90000000 /* scalar of 128-bit double fp */
#define OPTYPE_pd 0xA0000000 /* 128-bit double fp */
/* ---------------------------------------------- Opcode Table Descriptions */
/* the table type describes how to handle byte/size increments before
* and after lookup. Some tables re-use the current byte, others
* consume a byte only if the ModR/M encodes no operands, etc */
enum ia32_tbl_type_id {
tbl_opcode = 0, /* standard opcode table: no surprises */
tbl_prefix, /* Prefix Override, e.g. 66/F2/F3 */
tbl_suffix, /* 3D Now style */
tbl_extension, /* ModR/M extension: 00-FF -> 00-07 */
tbl_ext_ext, /* extension of modr/m using R/M field */
tbl_fpu, /* fpu table: 00-BF -> 00-0F */
tbl_fpu_ext /* fpu extension : C0-FF -> 00-1F */
};
/* How it works:
* Bytes are 'consumed' if the next table lookup requires that the byte
* pointer be advanced in the instruction stream. 'Does not consume' means
* that, when the lookup function recurses, the same byte it re-used in the
* new table. It also means that size is not decremented, for example when
* a ModR/M byte is used. Note that tbl_extension (ModR/M) instructions that
* do not increase the size of an insn with their operands have a forced
3 size increase in the lookup algo. Weird, yes, confusing, yes, welcome
* to the Intel ISA. Another note: tbl_prefix is used as an override, so an
* empty insn in a prefix table causes the instruction in the original table
* to be used, rather than an invalid insn being generated.
* tbl_opcode uses current byte and consumes it
* tbl_prefix uses current byte but does not consume it
* tbl_suffix uses and consumes last byte in insn
* tbl_extension uses current byte but does not consume it
* tbl_ext_ext uses current byte but does not consume it
* tbl_fpu uses current byte and consumes it
* tbl_fpu_ext uses current byte but does not consume it
*/
/* Convenience struct for opcode tables : these will be stored in a
* 'table of tables' so we can use a table index instead of a pointer */
typedef struct { /* Assembly instruction tables */
ia32_insn_t *table; /* Pointer to table of instruction encodings */
enum ia32_tbl_type_id type;
unsigned char shift; /* amount to shift modrm byte */
unsigned char mask; /* bit mask for look up */
unsigned char minlim,maxlim; /* limits on min/max entries. */
} ia32_table_desc_t;
/* ---------------------------------------------- 'Cooked' Operand Type Info */
/* Permissions: */
#define OP_R 0x001 /* operand is READ */
#define OP_W 0x002 /* operand is WRITTEN */
#define OP_RW 0x003 /* (OP_R|OP_W): convenience macro */
#define OP_X 0x004 /* operand is EXECUTED */
#define OP_PERM_MASK 0x0000007 /* perms are NOT mutually exclusive */
#define OP_PERM( type ) (type & OP_PERM_MASK)
/* Flags */
#define OP_SIGNED 0x010 /* operand is signed */
#define OP_FLAG_MASK 0x0F0 /* mods are NOT mutually exclusive */
#define OP_FLAGS( type ) (type & OP_FLAG_MASK)
#define OP_REG_MASK 0x0000FFFF /* lower WORD is register ID */
#define OP_REGTBL_MASK 0xFFFF0000 /* higher word is register type [gen/dbg] */
#define OP_REGID( type ) (type & OP_REG_MASK)
#define OP_REGTYPE( type ) (type & OP_REGTBL_MASK)
/* ------------------------------------------'Cooked' Instruction Type Info */
/* high-bit opcode types/insn meta-types */
#define INS_FLAG_PREFIX 0x10000000 /* insn is a prefix */
#define INS_FLAG_SUFFIX 0x20000000 /* followed by a suffix byte */
#define INS_FLAG_MASK 0xFF000000
/* insn notes */
#define INS_NOTE_RING0 0x00000001 /* insn is privileged */
#define INS_NOTE_SMM 0x00000002 /* Sys Mgt Mode only */
#define INS_NOTE_SERIAL 0x00000004 /* serializes */
#define INS_NOTE_NONSWAP 0x00000008 /* insn is not swapped in att format */ // could be separate field?
#define INS_NOTE_NOSUFFIX 0x00000010 /* insn has no size suffix in att format */ // could be separate field?
//#define INS_NOTE_NMI
#define INS_INVALID 0
/* instruction groups */
#define INS_EXEC 0x1000
#define INS_ARITH 0x2000
#define INS_LOGIC 0x3000
#define INS_STACK 0x4000
#define INS_COND 0x5000
#define INS_LOAD 0x6000
#define INS_ARRAY 0x7000
#define INS_BIT 0x8000
#define INS_FLAG 0x9000
#define INS_FPU 0xA000
#define INS_TRAPS 0xD000
#define INS_SYSTEM 0xE000
#define INS_OTHER 0xF000
#define INS_GROUP_MASK 0xF000
#define INS_GROUP( type ) ( type & INS_GROUP_MASK )
/* INS_EXEC group */
#define INS_BRANCH (INS_EXEC | 0x01) /* Unconditional branch */
#define INS_BRANCHCC (INS_EXEC | 0x02) /* Conditional branch */
#define INS_CALL (INS_EXEC | 0x03) /* Jump to subroutine */
#define INS_CALLCC (INS_EXEC | 0x04) /* Jump to subroutine */
#define INS_RET (INS_EXEC | 0x05) /* Return from subroutine */
/* INS_ARITH group */
#define INS_ADD (INS_ARITH | 0x01)
#define INS_SUB (INS_ARITH | 0x02)
#define INS_MUL (INS_ARITH | 0x03)
#define INS_DIV (INS_ARITH | 0x04)
#define INS_INC (INS_ARITH | 0x05) /* increment */
#define INS_DEC (INS_ARITH | 0x06) /* decrement */
#define INS_SHL (INS_ARITH | 0x07) /* shift right */
#define INS_SHR (INS_ARITH | 0x08) /* shift left */
#define INS_ROL (INS_ARITH | 0x09) /* rotate left */
#define INS_ROR (INS_ARITH | 0x0A) /* rotate right */
#define INS_MIN (INS_ARITH | 0x0B) /* min func */
#define INS_MAX (INS_ARITH | 0x0C) /* max func */
#define INS_AVG (INS_ARITH | 0x0D) /* avg func */
#define INS_FLR (INS_ARITH | 0x0E) /* floor func */
#define INS_CEIL (INS_ARITH | 0x0F) /* ceiling func */
/* INS_LOGIC group */
#define INS_AND (INS_LOGIC | 0x01)
#define INS_OR (INS_LOGIC | 0x02)
#define INS_XOR (INS_LOGIC | 0x03)
#define INS_NOT (INS_LOGIC | 0x04)
#define INS_NEG (INS_LOGIC | 0x05)
#define INS_NAND (INS_LOGIC | 0x06)
/* INS_STACK group */
#define INS_PUSH (INS_STACK | 0x01)
#define INS_POP (INS_STACK | 0x02)
#define INS_PUSHREGS (INS_STACK | 0x03) /* push register context */
#define INS_POPREGS (INS_STACK | 0x04) /* pop register context */
#define INS_PUSHFLAGS (INS_STACK | 0x05) /* push all flags */
#define INS_POPFLAGS (INS_STACK | 0x06) /* pop all flags */
#define INS_ENTER (INS_STACK | 0x07) /* enter stack frame */
#define INS_LEAVE (INS_STACK | 0x08) /* leave stack frame */
/* INS_COND group */
#define INS_TEST (INS_COND | 0x01)
#define INS_CMP (INS_COND | 0x02)
/* INS_LOAD group */
#define INS_MOV (INS_LOAD | 0x01)
#define INS_MOVCC (INS_LOAD | 0x02)
#define INS_XCHG (INS_LOAD | 0x03)
#define INS_XCHGCC (INS_LOAD | 0x04)
#define INS_CONV (INS_LOAD | 0x05) /* move and convert type */
/* INS_ARRAY group */
#define INS_STRCMP (INS_ARRAY | 0x01)
#define INS_STRLOAD (INS_ARRAY | 0x02)
#define INS_STRMOV (INS_ARRAY | 0x03)
#define INS_STRSTOR (INS_ARRAY | 0x04)
#define INS_XLAT (INS_ARRAY | 0x05)
/* INS_BIT group */
#define INS_BITTEST (INS_BIT | 0x01)
#define INS_BITSET (INS_BIT | 0x02)
#define INS_BITCLR (INS_BIT | 0x03)
/* INS_FLAG group */
#define INS_CLEARCF (INS_FLAG | 0x01) /* clear Carry flag */
#define INS_CLEARZF (INS_FLAG | 0x02) /* clear Zero flag */
#define INS_CLEAROF (INS_FLAG | 0x03) /* clear Overflow flag */
#define INS_CLEARDF (INS_FLAG | 0x04) /* clear Direction flag */
#define INS_CLEARSF (INS_FLAG | 0x05) /* clear Sign flag */
#define INS_CLEARPF (INS_FLAG | 0x06) /* clear Parity flag */
#define INS_SETCF (INS_FLAG | 0x07)
#define INS_SETZF (INS_FLAG | 0x08)
#define INS_SETOF (INS_FLAG | 0x09)
#define INS_SETDF (INS_FLAG | 0x0A)
#define INS_SETSF (INS_FLAG | 0x0B)
#define INS_SETPF (INS_FLAG | 0x0C)
#define INS_TOGCF (INS_FLAG | 0x10) /* toggle */
#define INS_TOGZF (INS_FLAG | 0x20)
#define INS_TOGOF (INS_FLAG | 0x30)
#define INS_TOGDF (INS_FLAG | 0x40)
#define INS_TOGSF (INS_FLAG | 0x50)
#define INS_TOGPF (INS_FLAG | 0x60)
/* INS_FPU */
#define INS_FMOV (INS_FPU | 0x1)
#define INS_FMOVCC (INS_FPU | 0x2)
#define INS_FNEG (INS_FPU | 0x3)
#define INS_FABS (INS_FPU | 0x4)
#define INS_FADD (INS_FPU | 0x5)
#define INS_FSUB (INS_FPU | 0x6)
#define INS_FMUL (INS_FPU | 0x7)
#define INS_FDIV (INS_FPU | 0x8)
#define INS_FSQRT (INS_FPU | 0x9)
#define INS_FCMP (INS_FPU | 0xA)
#define INS_FCOS (INS_FPU | 0xC) /* cosine */
#define INS_FLDPI (INS_FPU | 0xD) /* load pi */
#define INS_FLDZ (INS_FPU | 0xE) /* load 0 */
#define INS_FTAN (INS_FPU | 0xF) /* tanget */
#define INS_FSINE (INS_FPU | 0x10) /* sine */
#define INS_FSYS (INS_FPU | 0x20) /* misc */
/* INS_TRAP */
#define INS_TRAP (INS_TRAPS | 0x01) /* generate trap */
#define INS_TRAPCC (INS_TRAPS | 0x02) /* conditional trap gen */
#define INS_TRET (INS_TRAPS | 0x03) /* return from trap */
#define INS_BOUNDS (INS_TRAPS | 0x04) /* gen bounds trap */
#define INS_DEBUG (INS_TRAPS | 0x05) /* gen breakpoint trap */
#define INS_TRACE (INS_TRAPS | 0x06) /* gen single step trap */
#define INS_INVALIDOP (INS_TRAPS | 0x07) /* gen invalid insn */
#define INS_OFLOW (INS_TRAPS | 0x08) /* gen overflow trap */
#define INS_ICEBP (INS_TRAPS | 0x09) /* ICE breakpoint */
/* INS_SYSTEM */
#define INS_HALT (INS_SYSTEM | 0x01) /* halt machine */
#define INS_IN (INS_SYSTEM | 0x02) /* input form port */
#define INS_OUT (INS_SYSTEM | 0x03) /* output to port */
#define INS_CPUID (INS_SYSTEM | 0x04) /* identify cpu */
/* INS_OTHER */
#define INS_NOP (INS_OTHER | 0x01)
#define INS_BCDCONV (INS_OTHER | 0x02) /* convert to/from BCD */
#define INS_SZCONV (INS_OTHER | 0x03) /* convert size of operand */
#define INS_SALC (INS_OTHER | 0x04) /* set %al on carry */
#define INS_UNKNOWN (INS_OTHER | 0x05)
#define INS_TYPE_MASK 0xFFFF
#define INS_TYPE( type ) ( type & INS_TYPE_MASK )
/* flags effected by instruction */
#define INS_TEST_CARRY 0x01 /* carry */
#define INS_TEST_ZERO 0x02 /* zero/equal */
#define INS_TEST_OFLOW 0x04 /* overflow */
#define INS_TEST_DIR 0x08 /* direction */
#define INS_TEST_SIGN 0x10 /* negative */
#define INS_TEST_PARITY 0x20 /* parity */
#define INS_TEST_OR 0x40 /* used in jle */
#define INS_TEST_NCARRY 0x100 /* ! carry */
#define INS_TEST_NZERO 0x200 /* ! zero */
#define INS_TEST_NOFLOW 0x400 /* ! oflow */
#define INS_TEST_NDIR 0x800 /* ! dir */
#define INS_TEST_NSIGN 0x100 /* ! sign */
#define INS_TEST_NPARITY 0x2000 /* ! parity */
/* SF == OF */
#define INS_TEST_SFEQOF 0x4000
/* SF != OF */
#define INS_TEST_SFNEOF 0x8000
#define INS_TEST_ALL INS_TEST_CARRY | INS_TEST_ZERO | \
INS_TEST_OFLOW | INS_TEST_SIGN | \
INS_TEST_PARITY
#define INS_SET_CARRY 0x010000 /* carry */
#define INS_SET_ZERO 0x020000 /* zero/equal */
#define INS_SET_OFLOW 0x040000 /* overflow */
#define INS_SET_DIR 0x080000 /* direction */
#define INS_SET_SIGN 0x100000 /* negative */
#define INS_SET_PARITY 0x200000 /* parity */
#define INS_SET_NCARRY 0x1000000
#define INS_SET_NZERO 0x2000000
#define INS_SET_NOFLOW 0x4000000
#define INS_SET_NDIR 0x8000000
#define INS_SET_NSIGN 0x10000000
#define INS_SET_NPARITY 0x20000000
#define INS_SET_SFEQOF 0x40000000
#define INS_SET_SFNEOF 0x80000000
#define INS_SET_ALL INS_SET_CARRY | INS_SET_ZERO | \
INS_SET_OFLOW | INS_SET_SIGN | \
INS_SET_PARITY
#define INS_TEST_MASK 0x0000FFFF
#define INS_FLAGS_TEST(x) (x & INS_TEST_MASK)
#define INS_SET_MASK 0xFFFF0000
#define INS_FLAGS_SET(x) (x & INS_SET_MASK)
#if 0
/* TODO: actually start using these */
#define X86_PAIR_NP 1 /* not pairable; execs in U */
#define X86_PAIR_PU 2 /* pairable in U pipe */
#define X86_PAIR_PV 3 /* pairable in V pipe */
#define X86_PAIR_UV 4 /* pairable in UV pipe */
#define X86_PAIR_FX 5 /* pairable with FXCH */
#define X86_EXEC_PORT_0 1
#define X86_EXEC_PORT_1 2
#define X86_EXEC_PORT_2 4
#define X86_EXEC_PORT_3 8
#define X86_EXEC_PORT_4 16
#define X86_EXEC_UNITS
typedef struct { /* representation of an insn during decoding */
uint32_t flags; /* runtime settings */
/* instruction prefixes and other foolishness */
uint32_t prefix; /* encoding of prefix */
char prefix_str[16]; /* mnemonics for prefix */
uint32_t branch_hint; /* gah! */
unsigned int cpu_ver; /* TODO: cpu version */
unsigned int clocks; /* TODO: clock cycles: min/max */
unsigned char last_prefix;
/* runtime intruction decoding helpers */
unsigned char mode; /* 16, 32, 64 */
unsigned char gen_regs; /* offset of default general reg set */
unsigned char sz_operand; /* operand size for insn */
unsigned char sz_address; /* address size for insn */
unsigned char uops; /* uops per insn */
unsigned char pairing; /* np,pu,pv.lv */
unsigned char exec_unit;
unsigned char exec_port;
unsigned char latency;
} ia32_info_t;
#define MODE_32 0 /* default */
#define MODE_16 1
#define MODE_64 2
#endif
#endif

313
libdisasm/ia32_invariant.c Normal file
View File

@ -0,0 +1,313 @@
#include <stdlib.h>
#include <string.h>
#include "ia32_invariant.h"
#include "ia32_insn.h"
#include "ia32_settings.h"
extern ia32_table_desc_t *ia32_tables;
extern ia32_settings_t ia32_settings;
extern size_t ia32_table_lookup( unsigned char *buf, size_t buf_len,
unsigned int table, ia32_insn_t **raw_insn,
unsigned int *prefixes );
/* -------------------------------- ModR/M, SIB */
/* Convenience flags */
#define MODRM_EA 1 /* ModR/M is an effective addr */
#define MODRM_reg 2 /* ModR/M is a register */
/* ModR/M flags */
#define MODRM_RM_SIB 0x04 /* R/M == 100 */
#define MODRM_RM_NOREG 0x05 /* R/B == 101 */
/* if (MODRM.MOD_NODISP && MODRM.RM_NOREG) then just disp32 */
#define MODRM_MOD_NODISP 0x00 /* mod == 00 */
#define MODRM_MOD_DISP8 0x01 /* mod == 01 */
#define MODRM_MOD_DISP32 0x02 /* mod == 10 */
#define MODRM_MOD_NOEA 0x03 /* mod == 11 */
/* 16-bit modrm flags */
#define MOD16_MOD_NODISP 0
#define MOD16_MOD_DISP8 1
#define MOD16_MOD_DISP16 2
#define MOD16_MOD_REG 3
#define MOD16_RM_BXSI 0
#define MOD16_RM_BXDI 1
#define MOD16_RM_BPSI 2
#define MOD16_RM_BPDI 3
#define MOD16_RM_SI 4
#define MOD16_RM_DI 5
#define MOD16_RM_BP 6
#define MOD16_RM_BX 7
/* SIB flags */
#define SIB_INDEX_NONE 0x04
#define SIB_BASE_EBP 0x05
#define SIB_SCALE_NOBASE 0x00
/* Convenience struct for modR/M bitfield */
struct modRM_byte {
unsigned int mod : 2;
unsigned int reg : 3;
unsigned int rm : 3;
};
/* Convenience struct for SIB bitfield */
struct SIB_byte {
unsigned int scale : 2;
unsigned int index : 3;
unsigned int base : 3;
};
#ifdef WIN32
static void byte_decode(unsigned char b, struct modRM_byte *modrm) {
#else
static inline void byte_decode(unsigned char b, struct modRM_byte *modrm) {
#endif
/* generic bitfield-packing routine */
modrm->mod = b >> 6; /* top 2 bits */
modrm->reg = (b & 56) >> 3; /* middle 3 bits */
modrm->rm = b & 7; /* bottom 3 bits */
}
static int ia32_invariant_modrm( unsigned char *in, unsigned char *out,
unsigned int mode_16, x86_invariant_op_t *op) {
struct modRM_byte modrm;
struct SIB_byte sib;
unsigned char *c, *cin;
unsigned short *s;
unsigned int *i;
int size = 0; /* modrm byte is already counted */
byte_decode(*in, &modrm); /* get bitfields */
out[0] = in[0]; /* save modrm byte */
cin = &in[1];
c = &out[1];
s = (unsigned short *)&out[1];
i = (unsigned int *)&out[1];
op->type = op_expression;
op->flags |= op_pointer;
if ( ! mode_16 && modrm.rm == MODRM_RM_SIB &&
modrm.mod != MODRM_MOD_NOEA ) {
size ++;
byte_decode(*cin, (struct modRM_byte *)(void*)&sib);
out[1] = in[1]; /* save sib byte */
cin = &in[2];
c = &out[2];
s = (unsigned short *)&out[2];
i = (unsigned int *)&out[2];
if ( sib.base == SIB_BASE_EBP && ! modrm.mod ) {
/* disp 32 is variant! */
memset( i, X86_WILDCARD_BYTE, 4 );
size += 4;
}
}
if (! modrm.mod && modrm.rm == 101) {
if ( mode_16 ) { /* straight RVA in disp */
memset( s, X86_WILDCARD_BYTE, 2 );
size += 2;
} else {
memset( i, X86_WILDCARD_BYTE, 2 );
size += 4;
}
} else if (modrm.mod && modrm.mod < 3) {
if (modrm.mod == MODRM_MOD_DISP8) { /* offset in disp */
*c = *cin;
size += 1;
} else if ( mode_16 ) {
*s = (* ((unsigned short *) cin));
size += 2;
} else {
*i = (*((unsigned int *) cin));
size += 4;
}
} else if ( modrm.mod == 3 ) {
op->type = op_register;
op->flags &= ~op_pointer;
}
return (size);
}
static int ia32_decode_invariant( unsigned char *buf, size_t buf_len,
ia32_insn_t *t, unsigned char *out,
unsigned int prefixes, x86_invariant_t *inv) {
unsigned int addr_size, op_size, mode_16;
unsigned int op_flags[3] = { t->dest_flag, t->src_flag, t->aux_flag };
int x, type, bytes = 0, size = 0, modrm = 0;
/* set addressing mode */
if (ia32_settings.options & opt_16_bit) {
op_size = ( prefixes & PREFIX_OP_SIZE ) ? 4 : 2;
addr_size = ( prefixes & PREFIX_ADDR_SIZE ) ? 4 : 2;
mode_16 = ( prefixes & PREFIX_ADDR_SIZE ) ? 0 : 1;
} else {
op_size = ( prefixes & PREFIX_OP_SIZE ) ? 2 : 4;
addr_size = ( prefixes & PREFIX_ADDR_SIZE ) ? 2 : 4;
mode_16 = ( prefixes & PREFIX_ADDR_SIZE ) ? 1 : 0;
}
for (x = 0; x < 3; x++) {
inv->operands[x].access = (enum x86_op_access)
OP_PERM(op_flags[x]);
inv->operands[x].flags = (enum x86_op_flags)
(OP_FLAGS(op_flags[x]) >> 12);
switch (op_flags[x] & OPTYPE_MASK) {
case OPTYPE_c:
size = (op_size == 4) ? 2 : 1;
break;
case OPTYPE_a: case OPTYPE_v:
size = (op_size == 4) ? 4 : 2;
break;
case OPTYPE_p:
size = (op_size == 4) ? 6 : 4;
break;
case OPTYPE_b:
size = 1;
break;
case OPTYPE_w:
size = 2;
break;
case OPTYPE_d: case OPTYPE_fs: case OPTYPE_fd:
case OPTYPE_fe: case OPTYPE_fb: case OPTYPE_fv:
case OPTYPE_si: case OPTYPE_fx:
size = 4;
break;
case OPTYPE_s:
size = 6;
break;
case OPTYPE_q: case OPTYPE_pi:
size = 8;
break;
case OPTYPE_dq: case OPTYPE_ps: case OPTYPE_ss:
case OPTYPE_pd: case OPTYPE_sd:
size = 16;
break;
case OPTYPE_m:
size = (addr_size == 4) ? 4 : 2;
break;
default:
break;
}
type = op_flags[x] & ADDRMETH_MASK;
switch (type) {
case ADDRMETH_E: case ADDRMETH_M: case ADDRMETH_Q:
case ADDRMETH_R: case ADDRMETH_W:
modrm = 1;
bytes += ia32_invariant_modrm( buf, out,
mode_16, &inv->operands[x]);
break;
case ADDRMETH_C: case ADDRMETH_D: case ADDRMETH_G:
case ADDRMETH_P: case ADDRMETH_S: case ADDRMETH_T:
case ADDRMETH_V:
inv->operands[x].type = op_register;
modrm = 1;
break;
case ADDRMETH_A: case ADDRMETH_O:
/* pad with xF4's */
memset( &out[bytes + modrm], X86_WILDCARD_BYTE,
size );
bytes += size;
inv->operands[x].type = op_offset;
if ( type == ADDRMETH_O ) {
inv->operands[x].flags |= op_signed |
op_pointer;
}
break;
case ADDRMETH_I: case ADDRMETH_J:
/* grab imm value */
if ((op_flags[x] & OPTYPE_MASK) == OPTYPE_v) {
/* assume this is an address */
memset( &out[bytes + modrm],
X86_WILDCARD_BYTE, size );
} else {
memcpy( &out[bytes + modrm],
&buf[bytes + modrm], size );
}
bytes += size;
if ( type == ADDRMETH_J ) {
if ( size == 1 ) {
inv->operands[x].type =
op_relative_near;
} else {
inv->operands[x].type =
op_relative_far;
}
inv->operands[x].flags |= op_signed;
} else {
inv->operands[x].type = op_immediate;
}
break;
case ADDRMETH_F:
inv->operands[x].type = op_register;
break;
case ADDRMETH_X:
inv->operands[x].flags |= op_signed |
op_pointer | op_ds_seg | op_string;
break;
case ADDRMETH_Y:
inv->operands[x].flags |= op_signed |
op_pointer | op_es_seg | op_string;
break;
case ADDRMETH_RR:
inv->operands[x].type = op_register;
break;
case ADDRMETH_II:
inv->operands[x].type = op_immediate;
break;
default:
inv->operands[x].type = op_unused;
break;
}
}
return (bytes + modrm);
}
size_t ia32_disasm_invariant( unsigned char * buf, size_t buf_len,
x86_invariant_t *inv ) {
ia32_insn_t *raw_insn = NULL;
unsigned int prefixes;
unsigned int type;
size_t size;
/* Perform recursive table lookup starting with main table (0) */
size = ia32_table_lookup( buf, buf_len, 0, &raw_insn, &prefixes );
if ( size == INVALID_INSN || size > buf_len ) {
/* TODO: set errno */
return 0;
}
/* copy opcode bytes to buffer */
memcpy( inv->bytes, buf, size );
/* set mnemonic type and group */
type = raw_insn->mnem_flag & ~INS_FLAG_MASK;
inv->group = (enum x86_insn_group) (INS_GROUP(type)) >> 12;
inv->type = (enum x86_insn_type) INS_TYPE(type);
/* handle operands */
size += ia32_decode_invariant( buf + size, buf_len - size, raw_insn,
&buf[size - 1], prefixes, inv );
inv->size = size;
return size; /* return size of instruction in bytes */
}
size_t ia32_disasm_size( unsigned char *buf, size_t buf_len ) {
x86_invariant_t inv = { {0} };
return( ia32_disasm_invariant( buf, buf_len, &inv ) );
}

View File

@ -0,0 +1,11 @@
#ifndef IA32_INVARIANT_H
#define IA32_INVARIANT_H
#include "libdis.h"
size_t ia32_disasm_invariant( unsigned char *buf, size_t buf_len,
x86_invariant_t *inv);
size_t ia32_disasm_size( unsigned char *buf, size_t buf_len );
#endif

310
libdisasm/ia32_modrm.c Normal file
View File

@ -0,0 +1,310 @@
#include "ia32_modrm.h"
#include "ia32_reg.h"
#include "x86_imm.h"
/* NOTE: when decoding ModR/M and SIB, we have to add 1 to all register
* values obtained from decoding the ModR/M or SIB byte, since they
* are encoded with eAX = 0 and the tables in ia32_reg.c use eAX = 1.
* ADDENDUM: this is only the case when the register value is used
* directly as an index into the register table, not when it is added to
* a genregs offset. */
/* -------------------------------- ModR/M, SIB */
/* ModR/M flags */
#define MODRM_RM_SIB 0x04 /* R/M == 100 */
#define MODRM_RM_NOREG 0x05 /* R/B == 101 */
/* if (MODRM.MOD_NODISP && MODRM.RM_NOREG) then just disp32 */
#define MODRM_MOD_NODISP 0x00 /* mod == 00 */
#define MODRM_MOD_DISP8 0x01 /* mod == 01 */
#define MODRM_MOD_DISP32 0x02 /* mod == 10 */
#define MODRM_MOD_NOEA 0x03 /* mod == 11 */
/* 16-bit modrm flags */
#define MOD16_MOD_NODISP 0
#define MOD16_MOD_DISP8 1
#define MOD16_MOD_DISP16 2
#define MOD16_MOD_REG 3
#define MOD16_RM_BXSI 0
#define MOD16_RM_BXDI 1
#define MOD16_RM_BPSI 2
#define MOD16_RM_BPDI 3
#define MOD16_RM_SI 4
#define MOD16_RM_DI 5
#define MOD16_RM_BP 6
#define MOD16_RM_BX 7
/* SIB flags */
#define SIB_INDEX_NONE 0x04
#define SIB_BASE_EBP 0x05
#define SIB_SCALE_NOBASE 0x00
/* Convenience struct for modR/M bitfield */
struct modRM_byte {
unsigned int mod : 2;
unsigned int reg : 3;
unsigned int rm : 3;
};
/* Convenience struct for SIB bitfield */
struct SIB_byte {
unsigned int scale : 2;
unsigned int index : 3;
unsigned int base : 3;
};
#if 0
int modrm_rm[] = {0,1,2,3,MODRM_RM_SIB,MODRM_MOD_DISP32,6,7};
int modrm_reg[] = {0, 1, 2, 3, 4, 5, 6, 7};
int modrm_mod[] = {0, MODRM_MOD_DISP8, MODRM_MOD_DISP32, MODRM_MOD_NOEA};
int sib_scl[] = {0, 2, 4, 8};
int sib_idx[] = {0, 1, 2, 3, SIB_INDEX_NONE, 5, 6, 7 };
int sib_bas[] = {0, 1, 2, 3, 4, SIB_SCALE_NOBASE, 6, 7 };
#endif
/* this is needed to replace x86_imm_signsized() which does not sign-extend
* to dest */
static unsigned int imm32_signsized( unsigned char *buf, size_t buf_len,
int32_t *dest, unsigned int size ) {
if ( size > buf_len ) {
return 0;
}
switch (size) {
case 1:
*dest = *((signed char *) buf);
break;
case 2:
*dest = *((signed short *) buf);
break;
case 4:
default:
*dest = *((signed int *) buf);
break;
}
return size;
}
static void byte_decode(unsigned char b, struct modRM_byte *modrm) {
/* generic bitfield-packing routine */
modrm->mod = b >> 6; /* top 2 bits */
modrm->reg = (b & 56) >> 3; /* middle 3 bits */
modrm->rm = b & 7; /* bottom 3 bits */
}
static size_t sib_decode( unsigned char *buf, size_t buf_len, x86_ea_t *ea,
unsigned int mod ) {
/* set Address Expression fields (scale, index, base, disp)
* according to the contents of the SIB byte.
* b points to the SIB byte in the instruction-stream buffer; the
* byte after b[0] is therefore the byte after the SIB
* returns number of bytes 'used', including the SIB byte */
size_t size = 1; /* start at 1 for SIB byte */
struct SIB_byte sib;
if ( buf_len < 1 ) {
return 0;
}
byte_decode( *buf, (struct modRM_byte *)(void*)&sib ); /* get bit-fields */
if ( sib.base == SIB_BASE_EBP && ! mod ) { /* if base == 101 (ebp) */
/* IF BASE == EBP, deal with exception */
/* IF (ModR/M did not create a Disp */
/* ... create a 32-bit Displacement */
imm32_signsized( &buf[1], buf_len, &ea->disp, sizeof(int32_t));
ea->disp_size = sizeof(int32_t);
ea->disp_sign = (ea->disp < 0) ? 1 : 0;
size += 4; /* add sizeof disp to count */
} else {
/* ELSE BASE refers to a General Register */
ia32_handle_register( &ea->base, sib.base + 1 );
}
/* set scale to 1, 2, 4, 8 */
ea->scale = 1 << sib.scale;
if (sib.index != SIB_INDEX_NONE) {
/* IF INDEX is not 'ESP' (100) */
ia32_handle_register( &ea->index, sib.index + 1 );
}
return (size); /* return number of bytes processed */
}
static size_t modrm_decode16( unsigned char *buf, unsigned int buf_len,
x86_op_t *op, struct modRM_byte *modrm ) {
/* 16-bit mode: hackish, but not as hackish as 32-bit mode ;) */
size_t size = 1; /* # of bytes decoded [1 for modR/M byte] */
x86_ea_t * ea = &op->data.expression;
switch( modrm->rm ) {
case MOD16_RM_BXSI:
ia32_handle_register(&ea->base, REG_WORD_OFFSET + 3);
ia32_handle_register(&ea->index, REG_WORD_OFFSET + 6);
break;
case MOD16_RM_BXDI:
ia32_handle_register(&ea->base, REG_WORD_OFFSET + 3);
ia32_handle_register(&ea->index, REG_WORD_OFFSET + 7);
case MOD16_RM_BPSI:
op->flags |= op_ss_seg;
ia32_handle_register(&ea->base, REG_WORD_OFFSET + 5);
ia32_handle_register(&ea->index, REG_WORD_OFFSET + 6);
break;
case MOD16_RM_BPDI:
op->flags |= op_ss_seg;
ia32_handle_register(&ea->base, REG_WORD_OFFSET + 5);
ia32_handle_register(&ea->index, REG_WORD_OFFSET + 7);
break;
case MOD16_RM_SI:
ia32_handle_register(&ea->base, REG_WORD_OFFSET + 6);
break;
case MOD16_RM_DI:
ia32_handle_register(&ea->base, REG_WORD_OFFSET + 7);
break;
case MOD16_RM_BP:
if ( modrm->mod != MOD16_MOD_NODISP ) {
op->flags |= op_ss_seg;
ia32_handle_register(&ea->base,
REG_WORD_OFFSET + 5);
}
break;
case MOD16_RM_BX:
ia32_handle_register(&ea->base, REG_WORD_OFFSET + 3);
break;
}
/* move to byte after ModR/M */
++buf;
--buf_len;
if ( modrm->mod == MOD16_MOD_DISP8 ) {
imm32_signsized( buf, buf_len, &ea->disp, sizeof(char) );
ea->disp_sign = (ea->disp < 0) ? 1 : 0;
ea->disp_size = sizeof(char);
size += sizeof(char);
} else if ( modrm->mod == MOD16_MOD_DISP16 ) {
imm32_signsized( buf, buf_len, &ea->disp, sizeof(short) );
ea->disp_sign = (ea->disp < 0) ? 1 : 0;
ea->disp_size = sizeof(short);
size += sizeof(short);
}
return size;
}
/* TODO : Mark index modes
Use addressing mode flags to imply arrays (index), structure (disp),
two-dimensional arrays [disp + index], classes [ea reg], and so on.
*/
size_t ia32_modrm_decode( unsigned char *buf, unsigned int buf_len,
x86_op_t *op, x86_insn_t *insn, size_t gen_regs ) {
/* create address expression and/or fill operand based on value of
* ModR/M byte. Calls sib_decode as appropriate.
* flags specifies whether Reg or mod+R/M fields are being decoded
* returns the number of bytes in the instruction, including modR/M */
struct modRM_byte modrm;
size_t size = 1; /* # of bytes decoded [1 for modR/M byte] */
x86_ea_t * ea;
byte_decode(*buf, &modrm); /* get bitfields */
/* first, handle the case where the mod field is a register only */
if ( modrm.mod == MODRM_MOD_NOEA ) {
op->type = op_register;
ia32_handle_register(&op->data.reg, modrm.rm + gen_regs);
/* increase insn size by 1 for modrm byte */
return 1;
}
/* then deal with cases where there is an effective address */
ea = &op->data.expression;
op->type = op_expression;
op->flags |= op_pointer;
if ( insn->addr_size == 2 ) {
/* gah! 16 bit mode! */
return modrm_decode16( buf, buf_len, op, &modrm);
}
/* move to byte after ModR/M */
++buf;
--buf_len;
if (modrm.mod == MODRM_MOD_NODISP) { /* if mod == 00 */
/* IF MOD == No displacement, just Indirect Register */
if (modrm.rm == MODRM_RM_NOREG) { /* if r/m == 101 */
/* IF RM == No Register, just Displacement */
/* This is an Intel Moronic Exception TM */
imm32_signsized( buf, buf_len, &ea->disp,
sizeof(int32_t) );
ea->disp_size = sizeof(int32_t);
ea->disp_sign = (ea->disp < 0) ? 1 : 0;
size += 4; /* add sizeof disp to count */
} else if (modrm.rm == MODRM_RM_SIB) { /* if r/m == 100 */
/* ELSE IF an SIB byte is present */
/* TODO: check for 0 retval */
size += sib_decode( buf, buf_len, ea, modrm.mod);
/* move to byte after SIB for displacement */
++buf;
--buf_len;
} else { /* modR/M specifies base register */
/* ELSE RM encodes a general register */
ia32_handle_register( &ea->base, modrm.rm + 1 );
}
} else { /* mod is 01 or 10 */
if (modrm.rm == MODRM_RM_SIB) { /* rm == 100 */
/* IF base is an AddrExpr specified by an SIB byte */
/* TODO: check for 0 retval */
size += sib_decode( buf, buf_len, ea, modrm.mod);
/* move to byte after SIB for displacement */
++buf;
--buf_len;
} else {
/* ELSE base is a general register */
ia32_handle_register( &ea->base, modrm.rm + 1 );
}
/* ELSE mod + r/m specify a disp##[base] or disp##(SIB) */
if (modrm.mod == MODRM_MOD_DISP8) { /* mod == 01 */
/* If this is an 8-bit displacement */
imm32_signsized( buf, buf_len, &ea->disp,
sizeof(char));
ea->disp_size = sizeof(char);
ea->disp_sign = (ea->disp < 0) ? 1 : 0;
size += 1; /* add sizeof disp to count */
} else {
/* Displacement is dependent on address size */
imm32_signsized( buf, buf_len, &ea->disp,
insn->addr_size);
ea->disp_size = insn->addr_size;
ea->disp_sign = (ea->disp < 0) ? 1 : 0;
size += 4;
}
}
return size; /* number of bytes found in instruction */
}
void ia32_reg_decode( unsigned char byte, x86_op_t *op, size_t gen_regs ) {
struct modRM_byte modrm;
byte_decode( byte, &modrm ); /* get bitfields */
/* set operand to register ID */
op->type = op_register;
ia32_handle_register(&op->data.reg, modrm.reg + gen_regs);
return;
}

13
libdisasm/ia32_modrm.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef IA32_MODRM_H
#define IA32_MODRM_H
#include "libdis.h"
#include "ia32_insn.h"
size_t ia32_modrm_decode( unsigned char *buf, unsigned int buf_len,
x86_op_t *op, x86_insn_t *insn,
size_t gen_regs );
void ia32_reg_decode( unsigned char byte, x86_op_t *op, size_t gen_regs );
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,57 @@
#define idx_Main 0
#define idx_66 1
#define idx_F2 2
#define idx_F3 3
#define idx_0F 4
#define idx_660F 5
#define idx_F20F 6
#define idx_F30F 7
#define idx_0F00 8
#define idx_0F01 9
#define idx_0F0111 10
#define idx_0F12 11
#define idx_0F16 12
#define idx_0F18 13
#define idx_0F71 14
#define idx_660F71 15
#define idx_0F72 16
#define idx_660F72 17
#define idx_0F73 18
#define idx_660F73 19
#define idx_0FAE 20
#define idx_0FBA 21
#define idx_0FC7 22
#define idx_0FB9 23
#define idx_C6 24
#define idx_C7 25
#define idx_80 26
#define idx_81 27
#define idx_82 28
#define idx_83 29
#define idx_C0 30
#define idx_C1 31
#define idx_D0 32
#define idx_D1 33
#define idx_D2 34
#define idx_D3 35
#define idx_F6 36
#define idx_F7 37
#define idx_FE 38
#define idx_FF 39
#define idx_D8 40
#define idx_D8C0 41
#define idx_D9 42
#define idx_D9C0 43
#define idx_DA 44
#define idx_DAC0 45
#define idx_DB 46
#define idx_DBC0 47
#define idx_DC 48
#define idx_DCC0 49
#define idx_DD 50
#define idx_DDC0 51
#define idx_DE 52
#define idx_DEC0 53
#define idx_DF 54
#define idx_DFC0 55
#define idx_0F0F 56

425
libdisasm/ia32_operand.c Normal file
View File

@ -0,0 +1,425 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libdis.h"
#include "ia32_insn.h"
#include "ia32_operand.h"
#include "ia32_modrm.h"
#include "ia32_reg.h"
#include "x86_imm.h"
#include "x86_operand_list.h"
/* apply segment override to memory operand in insn */
static void apply_seg( x86_op_t *op, unsigned int prefixes ) {
if (! prefixes ) return;
/* apply overrides from prefix */
switch ( prefixes & PREFIX_REG_MASK ) {
case PREFIX_CS:
op->flags |= op_cs_seg; break;
case PREFIX_SS:
op->flags |= op_ss_seg; break;
case PREFIX_DS:
op->flags |= op_ds_seg; break;
case PREFIX_ES:
op->flags |= op_es_seg; break;
case PREFIX_FS:
op->flags |= op_fs_seg; break;
case PREFIX_GS:
op->flags |= op_gs_seg; break;
}
return;
}
static size_t decode_operand_value( unsigned char *buf, size_t buf_len,
x86_op_t *op, x86_insn_t *insn,
unsigned int addr_meth, size_t op_size,
unsigned int op_value, unsigned char modrm,
size_t gen_regs ) {
size_t size = 0;
/* ++ Do Operand Addressing Method / Decode operand ++ */
switch (addr_meth) {
/* This sets the operand Size based on the Intel Opcode Map
* (Vol 2, Appendix A). Letter encodings are from section
* A.1.1, 'Codes for Addressing Method' */
/* ---------------------- Addressing Method -------------- */
/* Note that decoding mod ModR/M operand adjusts the size of
* the instruction, but decoding the reg operand does not.
* This should not cause any problems, as every 'reg' operand
* has an associated 'mod' operand.
* Goddamn-Intel-Note:
* Some Intel addressing methods [M, R] specify that modR/M
* byte may only refer to a memory address/may only refer to
* a register -- however Intel provides no clues on what to do
* if, say, the modR/M for an M opcode decodes to a register
* rather than a memory address ... returning 0 is out of the
* question, as this would be an Immediate or a RelOffset, so
* instead these modR/Ms are decoded with total disregard to
* the M, R constraints. */
/* MODRM -- mod operand. sets size to at least 1! */
case ADDRMETH_E: /* ModR/M present, Gen reg or memory */
size = ia32_modrm_decode( buf, buf_len, op, insn,
gen_regs );
break;
case ADDRMETH_M: /* ModR/M only refers to memory */
size = ia32_modrm_decode( buf, buf_len, op, insn,
gen_regs );
break;
case ADDRMETH_Q: /* ModR/M present, MMX or Memory */
size = ia32_modrm_decode( buf, buf_len, op, insn,
REG_MMX_OFFSET );
break;
case ADDRMETH_R: /* ModR/M mod == gen reg */
size = ia32_modrm_decode( buf, buf_len, op, insn,
gen_regs );
break;
case ADDRMETH_W: /* ModR/M present, mem or SIMD reg */
size = ia32_modrm_decode( buf, buf_len, op, insn,
REG_SIMD_OFFSET );
break;
/* MODRM -- reg operand. does not effect size! */
case ADDRMETH_C: /* ModR/M reg == control reg */
ia32_reg_decode( modrm, op, REG_CTRL_OFFSET );
break;
case ADDRMETH_D: /* ModR/M reg == debug reg */
ia32_reg_decode( modrm, op, REG_DEBUG_OFFSET );
break;
case ADDRMETH_G: /* ModR/M reg == gen-purpose reg */
ia32_reg_decode( modrm, op, gen_regs );
break;
case ADDRMETH_P: /* ModR/M reg == qword MMX reg */
ia32_reg_decode( modrm, op, REG_MMX_OFFSET );
break;
case ADDRMETH_S: /* ModR/M reg == segment reg */
ia32_reg_decode( modrm, op, REG_SEG_OFFSET );
break;
case ADDRMETH_T: /* ModR/M reg == test reg */
ia32_reg_decode( modrm, op, REG_TEST_OFFSET );
break;
case ADDRMETH_V: /* ModR/M reg == SIMD reg */
ia32_reg_decode( modrm, op, REG_SIMD_OFFSET );
break;
/* No MODRM : note these set operand type explicitly */
case ADDRMETH_A: /* No modR/M -- direct addr */
op->type = op_absolute;
/* segment:offset address used in far calls */
x86_imm_sized( buf, buf_len,
&op->data.absolute.segment, 2 );
if ( insn->addr_size == 4 ) {
x86_imm_sized( buf, buf_len,
&op->data.absolute.offset.off32, 4 );
size = 6;
} else {
x86_imm_sized( buf, buf_len,
&op->data.absolute.offset.off16, 2 );
size = 4;
}
break;
case ADDRMETH_I: /* Immediate val */
op->type = op_immediate;
/* if it ever becomes legal to have imm as dest and
* there is a src ModR/M operand, we are screwed! */
if ( op->flags & op_signed ) {
x86_imm_signsized(buf, buf_len, &op->data.byte,
op_size);
} else {
x86_imm_sized(buf, buf_len, &op->data.byte,
op_size);
}
size = op_size;
break;
case ADDRMETH_J: /* Rel offset to add to IP [jmp] */
/* this fills op->data.near_offset or
op->data.far_offset depending on the size of
the operand */
op->flags |= op_signed;
if ( op_size == 1 ) {
/* one-byte near offset */
op->type = op_relative_near;
x86_imm_signsized(buf, buf_len,
&op->data.relative_near, 1);
} else {
/* far offset...is this truly signed? */
op->type = op_relative_far;
x86_imm_signsized(buf, buf_len,
&op->data.relative_far, op_size );
}
size = op_size;
break;
case ADDRMETH_O: /* No ModR/M; op is word/dword offset */
/* NOTE: these are actually RVAs not offsets to seg!! */
/* note bene: 'O' ADDR_METH uses addr_size to
determine operand size */
op->type = op_offset;
op->flags |= op_pointer;
x86_imm_sized( buf, buf_len, &op->data.offset,
insn->addr_size );
size = insn->addr_size;
break;
/* Hard-coded: these are specified in the insn definition */
case ADDRMETH_F: /* EFLAGS register */
op->type = op_register;
op->flags |= op_hardcode;
ia32_handle_register( &op->data.reg, REG_FLAGS_INDEX );
break;
case ADDRMETH_X: /* Memory addressed by DS:SI [string] */
op->type = op_expression;
op->flags |= op_hardcode;
op->flags |= op_ds_seg | op_pointer | op_string;
ia32_handle_register( &op->data.expression.base,
REG_DWORD_OFFSET + 6 );
break;
case ADDRMETH_Y: /* Memory addressed by ES:DI [string] */
op->type = op_expression;
op->flags |= op_hardcode;
op->flags |= op_es_seg | op_pointer | op_string;
ia32_handle_register( &op->data.expression.base,
REG_DWORD_OFFSET + 7 );
break;
case ADDRMETH_RR: /* Gen Register hard-coded in opcode */
op->type = op_register;
op->flags |= op_hardcode;
ia32_handle_register( &op->data.reg,
op_value + gen_regs );
break;
case ADDRMETH_RS: /* Seg Register hard-coded in opcode */
op->type = op_register;
op->flags |= op_hardcode;
ia32_handle_register( &op->data.reg,
op_value + REG_SEG_OFFSET );
break;
case ADDRMETH_RF: /* FPU Register hard-coded in opcode */
op->type = op_register;
op->flags |= op_hardcode;
ia32_handle_register( &op->data.reg,
op_value + REG_FPU_OFFSET );
break;
case ADDRMETH_RT: /* TST Register hard-coded in opcode */
op->type = op_register;
op->flags |= op_hardcode;
ia32_handle_register( &op->data.reg,
op_value + REG_TEST_OFFSET );
break;
case ADDRMETH_II: /* Immediate hard-coded in opcode */
op->type = op_immediate;
op->data.dword = op_value;
op->flags |= op_hardcode;
break;
case 0: /* Operand is not used */
default:
/* ignore -- operand not used in this insn */
op->type = op_unused; /* this shouldn't happen! */
break;
}
return size;
}
static size_t decode_operand_size( unsigned int op_type, x86_insn_t *insn,
x86_op_t *op ){
size_t size;
/* ++ Do Operand Type ++ */
switch (op_type) {
/* This sets the operand Size based on the Intel Opcode Map
* (Vol 2, Appendix A). Letter encodings are from section
* A.1.2, 'Codes for Operand Type' */
/* NOTE: in this routines, 'size' refers to the size
* of the operand in the raw (encoded) instruction;
* 'datatype' stores the actual size and datatype
* of the operand */
/* ------------------------ Operand Type ----------------- */
case OPTYPE_c: /* byte or word [op size attr] */
size = (insn->op_size == 4) ? 2 : 1;
op->datatype = (size == 4) ? op_word : op_byte;
break;
case OPTYPE_a: /* 2 word or 2 dword [op size attr] */
/* pointer to a 16:16 or 32:32 BOUNDS operand */
size = (insn->op_size == 4) ? 8 : 4;
op->datatype = (size == 4) ? op_bounds32 : op_bounds16;
break;
case OPTYPE_v: /* word or dword [op size attr] */
size = (insn->op_size == 4) ? 4 : 2;
op->datatype = (size == 4) ? op_dword : op_word;
break;
case OPTYPE_p: /* 32/48-bit ptr [op size attr] */
/* technically these flags are not accurate: the
* value s a 16:16 pointer or a 16:32 pointer, where
* the first '16' is a segment */
size = (insn->addr_size == 4) ? 6 : 4;
op->datatype = (size == 4) ? op_descr32 : op_descr16;
break;
case OPTYPE_b: /* byte, ignore op-size */
size = 1;
op->datatype = op_byte;
break;
case OPTYPE_w: /* word, ignore op-size */
size = 2;
op->datatype = op_word;
break;
case OPTYPE_d: /* dword , ignore op-size */
size = 4;
op->datatype = op_dword;
break;
case OPTYPE_s: /* 6-byte psuedo-descriptor */
/* ptr to 6-byte value which is 32:16 in 32-bit
* mode, or 8:24:16 in 16-bit mode. The high byte
* is ignored in 16-bit mode. */
size = 6;
op->datatype = (insn->addr_size == 4) ?
op_pdescr32 : op_pdescr16;
break;
case OPTYPE_q: /* qword, ignore op-size */
size = 8;
op->datatype = op_qword;
break;
case OPTYPE_dq: /* d-qword, ignore op-size */
size = 16;
op->datatype = op_dqword;
break;
case OPTYPE_ps: /* 128-bit FP data */
size = 16;
/* really this is 4 packed SP FP values */
op->datatype = op_ssimd;
break;
case OPTYPE_pd: /* 128-bit FP data */
size = 16;
/* really this is 2 packed DP FP values */
op->datatype = op_dsimd;
break;
case OPTYPE_ss: /* Scalar elem of 128-bit FP data */
size = 16;
/* this only looks at the low dword (4 bytes)
* of the xmmm register passed as a param.
* This is a 16-byte register where only 4 bytes
* are used in the insn. Painful, ain't it? */
op->datatype = op_sssimd;
break;
case OPTYPE_sd: /* Scalar elem of 128-bit FP data */
size = 16;
/* this only looks at the low qword (8 bytes)
* of the xmmm register passed as a param.
* This is a 16-byte register where only 8 bytes
* are used in the insn. Painful, again... */
op->datatype = op_sdsimd;
break;
case OPTYPE_pi: /* qword mmx register */
size = 8;
op->datatype = op_qword;
break;
case OPTYPE_si: /* dword integer register */
size = 4;
op->datatype = op_dword;
break;
case OPTYPE_fs: /* single-real */
size = 4;
op->datatype = op_sreal;
break;
case OPTYPE_fd: /* double real */
size = 8;
op->datatype = op_dreal;
break;
case OPTYPE_fe: /* extended real */
size = 10;
op->datatype = op_extreal;
break;
case OPTYPE_fb: /* packed BCD */
size = 10;
op->datatype = op_bcd;
break;
case OPTYPE_fv: /* pointer to FPU env: 14 or 28-bytes */
size = (insn->addr_size == 4)? 28 : 14;
op->datatype = (size == 28)? op_fpuenv32: op_fpuenv16;
break;
case OPTYPE_ft: /* pointer to FPU env: 94 or 108 bytes */
size = (insn->addr_size == 4)? 108 : 94;
op->datatype = (size == 108)?
op_fpustate32: op_fpustate16;
break;
case OPTYPE_fx: /* 512-byte register stack */
size = 512;
op->datatype = op_fpregset;
break;
case OPTYPE_fp: /* floating point register */
size = 10; /* double extended precision */
op->datatype = op_fpreg;
break;
case OPTYPE_m: /* fake operand type used for "lea Gv, M" */
size = insn->addr_size;
op->datatype = (size == 4) ? op_dword : op_word;
break;
case OPTYPE_none: /* handle weird instructions that have no encoding but use a dword datatype, like invlpg */
size = 0;
op->datatype = op_none;
break;
case 0:
default:
size = insn->op_size;
op->datatype = (size == 4) ? op_dword : op_word;
break;
}
return size;
}
size_t ia32_decode_operand( unsigned char *buf, size_t buf_len,
x86_insn_t *insn, unsigned int raw_op,
unsigned int raw_flags, unsigned int prefixes,
unsigned char modrm ) {
unsigned int addr_meth, op_type, op_size, gen_regs;
x86_op_t *op;
size_t size;
/* ++ Yank optype and addr mode out of operand flags */
addr_meth = raw_flags & ADDRMETH_MASK;
op_type = raw_flags & OPTYPE_MASK;
if ( raw_flags == ARG_NONE ) {
/* operand is not used in this instruction */
return 0;
}
/* allocate a new operand */
op = x86_operand_new( insn );
/* ++ Copy flags from opcode table to x86_insn_t */
op->access = (enum x86_op_access) OP_PERM(raw_flags);
op->flags = (enum x86_op_flags) (OP_FLAGS(raw_flags) >> 12);
/* Get size (for decoding) and datatype of operand */
op_size = decode_operand_size(op_type, insn, op);
/* override default register set based on Operand Type */
/* this allows mixing of 8, 16, and 32 bit regs in insn */
if (op_size == 1) {
gen_regs = REG_BYTE_OFFSET;
} else if (op_size == 2) {
gen_regs = REG_WORD_OFFSET;
} else {
gen_regs = REG_DWORD_OFFSET;
}
size = decode_operand_value( buf, buf_len, op, insn, addr_meth,
op_size, raw_op, modrm, gen_regs );
/* if operand is an address, apply any segment override prefixes */
if ( op->type == op_expression || op->type == op_offset ) {
apply_seg(op, prefixes);
}
return size; /* return number of bytes in instruction */
}

11
libdisasm/ia32_operand.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef IA32_OPERAND_H
#define IA32_OPERAND_H
#include "libdis.h"
#include "ia32_insn.h"
size_t ia32_decode_operand( unsigned char *buf, size_t buf_len,
x86_insn_t *insn, unsigned int raw_op,
unsigned int raw_flags, unsigned int prefixes,
unsigned char modrm );
#endif

234
libdisasm/ia32_reg.c Normal file
View File

@ -0,0 +1,234 @@
#include <stdlib.h>
#include <string.h>
#include "ia32_reg.h"
#include "ia32_insn.h"
#define NUM_X86_REGS 92
/* register sizes */
#define REG_DWORD_SIZE 4
#define REG_WORD_SIZE 2
#define REG_BYTE_SIZE 1
#define REG_MMX_SIZE 8
#define REG_SIMD_SIZE 16
#define REG_DEBUG_SIZE 4
#define REG_CTRL_SIZE 4
#define REG_TEST_SIZE 4
#define REG_SEG_SIZE 2
#define REG_FPU_SIZE 10
#define REG_FLAGS_SIZE 4
#define REG_FPCTRL_SIZE 2
#define REG_FPSTATUS_SIZE 2
#define REG_FPTAG_SIZE 2
#define REG_EIP_SIZE 4
#define REG_IP_SIZE 2
/* REGISTER ALIAS TABLE:
*
* NOTE: the MMX register mapping is fixed to the physical registers
* used by the FPU. The floating FP stack does not effect the location
* of the MMX registers, so this aliasing is not 100% accurate.
* */
static struct {
unsigned char alias; /* id of register this is an alias for */
unsigned char shift; /* # of bits register must be shifted */
} ia32_reg_aliases[] = {
{ 0,0 },
{ REG_DWORD_OFFSET, 0 }, /* al : 1 */
{ REG_DWORD_OFFSET, 8 }, /* ah : 2 */
{ REG_DWORD_OFFSET, 0 }, /* ax : 3 */
{ REG_DWORD_OFFSET + 1, 0 }, /* cl : 4 */
{ REG_DWORD_OFFSET + 1, 8 }, /* ch : 5 */
{ REG_DWORD_OFFSET + 1, 0 }, /* cx : 6 */
{ REG_DWORD_OFFSET + 2, 0 }, /* dl : 7 */
{ REG_DWORD_OFFSET + 2, 8 }, /* dh : 8 */
{ REG_DWORD_OFFSET + 2, 0 }, /* dx : 9 */
{ REG_DWORD_OFFSET + 3, 0 }, /* bl : 10 */
{ REG_DWORD_OFFSET + 3, 8 }, /* bh : 11 */
{ REG_DWORD_OFFSET + 3, 0 }, /* bx : 12 */
{ REG_DWORD_OFFSET + 4, 0 }, /* sp : 13 */
{ REG_DWORD_OFFSET + 5, 0 }, /* bp : 14 */
{ REG_DWORD_OFFSET + 6, 0 }, /* si : 15 */
{ REG_DWORD_OFFSET + 7, 0 }, /* di : 16 */
{ REG_EIP_INDEX, 0 }, /* ip : 17 */
{ REG_FPU_OFFSET, 0 }, /* mm0 : 18 */
{ REG_FPU_OFFSET + 1, 0 }, /* mm1 : 19 */
{ REG_FPU_OFFSET + 2, 0 }, /* mm2 : 20 */
{ REG_FPU_OFFSET + 3, 0 }, /* mm3 : 21 */
{ REG_FPU_OFFSET + 4, 0 }, /* mm4 : 22 */
{ REG_FPU_OFFSET + 5, 0 }, /* mm5 : 23 */
{ REG_FPU_OFFSET + 6, 0 }, /* mm6 : 24 */
{ REG_FPU_OFFSET + 7, 0 } /* mm7 : 25 */
};
/* REGISTER TABLE: size, type, and name of every register in the
* CPU. Does not include MSRs since the are, after all,
* model specific. */
static struct {
unsigned int size;
enum x86_reg_type type;
unsigned int alias;
char mnemonic[8];
} ia32_reg_table[NUM_X86_REGS + 2] = {
{ 0, 0, 0, "" },
/* REG_DWORD_OFFSET */
{ REG_DWORD_SIZE, reg_gen | reg_ret, 0, "eax" },
{ REG_DWORD_SIZE, reg_gen | reg_count, 0, "ecx" },
{ REG_DWORD_SIZE, reg_gen, 0, "edx" },
{ REG_DWORD_SIZE, reg_gen, 0, "ebx" },
/* REG_ESP_INDEX */
{ REG_DWORD_SIZE, reg_gen | reg_sp, 0, "esp" },
{ REG_DWORD_SIZE, reg_gen | reg_fp, 0, "ebp" },
{ REG_DWORD_SIZE, reg_gen | reg_src, 0, "esi" },
{ REG_DWORD_SIZE, reg_gen | reg_dest, 0, "edi" },
/* REG_WORD_OFFSET */
{ REG_WORD_SIZE, reg_gen | reg_ret, 3, "ax" },
{ REG_WORD_SIZE, reg_gen | reg_count, 6, "cx" },
{ REG_WORD_SIZE, reg_gen, 9, "dx" },
{ REG_WORD_SIZE, reg_gen, 12, "bx" },
{ REG_WORD_SIZE, reg_gen | reg_sp, 13, "sp" },
{ REG_WORD_SIZE, reg_gen | reg_fp, 14, "bp" },
{ REG_WORD_SIZE, reg_gen | reg_src, 15, "si" },
{ REG_WORD_SIZE, reg_gen | reg_dest, 16, "di" },
/* REG_BYTE_OFFSET */
{ REG_BYTE_SIZE, reg_gen, 1, "al" },
{ REG_BYTE_SIZE, reg_gen, 4, "cl" },
{ REG_BYTE_SIZE, reg_gen, 7, "dl" },
{ REG_BYTE_SIZE, reg_gen, 10, "bl" },
{ REG_BYTE_SIZE, reg_gen, 2, "ah" },
{ REG_BYTE_SIZE, reg_gen, 5, "ch" },
{ REG_BYTE_SIZE, reg_gen, 8, "dh" },
{ REG_BYTE_SIZE, reg_gen, 11, "bh" },
/* REG_MMX_OFFSET */
{ REG_MMX_SIZE, reg_simd, 18, "mm0" },
{ REG_MMX_SIZE, reg_simd, 19, "mm1" },
{ REG_MMX_SIZE, reg_simd, 20, "mm2" },
{ REG_MMX_SIZE, reg_simd, 21, "mm3" },
{ REG_MMX_SIZE, reg_simd, 22, "mm4" },
{ REG_MMX_SIZE, reg_simd, 23, "mm5" },
{ REG_MMX_SIZE, reg_simd, 24, "mm6" },
{ REG_MMX_SIZE, reg_simd, 25, "mm7" },
/* REG_SIMD_OFFSET */
{ REG_SIMD_SIZE, reg_simd, 0, "xmm0" },
{ REG_SIMD_SIZE, reg_simd, 0, "xmm1" },
{ REG_SIMD_SIZE, reg_simd, 0, "xmm2" },
{ REG_SIMD_SIZE, reg_simd, 0, "xmm3" },
{ REG_SIMD_SIZE, reg_simd, 0, "xmm4" },
{ REG_SIMD_SIZE, reg_simd, 0, "xmm5" },
{ REG_SIMD_SIZE, reg_simd, 0, "xmm6" },
{ REG_SIMD_SIZE, reg_simd, 0, "xmm7" },
/* REG_DEBUG_OFFSET */
{ REG_DEBUG_SIZE, reg_sys, 0, "dr0" },
{ REG_DEBUG_SIZE, reg_sys, 0, "dr1" },
{ REG_DEBUG_SIZE, reg_sys, 0, "dr2" },
{ REG_DEBUG_SIZE, reg_sys, 0, "dr3" },
{ REG_DEBUG_SIZE, reg_sys, 0, "dr4" },
{ REG_DEBUG_SIZE, reg_sys, 0, "dr5" },
{ REG_DEBUG_SIZE, reg_sys, 0, "dr6" },
{ REG_DEBUG_SIZE, reg_sys, 0, "dr7" },
/* REG_CTRL_OFFSET */
{ REG_CTRL_SIZE, reg_sys, 0, "cr0" },
{ REG_CTRL_SIZE, reg_sys, 0, "cr1" },
{ REG_CTRL_SIZE, reg_sys, 0, "cr2" },
{ REG_CTRL_SIZE, reg_sys, 0, "cr3" },
{ REG_CTRL_SIZE, reg_sys, 0, "cr4" },
{ REG_CTRL_SIZE, reg_sys, 0, "cr5" },
{ REG_CTRL_SIZE, reg_sys, 0, "cr6" },
{ REG_CTRL_SIZE, reg_sys, 0, "cr7" },
/* REG_TEST_OFFSET */
{ REG_TEST_SIZE, reg_sys, 0, "tr0" },
{ REG_TEST_SIZE, reg_sys, 0, "tr1" },
{ REG_TEST_SIZE, reg_sys, 0, "tr2" },
{ REG_TEST_SIZE, reg_sys, 0, "tr3" },
{ REG_TEST_SIZE, reg_sys, 0, "tr4" },
{ REG_TEST_SIZE, reg_sys, 0, "tr5" },
{ REG_TEST_SIZE, reg_sys, 0, "tr6" },
{ REG_TEST_SIZE, reg_sys, 0, "tr7" },
/* REG_SEG_OFFSET */
{ REG_SEG_SIZE, reg_seg, 0, "es" },
{ REG_SEG_SIZE, reg_seg, 0, "cs" },
{ REG_SEG_SIZE, reg_seg, 0, "ss" },
{ REG_SEG_SIZE, reg_seg, 0, "ds" },
{ REG_SEG_SIZE, reg_seg, 0, "fs" },
{ REG_SEG_SIZE, reg_seg, 0, "gs" },
/* REG_LDTR_INDEX */
{ REG_DWORD_SIZE, reg_sys, 0, "ldtr" },
/* REG_GDTR_INDEX */
{ REG_DWORD_SIZE, reg_sys, 0, "gdtr" },
/* REG_FPU_OFFSET */
{ REG_FPU_SIZE, reg_fpu, 0, "st(0)" },
{ REG_FPU_SIZE, reg_fpu, 0, "st(1)" },
{ REG_FPU_SIZE, reg_fpu, 0, "st(2)" },
{ REG_FPU_SIZE, reg_fpu, 0, "st(3)" },
{ REG_FPU_SIZE, reg_fpu, 0, "st(4)" },
{ REG_FPU_SIZE, reg_fpu, 0, "st(5)" },
{ REG_FPU_SIZE, reg_fpu, 0, "st(6)" },
{ REG_FPU_SIZE, reg_fpu, 0, "st(7)" },
/* REG_FLAGS_INDEX : 81 */
{ REG_FLAGS_SIZE, reg_cond, 0, "eflags" },
/* REG_FPCTRL_INDEX : 82*/
{ REG_FPCTRL_SIZE, reg_fpu | reg_sys, 0, "fpctrl" },
/* REG_FPSTATUS_INDEX : 83*/
{ REG_FPSTATUS_SIZE, reg_fpu | reg_sys, 0, "fpstat" },
/* REG_FPTAG_INDEX : 84 */
{ REG_FPTAG_SIZE, reg_fpu | reg_sys, 0, "fptag" },
/* REG_EIP_INDEX : 85 */
{ REG_EIP_SIZE, reg_pc, 0, "eip" },
/* REG_IP_INDEX : 86 */
{ REG_IP_SIZE, reg_pc, 17, "ip" },
/* REG_IDTR_INDEX : 87 */
{ REG_DWORD_SIZE, reg_sys, 0, "idtr" },
/* REG_MXCSG_INDEX : SSE Control Reg : 88 */
{ REG_DWORD_SIZE, reg_sys | reg_simd, 0, "mxcsr" },
/* REG_TR_INDEX : Task Register : 89 */
{ 16 + 64, reg_sys, 0, "tr" },
/* REG_CSMSR_INDEX : SYSENTER_CS_MSR : 90 */
{ REG_DWORD_SIZE, reg_sys, 0, "cs_msr" },
/* REG_ESPMSR_INDEX : SYSENTER_ESP_MSR : 91 */
{ REG_DWORD_SIZE, reg_sys, 0, "esp_msr" },
/* REG_EIPMSR_INDEX : SYSENTER_EIP_MSR : 92 */
{ REG_DWORD_SIZE, reg_sys, 0, "eip_msr" },
{ 0 }
};
static size_t sz_regtable = NUM_X86_REGS + 1;
void ia32_handle_register( x86_reg_t *reg, size_t id ) {
unsigned int alias;
if (! id || id > sz_regtable ) {
return;
}
memset( reg, 0, sizeof(x86_reg_t) );
strncpy( reg->name, ia32_reg_table[id].mnemonic, MAX_REGNAME );
reg->type = ia32_reg_table[id].type;
reg->size = ia32_reg_table[id].size;
alias = ia32_reg_table[id].alias;
if ( alias ) {
reg->alias = ia32_reg_aliases[alias].alias;
reg->shift = ia32_reg_aliases[alias].shift;
}
reg->id = id;
return;
}
size_t ia32_true_register_id( size_t id ) {
size_t reg;
if (! id || id > sz_regtable ) {
return 0;
}
reg = id;
if (ia32_reg_table[reg].alias) {
reg = ia32_reg_aliases[ia32_reg_table[reg].alias].alias;
}
return reg;
}

41
libdisasm/ia32_reg.h Normal file
View File

@ -0,0 +1,41 @@
#ifndef IA32_REG_H
#define IA32_REG_H
#include <sys/types.h> /* for size_t */
#include "libdis.h" /* for x86_reg_t */
/* NOTE these are used in opcode tables for hard-coded registers */
#define REG_DWORD_OFFSET 1 /* 0 + 1 */
#define REG_ECX_INDEX 2 /* 0 + 1 + 1 */
#define REG_ESP_INDEX 5 /* 0 + 4 + 1 */
#define REG_EBP_INDEX 6 /* 0 + 5 + 1 */
#define REG_ESI_INDEX 7 /* 0 + 6 + 1 */
#define REG_EDI_INDEX 8 /* 0 + 7 + 1 */
#define REG_WORD_OFFSET 9 /* 1 * 8 + 1 */
#define REG_BYTE_OFFSET 17 /* 2 * 8 + 1 */
#define REG_MMX_OFFSET 25 /* 3 * 8 + 1 */
#define REG_SIMD_OFFSET 33 /* 4 * 8 + 1 */
#define REG_DEBUG_OFFSET 41 /* 5 * 8 + 1 */
#define REG_CTRL_OFFSET 49 /* 6 * 8 + 1 */
#define REG_TEST_OFFSET 57 /* 7 * 8 + 1 */
#define REG_SEG_OFFSET 65 /* 8 * 8 + 1 */
#define REG_LDTR_INDEX 71 /* 8 * 8 + 1 + 1 */
#define REG_GDTR_INDEX 72 /* 8 * 8 + 2 + 1 */
#define REG_FPU_OFFSET 73 /* 9 * 8 + 1 */
#define REG_FLAGS_INDEX 81 /* 10 * 8 + 1 */
#define REG_FPCTRL_INDEX 82 /* 10 * 8 + 1 + 1 */
#define REG_FPSTATUS_INDEX 83 /* 10 * 8 + 2 + 1 */
#define REG_FPTAG_INDEX 84 /* 10 * 8 + 3 + 1 */
#define REG_EIP_INDEX 85 /* 10 * 8 + 4 + 1 */
#define REG_IP_INDEX 86 /* 10 * 8 + 5 + 1 */
#define REG_IDTR_INDEX 87 /* 10 * 8 + 6 + 1 */
#define REG_MXCSG_INDEX 88 /* 10 * 8 + 7 + 1 */
#define REG_TR_INDEX 89 /* 10 * 8 + 8 + 1 */
#define REG_CSMSR_INDEX 90 /* 10 * 8 + 9 + 1 */
#define REG_ESPMSR_INDEX 91 /* 10 * 8 + 10 + 1 */
#define REG_EIPMSR_INDEX 92 /* 10 * 8 + 11 + 1 */
void ia32_handle_register( x86_reg_t *reg, size_t id );
size_t ia32_true_register_id( size_t id );
#endif

13
libdisasm/ia32_settings.c Normal file
View File

@ -0,0 +1,13 @@
#include "libdis.h"
#include "ia32_settings.h"
#include "ia32_reg.h"
#include "ia32_insn.h"
ia32_settings_t ia32_settings = {
1, 0xF4,
MAX_INSTRUCTION_SIZE,
4, 4, 8, 4, 8,
REG_ESP_INDEX, REG_EBP_INDEX, REG_EIP_INDEX, REG_FLAGS_INDEX,
REG_DWORD_OFFSET, REG_SEG_OFFSET, REG_FPU_OFFSET,
opt_none
};

27
libdisasm/ia32_settings.h Normal file
View File

@ -0,0 +1,27 @@
#ifndef IA32_SETTINGS_H
#define IA32_SETTINGS_H
#include "libdis.h"
typedef struct {
/* options */
unsigned char endian, /* 0 = big, 1 = little */
wc_byte, /* wildcard byte */
max_insn, /* max insn size */
sz_addr, /* default address size */
sz_oper, /* default operand size */
sz_byte, /* # bits in byte */
sz_word, /* # bytes in machine word */
sz_dword; /* # bytes in machine dword */
unsigned int id_sp_reg, /* id of stack pointer */
id_fp_reg, /* id of frame pointer */
id_ip_reg, /* id of instruction pointer */
id_flag_reg, /* id of flags register */
offset_gen_regs, /* start of general regs */
offset_seg_regs, /* start of segment regs */
offset_fpu_regs; /* start of floating point regs */
/* user-controlled settings */
enum x86_options options;
} ia32_settings_t;
#endif

836
libdisasm/libdis.h Normal file
View File

@ -0,0 +1,836 @@
#ifndef LIBDISASM_H
#define LIBDISASM_H
#ifdef WIN32
#include <windows.h>
#endif
#include <stdint.h>
/* 'NEW" types
* __________________________________________________________________________*/
#ifndef LIBDISASM_QWORD_H /* do not interfere with qword.h */
#define LIBDISASM_QWORD_H
#ifdef _MSC_VER
typedef __int64 qword_t;
#else
typedef int64_t qword_t;
#endif
#endif
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/* 'NEW" x86 API
* __________________________________________________________________________*/
/* ========================================= Error Reporting */
/* REPORT CODES
* These are passed to a reporter function passed at initialization.
* Each code determines the type of the argument passed to the reporter;
* this allows the report to recover from errors, or just log them.
*/
enum x86_report_codes {
report_disasm_bounds, /* RVA OUT OF BOUNDS : The disassembler could
not disassemble the supplied RVA as it is
out of the range of the buffer. The
application should store the address and
attempt to determine what section of the
binary it is in, then disassemble the
address from the bytes in that section.
data: uint32_t rva */
report_insn_bounds, /* INSTRUCTION OUT OF BOUNDS: The disassembler
could not disassemble the instruction as
the instruction would require bytes beyond
the end of the current buffer. This usually
indicated garbage bytes at the end of a
buffer, or an incorrectly-sized buffer.
data: uint32_t rva */
report_invalid_insn, /* INVALID INSTRUCTION: The disassembler could
not disassemble the instruction as it has an
invalid combination of opcodes and operands.
This will stop automated disassembly; the
application can restart the disassembly
after the invalid instruction.
data: uint32_t rva */
report_unknown
};
/* 'arg' is optional arbitrary data provided by the code passing the
* callback -- for example, it could be 'this' or 'self' in OOP code.
* 'code' is provided by libdisasm, it is one of the above
* 'data' is provided by libdisasm and is context-specific, per the enums */
typedef void (*DISASM_REPORTER)( enum x86_report_codes code,
void *data, void *arg );
/* x86_report_error : Call the register reporter to report an error */
void x86_report_error( enum x86_report_codes code, void *data );
/* ========================================= Libdisasm Management Routines */
enum x86_options { /* these can be ORed together */
opt_none= 0,
opt_ignore_nulls=1, /* ignore sequences of > 4 NULL bytes */
opt_16_bit=2, /* 16-bit/DOS disassembly */
opt_att_mnemonics=4, /* use AT&T syntax names for alternate opcode mnemonics */
};
/* management routines */
/* 'arg' is caller-specific data which is passed as the first argument
* to the reporter callback routine */
int x86_init( enum x86_options options, DISASM_REPORTER reporter, void *arg);
void x86_set_reporter( DISASM_REPORTER reporter, void *arg);
void x86_set_options( enum x86_options options );
enum x86_options x86_get_options( void );
int x86_cleanup(void);
/* ========================================= Instruction Representation */
/* these defines are only intended for use in the array decl's */
#define MAX_REGNAME 8
#define MAX_PREFIX_STR 32
#define MAX_MNEM_STR 16
#define MAX_INSN_SIZE 20 /* same as in i386.h */
#define MAX_OP_STRING 32 /* max possible operand size in string form */
#define MAX_OP_RAW_STRING 64 /* max possible operand size in raw form */
#define MAX_OP_XML_STRING 256 /* max possible operand size in xml form */
#define MAX_NUM_OPERANDS 8 /* max # implicit and explicit operands */
/* in these, the '2 *' is arbitrary: the max # of operands should require
* more space than the rest of the insn */
#define MAX_INSN_STRING 512 /* 2 * 8 * MAX_OP_STRING */
#define MAX_INSN_RAW_STRING 1024 /* 2 * 8 * MAX_OP_RAW_STRING */
#define MAX_INSN_XML_STRING 4096 /* 2 * 8 * MAX_OP_XML_STRING */
enum x86_reg_type { /* NOTE: these may be ORed together */
reg_gen = 0x00001, /* general purpose */
reg_in = 0x00002, /* incoming args, ala RISC */
reg_out = 0x00004, /* args to calls, ala RISC */
reg_local = 0x00008, /* local vars, ala RISC */
reg_fpu = 0x00010, /* FPU data register */
reg_seg = 0x00020, /* segment register */
reg_simd = 0x00040, /* SIMD/MMX reg */
reg_sys = 0x00080, /* restricted/system register */
reg_sp = 0x00100, /* stack pointer */
reg_fp = 0x00200, /* frame pointer */
reg_pc = 0x00400, /* program counter */
reg_retaddr = 0x00800, /* return addr for func */
reg_cond = 0x01000, /* condition code / flags */
reg_zero = 0x02000, /* zero register, ala RISC */
reg_ret = 0x04000, /* return value */
reg_src = 0x10000, /* array/rep source */
reg_dest = 0x20000, /* array/rep destination */
reg_count = 0x40000 /* array/rep/loop counter */
};
/* x86_reg_t : an X86 CPU register */
typedef struct {
char name[MAX_REGNAME];
enum x86_reg_type type; /* what register is used for */
unsigned int size; /* size of register in bytes */
unsigned int id; /* register ID #, for quick compares */
unsigned int alias; /* ID of reg this is an alias for */
unsigned int shift; /* amount to shift aliased reg by */
} x86_reg_t;
/* x86_ea_t : an X86 effective address (address expression) */
typedef struct {
unsigned int scale; /* scale factor */
x86_reg_t index, base; /* index, base registers */
int32_t disp; /* displacement */
char disp_sign; /* is negative? 1/0 */
char disp_size; /* 0, 1, 2, 4 */
} x86_ea_t;
/* x86_absolute_t : an X86 segment:offset address (descriptor) */
typedef struct {
unsigned short segment; /* loaded directly into CS */
union {
unsigned short off16; /* loaded directly into IP */
uint32_t off32; /* loaded directly into EIP */
} offset;
} x86_absolute_t;
enum x86_op_type { /* mutually exclusive */
op_unused = 0, /* empty/unused operand: should never occur */
op_register = 1, /* CPU register */
op_immediate = 2, /* Immediate Value */
op_relative_near = 3, /* Relative offset from IP */
op_relative_far = 4, /* Relative offset from IP */
op_absolute = 5, /* Absolute address (ptr16:32) */
op_expression = 6, /* Address expression (scale/index/base/disp) */
op_offset = 7, /* Offset from start of segment (m32) */
op_unknown
};
#define x86_optype_is_address( optype ) \
( optype == op_absolute || optype == op_offset )
#define x86_optype_is_relative( optype ) \
( optype == op_relative_near || optype == op_relative_far )
#define x86_optype_is_memory( optype ) \
( optype > op_immediate && optype < op_unknown )
enum x86_op_datatype { /* these use Intel's lame terminology */
op_byte = 1, /* 1 byte integer */
op_word = 2, /* 2 byte integer */
op_dword = 3, /* 4 byte integer */
op_qword = 4, /* 8 byte integer */
op_dqword = 5, /* 16 byte integer */
op_sreal = 6, /* 4 byte real (single real) */
op_dreal = 7, /* 8 byte real (double real) */
op_extreal = 8, /* 10 byte real (extended real) */
op_bcd = 9, /* 10 byte binary-coded decimal */
op_ssimd = 10, /* 16 byte : 4 packed single FP (SIMD, MMX) */
op_dsimd = 11, /* 16 byte : 2 packed double FP (SIMD, MMX) */
op_sssimd = 12, /* 4 byte : scalar single FP (SIMD, MMX) */
op_sdsimd = 13, /* 8 byte : scalar double FP (SIMD, MMX) */
op_descr32 = 14, /* 6 byte Intel descriptor 2:4 */
op_descr16 = 15, /* 4 byte Intel descriptor 2:2 */
op_pdescr32 = 16, /* 6 byte Intel pseudo-descriptor 32:16 */
op_pdescr16 = 17, /* 6 byte Intel pseudo-descriptor 8:24:16 */
op_bounds16 = 18, /* signed 16:16 lower:upper bounds */
op_bounds32 = 19, /* signed 32:32 lower:upper bounds */
op_fpuenv16 = 20, /* 14 byte FPU control/environment data */
op_fpuenv32 = 21, /* 28 byte FPU control/environment data */
op_fpustate16 = 22, /* 94 byte FPU state (env & reg stack) */
op_fpustate32 = 23, /* 108 byte FPU state (env & reg stack) */
op_fpregset = 24, /* 512 bytes: register set */
op_fpreg = 25, /* FPU register */
op_none = 0xFF, /* operand without a datatype (INVLPG) */
};
enum x86_op_access { /* ORed together */
op_read = 1,
op_write = 2,
op_execute = 4
};
enum x86_op_flags { /* ORed together, but segs are mutually exclusive */
op_signed = 1, /* signed integer */
op_string = 2, /* possible string or array */
op_constant = 4, /* symbolic constant */
op_pointer = 8, /* operand points to a memory address */
op_sysref = 0x010, /* operand is a syscall number */
op_implied = 0x020, /* operand is implicit in the insn */
op_hardcode = 0x40, /* operand is hardcoded in insn definition */
/* NOTE: an 'implied' operand is one which can be considered a side
* effect of the insn, e.g. %esp being modified by PUSH or POP. A
* 'hard-coded' operand is one which is specified in the instruction
* definition, e.g. %es:%edi in MOVSB or 1 in ROL Eb, 1. The difference
* is that hard-coded operands are printed by disassemblers and are
* required to re-assemble, while implicit operands are invisible. */
op_es_seg = 0x100, /* ES segment override */
op_cs_seg = 0x200, /* CS segment override */
op_ss_seg = 0x300, /* SS segment override */
op_ds_seg = 0x400, /* DS segment override */
op_fs_seg = 0x500, /* FS segment override */
op_gs_seg = 0x600 /* GS segment override */
};
/* x86_op_t : an X86 instruction operand */
typedef struct {
enum x86_op_type type; /* operand type */
enum x86_op_datatype datatype; /* operand size */
enum x86_op_access access; /* operand access [RWX] */
enum x86_op_flags flags; /* misc flags */
union {
/* sizeof will have to work on these union members! */
/* immediate values */
char sbyte;
short sword;
int32_t sdword;
qword_t sqword;
unsigned char byte;
unsigned short word;
uint32_t dword;
qword_t qword;
float sreal;
double dreal;
/* misc large/non-native types */
unsigned char extreal[10];
unsigned char bcd[10];
qword_t dqword[2];
unsigned char simd[16];
unsigned char fpuenv[28];
/* offset from segment */
uint32_t offset;
/* ID of CPU register */
x86_reg_t reg;
/* offsets from current insn */
char relative_near;
int32_t relative_far;
/* segment:offset */
x86_absolute_t absolute;
/* effective address [expression] */
x86_ea_t expression;
} data;
/* this is needed to make formatting operands more sane */
void * insn; /* pointer to x86_insn_t owning operand */
} x86_op_t;
/* Linked list of x86_op_t; provided for manual traversal of the operand
* list in an insn. Users wishing to add operands to this list, e.g. to add
* implicit operands, should use x86_operand_new in x86_operand_list.h */
typedef struct x86_operand_list {
x86_op_t op;
struct x86_operand_list *next;
} x86_oplist_t;
enum x86_insn_group {
insn_none = 0, /* invalid instruction */
insn_controlflow = 1,
insn_arithmetic = 2,
insn_logic = 3,
insn_stack = 4,
insn_comparison = 5,
insn_move = 6,
insn_string = 7,
insn_bit_manip = 8,
insn_flag_manip = 9,
insn_fpu = 10,
insn_interrupt = 13,
insn_system = 14,
insn_other = 15
};
enum x86_insn_type {
insn_invalid = 0, /* invalid instruction */
/* insn_controlflow */
insn_jmp = 0x1001,
insn_jcc = 0x1002,
insn_call = 0x1003,
insn_callcc = 0x1004,
insn_return = 0x1005,
/* insn_arithmetic */
insn_add = 0x2001,
insn_sub = 0x2002,
insn_mul = 0x2003,
insn_div = 0x2004,
insn_inc = 0x2005,
insn_dec = 0x2006,
insn_shl = 0x2007,
insn_shr = 0x2008,
insn_rol = 0x2009,
insn_ror = 0x200A,
/* insn_logic */
insn_and = 0x3001,
insn_or = 0x3002,
insn_xor = 0x3003,
insn_not = 0x3004,
insn_neg = 0x3005,
/* insn_stack */
insn_push = 0x4001,
insn_pop = 0x4002,
insn_pushregs = 0x4003,
insn_popregs = 0x4004,
insn_pushflags = 0x4005,
insn_popflags = 0x4006,
insn_enter = 0x4007,
insn_leave = 0x4008,
/* insn_comparison */
insn_test = 0x5001,
insn_cmp = 0x5002,
/* insn_move */
insn_mov = 0x6001, /* move */
insn_movcc = 0x6002, /* conditional move */
insn_xchg = 0x6003, /* exchange */
insn_xchgcc = 0x6004, /* conditional exchange */
/* insn_string */
insn_strcmp = 0x7001,
insn_strload = 0x7002,
insn_strmov = 0x7003,
insn_strstore = 0x7004,
insn_translate = 0x7005, /* xlat */
/* insn_bit_manip */
insn_bittest = 0x8001,
insn_bitset = 0x8002,
insn_bitclear = 0x8003,
/* insn_flag_manip */
insn_clear_carry = 0x9001,
insn_clear_zero = 0x9002,
insn_clear_oflow = 0x9003,
insn_clear_dir = 0x9004,
insn_clear_sign = 0x9005,
insn_clear_parity = 0x9006,
insn_set_carry = 0x9007,
insn_set_zero = 0x9008,
insn_set_oflow = 0x9009,
insn_set_dir = 0x900A,
insn_set_sign = 0x900B,
insn_set_parity = 0x900C,
insn_tog_carry = 0x9010,
insn_tog_zero = 0x9020,
insn_tog_oflow = 0x9030,
insn_tog_dir = 0x9040,
insn_tog_sign = 0x9050,
insn_tog_parity = 0x9060,
/* insn_fpu */
insn_fmov = 0xA001,
insn_fmovcc = 0xA002,
insn_fneg = 0xA003,
insn_fabs = 0xA004,
insn_fadd = 0xA005,
insn_fsub = 0xA006,
insn_fmul = 0xA007,
insn_fdiv = 0xA008,
insn_fsqrt = 0xA009,
insn_fcmp = 0xA00A,
insn_fcos = 0xA00C,
insn_fldpi = 0xA00D,
insn_fldz = 0xA00E,
insn_ftan = 0xA00F,
insn_fsine = 0xA010,
insn_fsys = 0xA020,
/* insn_interrupt */
insn_int = 0xD001,
insn_intcc = 0xD002, /* not present in x86 ISA */
insn_iret = 0xD003,
insn_bound = 0xD004,
insn_debug = 0xD005,
insn_trace = 0xD006,
insn_invalid_op = 0xD007,
insn_oflow = 0xD008,
/* insn_system */
insn_halt = 0xE001,
insn_in = 0xE002, /* input from port/bus */
insn_out = 0xE003, /* output to port/bus */
insn_cpuid = 0xE004,
/* insn_other */
insn_nop = 0xF001,
insn_bcdconv = 0xF002, /* convert to or from BCD */
insn_szconv = 0xF003 /* change size of operand */
};
/* These flags specify special characteristics of the instruction, such as
* whether the inatruction is privileged or whether it serializes the
* pipeline.
* NOTE : These may not be accurate for all instructions; updates to the
* opcode tables have not been completed. */
enum x86_insn_note {
insn_note_ring0 = 1, /* Only available in ring 0 */
insn_note_smm = 2, /* "" in System Management Mode */
insn_note_serial = 4, /* Serializing instruction */
insn_note_nonswap = 8, /* Does not swap arguments in att-style formatting */
insn_note_nosuffix = 16, /* Does not have size suffix in att-style formatting */
};
/* This specifies what effects the instruction has on the %eflags register */
enum x86_flag_status {
insn_carry_set = 0x1, /* CF */
insn_zero_set = 0x2, /* ZF */
insn_oflow_set = 0x4, /* OF */
insn_dir_set = 0x8, /* DF */
insn_sign_set = 0x10, /* SF */
insn_parity_set = 0x20, /* PF */
insn_carry_or_zero_set = 0x40,
insn_zero_set_or_sign_ne_oflow = 0x80,
insn_carry_clear = 0x100,
insn_zero_clear = 0x200,
insn_oflow_clear = 0x400,
insn_dir_clear = 0x800,
insn_sign_clear = 0x1000,
insn_parity_clear = 0x2000,
insn_sign_eq_oflow = 0x4000,
insn_sign_ne_oflow = 0x8000
};
/* The CPU model in which the insturction first appeared; this can be used
* to mask out instructions appearing in earlier or later models or to
* check the portability of a binary.
* NOTE : These may not be accurate for all instructions; updates to the
* opcode tables have not been completed. */
enum x86_insn_cpu {
cpu_8086 = 1, /* Intel */
cpu_80286 = 2,
cpu_80386 = 3,
cpu_80387 = 4,
cpu_80486 = 5,
cpu_pentium = 6,
cpu_pentiumpro = 7,
cpu_pentium2 = 8,
cpu_pentium3 = 9,
cpu_pentium4 = 10,
cpu_k6 = 16, /* AMD */
cpu_k7 = 32,
cpu_athlon = 48
};
/* CPU ISA subsets: These are derived from the Instruction Groups in
* Intel Vol 1 Chapter 5; they represent subsets of the IA32 ISA but
* do not reflect the 'type' of the instruction in the same way that
* x86_insn_group does. In short, these are AMD/Intel's somewhat useless
* designations.
* NOTE : These may not be accurate for all instructions; updates to the
* opcode tables have not been completed. */
enum x86_insn_isa {
isa_gp = 1, /* general purpose */
isa_fp = 2, /* floating point */
isa_fpumgt = 3, /* FPU/SIMD management */
isa_mmx = 4, /* Intel MMX */
isa_sse1 = 5, /* Intel SSE SIMD */
isa_sse2 = 6, /* Intel SSE2 SIMD */
isa_sse3 = 7, /* Intel SSE3 SIMD */
isa_3dnow = 8, /* AMD 3DNow! SIMD */
isa_sys = 9 /* system instructions */
};
enum x86_insn_prefix {
insn_no_prefix = 0,
insn_rep_zero = 1, /* REPZ and REPE */
insn_rep_notzero = 2, /* REPNZ and REPNZ */
insn_lock = 4 /* LOCK: */
};
/* TODO: maybe provide insn_new/free(), and have disasm return new insn_t */
/* x86_insn_t : an X86 instruction */
typedef struct {
/* information about the instruction */
uint32_t addr; /* load address */
uint32_t offset; /* offset into file/buffer */
enum x86_insn_group group; /* meta-type, e.g. INS_EXEC */
enum x86_insn_type type; /* type, e.g. INS_BRANCH */
enum x86_insn_note note; /* note, e.g. RING0 */
unsigned char bytes[MAX_INSN_SIZE];
unsigned char size; /* size of insn in bytes */
/* 16/32-bit mode settings */
unsigned char addr_size; /* default address size : 2 or 4 */
unsigned char op_size; /* default operand size : 2 or 4 */
/* CPU/instruction set */
enum x86_insn_cpu cpu;
enum x86_insn_isa isa;
/* flags */
enum x86_flag_status flags_set; /* flags set or tested by insn */
enum x86_flag_status flags_tested;
/* stack */
unsigned char stack_mod; /* 0 or 1 : is the stack modified? */
int32_t stack_mod_val; /* val stack is modified by if known */
/* the instruction proper */
enum x86_insn_prefix prefix; /* prefixes ORed together */
char prefix_string[MAX_PREFIX_STR]; /* prefixes [might be truncated] */
char mnemonic[MAX_MNEM_STR];
x86_oplist_t *operands; /* list of explicit/implicit operands */
size_t operand_count; /* total number of operands */
size_t explicit_count; /* number of explicit operands */
/* convenience fields for user */
void *block; /* code block containing this insn */
void *function; /* function containing this insn */
int tag; /* tag the insn as seen/processed */
} x86_insn_t;
/* returns 0 if an instruction is invalid, 1 if valid */
int x86_insn_is_valid( x86_insn_t *insn );
/* DISASSEMBLY ROUTINES
* Canonical order of arguments is
* (buf, buf_len, buf_rva, offset, len, insn, func, arg, resolve_func)
* ...but of course all of these are not used at the same time.
*/
/* Function prototype for caller-supplied callback routine
* These callbacks are intended to process 'insn' further, e.g. by
* adding it to a linked list, database, etc */
typedef void (*DISASM_CALLBACK)( x86_insn_t *insn, void * arg );
/* Function prototype for caller-supplied address resolver.
* This routine is used to determine the rva to disassemble next, given
* the 'dest' operand of a jump/call. This allows the caller to resolve
* jump/call targets stored in a register or on the stack, and also allows
* the caller to prevent endless loops by checking if an address has
* already been disassembled. If an address cannot be resolved from the
* operand, or if the address has already been disassembled, this routine
* should return -1; in all other cases the RVA to be disassembled next
* should be returned. */
typedef int32_t (*DISASM_RESOLVER)( x86_op_t *op, x86_insn_t * current_insn,
void *arg );
/* x86_disasm: Disassemble a single instruction from a buffer of bytes.
* Returns size of instruction in bytes.
* Caller is responsible for calling x86_oplist_free() on
* a reused "insn" to avoid leaking memory when calling this
* function repeatedly.
* buf : Buffer of bytes to disassemble
* buf_len : Length of the buffer
* buf_rva : Load address of the start of the buffer
* offset : Offset in buffer to disassemble
* insn : Structure to fill with disassembled instruction
*/
unsigned int x86_disasm( unsigned char *buf, unsigned int buf_len,
uint32_t buf_rva, unsigned int offset,
x86_insn_t * insn );
/* x86_disasm_range: Sequential disassembly of a range of bytes in a buffer,
* invoking a callback function each time an instruction
* is successfully disassembled. The 'range' refers to the
* bytes between 'offset' and 'offset + len' in the buffer;
* 'len' is assumed to be less than the length of the buffer.
* Returns number of instructions processed.
* buf : Buffer of bytes to disassemble (e.g. .text section)
* buf_rva : Load address of buffer (e.g. ELF Virtual Address)
* offset : Offset in buffer to start disassembly at
* len : Number of bytes to disassemble
* func : Callback function to invoke (may be NULL)
* arg : Arbitrary data to pass to callback (may be NULL)
*/
unsigned int x86_disasm_range( unsigned char *buf, uint32_t buf_rva,
unsigned int offset, unsigned int len,
DISASM_CALLBACK func, void *arg );
/* x86_disasm_forward: Flow-of-execution disassembly of the bytes in a buffer,
* invoking a callback function each time an instruction
* is successfully disassembled.
* buf : Buffer to disassemble (e.g. .text section)
* buf_len : Number of bytes in buffer
* buf_rva : Load address of buffer (e.g. ELF Virtual Address)
* offset : Offset in buffer to start disassembly at (e.g. entry point)
* func : Callback function to invoke (may be NULL)
* arg : Arbitrary data to pass to callback (may be NULL)
* resolver: Caller-supplied address resolver. If no resolver is
* supplied, a default internal one is used -- however the
* internal resolver does NOT catch loops and could end up
* disassembling forever..
* r_arg : Arbitrary data to pass to resolver (may be NULL)
*/
unsigned int x86_disasm_forward( unsigned char *buf, unsigned int buf_len,
uint32_t buf_rva, unsigned int offset,
DISASM_CALLBACK func, void *arg,
DISASM_RESOLVER resolver, void *r_arg );
/* Instruction operands: these are stored as a list of explicit and
* implicit operands. It is recommended that the 'foreach' routines
* be used to when examining operands for purposes of data flow analysis */
/* Operand FOREACH callback: 'arg' is an abritrary parameter passed to the
* foreach routine, 'insn' is the x86_insn_t whose operands are being
* iterated over, and 'op' is the current x86_op_t */
typedef void (*x86_operand_fn)(x86_op_t *op, x86_insn_t *insn, void *arg);
/* FOREACH types: these are used to limit the foreach results to
* operands which match a certain "type" (implicit or explicit)
* or which are accessed in certain ways (e.g. read or write). Note
* that this operates on the operand list of single instruction, so
* specifying the 'real' operand type (register, memory, etc) is not
* useful. Note also that by definition Execute Access implies Read
* Access and implies Not Write Access.
* The "type" (implicit or explicit) and the access method can
* be ORed together, e.g. op_wo | op_explicit */
enum x86_op_foreach_type {
op_any = 0, /* ALL operands (explicit, implicit, rwx) */
op_dest = 1, /* operands with Write access */
op_src = 2, /* operands with Read access */
op_ro = 3, /* operands with Read but not Write access */
op_wo = 4, /* operands with Write but not Read access */
op_xo = 5, /* operands with Execute access */
op_rw = 6, /* operands with Read AND Write access */
op_implicit = 0x10, /* operands that are implied by the opcode */
op_explicit = 0x20 /* operands that are not side-effects */
};
/* free the operand list associated with an instruction -- useful for
* preventing memory leaks when free()ing an x86_insn_t */
void x86_oplist_free( x86_insn_t *insn );
/* Operand foreach: invokes 'func' with 'insn' and 'arg' as arguments. The
* 'type' parameter is used to select only operands matching specific
* criteria. */
int x86_operand_foreach( x86_insn_t *insn, x86_operand_fn func, void *arg,
enum x86_op_foreach_type type);
/* convenience routine: returns count of operands matching 'type' */
size_t x86_operand_count( x86_insn_t *insn, enum x86_op_foreach_type type );
/* accessor functions for the operands */
x86_op_t * x86_operand_1st( x86_insn_t *insn );
x86_op_t * x86_operand_2nd( x86_insn_t *insn );
x86_op_t * x86_operand_3rd( x86_insn_t *insn );
/* these allow libdisasm 2.0 accessor functions to still be used */
#define x86_get_dest_operand( insn ) x86_operand_1st( insn )
#define x86_get_src_operand( insn ) x86_operand_2nd( insn )
#define x86_get_imm_operand( insn ) x86_operand_3rd( insn )
/* get size of operand data in bytes */
unsigned int x86_operand_size( x86_op_t *op );
/* Operand Convenience Routines: the following three routines are common
* operations on operands, intended to ease the burden of the programmer. */
/* Get Address: return the value of an offset operand, or the offset of
* a segment:offset absolute address */
uint32_t x86_get_address( x86_insn_t *insn );
/* Get Relative Offset: return as a sign-extended int32_t the near or far
* relative offset operand, or 0 if there is none. There can be only one
* relaive offset operand in an instruction. */
int32_t x86_get_rel_offset( x86_insn_t *insn );
/* Get Branch Target: return the x86_op_t containing the target of
* a jump or call operand, or NULL if there is no branch target.
* Internally, a 'branch target' is defined as any operand with
* Execute Access set. There can be only one branch target per instruction. */
x86_op_t * x86_get_branch_target( x86_insn_t *insn );
/* Get Immediate: return the x86_op_t containing the immediate operand
* for this instruction, or NULL if there is no immediate operand. There
* can be only one immediate operand per instruction */
x86_op_t * x86_get_imm( x86_insn_t *insn );
/* Get Raw Immediate Data: returns a pointer to the immediate data encoded
* in the instruction. This is useful for large data types [>32 bits] currently
* not supported by libdisasm, or for determining if the disassembler
* screwed up the conversion of the immediate data. Note that 'imm' in this
* context refers to immediate data encoded at the end of an instruction as
* detailed in the Intel Manual Vol II Chapter 2; it does not refer to the
* 'op_imm' operand (the third operand in instructions like 'mul' */
unsigned char * x86_get_raw_imm( x86_insn_t *insn );
/* More accessor fuctions, this time for user-defined info... */
/* set the address (usually RVA) of the insn */
void x86_set_insn_addr( x86_insn_t *insn, uint32_t addr );
/* set the offset (usually offset into file) of the insn */
void x86_set_insn_offset( x86_insn_t *insn, unsigned int offset );
/* set a pointer to the function owning the instruction. The
* type of 'func' is user-defined; libdisasm does not use the func field. */
void x86_set_insn_function( x86_insn_t *insn, void * func );
/* set a pointer to the block of code owning the instruction. The
* type of 'block' is user-defined; libdisasm does not use the block field. */
void x86_set_insn_block( x86_insn_t *insn, void * block );
/* instruction tagging: these routines allow the programmer to mark
* instructions as "seen" in a DFS, for example. libdisasm does not use
* the tag field.*/
/* set insn->tag to 1 */
void x86_tag_insn( x86_insn_t *insn );
/* set insn->tag to 0 */
void x86_untag_insn( x86_insn_t *insn );
/* return insn->tag */
int x86_insn_is_tagged( x86_insn_t *insn );
/* Disassembly formats:
* AT&T is standard AS/GAS-style: "mnemonic\tsrc, dest, imm"
* Intel is standard MASM/NASM/TASM: "mnemonic\tdest,src, imm"
* Native is tab-delimited: "RVA\tbytes\tmnemonic\tdest\tsrc\timm"
* XML is your typical <insn> ... </insn>
* Raw is addr|offset|size|bytes|prefix... see libdisasm_formats.7
*/
enum x86_asm_format {
unknown_syntax = 0, /* never use! */
native_syntax, /* header: 35 bytes */
intel_syntax, /* header: 23 bytes */
att_syntax, /* header: 23 bytes */
xml_syntax, /* header: 679 bytes */
raw_syntax /* header: 172 bytes */
};
/* format (sprintf) an operand into 'buf' using specified syntax */
int x86_format_operand(x86_op_t *op, char *buf, int len,
enum x86_asm_format format);
/* format (sprintf) an instruction mnemonic into 'buf' using specified syntax */
int x86_format_mnemonic(x86_insn_t *insn, char *buf, int len,
enum x86_asm_format format);
/* format (sprintf) an instruction into 'buf' using specified syntax;
* this includes formatting all operands */
int x86_format_insn(x86_insn_t *insn, char *buf, int len, enum x86_asm_format);
/* fill 'buf' with a description of the format's syntax */
int x86_format_header( char *buf, int len, enum x86_asm_format format);
/* Endianness of an x86 CPU : 0 is big, 1 is little; always returns 1 */
unsigned int x86_endian(void);
/* Default address and operand size in bytes */
unsigned int x86_addr_size(void);
unsigned int x86_op_size(void);
/* Size of a machine word in bytes */
unsigned int x86_word_size(void);
/* maximum size of a code instruction */
#define x86_max_inst_size(x) x86_max_insn_size(x)
unsigned int x86_max_insn_size(void);
/* register IDs of Stack, Frame, Instruction pointer and Flags register */
unsigned int x86_sp_reg(void);
unsigned int x86_fp_reg(void);
unsigned int x86_ip_reg(void);
unsigned int x86_flag_reg(void);
/* fill 'reg' struct with details of register 'id' */
void x86_reg_from_id( unsigned int id, x86_reg_t * reg );
/* convenience macro demonstrating how to get an aliased register; proto is
* void x86_get_aliased_reg( x86_reg_t *alias_reg, x86_reg_t *output_reg )
* where 'alias_reg' is a reg operand and 'output_reg' is filled with the
* register that the operand is an alias for */
#define x86_get_aliased_reg( alias_reg, output_reg ) \
x86_reg_from_id( alias_reg->alias, output_reg )
/* ================================== Invariant Instruction Representation */
/* Invariant instructions are used for generating binary signatures;
* the instruction is modified so that all variant bytes in an instruction
* are replaced with a wildcard byte.
*
* A 'variant byte' is one that is expected to be modified by either the
* static or the dynamic linker: for example, an address encoded in an
* instruction.
*
* By comparing the invariant representation of one instruction [or of a
* sequence of instructions] with the invariant representation of another,
* one determine whether the two invariant representations are from the same
* relocatable object [.o] file. Thus one can use binary signatures [which
* are just sequences of invariant instruction representations] to look for
* library routines which have been statically-linked into a binary.
*
* The invariant routines are faster and smaller than the disassembly
* routines; they can be used to determine the size of an instruction
* without all of the overhead of a full instruction disassembly.
*/
/* This byte is used to replace variant bytes */
#define X86_WILDCARD_BYTE 0xF4
typedef struct {
enum x86_op_type type; /* operand type */
enum x86_op_datatype datatype; /* operand size */
enum x86_op_access access; /* operand access [RWX] */
enum x86_op_flags flags; /* misc flags */
} x86_invariant_op_t;
typedef struct {
unsigned char bytes[64]; /* invariant representation */
unsigned int size; /* number of bytes in insn */
enum x86_insn_group group; /* meta-type, e.g. INS_EXEC */
enum x86_insn_type type; /* type, e.g. INS_BRANCH */
x86_invariant_op_t operands[3]; /* operands: dest, src, imm */
} x86_invariant_t;
/* return a version of the instruction with the variant bytes masked out */
size_t x86_invariant_disasm( unsigned char *buf, int buf_len,
x86_invariant_t *inv );
/* return the size in bytes of the intruction pointed to by 'buf';
* this used x86_invariant_disasm since it faster than x86_disasm */
size_t x86_size_disasm( unsigned char *buf, unsigned int buf_len );
#ifdef __cplusplus
}
#endif
#endif

14
libdisasm/qword.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef LIBDISASM_QWORD_H
#define LIBDISASM_QWORD_H
#include <stdint.h>
/* platform independent data types */
#ifdef _MSC_VER
typedef __int64 qword_t;
#else
typedef int64_t qword_t;
#endif
#endif

70
libdisasm/swig/Makefile Normal file
View File

@ -0,0 +1,70 @@
# change these values if you need to
SWIG = swig # apt-get install swig !
GCC = gcc
CC_FLAGS = -c -fPIC
LD_FLAGS = -shared -L../.. -ldisasm
BASE_NAME = x86disasm
export INTERFACE_FILE BASE_NAME SWIG GCC CC_FLAGS LD_FLAGS
#====================================================
# TARGETS
all: swig
dummy: swig swig-python swig-ruby swig-perl swig-tcl install uninstall clean
swig: swig-python swig-perl
# swig-rub swig-tcl
swig-python:
cd python && make -f Makefile-swig
swig-ruby:
cd ruby && make -f Makefile-swig
swig-perl:
cd perl && make -f Makefile-swig
swig-tcl:
cd tcl && make -f Makefile-swig
# ==================================================================
install: install-python install-perl
# install-ruby install-tcl
install-python:
cd python && sudo make -f Makefile-swig install
install-ruby:
cd ruby && sudo make -f Makefile-swig install
install-perl:
cd perl && sudo make -f Makefile-swig install
install-tcl:
cd tcl && sudo make -f Makefile-swig install
# ==================================================================
uninstall: uninstall-python
#uninstall-ruby uninstall-perl uninstall-tcl
uninstall-python:
cd python && sudo make -f Makefile-swig uninstall
uninstall-ruby:
cd ruby && sudo make -f Makefile-swig uninstall
uninstall-perl:
cd perl && sudo make -f Makefile-swig uninstall
uninstall-tcl:
cd tcl && sudo make -f Makefile-swig uninstall
# ==================================================================
clean:
cd python && make -f Makefile-swig clean
cd ruby && make -f Makefile-swig clean
cd perl && make -f Makefile-swig clean
cd tcl && make -f Makefile-swig clean

128
libdisasm/swig/README Normal file
View File

@ -0,0 +1,128 @@
Libdisasm SWIG README
The SWIG utility (www.swig.org) can be used to generate
Building SWIG Modules
---------------------
make
make install
Make and Install both build Python, Perl, Ruby, and Tcl modules. If you
do not have one of these languages installed, comment out the relevant
target in the main Makefile.
Install uses 'sudo' to put files in the correct locations; if you
do not have sudo installed, change the install targets.
The Module API
--------------
The OOP API
-----------
The Python Module
-----------------
To test that the module loads:
bash# python
>>> import x86disasm
>>> x86disasm.version_string()
'0.21-pre'
>>>^D
bash#
>>> import x86disasm
>>> import array
>>> disasm = x86disasm.X86_Disasm( )
>>> tgt = open( "/tmp/a.out", "rb" )
>>> tgt.seek( 0, 2 )
>>> size = tgt.tell()
>>> tgt.seek( 0, 0 )
>>> buf = array.array( 'B' )
>>> buf.fromfile( tgt, size )
>>> tgt.close()
>>> data = x86disasm.byteArray( size )
>>> for i in range( size ):
... data[i] = buf.pop(0)
...
>>> del buf
>>> del tgt
>>> insn = disasm.disasm( data, size - 1, 0, 0 )
>>> insn.format( x86disasm.att_syntax )
'jg\t0x00000047'
>>> insn.format( x86disasm.raw_syntax )
'0x00000000|0x00000000|2|7F 45 |||controlflow|jcc|jg|80386|General Purpose|||zero_clear sign_eq_oflow |0|0|relative|sbyte|00000047|'
>>> ops = insn.operand_list()
>>> node = ops.first()
>>> while node is not None:
... s = node.op.format(x86disasm.raw_syntax)
... print s
... node = ops.next()
...
relative|sbyte|00000047|
The Perl Module
---------------
To test that the module loads:
bash# perl
use x86disasm;
print x86disasm::version_string() . "\n";
^D
0.21-pre
bash#
The Ruby Module
---------------
To test that the module loads:
bash# irb
irb(main):001:0> require 'x86disasm'
=> true
irb(main):002:0> X86disasm.version_string()
=> "0.21-pre"
irb(main):003:0> x = X86disasm::X86_Disasm.new
=> #<X86disasm::X86_Disasm:0xb7d624a4>
irb(main):004:0> x.max_register_string()
=> 8
irb(main):003:0> ^D
bash#
The Tcl Module
---------------
To test that the module loads:
bash# tclsh
% load /usr/lib/tcl8.3/x86disasm.so X86disasm
% version_string
0.21-pre
% ^D
bash#
% x86_init 0 NULL NULL
OR
% x86disasm dis
_486b0708_p_x86disasm
% puts "[dis cget -last_error]"
0
The Interface Files
-------------------
libdisasm.i -- interface file without shadow classes
libdisasm_oop.i -- interface file with shadow classes

508
libdisasm/swig/libdisasm.i Normal file
View File

@ -0,0 +1,508 @@
%module x86disasm
%{
#include "../../libdis.h"
#include "../../../config.h"
%}
%rename(version_string) x86_version_string;
%include "../../libdis.h"
#include "../../../config.h"
%inline %{
const char * x86_version_string( void ) {
return PACKAGE_VERSION;
}
%}
%rename(report_codes) x86_report_codes;
%rename(report_error) x86_report_error;
%rename(options) x86_options;
%rename(init) x86_init;
%rename(set_reporter) x86_set_reporter;
%rename(set_options) x86_set_options;
%rename(options) x86_get_options;
%rename(cleanup) x86_cleanup;
%rename(reg_type) x86_reg_type;
%rename(reg) x86_reg_t;
%rename(eaddr) x86_ea_t;
%rename(op_type) x86_op_type;
%rename(optype_is_address) x86_optype_is_address;
%rename(optype_is_relative) x86_optype_is_relative;
%rename(op_datatype) x86_op_datatype;
%rename(op_access) x86_op_access;
%rename(op_flags) x86_op_flags;
%rename(operand) x86_op_t;
%rename(insn_group) x86_insn_group;
%rename(insn_type) x86_insn_type;
%rename(insn_note) x86_insn_note ;
%rename(flag_status) x86_flag_status;
%rename(insn_cpu) x86_insn_cpu ;
%rename(insn_isa) x86_insn_isa ;
%rename(insn_prefix) x86_insn_prefix ;
%rename(insn) x86_insn_t;
%rename(insn_is_valid) x86_insn_is_valid;
%rename(i_disasm) x86_disasm;
%rename(i_disasm_range) x86_disasm_range;
%rename(i_disasm_forward) x86_disasm_forward;
%rename(insn_operand_count) x86_operand_count;
%rename(insn_operand_1st) x86_operand_1st;
%rename(insn_operand_2nd) x86_operand_2nd;
%rename(insn_operand_3rd) x86_operand_3rd;
%rename(insn_dest_operand) x86_get_dest_operand;
%rename(insn_src_operand) x86_get_src_operand;
%rename(insn_imm_operand) x86_get_imm_operand;
%rename(operand_size) x86_operand_size;
%rename(insn_rel_offset) x86_get_rel_offset;
%rename(insn_branch_target) x86_get_branch_target;
%rename(insn_imm) x86_get_imm;
%rename(insn_raw_imm) x86_get_raw_imm;
%rename(insn_set_addr) x86_set_insn_addr;
%rename(insn_set_offset) x86_set_insn_offset;
%rename(insn_set_function) x86_set_insn_function;
%rename(insn_set_block) x86_set_insn_block;
%rename(insn_tag) x86_tag_insn;
%rename(insn_untag) x86_untag_insn;
%rename(insn_is_tagged) x86_insn_is_tagged;
%rename(asm_format) x86_asm_format;
%rename(operand_format) x86_format_operand;
%rename(insn_format_mnemonic) x86_format_mnemonic;
%rename(insn_format) x86_format_insn;
%rename(header_format) x86_format_header;
%rename(endian) x86_endian;
%rename(size_default_address) x86_addr_size;
%rename(size_default_operand) x86_op_size;
%rename(size_machine_word) x86_word_size;
%rename(size_max_insn) x86_max_insn_size;
%rename(reg_sp) x86_sp_reg;
%rename(reg_fp) x86_fp_reg;
%rename(reg_ip) x86_ip_reg;
%rename(reg_from_id) x86_reg_from_id;
%rename(reg_from_alias) x86_get_aliased_reg;
%rename(invariant_op) x86_invariant_op_t;
%rename(invariant) x86_invariant_t;
%rename(disasm_invariant) x86_invariant_disasm;
%rename(disasm_size) x86_size_disasm;
%include "carrays.i"
%array_class( unsigned char, byteArray );
%apply (unsigned char *STRING, int LENGTH) {
(unsigned char *buf, size_t buf_len)
};
%newobject x86_op_copy;
%inline %{
x86_op_t * x86_op_copy( x86_op_t * src ) {
x86_op_t *op;
if (! src ) {
return NULL;
}
op = (x86_op_t *) calloc( sizeof(x86_op_t), 1 );
if ( op ) {
memcpy( op, src, sizeof(x86_op_t) );
}
return op;
}
typedef struct x86_op_list_node {
x86_op_t *op;
struct x86_op_list_node *next, *prev;
} x86_op_list_node;
typedef struct x86_op_list {
size_t count;
x86_op_list_node *head, *tail, *curr;
} x86_op_list;
x86_op_list * x86_op_list_new () {
x86_op_list *list = (x86_op_list *)
calloc( sizeof(x86_op_list), 1 );
list->count = 0;
return list;
}
void x86_op_list_free(x86_op_list *list) {
x86_op_list_node *node, *next;
node = list->head;
while ( node ) {
next = node->next;
/* free( node->insn ); */
free( node );
node = next;
}
free( list );
}
x86_op_list_node * x86_op_list_first(x86_op_list *list) {
return list->head;
}
x86_op_list_node * x86_op_list_last(x86_op_list *list) {
return list->tail;
}
x86_op_list_node * x86_op_list_next(x86_op_list *list) {
if (! list->curr ) {
list->curr = list->head;
return list->head;
}
list->curr = list->curr->next;
return list->curr;
}
x86_op_list_node * x86_op_list_prev(x86_op_list *list) {
if (! list->curr ) {
list->curr = list->tail;
return list->tail;
}
list->curr = list->curr->prev;
return list->curr;
}
%}
%newobject x86_op_list_append;
%inline %{
void x86_op_list_append( x86_op_list * list, x86_op_t *op ) {
x86_op_list_node *node = (x86_op_list_node *)
calloc( sizeof(x86_op_list_node) , 1 );
if (! node ) {
return;
}
list->count++;
if ( ! list->tail ) {
list->head = list->tail = node;
} else {
list->tail->next = node;
node->prev = list->tail;
list->tail = node;
}
node->op = x86_op_copy( op );
}
x86_oplist_t * x86_op_list_node_copy( x86_oplist_t * list ) {
x86_oplist_t *ptr;
ptr = (x86_oplist_t *) calloc( sizeof(x86_oplist_t), 1 );
if ( ptr ) {
memcpy( &ptr->op, &list->op, sizeof(x86_op_t) );
}
return ptr;
}
x86_insn_t * x86_insn_new() {
x86_insn_t *insn = (x86_insn_t *)
calloc( sizeof(x86_insn_t), 1 );
return insn;
}
void x86_insn_free( x86_insn_t *insn ) {
x86_oplist_free( insn );
free( insn );
}
%}
%newobject x86_insn_copy;
%inline %{
x86_insn_t * x86_insn_copy( x86_insn_t *src) {
x86_oplist_t *ptr, *list, *last = NULL;
x86_insn_t *insn = (x86_insn_t *)
calloc( sizeof(x86_insn_t), 1 );
if ( insn ) {
memcpy( insn, src, sizeof(x86_insn_t) );
insn->operands = NULL;
insn->block = NULL;
insn->function = NULL;
/* copy operand list */
for ( list = src->operands; list; list = list->next ) {
ptr = x86_op_list_node_copy( list );
if (! ptr ) {
continue;
}
if ( insn->operands ) {
last->next = ptr;
} else {
insn->operands = ptr;
}
last = ptr;
}
}
return insn;
}
x86_op_list * x86_insn_op_list( x86_insn_t *insn ) {
x86_oplist_t *list = insn->operands;
x86_op_list *op_list = x86_op_list_new();
for ( list = insn->operands; list; list = list->next ) {
x86_op_list_append( op_list, &list->op );
}
return op_list;
}
typedef struct x86_insn_list_node {
x86_insn_t *insn;
struct x86_insn_list_node *next, *prev;
} x86_insn_list_node;
typedef struct x86_insn_list {
size_t count;
x86_insn_list_node *head, *tail, *curr;
} x86_insn_list;
%}
%newobject x86_insn_list_new;
%inline %{
x86_insn_list * x86_insn_list_new () {
x86_insn_list *list = (x86_insn_list *)
calloc( sizeof(x86_insn_list), 1 );
list->count = 0;
return list;
}
void x86_insn_list_free( x86_insn_list * list ) {
x86_insn_list_node *node, *next;
if (! list ) {
return;
}
node = list->head;
while ( node ) {
next = node->next;
/* free( node->insn ); */
free( node );
node = next;
}
free( list );
}
x86_insn_list_node * x86_insn_list_first( x86_insn_list *list ) {
if (! list ) {
return NULL;
}
return list->head;
}
x86_insn_list_node * x86_insn_list_last( x86_insn_list *list ) {
if (! list ) {
return NULL;
}
return list->tail;
}
x86_insn_list_node * x86_insn_list_next( x86_insn_list *list ) {
if (! list ) {
return NULL;
}
if (! list->curr ) {
list->curr = list->head;
return list->head;
}
list->curr = list->curr->next;
return list->curr;
}
x86_insn_list_node * x86_insn_list_prev( x86_insn_list *list ) {
if (! list ) {
return NULL;
}
if (! list->curr ) {
list->curr = list->tail;
return list->tail;
}
list->curr = list->curr->prev;
return list->curr;
}
%}
%newobject x86_insn_list_append;
%inline %{
void x86_insn_list_append( x86_insn_list *list, x86_insn_t *insn ) {
x86_insn_list_node *node;
if (! list ) {
return;
}
node = (x86_insn_list_node *)
calloc( sizeof(x86_insn_list_node) , 1 );
if (! node ) {
return;
}
list->count++;
if ( ! list->tail ) {
list->head = list->tail = node;
} else {
list->tail->next = node;
node->prev = list->tail;
list->tail = node;
}
node->insn = x86_insn_copy( insn );
}
typedef struct {
enum x86_report_codes last_error;
void * last_error_data;
void * disasm_callback;
void * disasm_resolver;
} x86disasm;
void x86_default_reporter( enum x86_report_codes code,
void *data, void *arg ) {
x86disasm *dis = (x86disasm *) arg;
if ( dis ) {
dis->last_error = code;
dis->last_error_data = data;
}
}
void x86_default_callback( x86_insn_t *insn, void *arg ) {
x86_insn_list *list = (x86_insn_list *) arg;
if ( list ) {
x86_insn_list_append( list, insn );
}
}
/* TODO: resolver stack, maybe a callback */
long x86_default_resolver( x86_op_t *op, x86_insn_t *insn, void *arg ) {
x86disasm *dis = (x86disasm *) arg;
if ( dis ) {
//return dis->resolver( op, insn );
return 0;
}
return 0;
}
%}
%newobject x86disasm_new;
%inline %{
x86disasm * x86disasm_new ( enum x86_options options ) {
x86disasm * dis = (x86disasm *)
calloc( sizeof( x86disasm ), 1 );
x86_init( options, x86_default_reporter, dis );
return dis;
}
void x86disasm_free( x86disasm * dis ) {
x86_cleanup();
free( dis );
}
%}
%newobject x86_disasm;
%inline %{
x86_insn_t * disasm( unsigned char *buf, size_t buf_len,
unsigned long buf_rva, unsigned int offset ) {
x86_insn_t *insn = calloc( sizeof( x86_insn_t ), 1 );
x86_disasm( buf, buf_len, buf_rva, offset, insn );
return insn;
}
int disasm_range( unsigned char *buf, size_t buf_len,
unsigned long buf_rva, unsigned int offset,
unsigned int len ) {
x86_insn_list *list = x86_insn_list_new();
if ( len > buf_len ) {
len = buf_len;
}
return x86_disasm_range( buf, buf_rva, offset, len,
x86_default_callback, list );
}
int disasm_forward( unsigned char *buf, size_t buf_len,
unsigned long buf_rva, unsigned int offset ) {
x86_insn_list *list = x86_insn_list_new();
/* use default resolver: damn SWIG callbacks! */
return x86_disasm_forward( buf, buf_len, buf_rva, offset,
x86_default_callback, list,
x86_default_resolver, NULL );
}
size_t disasm_invariant( unsigned char *buf, size_t buf_len,
x86_invariant_t *inv ) {
return x86_invariant_disasm( buf, buf_len, inv );
}
size_t disasm_size( unsigned char *buf, size_t buf_len ) {
return x86_size_disasm( buf, buf_len );
}
int x86_max_operand_string( enum x86_asm_format format ) {
switch ( format ) {
case xml_syntax:
return MAX_OP_XML_STRING;
break;
case raw_syntax:
return MAX_OP_RAW_STRING;
break;
case native_syntax:
case intel_syntax:
case att_syntax:
case unknown_syntax:
default:
return MAX_OP_STRING;
break;
}
}
int x86_max_insn_string( enum x86_asm_format format ) {
switch ( format ) {
case xml_syntax:
return MAX_INSN_XML_STRING;
break;
case raw_syntax:
return MAX_INSN_RAW_STRING;
break;
case native_syntax:
case intel_syntax:
case att_syntax:
case unknown_syntax:
default:
return MAX_INSN_STRING;
break;
}
}
int x86_max_num_operands( ) { return MAX_NUM_OPERANDS; }
%}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,65 @@
ifndef BASE_NAME
BASE_NAME = x86disasm
endif
ifndef SWIG
SWIG = swig # apt-get install swig !
endif
ifndef GCC
GCC = gcc
endif
ifndef CC_FLAGS
CC_FLAGS = -c -fPIC
endif
ifndef LD_FLAGS
LD_FLAGS = -shared -L.. -ldisasm
endif
INTERFACE_FILE = libdisasm_oop.i
SWIG_INTERFACE = ../$(INTERFACE_FILE)
# PERL rules
PERL_MOD = blib/arch/auto/$(BASE_NAME)/$(BASE_NAME).so
PERL_SHADOW = $(BASE_NAME)_wrap.c
PERL_SWIG = $(BASE_NAME).pl
PERL_OBJ = $(BASE_NAME)_wrap.o
PERL_INC = `perl -e 'use Config; print $$Config{archlib};'`/CORE
PERL_CC_FLAGS = `perl -e 'use Config; print $$Config{ccflags};'`
#====================================================
# TARGETS
all: swig-perl
dummy: swig-perl install uninstall clean
swig-perl: $(PERL_MOD)
$(PERL_MOD): $(PERL_OBJ)
perl Makefile.PL
make
#$(GCC) $(LD_FLAGS) $(PERL_OBJ) -o $@
$(PERL_OBJ): $(PERL_SHADOW)
$(GCC) $(CC_FLAGS) $(PERL_CC_FLAGS) -I$(PERL_INC) -o $@ $<
$(PERL_SHADOW): $(SWIG_INTERFACE)
swig -perl -shadow -o $(PERL_SHADOW) -outdir . $<
# ==================================================================
install: $(PERL_MOD)
make install
# ==================================================================
uninstall:
# ==================================================================
clean:
rm $(PERL_MOD) $(PERL_OBJ)
rm $(PERL_SHADOW)
rm -rf Makefile blib pm_to_blib

View File

@ -0,0 +1,7 @@
use ExtUtils::MakeMaker;
WriteMakefile(
'NAME' => 'x86disasm',
'LIBS' => ['-ldisasm'],
'OBJECT' => 'x86disasm_wrap.o'
);

View File

@ -0,0 +1,64 @@
ifndef BASE_NAME
BASE_NAME = x86disasm
endif
ifndef SWIG
SWIG = swig # apt-get install swig !
endif
ifndef GCC
GCC = gcc
endif
ifndef CC_FLAGS
CC_FLAGS = -c -fPIC
endif
ifndef LD_FLAGS
LD_FLAGS = -shared -L.. -ldisasm
endif
INTERFACE_FILE = libdisasm_oop.i
SWIG_INTERFACE = ../$(INTERFACE_FILE)
# PYTHON rules
PYTHON_MOD = $(BASE_NAME)-python.so
PYTHON_SHADOW = $(BASE_NAME)_wrap.c
PYTHON_SWIG = $(BASE_NAME).py
PYTHON_OBJ = $(BASE_NAME)_wrap.o
PYTHON_INC = `/bin/echo -e 'import sys\nprint sys.prefix + "/include/python" + sys.version[:3]' | python`
PYTHON_LIB = `/bin/echo -e 'import sys\nprint sys.prefix + "/lib/python" + sys.version[:3]' | python`
PYTHON_DEST = $(PYTHON_LIB)/lib-dynload/_$(BASE_NAME).so
#====================================================
# TARGETS
all: swig-python
dummy: swig-python install uninstall clean
swig-python: $(PYTHON_MOD)
$(PYTHON_MOD): $(PYTHON_OBJ)
$(GCC) $(LD_FLAGS) $(PYTHON_OBJ) -o $@
$(PYTHON_OBJ): $(PYTHON_SHADOW)
$(GCC) $(CC_FLAGS) -I$(PYTHON_INC) -I.. -o $@ $<
$(PYTHON_SHADOW): $(SWIG_INTERFACE)
swig -python -shadow -o $(PYTHON_SHADOW) -outdir . $<
# ==================================================================
install: $(PYTHON_MOD)
sudo cp $(PYTHON_MOD) $(PYTHON_DEST)
sudo cp $(PYTHON_SWIG) $(PYTHON_LIB)
# ==================================================================
uninstall:
# ==================================================================
clean:
rm $(PYTHON_MOD) $(PYTHON_SWIG) $(PYTHON_OBJ)
rm $(PYTHON_SHADOW)

View File

@ -0,0 +1,68 @@
ifndef BASE_NAME
BASE_NAME = x86disasm
endif
ifndef SWIG
SWIG = swig # apt-get install swig !
endif
ifndef GCC
GCC = gcc
endif
ifndef CC_FLAGS
CC_FLAGS = -c -fPIC
endif
ifndef LD_FLAGS
LD_FLAGS = -shared -L../.. -ldisasm
endif
LIBDISASM_DIR = ../..
INTERFACE_FILE = libdisasm_oop.i
SWIG_INTERFACE = ../$(INTERFACE_FILE)
# RUBY rules
RUBY_MAKEFILE = Makefile
RUBY_MOD = $(BASE_NAME).so
RUBY_SHADOW = $(BASE_NAME)_wrap.c
#RUBY_SWIG = $(BASE_NAME).rb
RUBY_OBJ = $(BASE_NAME)_wrap.o
RUBY_INC = `ruby -e 'puts $$:.join("\n")' | tail -2 | head -1`
#RUBY_LIB =
#RUBY_DEST =
#====================================================
# TARGETS
all: swig-ruby
dummy: swig-ruby install uninstall clean
swig-ruby: $(RUBY_MOD)
$(RUBY_MOD): $(RUBY_MAKEFILE)
make
$(RUBY_MAKEFILE): $(RUBY_OBJ)
ruby extconf.rb
$(RUBY_OBJ):$(RUBY_SHADOW)
$(GCC) $(CC_FLAGS) -I$(RUBY_INC) -I.. -o $@ $<
$(RUBY_SHADOW): $(SWIG_INTERFACE)
swig -ruby -o $(RUBY_SHADOW) -outdir . $<
# ==================================================================
install: $(RUBY_MOD)
make install
# ==================================================================
uninstall:
# ==================================================================
clean:
make clean || true
rm $(RUBY_SHADOW) $(RUBY_MAKEFILE) $(RUBY_MOD) $(RUBY_OBJ)

View File

@ -0,0 +1,4 @@
require 'mkmf'
find_library('disasm', 'x86_init', "/usr/local/lib", "../..")
create_makefile('x86disasm')

View File

@ -0,0 +1,63 @@
ifndef BASE_NAME
BASE_NAME = x86disasm
endif
ifndef SWIG
SWIG = swig # apt-get install swig !
endif
ifndef GCC
GCC = gcc
endif
ifndef CC_FLAGS
CC_FLAGS = -c -fPIC
endif
ifndef LD_FLAGS
LD_FLAGS = -shared -L../.. -ldisasm
endif
INTERFACE_FILE = libdisasm.i
SWIG_INTERFACE = ../$(INTERFACE_FILE)
# TCL rules
TCL_VERSION = 8.3
TCL_MOD = $(BASE_NAME)-tcl.so
TCL_SHADOW = $(BASE_NAME)_wrap.c
TCL_OBJ = $(BASE_NAME)_wrap.o
TCL_INC = /usr/include/tcl$(TCL_VERSION)
TCL_LIB = /usr/lib/tcl$(TCL_VERSION)
TCL_DEST = $(TCL_LIB)/$(BASE_NAME).so
#====================================================
# TARGETS
all: swig-tcl
dummy: swig-tcl install uninstall clean
swig-tcl: $(TCL_MOD)
$(TCL_MOD): $(TCL_OBJ)
$(GCC) $(LD_FLAGS) $(TCL_OBJ) -o $@
$(TCL_OBJ): $(TCL_SHADOW)
$(GCC) $(CC_FLAGS) -I$(TCL_INC) -I.. -o $@ $<
$(TCL_SHADOW): $(SWIG_INTERFACE)
swig -tcl -o $(TCL_SHADOW) -outdir . $<
# ==================================================================
install: $(TCL_MOD)
sudo cp $(TCL_MOD) $(TCL_DEST)
# ==================================================================
uninstall:
# ==================================================================
clean:
rm $(TCL_MOD) $(TCL_SWIG) $(TCL_OBJ)
rm $(TCL_SHADOW)

210
libdisasm/x86_disasm.c Normal file
View File

@ -0,0 +1,210 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libdis.h"
#include "ia32_insn.h"
#include "ia32_invariant.h"
#include "x86_operand_list.h"
#ifdef _MSC_VER
#define snprintf _snprintf
#define inline __inline
#endif
unsigned int x86_disasm( unsigned char *buf, unsigned int buf_len,
uint32_t buf_rva, unsigned int offset,
x86_insn_t *insn ){
int len, size;
unsigned char bytes[MAX_INSTRUCTION_SIZE];
if ( ! buf || ! insn || ! buf_len ) {
/* caller screwed up somehow */
return 0;
}
/* ensure we are all NULLed up */
memset( insn, 0, sizeof(x86_insn_t) );
insn->addr = buf_rva + offset;
insn->offset = offset;
/* default to invalid insn */
insn->type = insn_invalid;
insn->group = insn_none;
if ( offset >= buf_len ) {
/* another caller screwup ;) */
x86_report_error(report_disasm_bounds, (void*)(long)buf_rva+offset);
return 0;
}
len = buf_len - offset;
/* copy enough bytes for disassembly into buffer : this
* helps prevent buffer overruns at the end of a file */
memset( bytes, 0, MAX_INSTRUCTION_SIZE );
memcpy( bytes, &buf[offset], (len < MAX_INSTRUCTION_SIZE) ? len :
MAX_INSTRUCTION_SIZE );
/* actually do the disassembly */
/* TODO: allow switching when more disassemblers are added */
size = ia32_disasm_addr( bytes, len, insn);
/* check and see if we had an invalid instruction */
if (! size ) {
x86_report_error(report_invalid_insn, (void*)(long)buf_rva+offset );
return 0;
}
/* check if we overran the end of the buffer */
if ( size > len ) {
x86_report_error( report_insn_bounds, (void*)(long)buf_rva + offset );
MAKE_INVALID( insn, bytes );
return 0;
}
/* fill bytes field of insn */
memcpy( insn->bytes, bytes, size );
return size;
}
unsigned int x86_disasm_range( unsigned char *buf, uint32_t buf_rva,
unsigned int offset, unsigned int len,
DISASM_CALLBACK func, void *arg ) {
x86_insn_t insn;
unsigned int buf_len, size, count = 0, bytes = 0;
/* buf_len is implied by the arguments */
buf_len = len + offset;
while ( bytes < len ) {
size = x86_disasm( buf, buf_len, buf_rva, offset + bytes,
&insn );
if ( size ) {
/* invoke callback if it exists */
if ( func ) {
(*func)( &insn, arg );
}
bytes += size;
count ++;
} else {
/* error */
bytes++; /* try next byte */
}
x86_oplist_free( &insn );
}
return( count );
}
static inline int follow_insn_dest( x86_insn_t *insn ) {
if ( insn->type == insn_jmp || insn->type == insn_jcc ||
insn->type == insn_call || insn->type == insn_callcc ) {
return(1);
}
return(0);
}
static inline int insn_doesnt_return( x86_insn_t *insn ) {
return( (insn->type == insn_jmp || insn->type == insn_return) ? 1: 0 );
}
static int32_t internal_resolver( x86_op_t *op, x86_insn_t *insn ){
int32_t next_addr = -1;
if ( x86_optype_is_address(op->type) ) {
next_addr = op->data.sdword;
} else if ( op->type == op_relative_near ) {
next_addr = insn->addr + insn->size + op->data.relative_near;
} else if ( op->type == op_relative_far ) {
next_addr = insn->addr + insn->size + op->data.relative_far;
}
return( next_addr );
}
unsigned int x86_disasm_forward( unsigned char *buf, unsigned int buf_len,
uint32_t buf_rva, unsigned int offset,
DISASM_CALLBACK func, void *arg,
DISASM_RESOLVER resolver, void *r_arg ){
x86_insn_t insn;
x86_op_t *op;
int32_t next_addr;
uint32_t next_offset;
unsigned int size, count = 0, bytes = 0, cont = 1;
while ( cont && bytes < buf_len ) {
size = x86_disasm( buf, buf_len, buf_rva, offset + bytes,
&insn );
if ( size ) {
/* invoke callback if it exists */
if ( func ) {
(*func)( &insn, arg );
}
bytes += size;
count ++;
} else {
/* error */
bytes++; /* try next byte */
}
if ( follow_insn_dest(&insn) ) {
op = x86_get_dest_operand( &insn );
next_addr = -1;
/* if caller supplied a resolver, use it to determine
* the address to disassemble */
if ( resolver ) {
next_addr = resolver(op, &insn, r_arg);
} else {
next_addr = internal_resolver(op, &insn);
}
if (next_addr != -1 ) {
next_offset = next_addr - buf_rva;
/* if offset is in this buffer... */
if ( next_offset >= 0 &&
next_offset < buf_len ) {
/* go ahead and disassemble */
count += x86_disasm_forward( buf,
buf_len,
buf_rva,
next_offset,
func, arg,
resolver, r_arg );
} else {
/* report unresolved address */
x86_report_error( report_disasm_bounds,
(void*)(long)next_addr );
}
}
} /* end follow_insn */
if ( insn_doesnt_return(&insn) ) {
/* stop disassembling */
cont = 0;
}
x86_oplist_free( &insn );
}
return( count );
}
/* invariant instruction representation */
size_t x86_invariant_disasm( unsigned char *buf, int buf_len,
x86_invariant_t *inv ){
if (! buf || ! buf_len || ! inv ) {
return(0);
}
return ia32_disasm_invariant(buf, buf_len, inv);
}
size_t x86_size_disasm( unsigned char *buf, unsigned int buf_len ) {
if (! buf || ! buf_len ) {
return(0);
}
return ia32_disasm_size(buf, buf_len);
}

1430
libdisasm/x86_format.c Normal file

File diff suppressed because it is too large Load Diff

70
libdisasm/x86_imm.c Normal file
View File

@ -0,0 +1,70 @@
#include "qword.h"
#include "x86_imm.h"
#include <stdio.h>
unsigned int x86_imm_signsized( unsigned char * buf, size_t buf_len,
void *dest, unsigned int size ) {
signed char *cp = (signed char *) dest;
signed short *sp = (signed short *) dest;
int32_t *lp = (int32_t *) dest;
qword_t *qp = (qword_t *) dest;
if ( size > buf_len ) {
return 0;
}
/* Copy 'size' bytes from *buf to *op
* return number of bytes copied */
switch (size) {
case 1: /* BYTE */
*cp = *((signed char *) buf);
break;
case 2: /* WORD */
*sp = *((signed short *) buf);
break;
case 6:
case 8: /* QWORD */
*qp = *((qword_t *) buf);
break;
case 4: /* DWORD */
default:
*lp = *((int32_t *) buf);
break;
}
return (size);
}
unsigned int x86_imm_sized( unsigned char * buf, size_t buf_len, void *dest,
unsigned int size ) {
unsigned char *cp = (unsigned char *) dest;
unsigned short *sp = (unsigned short *) dest;
uint32_t *lp = (uint32_t *) dest;
qword_t *qp = (qword_t *) dest;
if ( size > buf_len ) {
return 0;
}
/* Copy 'size' bytes from *buf to *op
* return number of bytes copied */
switch (size) {
case 1: /* BYTE */
*cp = *((unsigned char *) buf);
break;
case 2: /* WORD */
*sp = *((unsigned short *) buf);
break;
case 6:
case 8: /* QWORD */
*qp = *((qword_t *) buf);
break;
case 4: /* DWORD */
default:
*lp = *((uint32_t *) buf);
break;
}
return (size);
}

18
libdisasm/x86_imm.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef x86_IMM_H
#define x86_IMM_H
#include "./qword.h"
#include <sys/types.h>
#ifdef WIN32
#include <windows.h>
#endif
/* these are in the global x86 namespace but are not a part of the
* official API */
unsigned int x86_imm_sized( unsigned char *buf, size_t buf_len, void *dest,
unsigned int size );
unsigned int x86_imm_signsized( unsigned char *buf, size_t buf_len, void *dest,
unsigned int size );
#endif

182
libdisasm/x86_insn.c Normal file
View File

@ -0,0 +1,182 @@
#include <stdio.h>
#include <stdlib.h>
#include "libdis.h"
#ifdef _MSC_VER
#define snprintf _snprintf
#define inline __inline
#endif
int x86_insn_is_valid( x86_insn_t *insn ) {
if ( insn && insn->type != insn_invalid && insn->size > 0 ) {
return 1;
}
return 0;
}
uint32_t x86_get_address( x86_insn_t *insn ) {
x86_oplist_t *op_lst;
if (! insn || ! insn->operands ) {
return 0;
}
for (op_lst = insn->operands; op_lst; op_lst = op_lst->next ) {
if ( op_lst->op.type == op_offset ) {
return op_lst->op.data.offset;
} else if ( op_lst->op.type == op_absolute ) {
if ( op_lst->op.datatype == op_descr16 ) {
return (uint32_t)
op_lst->op.data.absolute.offset.off16;
}
return op_lst->op.data.absolute.offset.off32;
}
}
return 0;
}
int32_t x86_get_rel_offset( x86_insn_t *insn ) {
x86_oplist_t *op_lst;
if (! insn || ! insn->operands ) {
return 0;
}
for (op_lst = insn->operands; op_lst; op_lst = op_lst->next ) {
if ( op_lst->op.type == op_relative_near ) {
return (int32_t) op_lst->op.data.relative_near;
} else if ( op_lst->op.type == op_relative_far ) {
return op_lst->op.data.relative_far;
}
}
return 0;
}
x86_op_t * x86_get_branch_target( x86_insn_t *insn ) {
x86_oplist_t *op_lst;
if (! insn || ! insn->operands ) {
return NULL;
}
for (op_lst = insn->operands; op_lst; op_lst = op_lst->next ) {
if ( op_lst->op.access & op_execute ) {
return &(op_lst->op);
}
}
return NULL;
}
x86_op_t * x86_get_imm( x86_insn_t *insn ) {
x86_oplist_t *op_lst;
if (! insn || ! insn->operands ) {
return NULL;
}
for (op_lst = insn->operands; op_lst; op_lst = op_lst->next ) {
if ( op_lst->op.type == op_immediate ) {
return &(op_lst->op);
}
}
return NULL;
}
#define IS_PROPER_IMM( x ) \
x->op.type == op_immediate && ! (x->op.flags & op_hardcode)
/* if there is an immediate value in the instruction, return a pointer to
* it */
unsigned char * x86_get_raw_imm( x86_insn_t *insn ) {
int size, offset;
x86_op_t *op = NULL;
if (! insn || ! insn->operands ) {
return(NULL);
}
/* a bit inelegant, but oh well... */
if ( IS_PROPER_IMM( insn->operands ) ) {
op = &insn->operands->op;
} else if ( insn->operands->next ) {
if ( IS_PROPER_IMM( insn->operands->next ) ) {
op = &insn->operands->next->op;
} else if ( insn->operands->next->next &&
IS_PROPER_IMM( insn->operands->next->next ) ) {
op = &insn->operands->next->next->op;
}
}
if (! op ) {
return( NULL );
}
/* immediate data is at the end of the insn */
size = x86_operand_size( op );
offset = insn->size - size;
return( &insn->bytes[offset] );
}
unsigned int x86_operand_size( x86_op_t *op ) {
switch (op->datatype ) {
case op_byte: return 1;
case op_word: return 2;
case op_dword: return 4;
case op_qword: return 8;
case op_dqword: return 16;
case op_sreal: return 4;
case op_dreal: return 8;
case op_extreal: return 10;
case op_bcd: return 10;
case op_ssimd: return 16;
case op_dsimd: return 16;
case op_sssimd: return 4;
case op_sdsimd: return 8;
case op_descr32: return 6;
case op_descr16: return 4;
case op_pdescr32: return 6;
case op_pdescr16: return 6;
case op_bounds16: return 4;
case op_bounds32: return 8;
case op_fpuenv16: return 14;
case op_fpuenv32: return 28;
case op_fpustate16: return 94;
case op_fpustate32: return 108;
case op_fpregset: return 512;
case op_fpreg: return 10;
case op_none: return 0;
}
return(4); /* default size */
}
void x86_set_insn_addr( x86_insn_t *insn, uint32_t addr ) {
if ( insn ) insn->addr = addr;
}
void x86_set_insn_offset( x86_insn_t *insn, unsigned int offset ){
if ( insn ) insn->offset = offset;
}
void x86_set_insn_function( x86_insn_t *insn, void * func ){
if ( insn ) insn->function = func;
}
void x86_set_insn_block( x86_insn_t *insn, void * block ){
if ( insn ) insn->block = block;
}
void x86_tag_insn( x86_insn_t *insn ){
if ( insn ) insn->tag = 1;
}
void x86_untag_insn( x86_insn_t *insn ){
if ( insn ) insn->tag = 0;
}
int x86_insn_is_tagged( x86_insn_t *insn ){
return insn->tag;
}

71
libdisasm/x86_misc.c Normal file
View File

@ -0,0 +1,71 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libdis.h"
#include "ia32_insn.h"
#include "ia32_reg.h" /* for ia32_reg wrapper */
#include "ia32_settings.h"
extern ia32_settings_t ia32_settings;
#ifdef _MSC_VER
#define snprintf _snprintf
#define inline __inline
#endif
/* =========================================================== INIT/TERM */
static DISASM_REPORTER __x86_reporter_func = NULL;
static void * __x86_reporter_arg = NULL;
int x86_init( enum x86_options options, DISASM_REPORTER reporter, void * arg )
{
ia32_settings.options = options;
__x86_reporter_func = reporter;
__x86_reporter_arg = arg;
return 1;
}
void x86_set_reporter( DISASM_REPORTER reporter, void * arg ) {
__x86_reporter_func = reporter;
__x86_reporter_arg = arg;
}
void x86_set_options( enum x86_options options ){
ia32_settings.options = options;
}
enum x86_options x86_get_options( void ) {
return ia32_settings.options;
}
int x86_cleanup( void )
{
return 1;
}
/* =========================================================== ERRORS */
void x86_report_error( enum x86_report_codes code, void *data ) {
if ( __x86_reporter_func ) {
(*__x86_reporter_func)(code, data, __x86_reporter_arg);
}
}
/* =========================================================== MISC */
unsigned int x86_endian(void) { return ia32_settings.endian; }
unsigned int x86_addr_size(void) { return ia32_settings.sz_addr; }
unsigned int x86_op_size(void) { return ia32_settings.sz_oper; }
unsigned int x86_word_size(void) { return ia32_settings.sz_word; }
unsigned int x86_max_insn_size(void) { return ia32_settings.max_insn; }
unsigned int x86_sp_reg(void) { return ia32_settings.id_sp_reg; }
unsigned int x86_fp_reg(void) { return ia32_settings.id_fp_reg; }
unsigned int x86_ip_reg(void) { return ia32_settings.id_ip_reg; }
unsigned int x86_flag_reg(void) { return ia32_settings.id_flag_reg; }
/* wrapper function to hide the IA32 register fn */
void x86_reg_from_id( unsigned int id, x86_reg_t * reg ) {
ia32_handle_register( reg, id );
return;
}

View File

@ -0,0 +1,191 @@
#include <stdlib.h>
#include "libdis.h"
static void x86_oplist_append( x86_insn_t *insn, x86_oplist_t *op ) {
x86_oplist_t *list;
if (! insn ) {
return;
}
list = insn->operands;
if (! list ) {
insn->operand_count = 1;
/* Note that we have no way of knowing if this is an
* exlicit operand or not, since the caller fills
* the x86_op_t after we return. We increase the
* explicit count automatically, and ia32_insn_implicit_ops
* decrements it */
insn->explicit_count = 1;
insn->operands = op;
return;
}
/* get to end of list */
for ( ; list->next; list = list->next )
;
insn->operand_count = insn->operand_count + 1;
insn->explicit_count = insn->explicit_count + 1;
list->next = op;
return;
}
x86_op_t * x86_operand_new( x86_insn_t *insn ) {
x86_oplist_t *op;
if (! insn ) {
return(NULL);
}
op = calloc( sizeof(x86_oplist_t), 1 );
op->op.insn = insn;
x86_oplist_append( insn, op );
return( &(op->op) );
}
void x86_oplist_free( x86_insn_t *insn ) {
x86_oplist_t *op, *list;
if (! insn ) {
return;
}
for ( list = insn->operands; list; ) {
op = list;
list = list->next;
free(op);
}
insn->operands = NULL;
insn->operand_count = 0;
insn->explicit_count = 0;
return;
}
/* ================================================== LIBDISASM API */
/* these could probably just be #defines, but that means exposing the
enum... yet one more confusing thing in the API */
int x86_operand_foreach( x86_insn_t *insn, x86_operand_fn func, void *arg,
enum x86_op_foreach_type type ){
x86_oplist_t *list;
char explicit = 1, implicit = 1;
if (! insn || ! func ) {
return 0;
}
/* note: explicit and implicit can be ORed together to
* allow an "all" limited by access type, even though the
* user is stupid to do this since it is default behavior :) */
if ( (type & op_explicit) && ! (type & op_implicit) ) {
implicit = 0;
}
if ( (type & op_implicit) && ! (type & op_explicit) ) {
explicit = 0;
}
type = type & 0x0F; /* mask out explicit/implicit operands */
for ( list = insn->operands; list; list = list->next ) {
if (! implicit && (list->op.flags & op_implied) ) {
/* operand is implicit */
continue;
}
if (! explicit && ! (list->op.flags & op_implied) ) {
/* operand is not implicit */
continue;
}
switch ( type ) {
case op_any:
break;
case op_dest:
if (! (list->op.access & op_write) ) {
continue;
}
break;
case op_src:
if (! (list->op.access & op_read) ) {
continue;
}
break;
case op_ro:
if (! (list->op.access & op_read) ||
(list->op.access & op_write ) ) {
continue;
}
break;
case op_wo:
if (! (list->op.access & op_write) ||
(list->op.access & op_read ) ) {
continue;
}
break;
case op_xo:
if (! (list->op.access & op_execute) ) {
continue;
}
break;
case op_rw:
if (! (list->op.access & op_write) ||
! (list->op.access & op_read ) ) {
continue;
}
break;
case op_implicit: case op_explicit: /* make gcc happy */
break;
}
/* any non-continue ends up here: invoke the callback */
(*func)( &list->op, insn, arg );
}
return 1;
}
static void count_operand( x86_op_t *op, x86_insn_t *insn, void *arg ) {
size_t * count = (size_t *) arg;
*count = *count + 1;
}
size_t x86_operand_count( x86_insn_t *insn, enum x86_op_foreach_type type ) {
size_t count = 0;
/* save us a list traversal for common counts... */
if ( type == op_any ) {
return insn->operand_count;
} else if ( type == op_explicit ) {
return insn->explicit_count;
}
x86_operand_foreach( insn, count_operand, &count, type );
return count;
}
/* accessor functions */
x86_op_t * x86_operand_1st( x86_insn_t *insn ) {
if (! insn->explicit_count ) {
return NULL;
}
return &(insn->operands->op);
}
x86_op_t * x86_operand_2nd( x86_insn_t *insn ) {
if ( insn->explicit_count < 2 ) {
return NULL;
}
return &(insn->operands->next->op);
}
x86_op_t * x86_operand_3rd( x86_insn_t *insn ) {
if ( insn->explicit_count < 3 ) {
return NULL;
}
return &(insn->operands->next->next->op);
}

View File

@ -0,0 +1,8 @@
#ifndef X86_OPERAND_LIST_H
#define X86_OPERAND_LIST_H
#include "libdis.h"
x86_op_t * x86_operand_new( x86_insn_t *insn );
#endif

View File

@ -8,7 +8,8 @@ ldflags_popnhax := \
libs_popnhax := \
util \
minhook
minhook \
libdisasm
srcpp_popnhax := \
dllmain.cc \
@ -16,4 +17,5 @@ srcpp_popnhax := \
loader.cc \
SearchFile.cc \
translation.cc \
omnimix_patch.cc \
custom_categs.cc

View File

@ -35,9 +35,9 @@ struct popnhax_config {
bool patch_db;
bool disable_multiboot;
bool patch_xml_auto;
bool patch_xml_dump;
bool ignore_music_limit;
char patch_xml_filename[MAX_PATH];
char force_patch_xml[MAX_PATH];
char force_datecode[11];
bool network_datecode;
int8_t audio_offset;

View File

@ -11,7 +11,7 @@
#include <stdio.h>
#include <sys/stat.h>
#include <io.h>
#define F_OK 0
#include <fcntl.h>
#define access _access
#include "util/search.h"
@ -20,12 +20,14 @@
#include "minhook/include/MinHook.h"
#include "popnhax/config.h"
#include "util/membuf.h"
#include "util/log.h"
#include "util/patch.h"
#include "util/xmlprop.hpp"
#include "xmlhelper.h"
#include "translation.h"
#include "custom_categs.h"
#include "omnimix_patch.h"
#include "tableinfo.h"
#include "loader.h"
@ -138,10 +140,10 @@ PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, patch_db,
"/popnhax/patch_db")
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, disable_multiboot,
"/popnhax/disable_multiboot")
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, patch_xml_auto,
"/popnhax/patch_xml_auto")
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_STR, struct popnhax_config, patch_xml_filename,
"/popnhax/patch_xml_filename")
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_STR, struct popnhax_config, force_patch_xml,
"/popnhax/force_patch_xml")
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, patch_xml_dump,
"/popnhax/patch_xml_dump")
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_BOOL, struct popnhax_config, practice_mode,
"/popnhax/practice_mode")
PSMAP_MEMBER_REQ(PSMAP_PROPERTY_TYPE_STR, struct popnhax_config, force_datecode,
@ -1636,15 +1638,24 @@ asm(
" jmp [_real_check_music_idx_usaneko]\n"
);
char *parse_patchdb(const char *input_filename, char *base_data) {
char *parse_patchdb(const char *input_filename, char *base_data, membuf_t *membuf) {
property* config_xml;
if ( input_filename == NULL )
{
config_xml = load_prop_membuf(membuf);
}
else
{
const char *folder = "data_mods\\";
char *input_filepath = (char*)calloc(strlen(input_filename) + strlen(folder) + 1, sizeof(char));
sprintf(input_filepath, "%s%s", folder, input_filename);
property* config_xml = load_prop_file(input_filepath);
config_xml = load_prop_file(input_filepath);
free(input_filepath);
}
char *target = (char*)calloc(64, sizeof(char));
property_node_refer(config_xml, property_search(config_xml, NULL, "/patches"), "target@", PROPERTY_TYPE_ATTR, target, 64);
@ -1998,35 +2009,45 @@ static bool patch_database() {
char *target;
if (config.patch_xml_auto) {
if ( strcmp(config.force_patch_xml, "") != 0 )
{
LOG("popnhax: patch_db: force patch file %s", config.force_patch_xml);
target = parse_patchdb(config.force_patch_xml, data, NULL);
}
else
{
const char *filename = NULL;
SearchFile s;
uint8_t *datecode = NULL;
bool found = false;
uint32_t music_limit = 0;
if ( !config.ignore_music_limit && !get_music_limit(&music_limit) )
if ( !config.ignore_music_limit )
{
if ( !get_music_limit(&music_limit) ) {
LOG("WARNING: could not retrieve music limit\n");
} else {
LOG("popnhax: patch_db: music limit : %d\n", music_limit);
}
} else {
LOG("popnhax: patch_db: ignore music limit\n");
}
if ( g_datecode_override != NULL )
{
LOG("popnhax: auto detect patch file with datecode override %s\n", g_datecode_override);
LOG("popnhax: patch_db: auto detect/generate patch file with datecode override\n");
datecode = (uint8_t*) strdup(g_datecode_override);
}
else
{
LOG("popnhax: auto detect patch file with datecode from ea3-config\n");
LOG("popnhax: patch_db: auto detect/generate patch file with datecode from ea3-config\n");
property *config_xml = load_prop_file("prop/ea3-config.xml");
READ_STR_OPT(config_xml, property_search(config_xml, NULL, "/ea3/soft"), "ext", datecode)
free(config_xml);
}
if (datecode == NULL) {
LOG("popnhax: patch_db: failed to retrieve datecode from ea3-config. Please disable patch_xml_auto option and use patch_xml_filename to specify which file should be used.\n");
return false;
LOG("popnhax: patch_db: failed to retrieve datecode. You can either rename popn22.dll to popn22_<datecode>.dll, fix ea3-config.xml, use force_datecode or use force_patch_xml.\n");
exit(1);
}
LOG("popnhax: patch_db: datecode : %s\n", datecode);
@ -2079,15 +2100,31 @@ static bool patch_database() {
}
if (!found) {
LOG("popnhax: patch_db: matching %s not found, please add the correct patch xml file in data_mods folder.\n", (music_limit == 0) ? "datecode" : "music limit");
return false;
LOG("popnhax: patch_db: no matching patch file found, generating our own (datecode %s)\n", datecode);
membuf_t *membuf = membuf_new(30000);
if (membuf == NULL)
{
LOG("popnhax: patch_db: Failed to allocate membuf\n");
exit(1);
}
LOG("popnhax: patch_db: using %s\n",filename);
target = parse_patchdb(filename, data);
make_omnimix_patch(g_game_dll_fn, membuf, (char*)datecode);
membuf_rewind(membuf);
if ( config.patch_xml_dump )
{
char out_filename[128];
sprintf(out_filename, "data_mods\\patches_%s.xml", (char*)datecode);
LOG("popnhax: patch_db: saving generated patches to %s\n", out_filename);
membuf_tofile(membuf, out_filename);
}
target = parse_patchdb(NULL, data, membuf);
membuf_free(membuf);
} else {
target = parse_patchdb(config.patch_xml_filename, data);
LOG("popnhax: patch_db: using %s\n",filename);
target = parse_patchdb(filename, data, NULL);
}
}
if (config.disable_redirection) {
@ -7873,7 +7910,7 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
if ( !config_process(g_config_fn) )
{
LOG("FATAL ERROR: Could not pre-process config file\n");
exit(0);
exit(1);
}
strcpy(g_config_fn+strlen(g_config_fn)-3, "opt");
@ -7881,7 +7918,7 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserv
if (!_load_config(g_config_fn, &config, config_psmap))
{
LOG("FATAL ERROR: Could not parse %s\n", g_config_fn);
exit(0);
exit(1);
}
config.game_version = game_version;

755
popnhax/omnimix_patch.cc Normal file
View File

@ -0,0 +1,755 @@
#include <cstdio>
#include <cstring>
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include "util/bst.h"
#include "util/log.h"
#include "util/patch.h"
#include "util/search.h"
#include "libdisasm/libdis.h"
#include "omnimix_patch.h"
#define LINE_SIZE 512
#define MUSIC_IDX 0
#define CHART_IDX 1
#define STYLE_IDX 2
#define FLAVOR_IDX 3
#define CHARA_IDX 4
#define NUM_IDX 5
bst_t *g_xref_bst = NULL;
bst_t *g_data_xref_bst = NULL;
bst_t *g_bad_offset_bst = NULL;
#if DEBUG==1
#define membuf_printf(membuf, ...) LOG(__VA_ARGS__)
#endif
// These offsets are known bads, you can add new offsets either in there or in data_mods\patches_blacklist.dat
uint32_t g_offset_blacklist[] = {
0x100be154,
0x100be346,
0x100bed91,
0x100e56ec,
0x100e56f5,
0x100e5a55,
0x100e5a5e,
0x100fa4e2
};
/* extra_data for bst */
typedef struct offset_list_s {
uint32_t offset;
struct offset_list_s *next;
struct offset_list_s *last;
} offset_list_t;
static void print_list(offset_list_t *list)
{
int depth = 0;
offset_list_t *curr_elem = list;
while ( curr_elem )
{
LOG("-> offset %0X ", curr_elem->offset );
depth++;
curr_elem = curr_elem->next;
}
LOG("\ndepth %d\n\n", depth);
}
/* extra_data_cb */
void* add_to_offset_list(void *list, void *new_tail)
{
offset_list_t *curr_elem = ((offset_list_t *)list)->last;
curr_elem->next = (offset_list_t *)new_tail;
((offset_list_t *)list)->last = (offset_list_t *)new_tail;
return list;
}
typedef struct table_s {
char *name;
uint32_t *addr;
uint32_t size; // size of one entry
uint32_t limit; // number of entries
} table_t;
table_t g_tables[NUM_IDX];
typedef struct update_patches_s {
uint8_t idx;
uint8_t patch_method;
uint32_t value;
uint32_t offset; // optional, for weird patches
} update_patches_t;
static uint32_t *_find_binary_ex(char *data, DWORD dllSize, const char *search_pattern, uint32_t search_size, uint32_t search_head, int8_t search_idx, bool wildcards)
{
//fprintf(stderr, "find binary %.*s\n", search_size, search_pattern);
uint32_t *ea = 0;
int8_t found = -1;
while (true)
{
int64_t pattern_offset;
if (wildcards)
pattern_offset = wildcard_search(data, dllSize -((uint32_t) ea)-1, search_pattern, search_size, ((uint32_t) ea)+1);
else
pattern_offset = search(data, dllSize -((uint32_t) ea)-1, search_pattern, search_size, ((uint32_t) ea)+1);
if (pattern_offset == -1)
break;
ea = (uint32_t *) (pattern_offset + search_head);
found++;
if (found != search_idx)
continue;
return (uint32_t *) ((int64_t) data + (int64_t) ea);
}
return NULL;
}
uint32_t *find_binary(char *data, DWORD dllSize, const char *search_pattern, uint32_t search_size, uint32_t search_head, int8_t search_idx)
{
return _find_binary_ex(data, dllSize, search_pattern, search_size, search_head, search_idx, false);
}
uint32_t *find_binary_wildcard(char *data, DWORD dllSize, const char *search_pattern, uint32_t search_size, uint32_t search_head, int8_t search_idx)
{
return _find_binary_ex(data, dllSize, search_pattern, search_size, search_head, search_idx, true);
}
uint32_t *find_binary_xref(char *data, DWORD dllSize, const char *search_pattern, uint32_t search_size, uint32_t search_head, int8_t search_idx, int8_t xref_search_idx)
{
uint32_t* ea = find_binary(data, dllSize, search_pattern, search_size, search_head, search_idx);
if (ea == NULL)
return NULL;
char *as_hex = (char *) &ea;
ea = find_binary(data, dllSize, as_hex, 4, 0, xref_search_idx);
return ea;
}
uint32_t get_table_size_by_xref(uint32_t *ea, uint32_t entry_size)
{
uint32_t *orig_ea = ea;
// Skip 10 entries because why not. We're looking for the end anyway
ea = (uint32_t*)((uint32_t)ea+(entry_size*10));
//fprintf(stderr, "orig ea %p, entry size %u, ea %p\n", orig_ea, entry_size, ea);
bool found = false;
while (!found)
{
uint32_t *as_int = (uint32_t*)&ea;
if ( bst_search(g_xref_bst, *as_int) != NULL )
{
//fprintf(stderr, "found .text XREF in BST\n");
found = true;
}
else if ( bst_search(g_data_xref_bst, *as_int) != NULL )
{
//fprintf(stderr, "found .data XREF in BST, data %0X\n", *as_int);
found = true;
}
if (!found)
ea = (uint32_t*)((uint32_t)ea+entry_size);
}
//fprintf(stderr, "found value %u (ea = %p, orig_ea = %p, entry size = %u)\n", ((((uint32_t)ea)-((uint32_t)orig_ea))/entry_size), ea, orig_ea, entry_size);
return (((uint32_t)ea)-((uint32_t)orig_ea))/entry_size;
}
void print_cb(x86_insn_t *insn, void *arg)
{
char line[LINE_SIZE]; /* buffer of line to print */
x86_format_insn(insn, line, LINE_SIZE, intel_syntax);
fprintf(stderr,"%s\n", line);
}
static void add_xref_to_bst(bst_t **bst, uint32_t addr, offset_list_t *extra_data)
{
*bst = bst_insert_ex(*bst, addr, (void *)extra_data, &add_to_offset_list);
}
offset_list_t *make_new(x86_insn_t *insn)
{
offset_list_t *res = (offset_list_t *)malloc(sizeof(offset_list_t));
if (res == NULL)
return NULL;
res->offset = insn->offset;
res->next = NULL;
res->last = res;
return res;
}
void add_xref(x86_insn_t *insn, void *arg)
{
uint32_t addr = x86_get_address(insn);
if ( addr > 0x5 )
{
offset_list_t *extra_data = make_new(insn);
add_xref_to_bst(&g_xref_bst, addr, extra_data);
}
x86_op_t *op = x86_get_imm(insn);
if ( op != NULL )
{
offset_list_t *extra_data = make_new(insn);
add_xref_to_bst(&g_xref_bst, op->data.dword, extra_data);
}
// doesn't have an address directly
op = x86_operand_1st(insn);
if ( op != NULL && op->type == op_expression )
{
x86_ea_t exp = op->data.expression;
offset_list_t *extra_data = make_new(insn);
add_xref_to_bst(&g_xref_bst, exp.disp, extra_data);
}
op = x86_operand_2nd(insn);
if ( op != NULL && op->type == op_expression )
{
x86_ea_t exp = op->data.expression;
offset_list_t *extra_data = make_new(insn);
add_xref_to_bst(&g_xref_bst, exp.disp, extra_data);
}
op = x86_operand_3rd(insn);
if ( op != NULL && op->type == op_expression )
{
x86_ea_t exp = op->data.expression;
offset_list_t *extra_data = make_new(insn);
add_xref_to_bst(&g_xref_bst, exp.disp, extra_data);
}
}
/* filtrer against known bad offsets and known bad instruction then write the disassembly in instruction_str and returns the offset containing the search value as hex */
static int32_t parse_instruction(char *buf, uint32_t buf_size, int32_t reloc_delta, uint32_t offset, uint32_t search_value, char *instruction_str)
{
char *as_hex = (char*)&search_value;
int32_t pattern_offset = search(buf+offset, 0x10, as_hex, 4, 0);
if (pattern_offset == -1)
{
//LOG("Couldnt find address in instruction, skipping...\n");
return -1;
/* fallback to 2 bytes: not needed so far */
/* if ( search_value <= 0xFFFF )
pattern_offset = search(buf+offset, 0x10, as_hex, 2, 0);
if ( pattern_offset == -1 )
{
LOG("Couldnt find address in instruction, skipping... (should not happen)\n");
return -1;
}
*/
}
if ( bst_search(g_bad_offset_bst, offset+(uint32_t)buf+pattern_offset+reloc_delta) != NULL )
{
LOG("skipping known bad offset\n");
return -1;
}
x86_insn_t insn;
int size = x86_disasm((unsigned char*)buf, buf_size, 0, offset, &insn);
if ( size )
{
x86_op_t *op = x86_operand_1st(&insn);
x86_op_t *op2 = x86_operand_2nd(&insn);
if ( op != NULL && op->type == op_expression && op->data.expression.disp == (int32_t)search_value && strcmp(op->data.expression.base.name, "esp") == 0 )
{
x86_oplist_free(&insn);
return -1;
}
if ( op2 != NULL && op2->type == op_expression && op2->data.expression.disp == (int32_t)search_value && strcmp(op2->data.expression.base.name, "esp") == 0 )
{
x86_oplist_free(&insn);
return -1;
}
/* print instruction */
x86_format_insn(&insn, instruction_str, LINE_SIZE, intel_syntax);
x86_oplist_free(&insn);
}
return pattern_offset;
}
static uint32_t get_previous_lea_ebx(char *buf, uint32_t buf_size, uint32_t offset, uint32_t* value)
{
x86_insn_t insn;
int delta = 1;
//TODO find a better way to go back in instructions... this might fail if we're unlucky
while(true)
{
int size = x86_disasm((unsigned char*)buf, buf_size, 0, offset-delta, &insn);
if ( size != 0 && insn.type == insn_mov && strcmp(insn.mnemonic, "lea")==0 )
{
x86_op_t *op = x86_operand_2nd(&insn);
if ( op != NULL && op->type == op_expression )
{
x86_ea_t exp = op->data.expression;
if (strcmp(exp.base.name, "ebx") == 0)
{
/* FOUND! */
*value = exp.disp;
break;
}
}
}
x86_oplist_free(&insn);
delta++;
}
x86_oplist_free(&insn);
return (uint32_t)buf+offset-delta;
}
static uint32_t get_previous_push(char *buf, uint32_t buf_size, uint32_t offset, uint32_t* push_value)
{
x86_insn_t insn;
int delta = 1;
//TODO find a better way to go back in instructions... this might fail if we're unlucky
while(true)
{
int size = x86_disasm((unsigned char*)buf, buf_size, 0, offset-delta, &insn);
if ( size != 0 && insn.type == insn_push )
{
/* FOUND! */
break;
}
x86_oplist_free(&insn);
delta++;
}
x86_op_t *op = x86_operand_1st(&insn);
if (op->datatype == op_word)
{
*push_value = op->data.sword;
}
else
{
*push_value = op->data.sdword;
}
x86_oplist_free(&insn);
return (uint32_t)buf+offset-delta;
}
static uint32_t get_next_call(char *buf, uint32_t buf_size, uint32_t offset, uint32_t* func_ea)
{
x86_insn_t insn;
int delta = 0;
while(true)
{
int size = x86_disasm((unsigned char*)buf, buf_size, 0, offset+delta, &insn);
if (insn.type == insn_call)
{
/* FOUND! */
break;
}
x86_oplist_free(&insn);
delta+=size;
}
x86_op_t *op = x86_operand_1st(&insn);
if ( op->type == op_relative_far )
{
if (op->datatype == op_word)
{
*func_ea = (uint32_t)buf + op->data.sword + insn.addr + insn.size;
}
else
{
*func_ea = (uint32_t)buf + op->data.sdword + insn.addr + insn.size;
}
}
x86_oplist_free(&insn);
return (uint32_t)buf+offset+delta;
}
bool find_weird_update_patches(char *buf, uint32_t buf_size, uint32_t music_limit, update_patches_t *out)
{
/* There are 4 patches to find with this routine, we assume "out" has room to store them */
bool new_popn = false;
if (music_limit > 2040)
new_popn = true;
uint32_t* ea;
if ( new_popn )
ea = find_binary(buf, buf_size, "\x83\xC4\x04\x89\x44\x24\x14\xC7", 8, 0, 0);
else
ea = find_binary(buf, buf_size, "\x83\xC4\x04\x3B\xC5\x74\x09", 7, 0, 0);
/* part1: find "push" patch */
uint32_t push_value = 0;
uint32_t push_ea = get_previous_push(buf, buf_size, (uint32_t)ea-(uint32_t)buf, &push_value);
out[3] = { .idx = MUSIC_IDX, .patch_method = 11, .value = push_value, .offset = push_ea};
uint32_t func_ea = 0;
(void)get_next_call(buf, buf_size, (uint32_t)ea-(uint32_t)buf, &func_ea);
/* seek end of func_ea */
uint32_t *func_ea_end = find_binary((char *)func_ea, 0x10000, "\xCC\xCC\xCC\xCC\xCC", 5, 0, 0);
/* part2: now we need to find 3 "lea ?, [ebx+?????]" instructions at the end of func_ea */
uint32_t lea_value = 0;
uint32_t lea_ea = (uint32_t)func_ea_end;
for (uint8_t i = 0; i < 3; i++)
{
if ( new_popn ) //in newer games we have to skip every other match
lea_ea = get_previous_lea_ebx(buf, buf_size, (uint32_t)lea_ea-(uint32_t)buf, &lea_value);
lea_ea = get_previous_lea_ebx(buf, buf_size, (uint32_t)lea_ea-(uint32_t)buf, &lea_value);
out[2-i] = { .idx = MUSIC_IDX, .patch_method = (uint8_t)(11-i), .value = lea_value, .offset = lea_ea};
}
return true;
}
int load_offset_blacklist_from_file(){
int res = 0;
FILE *file = fopen("data_mods\\patches_blacklist.dat", "rb");
if ( file == NULL )
{
return 0;
}
char line[32];
while (fgets(line, sizeof(line), file)) {
uint32_t offset = strtol(line, NULL, 16);
if ( offset != 0 && bst_search(g_bad_offset_bst, offset) == NULL )
{
res++;
add_xref_to_bst(&g_bad_offset_bst, offset, NULL);
}
}
fclose(file);
return res;
}
bool make_omnimix_patch(const char *dllFilename, membuf_t *membuf, char *datecode){
DWORD dllSize = 0;
char *buf = getDllData(dllFilename, &dllSize);
char *text = NULL;
DWORD text_size = 0;
char *data = NULL;
DWORD data_size = 0;
PIMAGE_NT_HEADERS headers = (PIMAGE_NT_HEADERS)(buf + ((PIMAGE_DOS_HEADER)buf)->e_lfanew);
PIMAGE_SECTION_HEADER section_header = IMAGE_FIRST_SECTION(headers);
uint32_t buf_base_addr = (uint32_t)(headers->OptionalHeader.ImageBase);
int32_t buf_reloc_delta = buf_base_addr - (uint32_t)buf;
uint32_t text_base_addr = 0;
for (WORD SectionIndex = 0; SectionIndex < headers->FileHeader.NumberOfSections; SectionIndex++)
{
PIMAGE_SECTION_HEADER curr_header = &section_header[SectionIndex];
if ( strcmp( (const char*)curr_header->Name, ".text" ) == 0 || curr_header->Characteristics & IMAGE_SCN_CNT_CODE )
{
text = (char*) ((int64_t)buf + curr_header->VirtualAddress);
text_base_addr = buf_base_addr + curr_header->VirtualAddress;
text_size = curr_header->Misc.VirtualSize;
}
if ( strcmp( (const char*)curr_header->Name, ".data" ) == 0 )
{
data = (char*) ((int64_t)buf + curr_header->VirtualAddress);
data_size = curr_header->Misc.VirtualSize;
}
}
x86_init(opt_none, NULL, NULL);
// disassemble .text section and add all xrefs to BST
LOG("popnhax: patch_db_gen: Disassembling .text section... ");
unsigned int num_insn = x86_disasm_range( (unsigned char *)text, (uint32_t) buf, 0, text_size, &add_xref, NULL);
LOG("done (%u instructions)\n", num_insn);
//add all data xrefs to BST (take anything which looks like an address in .data section (eg. 0x10******) and is at an offset =0%4.. experimental)
{
uint8_t search_byte = ((uint8_t*)&buf)[3]; //retrieve highest byte from base address to account for relocations
LOG("popnhax: patch_db_gen: Parsing .data section... ");
uint32_t *data_ea = 0;
while (true)
{
int64_t pattern_offset = search(data, data_size -((uint32_t) data_ea)-1, (const char *)&search_byte, 1, ((uint32_t) data_ea)+1);
if (pattern_offset == -1)
break;
data_ea = (uint32_t *) ((uint32_t)data + pattern_offset - 3);
uint32_t *as_int = (uint32_t*)data_ea;
if (((((uint32_t)data_ea)&3) == 0))
add_xref_to_bst(&g_data_xref_bst, *as_int, NULL);
data_ea = (uint32_t *) (pattern_offset);
}
LOG("done\n");
}
//make bad offset bst
LOG("popnhax: patch_db_gen: Loading offset blacklist... ");
for (uint8_t i = 0; i < sizeof(g_offset_blacklist)/sizeof(uint32_t); i++)
{
add_xref_to_bst(&g_bad_offset_bst, g_offset_blacklist[i], NULL);
}
int bad_offset_count = load_offset_blacklist_from_file();
LOG("done");
if ( bad_offset_count > 0 )
LOG(" (%d additional bad offsets found in data_mods\\patches_blacklist.dat)", bad_offset_count);
LOG("\n");
LOG("popnhax: patch_db_gen: Generating patches\n");
membuf_printf(membuf, "<?xml version='1.0' encoding='shift-jis'?>\n");
membuf_printf(membuf, "<patches target=\"%s\">\n", datecode);
/* init tables */
g_tables[MUSIC_IDX].name = strdup("music");
g_tables[CHART_IDX].name = strdup("chart");
g_tables[STYLE_IDX].name = strdup("style");
g_tables[FLAVOR_IDX].name = strdup("flavor");
g_tables[CHARA_IDX].name = strdup("chara");
LOG("popnhax: patch_db_gen: buffer_base_addrs... ");
// These all reference the first entry in their respective tables
g_tables[MUSIC_IDX].addr = find_binary_xref(buf, dllSize, "\x00\x83\x7C\x83\x62\x83\x76\x83\x58\x00", 10, 1, 0, 0);
g_tables[CHART_IDX].addr = find_binary_xref(buf, dllSize, "\x00\x70\x6F\x70\x6E\x31\x00\x00", 8, 1, 0, 1);
g_tables[STYLE_IDX].addr = find_binary(buf, dllSize, "\x01\x00\x00\x00\xFF\x54\x0C\x00\x1A\x00\x00\x00\x11\x00\x00\x00", 16, 0, 2);
g_tables[FLAVOR_IDX].addr = find_binary(buf, dllSize, "\x00\x82\xBB\x82\xEA\x82\xA2\x82\xAF\x81\x5B\x00\x00\x00\x82\xA4\x82", 17, 1, 0);
g_tables[CHARA_IDX].addr = find_binary_xref(buf, dllSize, "\x00\x62\x61\x6D\x62\x5F\x31\x61\x00", 9, 1, 0, 0);
LOG("done\n");
// Modify the entry sizes as required
g_tables[MUSIC_IDX].size = 0xAC;
g_tables[CHART_IDX].size = 0x20; // Probably won't change?
g_tables[STYLE_IDX].size = 0x10; // Unlikely to change
g_tables[FLAVOR_IDX].size = 0x60;
g_tables[CHARA_IDX].size = 0x4C;
LOG("popnhax: patch_db_gen: limits... ");
// buffer_addr + (buffer_entry_size * limit) should give you the very end of the array (after the last entry)
g_tables[MUSIC_IDX].limit = get_table_size_by_xref(g_tables[MUSIC_IDX].addr, g_tables[MUSIC_IDX].size);
g_tables[CHART_IDX].limit = get_table_size_by_xref(g_tables[CHART_IDX].addr, g_tables[CHART_IDX].size);
g_tables[STYLE_IDX].limit = get_table_size_by_xref(g_tables[STYLE_IDX].addr, g_tables[STYLE_IDX].size);
g_tables[FLAVOR_IDX].limit = get_table_size_by_xref(g_tables[FLAVOR_IDX].addr, g_tables[FLAVOR_IDX].size);
g_tables[CHARA_IDX].limit = get_table_size_by_xref(g_tables[CHARA_IDX].addr, g_tables[CHARA_IDX].size);
LOG("done\n");
membuf_printf(membuf, "\t<limits>\n");
for (int i=0; i < NUM_IDX; i++)
{
//patch_target, buffer_addr, entry_size = buffer_info
membuf_printf(membuf, "\t\t<%s __type=\"u32\">%d</%s>\n", g_tables[i].name, g_tables[i].limit, g_tables[i].name);
}
membuf_printf(membuf, "\t</limits>\n");
membuf_printf(membuf, "\t<buffer_base_addrs>\n");
for (int i=0; i < NUM_IDX; i++)
{
//patch_target, buffer_addr, entry_size = buffer_info
membuf_printf(membuf, "\t\t<%s __type=\"str\">0x%0x</%s>\n", g_tables[i].name, (uint32_t)(g_tables[i].addr)+buf_reloc_delta, g_tables[i].name);
}
membuf_printf(membuf, "\t</buffer_base_addrs>\n");
LOG("popnhax: patch_db_gen: buffers_patch_addrs... ");
membuf_printf(membuf, "\t<buffers_patch_addrs>\n");
for (int i=0; i < NUM_IDX; i++)
{
uint32_t search_value_base = (uint32_t)g_tables[i].addr;
for (uint32_t j = search_value_base; j < (search_value_base + g_tables[i].size + 1); j++)
{
bst_t *bst = bst_search(g_xref_bst, j);
if ( bst != NULL )
{
offset_list_t *curr_elem = (offset_list_t*)(bst->extra_data);
while ( curr_elem )
{
char line[LINE_SIZE];
line[0] = '\0';
int32_t addr_offset = parse_instruction(text, text_size, buf_reloc_delta, curr_elem->offset, j, line);
if (addr_offset != -1)
{
membuf_printf(membuf, "\t\t<!-- %s -->\n", line);
membuf_printf(membuf, "\t\t<%s __type=\"str\">0x%0x</%s>\n\n", g_tables[i].name, (uint32_t)text_base_addr+(curr_elem->offset)+addr_offset, g_tables[i].name);
}
curr_elem = curr_elem->next;
}
}
}
}
// This is a hack for Usaneko.
// Usaneko's code is dumb.
// If it doesn't find *this* address it won't stop the loop.
{
uint32_t random_lv7 = (uint32_t)find_binary_xref(buf, dllSize, "\x83\x89\x83\x93\x83\x5F\x83\x80\x20\x4C\x76\x20\x37\x00\x00\x00", 16, 0, 0, 0);
bst_t *bst = bst_search(g_xref_bst, random_lv7);
if ( bst != NULL )
{
offset_list_t *curr_elem = (offset_list_t*)(bst->extra_data);
while ( curr_elem )
{
char line[LINE_SIZE];
line[0] = '\0';
int32_t addr_offset = parse_instruction(text, text_size, buf_reloc_delta, curr_elem->offset, random_lv7, line);
if (addr_offset != -1)
{
membuf_printf(membuf, "\t\t<!-- %s -->\n", line);
membuf_printf(membuf, "\t\t<%s __type=\"str\">0x%0x</%s>\n\n", g_tables[MUSIC_IDX].name, (uint32_t)text_base_addr+(curr_elem->offset)+addr_offset, g_tables[MUSIC_IDX].name);
}
curr_elem = curr_elem->next;
}
}
}
LOG("done\n");
membuf_printf(membuf, "\t</buffers_patch_addrs>\n");
membuf_printf(membuf, "\t<other_patches>\n");
/* create update_patches */
update_patches_t update_patches[23] = {
{ .idx = MUSIC_IDX, .patch_method = 0, .value = g_tables[MUSIC_IDX].limit - 1 },
{ .idx = MUSIC_IDX, .patch_method = 0, .value = g_tables[MUSIC_IDX].limit },
{ .idx = CHART_IDX, .patch_method = 0, .value = g_tables[CHART_IDX].limit },
{ .idx = CHART_IDX, .patch_method = 0, .value = g_tables[CHART_IDX].limit - 1 },
{ .idx = CHARA_IDX, .patch_method = 0, .value = g_tables[CHARA_IDX].limit },
{ .idx = FLAVOR_IDX, .patch_method = 0, .value = g_tables[FLAVOR_IDX].limit - 1 },
{ .idx = FLAVOR_IDX, .patch_method = 0, .value = g_tables[FLAVOR_IDX].limit },
/* These values may change in a future patch, but they worked for usaneko/peace/kaimei/unilab for now.
* These could possibly be done using something similar to the find_weird_update_patches code. */
{ .idx = MUSIC_IDX, .patch_method = 1, .value = 0x1BD0 - (1780 - g_tables[MUSIC_IDX].limit) * 4 },
{ .idx = MUSIC_IDX, .patch_method = 1, .value = 0x1Bcf - (1780 - g_tables[MUSIC_IDX].limit) * 4 },
{ .idx = MUSIC_IDX, .patch_method = 2, .value = 0xA6E0 - (1780 - g_tables[MUSIC_IDX].limit) * 0x18 },
{ .idx = MUSIC_IDX, .patch_method = 3, .value = 0x29B7 - (1780 - g_tables[MUSIC_IDX].limit) * 6 },
{ .idx = MUSIC_IDX, .patch_method = 4, .value = 0x3E944 - (1780 - g_tables[MUSIC_IDX].limit) * 0x90 },
{ .idx = MUSIC_IDX, .patch_method = 4, .value = 0x3E948 - (1780 - g_tables[MUSIC_IDX].limit) * 0x90 },
{ .idx = MUSIC_IDX, .patch_method = 5, .value = 0x1F4F4 - (1780 - g_tables[MUSIC_IDX].limit) * 0x48 },
{ .idx = MUSIC_IDX, .patch_method = 5, .value = 0x1F4C0 - (1780 - g_tables[MUSIC_IDX].limit) * 0x48 },
{ .idx = MUSIC_IDX, .patch_method = 5, .value = 0x1F4F0 - (1780 - g_tables[MUSIC_IDX].limit) * 0x48 },
{ .idx = MUSIC_IDX, .patch_method = 6, .value = 0x7D3D8 - (1780 - g_tables[MUSIC_IDX].limit) * 0x120 },
{ .idx = MUSIC_IDX, .patch_method = 6, .value = 0x7D3D4 - (1780 - g_tables[MUSIC_IDX].limit) * 0x120 },
{ .idx = MUSIC_IDX, .patch_method = 7, .value = 0x1D8E58 - (1780 - g_tables[MUSIC_IDX].limit) * 0x440 },
{ .idx = MUSIC_IDX, .patch_method = 7, .value = 0x1D9188 - (1780 - g_tables[MUSIC_IDX].limit) * 0x440 },
{ .idx = MUSIC_IDX, .patch_method = 8, .value = 0x5370 - (1780 - g_tables[MUSIC_IDX].limit) * 0x0c },
{ .idx = FLAVOR_IDX, .patch_method = 8, .value = g_tables[FLAVOR_IDX].limit * 0x0c },
{ .idx = FLAVOR_IDX, .patch_method = 8, .value = g_tables[FLAVOR_IDX].limit * 0x0c + 4 },
};
LOG("popnhax: patch_db_gen: other_patches... ");
for (int i = 0; i < 23; i++)
{
uint32_t j = update_patches[i].value;
bst_t *bst = bst_search(g_xref_bst, j);
if ( bst != NULL )
{
offset_list_t *curr_elem = (offset_list_t*)(bst->extra_data);
while ( curr_elem )
{
char line[LINE_SIZE];
line[0] = '\0';
int32_t addr_offset = parse_instruction(text, text_size, buf_reloc_delta, curr_elem->offset, j, line);
if (addr_offset != -1)
{
membuf_printf(membuf, "\t\t<!-- %s -->\n", line);
membuf_printf(membuf, "\t\t<%s __type=\"str\" method=\"%d\" expected=\"0x%x\">0x%0x</%s>\n\n", g_tables[update_patches[i].idx].name, update_patches[i].patch_method, update_patches[i].value, (uint32_t)text_base_addr+(curr_elem->offset)+addr_offset, g_tables[update_patches[i].idx].name);
}
curr_elem = curr_elem->next;
}
}
}
update_patches_t weird_update_patches[4];
find_weird_update_patches(buf, dllSize, g_tables[MUSIC_IDX].limit, weird_update_patches);
for (int i = 0; i < 4; i++)
{
char line[LINE_SIZE];
line[0] = '\0';
int32_t addr_offset = parse_instruction(buf, dllSize, buf_reloc_delta, weird_update_patches[i].offset-(uint32_t)buf, weird_update_patches[i].value, line);
if (addr_offset != -1)
{
membuf_printf(membuf, "\t\t<!-- %s -->\n", line);
membuf_printf(membuf, "\t\t<%s __type=\"str\" method=\"%d\" expected=\"0x%x\">0x%0x</%s>\n\n", g_tables[weird_update_patches[i].idx].name, weird_update_patches[i].patch_method, weird_update_patches[i].value, weird_update_patches[i].offset+addr_offset+buf_reloc_delta, g_tables[weird_update_patches[i].idx].name);
}
}
LOG("done\n");
membuf_printf(membuf, "\t</other_patches>\n");
uint32_t *hook_addrs[2] = { find_binary_wildcard(buf, dllSize, "\x8B\xC6\xE8????\x83\xF8?\x7D?\x56\x8A\xC3\xE8????\x83\xC4\x04\x3D????\x7D?", 30, 0, 0),
find_binary_wildcard(buf, dllSize, "\x83\xF8?\x0F\x9C\xC0\xE8", 7, 0, 0)};
LOG("popnhax: patch_db_gen: hook_addrs... ");
membuf_printf(membuf, "\t<hook_addrs>\n");
for (int i=0; i<2; i++)
{
if (hook_addrs[i] == NULL)
continue;
char line[LINE_SIZE];
x86_insn_t insn;
int delta = 0;
if (i == 1)
{
//hook offset is actually 2 instructions later in this case
delta = x86_disasm((unsigned char*)buf, dllSize, 0, ((uint32_t)(hook_addrs[i])-(uint32_t)buf), &insn);
x86_oplist_free(&insn);
delta += x86_disasm((unsigned char*)buf, dllSize, 0, ((uint32_t)(hook_addrs[i])-(uint32_t)buf+delta), &insn);
x86_oplist_free(&insn);
}
int size = x86_disasm((unsigned char*)buf, dllSize, 0, ((uint32_t)(hook_addrs[i])-(uint32_t)buf+delta), &insn);
if ( size ) {
/* print instruction */
x86_format_insn(&insn, line, LINE_SIZE, intel_syntax);
membuf_printf(membuf, "\t\t<!-- %s -->\n", line);
x86_oplist_free(&insn);
}
membuf_printf(membuf, "\t\t<offset __type=\"str\" method=\"%d\">0x%x</offset>\n\n", i, (uint32_t)hook_addrs[i]+delta+buf_reloc_delta);
}
LOG("done\n");
membuf_printf(membuf, "\t</hook_addrs>\n");
membuf_printf(membuf, "</patches>\n");
LOG("popnhax: patch_db_gen: Patch data generated\n");
x86_cleanup();
return true;
}

8
popnhax/omnimix_patch.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef __OMNIMIX_PATCH_H__
#define __OMNIMIX_PATCH_H__
#include "util/membuf.h"
bool make_omnimix_patch(const char *dllFilename, membuf_t *membuf, char *datecode);
#endif

View File

@ -2,6 +2,7 @@ libs += util
srcpp_util := \
bst.cc \
membuf.cc \
search.cc \
cmdline.cc \
patch.cc \

View File

@ -14,7 +14,7 @@ bst_t* bst_search(bst_t *root, uint32_t val)
return bst_search(root->left,val);
}
bst_t* bst_insert(bst_t *root, uint32_t val)
bst_t* bst_insert_ex(bst_t *root, uint32_t val, void* extra_data, void*(*extra_data_cb)(void *arg1, void *arg2))
{
if ( root == NULL )
{
@ -23,12 +23,23 @@ bst_t* bst_insert(bst_t *root, uint32_t val)
p->data = val;
p->left = NULL;
p->right = NULL;
p->extra_data = extra_data;
return p;
}
else if ( extra_data != NULL && extra_data_cb != NULL && val == root->data )
{
//already existing, process extra_data
root->extra_data = extra_data_cb(root->extra_data, extra_data);
}
else if ( val > root->data )
root->right = bst_insert(root->right, val);
root->right = bst_insert_ex(root->right, val, extra_data, extra_data_cb);
else
root->left = bst_insert(root->left, val);
root->left = bst_insert_ex(root->left, val, extra_data, extra_data_cb);
return root;
}
bst_t* bst_insert(bst_t *root, uint32_t val)
{
return bst_insert_ex(root, val, NULL, NULL);
}

View File

@ -1,14 +1,18 @@
#ifndef __BST_H__
#define __BST_H__
#include <stdint.h>
typedef struct bst_s
{
uint32_t data;
void *extra_data;
struct bst_s *right;
struct bst_s *left;
} bst_t;
bst_t* bst_search(bst_t *root, uint32_t val);
bst_t* bst_insert(bst_t *root, uint32_t val);
bst_t* bst_insert_ex(bst_t *root, uint32_t val, void *extra_data, void*(*extra_data_cb)(void *arg1, void *arg2));
#endif

59
util/membuf.cc Normal file
View File

@ -0,0 +1,59 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include "membuf.h"
membuf_t* membuf_new(uint32_t size)
{
char *buffer = (char *)malloc(size);
membuf_t *res = (membuf_t *)malloc(sizeof(membuf_t));
res->buffer = buffer;
res->size = 0;
res->idx = 0;
return res;
}
void membuf_free(membuf_t* membuf)
{
free(membuf->buffer);
free(membuf);
}
void membuf_rewind(membuf_t *membuf)
{
membuf->idx = 0;
}
int membuf_printf(membuf_t *membuf, const char *fmt, ...)
{
va_list args;
char line[256];
va_start(args, fmt);
vsprintf(line, fmt, args);
va_end(args);
strcpy((membuf->buffer)+(membuf->idx), line);
membuf->size += strlen(line);
membuf->idx += strlen(line);
return strlen(line);
}
bool membuf_tofile(membuf_t *membuf, char *filepath)
{
FILE *file = fopen(filepath, "w");
if (!file)
return false;
int results = fputs(membuf->buffer, file);
fclose(file);
if (results == EOF) {
return false;
}
return true;
}

18
util/membuf.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef __MEMBUF_H__
#define __MEMBUF_H__
#include <stdint.h>
typedef struct membuf_s {
char *buffer;
uint32_t size;
uint32_t idx;
} membuf_t;
membuf_t* membuf_new(uint32_t size);
void membuf_free(membuf_t* membuf);
void membuf_rewind(membuf_t *membuf);
int membuf_printf(membuf_t *membuf, const char *fmt, ...);
bool membuf_tofile(membuf_t *membuf, char *filepath);
#endif

View File

@ -4,6 +4,7 @@
#include <stdio.h>
#include "imports/avs.h"
#include "util/membuf.h"
struct property_psmap {
uint8_t type;
@ -52,6 +53,45 @@ int reader_callback(uint32_t context, void *bytes, size_t nbytes) {
return fread(bytes, 1, nbytes, (FILE *)TlsGetValue(context));
}
int membuf_reader_callback(uint32_t context, void *bytes, size_t nbytes) {
int written = nbytes;
membuf_t *membuf = (membuf_t *)TlsGetValue(context);
if ( membuf->idx + nbytes > membuf->size )
written = membuf->size - membuf->idx;
memcpy(bytes, (membuf->buffer)+membuf->idx, written);
membuf->idx += written;
return written;
}
struct property *load_prop_membuf(membuf_t *membuf) {
if (!membuf) {
printf("Could not open membuf %p\n", membuf);
return nullptr;
}
DWORD tlsIndex = TlsAlloc();
TlsSetValue(tlsIndex, (void *)membuf);
const size_t size = property_read_query_memsize(membuf_reader_callback, tlsIndex, NULL, NULL);
membuf_rewind(membuf);
struct property_node *buffer =
(struct property_node *)calloc(size + 0x10000, sizeof(unsigned char));
struct property *config_data = property_create(0x17, buffer, size + 0x10000);
if (size > 0) {
const int ret = property_insert_read(config_data, 0, membuf_reader_callback, tlsIndex);
if (!ret) {
printf("Could not load buffer\n");
exit(-6);
}
}
return config_data;
}
struct property *load_prop_file(const char *filename) {
FILE *file = fopen(filename, "rb");
DWORD tlsIndex = TlsAlloc();