SPICE86은 소스 코드를 사용할 수없는 실제 모드 DOS 프로그램을 실행, 리버스 엔지니어링 및 재 작성하는 도구입니다.
릴리스는 Nuget에서 제공됩니다.
프리 릴리스는 릴리스 페이지에서도 제공됩니다
참고 : 이것은 포트이며 원래 Java Spice86의 연속입니다.
.NET 9가 필요하고 Windows, MacOS 및 Linux에서 실행됩니다.
바이너리에서만 프로그램을 다시 작성하는 것은 어려운 작업입니다.
SPICE86은 방법 적 분열 및 정복 접근 방식으로 수행하는 데 도움이되는 도구입니다.
일반 과정 :
이것은 .NET 프로그램이며 일반 명령 줄 또는 도트 넷 실행으로 실행합니다. File.exe라는 프로그램을 실행하는 예제 :
Spice86 -e file.exe
COM 파일 및 BIOS 파일도 지원됩니다.
에뮬레이터가 런타임 데이터를 덤프 해야하는 위치를 가리키는 SPICE86_DUMPS_Folder 환경 변수를 설정하는 것이 좋습니다. 변수가 설정되거나 -recordedDatadirectory 매개 변수가 통과 된 경우 에뮬레이터는 실행에 대한 많은 정보를 버립니다. 아무것도 설정되지 않으면 현재 디렉토리에 데이터가 덤프됩니다. 이미 데이터가 있으면 에뮬레이터가 먼저로드되어 완료하면 매번 0에서 시작할 필요가 없습니다!
--Ems (Default: false) Enables EMS memory. EMS adds 8 MB of memory accessible to DOS programs through the EMM Page Frame.
--A20Gate (Default: false) Disables the 20th address line to support programs relying on the rollover of memory addresses above the HMA (slightly above 1 MB).
-m, --Mt32RomsPath Zip file or directory containing the MT-32 ROM files
-c, --CDrive Path to C drive, default is exe parent
-r, --RecordedDataDirectory Directory to dump data to when not specified otherwise. Working directory if blank
-e, --Exe Required. Path to executable
-a, --ExeArgs List of parameters to give to the emulated program
-x, --ExpectedChecksum Hexadecimal string representing the expected SHA256 checksum of the emulated program
-f, --FailOnUnhandledPort (Default: false) If true, will fail when encountering an unhandled IO port. Useful to check for unimplemented hardware. false by default.
-g, --GdbPort gdb port, if empty gdb server will not be created. If not empty, application will pause until gdb connects
-o, --OverrideSupplierClassName Name of a class that will generate the initial function information. See documentation for more information.
-p, --ProgramEntryPointSegment (Default: 4096) Segment where to load the program. DOS PSP and MCB will be created before it.
-u, --UseCodeOverride (Default: true) <true or false> if false it will use the names provided by overrideSupplierClassName but not the code
-i, --InstructionsPerSecond <number of instructions that have to be executed by the emulator to consider a second passed> if blank will use time based timer.
-t, --TimeMultiplier (Default: 1) <time multiplier> if >1 will go faster, if <1 will go slower.
-d, --DumpDataOnExit (Default: true) When true, records data at runtime and dumps them at exit time
-h, --HeadlessMode (Default: false) Headless mode. If true, no GUI is shown.
-l, --VerboseLogs (Default: false) Enable verbose level logs
-w, --WarningLogs (Default: false) Enable warning level logs
-s, --SilencedLogs (Default: false) Disable all logs
-i, --InitializeDOS (Default: true) Install DOS interrupt vectors or not.
--StructureFile Path to a C header file that describes the structures in the application. Works best with exports from IDA or Ghidra
--help Display this help screen.
--version Display version information.
Spice86은 GDB 원격 프로토콜을 사용합니다.
SPICE86을 시작할 때 시작하려면 GDB 서버의 포트를 지정해야합니다.
Spice86 --GdbPort=10000
SPICE86은 실행을 시작하기 전에 GDB가 연결되기를 기다리므로 중단 점을 설정할 수 있습니다.
GDB 명령 줄 클라이언트에서 연결하는 방법과 아키텍처 설정 방법은 다음과 같습니다.
(gdb) target remote localhost:10000
(gdb) set architecture i8086
중단 점, 단계, 메모리보기 등을 추가 할 수 있습니다.
VGA VRAM의 중단 점이있는 예는 다음과 같습니다.
(gdb) watch *0xA0000
조립 조립 :
(gdb) layout asm
중단 점 제거 :
(gdb) remove 1
메모리에서 바이트 시퀀스를 검색합니다 (시작 주소 0, 길이 F0000, 'spice86'string의 ASCII 바이트) :
(gdb) find /b 0x0, 0xF0000, 0x53, 0x70, 0x69, 0x63, 0x65, 0x38, 0x36
GDB는 X86 실제 모드 세그먼트 주소 지정을 지원하지 않으므로 포인터는 메모리의 실제 물리적 주소를 참조해야합니다. 주소 A000 : 0000의 VRAM은 GDB에서 0xa0000입니다.
마찬가지로, GDB의 $ PC 변수는 CS : IP가 지적한 물리적 주소로 SPICE86에 의해 노출됩니다.
사용자 정의 명령 목록은 다음과 같이 표시 될 수 있습니다.
(gdb) monitor help
(gdb) monitor dumpall
아래에 설명 된 모든 것을 한 번에 덤프합니다. 여기에 설명 된대로 덤프 폴더에서 파일이 생성됩니다. 여러 파일이 생성됩니다.
X 후 CPU 사이클 후 중단 :
(gdb) monitor breakCycles 1000
에뮬레이션 된 프로그램의 끝에서 중단하십시오.
(gdb) monitor breakStop
#디버깅하는 동안 화면 또는 버퍼를 반발시킵니다
(gdb) monitor vbuffer refresh
GDB에 대한 즐겁고 생산적인 경험을 위해 SEERGDB 클라이언트를 적극 권장합니다.
Cryo Dune이있는 콘크리트 예.
먼저 프로그램을 실행하고 Spice86에서 모든 것이 잘 작동하는지 확인하십시오. 문제가 발생하면 구현되지 않은 하드웨어 / DOS / BIOS 기능으로 인한 것일 수 있습니다.
SPICE86이 종료되면 현재 폴더 또는 ENV 변수로 지정된 폴더에 데이터를 덤프해야합니다.
Spice86-Ghidra-Plugin으로 Ghidra에서 데이터를 열고 코드를 생성하십시오. SPICE86-DOTNET-TEMPLATES를 통해 생성하는 템플릿 프로젝트에서 생성 된 파일을 가져올 수 있습니다.
dotnet new spice86.project
프로그램 원본 어셈블리 코드를 무시하기 위해 자신의 C# 코드를 제공 할 수 있습니다.
SPICE86은 함수의 메모리 주소와 C# 오버라이드 사이에 매핑을 빌드하는 spice86.core.emulator.function.ioverridesupplier의 인스턴스를 입력 할 수 있습니다.
완전한 예를 들어, 극저온의 소스 코드를 확인할 수 있습니다.
다음은 어떻게 보이는지에 대한 간단한 예입니다.
namespace My . Program ;
// This class is responsible for providing the overrides to spice86.
// There is only one per program you reimplement.
public class MyProgramOverrideSupplier : IOverrideSupplier {
public IDictionary < SegmentedAddress , FunctionInformation > GenerateFunctionInformations ( int programStartSegment ,
Machine machine ) {
Dictionary < SegmentedAddress , FunctionInformation > res = new ( ) ;
// In more complex examples, overrides may call each other
new MyOverrides ( res , programStartSegment , machine ) ;
return res ;
}
public override string ToString ( ) {
return "Overrides My program exe. class is " + GetType ( ) . FullName ;
}
}
// This class contains the actual overrides. As the project grows, you will probably need to split the reverse engineered code in several classes.
public class MyOverrides : CSharpOverrideHelper {
private MyOverridesGlobalsOnDs globalsOnDs ;
public MyOverrides ( IDictionary < SegmentedAddress , FunctionInformation > functionInformations , int segment , Machine machine ) {
// "myOverides" is a prefix that will be appended to all the function names defined in this class
base ( functionInformations , "myOverides" , machine ) ;
globalsOnDs = new MyOverridesGlobalsOnDs ( machine ) ;
// incUnknown47A8_0x1ED_0xA1E8_0xC0B8 will get executed instead of the assembly code when a call to 1ED:A1E8 is performed.
// Also when dumping functions, the name myOverides.incUnknown47A8 or instead of unknown
// Note: the segment is provided in parameter as spice86 can load executables in different places depending on the configuration
DefineFunction ( segment , 0xA1E8 , "incDialogueCount47A8" , IncDialogueCount47A8_0x1ED_0xA1E8_0xC0B8 ) ;
DefineFunction ( segment , 0x0100 , "addOneToAX" , AddOneToAX_0x1ED_0x100_0x1FD0 ) ;
}
public Action IncDialogueCount47A8_0x1ED_0xA1E8_0xC0B8 ( ) {
// Accessing the memory via accessors
globalsOnDs . SetDialogueCount47A8 ( globalsOnDs . GetDialogueCount47A8 ( ) + 1 ) ;
// Depends on the actual return instruction performed by the function, needed to be called from the emulated code as
// some programs like to mess with the stack ...
return NearRet ( ) ;
}
private Action AddOneToAX_0x1ED_0x100_0x1FD0 ( ) {
// Assembly for this would be
// INC AX
// RETF
// Note that you can access the whole emulator to change the state in the overrides.
state . AX ++ ;
return NearRet ( ) ;
}
}
// Memory accesses can be encapsulated into classes like this to give names to addresses and make the code shorter.
public class MyOverridesGlobalsOnDs : MemoryBasedDataStructureWithDsBaseAddress {
public DialoguesGlobalsOnDs ( Machine machine ) {
base ( machine ) ;
}
public void SetDialogueCount47A8 ( int value ) {
this . SetUint8 ( 0x47A8 , value ) ;
}
public int GetDialogueCount47A8 ( ) {
return this . GetUint8 ( 0x47A8 ) ;
}
}기억하십시오 : spice86에 조립 코드를 사용하여 프로젝트를 디버깅 할 때 "-usecodeoverride true"명령 줄 인수와 함께 재정의해야합니다.
DOS 프로그램의 필수 경로와 함께 -exepath 인수와 함께 전달되었습니다.
SPICE86에는 모방 된 프로그램을 디버깅하는 데 사용할 수있는 내장 디버거가 함께 제공됩니다. 메모리, 분해, 레지스터 및 스택을 검사 할 수있는 간단한 디버거입니다.
구조 뷰어를 사용하면 체계적인 방식으로 메모리를 검사 할 수 있습니다. DOS PSP, DOS MCB, VGA 레지스터 등과 같은 구조로 메모리를 검사하는 것이 유용합니다.
먼저 응용 프로그램의 구조를 설명하는 C 헤더 파일이 필요합니다. Ghidra 또는 IDA로 하나를 생성 할 수 있습니다. 그런 다음 --StructureFile 파일 명령 라인 인수로로드 할 수 있습니다. 이렇게하면 디버거의 메모리 탭에서 "구조보기"버튼이 활성화됩니다.
그곳에서 세그먼트를 입력하십시오 : 오프셋 주소와 보려는 구조를 선택하십시오. 구조는 트리 뷰와 메모리가 16 진수로 표시됩니다.
응용 프로그램이 일시 중지 될 때마다 디스플레이가 업데이트되므로 프로그램을 통해 구조가 어떻게 변하는 지 확인할 수 있습니다. GHIDRA 또는 IDA에서 새 C 헤더 파일을 내보내면 새 정보를 실시간으로 구조 뷰어를 업데이트합니다.
메모리 탭에서 다양한 바이트를 선택하고 마우스 오른쪽 버튼을 클릭하여 구조보기를 입력 할 수도 있습니다.
옵션 -Cdrive 와 함께 에뮬레이션 된 DOS 기능에 대한 C : 드라이브를 제공 할 수 있습니다. 기본값은 현재 폴더입니다. 일부 게임의 경우 C 드라이브를 게임 폴더로 설정해야 할 수도 있습니다.
옵션 -exeargs를 사용하여 인수 (Max 127 Chars!)를 에뮬레이션 프로그램에 전달할 수 있습니다. 기본값이 비어 있습니다.
PC (Intel 8259)의 에뮬레이션 타이머 하드웨어는 다음 중 하나에서 측정 시간을 지원합니다.
화면은 초당 30 회 새로 고쳐지고 VGA 후퇴 대기가 감지 될 때마다 (Renderer.cs 참조).
CPU :
메모리:
제도법:
DOS :
입력:
CD-ROM :
소리:
*닉스 시스템에서는 libportaudio를 설치해야합니다. 그것 없이는 소리가 없을 것입니다.
호환성 목록은 여기에서 사용할 수 있습니다.
dotnet build Spice86 -e < path to executable >또는 spice86.csproj가있는 곳에서 사용하십시오.
dotnet run -e < path to executable >이것은 Ghidra와 Java 17을 사용합니다.
그것을 사용하기 전에 Spice86 덤프가있는 폴더를 가리키는 Spice86_dumps_folder라는 환경 변수를 정의하십시오. 그들은 출구에서 생성됩니다.
순서 : 일반 절차 :
1.Ghidra의 자체 스크립트 'importsymbolscript.py'(사용 된 입력은 "spice86dumpghidrasymbols.txt"입니다.
2. Ghidra의 자동 분석 ( '불화 입력 지점'만 활성화)
3. NOW, 플러그인을 사용할 수 있습니다.
기억하십시오 : Ghidra가 서브 루틴을 표시하는 경우 'F'키를 사용하여 기능으로 변환하십시오. 코드 생성기는 함수와 함께 작동합니다.
또한 이상한 행동이 있다면 Java 17과 Java 17 만 있는지 확인하십시오. Ghidra가 좋아하는 방식입니다.
Cryo Dune :
UI는 Avalonia UI에 의해 구동됩니다.
