Soft Ice in Action
Soft Ice in Action
Contents
1. Introduction.
2. What is SoftIce?
3. Installation.
4. First steps.
5. Epilogue.
Introduction
All these questions arise for one simple reason: all the documentation that can be found
on the Internet is written in English, the author knows only one article in Russian, and
that, unfortunately, is very incomplete. This essay was written to fill this information
vacuum. It is difficult to count how many people, faced with these problems,
“demolished” SoftIce and forgot about it like a bad dream, but it’s a pity - after all, this is
the best debugger today.
I would like to immediately make a few comments: first, if after reading this article you
still take up SoftIce, then you will have to study English, whether you want it or not (why
- see above); second, this article does not pretend to be documentation, it was compiled
as an overview of the information that the author had in English, and this is a proprietary
manual from NuMega , the author of SoftIce, articles and essays found on the Internet
on various sites, dedicated to SoftIce and Reverse Engineering. For this reason, there
may be errors and inaccuracies in the article; the author will be grateful for their
correction. This article describes the latest version of SoftIce 3.24 for WIN95, but most
of what is said also applies to previous versions. It is assumed that those reading this
article have minimal knowledge about the structure of the processor and computer and
have an idea of what assembler is.
What is SoftIce?
SoftIce consists of a kernel mode debugger (in fact, this is a debugger) and a
debugging information loader utility (Symbol Loader). SoftIce is a universal debugger
that can debug any code, including interrupt routines and I/O drivers. The Symbol
Loader utility loads debugging information for your module, allows you to configure
SoftIce, and gives you the ability to write the command history (history buffer) to a file.
SoftIce combines the power of a hardware debugger and the convenience of a symbolic
one; it has the following features:
1 of 15 09/06/2024, 15:03
Soft Ice in action https://joj-narod-ru.translate.goog/softice.html?_x_tr_sl=ru&_x_tr_tl=...
NT, drivers for WIN95, VxD, 16-bit programs for DOS and Windows.
Debugging virtually any code, including interrupt routines and internal routines of
WIN 95 and WIN NT.
Setting breakpoints for memory read/write operations, I/O port read/write
operations, and interrupts.
Setting breakpoints on Windows messages.
Setting breakpoints that are triggered under certain conditions (conditional
breakpoints) and actions that should happen when the breakpoint is triggered.
And much more ...
Symbol Loader allows you to read debug information from debugged programs (EXE,
DLL, VxD, 386, OCX) and load it into the debugger, run your application and
automatically set a breakpoint to the program entry point, write it to the debug log file.
Installation
During installation, as usual, enter the directory where you want to install SoftIce, select
the components you want to install, and then you will be taken to the video adapter
selection window. SoftIce itself detects the video card, if it matches yours - click the Test
button, the screen should switch to text mode and you will see the phrases “SoftIce is
totally awesome!” on the entire screen, and in the middle the numbers are counting from
5 to 1. If you see this, it means that the video adapter setup was successful - press the
Next button, but if your scan is lost, the image disappears or the machine freezes, then
SoftIce has problems with the video card , there are two ways to solve them: first, turn
on the universal video adapter - it should always help (only this option works on my
Matrox Mistique), then SoftIce will work in the window: second, select an analogue of
yours from among the presented video adapters (I have an S3 TRIO 64 vis (although
SoftIce was 3.01), but it worked great with DS 2000); It's up to you to decide which
option is better.
After selecting the adapter, select the type of mouse you are using (although you don’t
2 of 15 09/06/2024, 15:03
Soft Ice in action https://joj-narod-ru.translate.goog/softice.html?_x_tr_sl=ru&_x_tr_tl=...
have to install a mouse, it’s hard to work in SoftIce without it). Next, allow us to modify
your autoexec.bat so that SoftIce loads when the computer starts. Confirm the selected
configuration and watch as SoftIce is installed on your machine. When asked to restart
the computer, agree. After reboot, press Ctrl-D. If instead of the desktop you see some
strange letters and numbers, then consider that you have finished installing SoftIce on
your machine, and the setup stage begins. If nothing happened, or the machine hung
while loading, then check whether you did everything correctly. If SoftIce does not load,
check whether the startup line is written in autoexec.bat, it is always the last one and
looks something like this: C:\WINDEBUG\SICE324\WINICE.EXE, if not, write it with
your path. If the machine hangs, then the reason is most likely the incorrect
configuration of the video adapter. Try changing it.
By the way, all the settings that you made during installation can be changed: Start -
Programs - NuMega SoftIce.
Setting:
Open for editing the file winice.dat, which is located in the same directory as SoftIce (in
the case of NT - in the %SystemPath%\system32\drivers directory), and remove the
semicolon from the following lines:
; ***** Examples of export symbols that can be included for Windows 95 *****
;Change the path to the appropriate drive and directory
EXP=c:\windows\system\kernel32.dll - remove;
EXP=c:\windows\system\user32.dll - remove;
EXP=c:\windows\system\gdi32.dll - remove;
Check the path to the files kernel32, user32, gdi32, it should match yours (Relevant if
windows was installed in a directory other than C:\WINDOWS).
This operation is needed so that when debugging a program when calling system
functions, you see the name of the called function, and not some call [xxxxxxxx].
Example:
INIT="WR;WD;WL;X;" – if you are using a video driver for a “native” video card (full
screen mode).
INIT="SET FONT 3;SET ORIGIN -10 25;WR;WD 4;WL;WC 12;X;" – if you are using a
universal video driver (windowed mode).
3 of 15 09/06/2024, 15:03
Soft Ice in action https://joj-narod-ru.translate.goog/softice.html?_x_tr_sl=ru&_x_tr_tl=...
If you want to move a window with SoftIce, then use the Ctrl-Alt-keys (one of the cursor
control keys).
SoftIce commands and variables will be discussed in more detail below, then you can
customize SoftIce to your taste.
First steps
If you are still reading this opus, then the long-awaited moment of truth has come for
you :-), it's time to see how it all works in action. The SoftIce distribution includes an
example of GdiDemo, and we will debug it (following the instructions from NuMega).
The example was compiled without problems in VC4 (don't forget to include debugging
information), the example was also compiled in BC 5.02, but Symbol Loader did not see
the debugging information, although it is loaded in TD. Anyone who is too lazy to
assemble the project themselves can download it from here: Gdidemo.zip (or from here
), the archive takes up 0.1 MB.
Launch Symbol Loader, select the Open module option in the File menu, go to where
you have Gdidemo.exe and open it, then in the Module menu click on the Load option.
SL will translate the debugging information into a .NMS file, download the source files,
run the program being debugged (in this case Gdidemo) and pop up in SoftIce, where
you will see the source code of the program.
The highlighted line number 35 is the entry point to your program. If SL displays a
message like "An error occurs during symbol translation/load", it means there is no
debugging information in the file being debugged, click OK and enjoy the
[dis]assembler.
2. SoftIce management.
If you did everything correctly, then you should see SoftIce split into several windows.
The top window—Register Window—shows the status of the processor's working
registers. Below it is the Data Window, in which you can view or edit the memory dump.
Below is the Code Window - it contains the source text of the program (if you
downloaded debugging information), or disassembled code of the program. At the very
bottom there is a command window - Command Window, in which you can enter a
command and see the result of their execution. The bottom line is the help line; as you
type, possible command options and their syntax are highlighted. It is most convenient
to control SoftIce with a mouse, but it is also possible without it, although not as
convenient.
You can: resize windows and close them (you cannot resize register and FPU windows);
scroll windows both on one line and on the whole screen; change the value of registers,
flags, memory cells, set execution breakpoints, delete variables from the Watch Window
that are no longer needed. The right mouse button brings up a context menu in which
you can select one of the presented commands; commands work with the Windows
clipboard.
4 of 15 09/06/2024, 15:03
Soft Ice in action https://joj-narod-ru.translate.goog/softice.html?_x_tr_sl=ru&_x_tr_tl=...
Resizing a window - move the cursor to the bottom border of the window that you
want to resize or close, press the left mouse button and move it down (increase
size) or down (decrease size), if you want to close the window, move the bottom
border to the top, The phrase Close current window will appear in the window and
the window will disappear.
Scrolling by one line - move the cursor to the small arrows located at the borders
of the window you want to scroll and press the left mouse button (arrows appear if
the window size is greater than or equal to two lines).
Scrolling to the screen - move the cursor to the large arrows located inside the
window that you want to scroll and press the left mouse button (arrows appear if
the window size is greater than or equal to four lines).
Changing register values - move the cursor to the register whose value you want
to change, click the left mouse button and enter a number; if you need to change
one digit, then move the cursor to this digit and change it.
Changing flag values - move the cursor to the flag you want to change, click the
left mouse button, after which you can use the Ins key to change the flag value to
the opposite (a small letter means that the flag is not set, a large letter is set).
Changing the values of memory cells - move the cursor to the byte (word, double
word, etc.) that you want to change, press the left mouse button and enter your
value, if you want to change one or more digits in the number, then move the
cursor using keyboard to the desired numbers and change.
Note: In all cases of changing values, they take effect after you switch to any other
window, before which you can cancel the last change by pressing Esc.
Setting breakpoints for execution - move the cursor to the line in the Code Window
where you want to stop, and double-click on the left mouse button to set a
breakpoint, the line will be highlighted.
Removing variables from the Watch Window - place the cursor on the variable you
want to delete, press the left mouse button, the variable will be highlighted, press
the Del button - the variable will disappear.
Context menu – right-clicking the mouse takes you to the context menu where the
following commands are available to you:
Copy – copy the address or data under the cursor to the clipboard.
Paste – paste an address or data on the clipboard into the command window
Copy&Paste - copy the address or data under the cursor to the clipboard and
paste it into the command window.
Display – display a memory dump in the data window located at the address over
which the cursor is currently located (analogous to command D).
Un-Assemble – display in the Code Window the original (if there is debugging
information) or disassembled program text located at the address over which the
5 of 15 09/06/2024, 15:03
Soft Ice in action https://joj-narod-ru.translate.goog/softice.html?_x_tr_sl=ru&_x_tr_tl=...
What – identifies the value located under the cursor with predefined values
(similar to the Wath command).
Previous – cancels the previous command entered from the context menu (works
with the Display and Un-Assemble commands).
You can do the same thing, but the context menu and clipboard will be missing.
Changing the window size - the size (in lines) is directly specified in the window opening
command, if necessary.
By default, the cursor is in the command window. You can move the cursor to the
desired window using the following key combinations (if you are in the Command
Windows window):
Go to Register WindowAlt-R
Pressing these keys again will return you back to the command window (all of the above
also applies to the Code Window).
In the data window, you can switch between dumps in HEX and ASCII format using the
Tab key, and you can also use it to switch between registers in the register window.
Scrolling one line - go to the desired window and use the cursor up and cursor down
control keys to scroll the window.
6 of 15 09/06/2024, 15:03
Soft Ice in action https://joj-narod-ru.translate.goog/softice.html?_x_tr_sl=ru&_x_tr_tl=...
Scrolling to the screen - go to the desired window and use the PageUp and PageDown
keys to scroll the window.
Note: The Code and Data Window can be scrolled without switching to them from the
command window; to do this, in addition to the control keys, you also need to hold Alt
for the Data Window and Ctrl for the Code Window.
Changing register values - go to the registers window, move the cursor to the register
whose value you want to change and enter a number, if you need to change one digit,
then move the cursor to this digit and change, pressing Enter confirms the change, Esc
cancels.
Changing flag values - go to the registers window, move the cursor to the flag you want
to change. After that, you can use the Ins key to change the value of the flag to the
opposite (a small letter means that the flag is not set, a large letter means that it is set),
you don’t have to press Enter.
Changing the values of memory cells - go to the data window, move the cursor to the
byte (word, double word, etc.) that you want to change and enter your value, if you want
to change one or more digits in a number, then move the cursor using keyboard to the
desired numbers and change. Pressing Enter or moving to the next number confirms
the change, Esc cancels.
Setting execution breakpoints - in the command window, type the BPX command and
enter the address of the line where you want to stop.
Removing variables from the Watch Window - go to the Watch Window, place the cursor
on the variable you want to delete, the variable will be highlighted, press the Del button -
the variable will disappear.
After a short lecture on managing SoftIce, you can start debugging itself. Since we are
debugging a program written in a high-level language and with debugging information,
we can disable the register window and the data window, and enable the Locals
Window to see what parameters are passed to the procedures (i.e. WD, WR, WL,
provided , that you used the settings given above).
To see which of the source codes of the program were downloaded, issue the FILE *
command .
On the screen in the command window you will see the names of files containing the
source code of the GdiDemo program: Bounce.c, Wininfo.c, Poly.c, Maze.c, Init.c,
Draw.c, Dialog.c, Xform.c, Gdidemo.c . Since the command window is usually small,
you will see the first few file names. The help line will contain the words “Press any key
to continue; Esc to cancel”, you can use the Enter key. Get the next line with the file
name, or by pressing the spacebar, get the next portion of lines with the file names
(valid for all messages displayed in the command window).
If, while studying the program listing, you got too far from the current EIP value, you can
return with the command:
:U EIP – disassemble the program starting from the current EIP address, or with a
command;
7 of 15 09/06/2024, 15:03
Soft Ice in action https://joj-narod-ru.translate.goog/softice.html?_x_tr_sl=ru&_x_tr_tl=...
Use the T (trace) command to trace one command, or the F8 key, which is assigned by
default to the T command. The command located on the current line will be executed
and the cursor will move to the next line and highlight it. This is the line:
LpszLine=LpszLine;
if(!hPrevInst) .
In the Code Window you see the source text of the program (source mode). If you want
to view the disassembled (code mode) program text or the source and disassembled
(mixed mode) text together, use the SRC command or the F3 key assigned to this
command. The first time you click, you will see a mixed text (the source text of the
program and the assembly instructions that make up this line), the second click is
disassembled, the third click will return you to the mode of viewing the source text of the
program.
if(!RegisterAppClass(hInst)) ;
To debug a program, you use the T instruction, which executes one source program
statement or one machine instruction.
There is also the P command or the F10 key, which performs one step in the program,
i.e. When tracing a function or interrupt, you will not receive control until the function has
completed and you have returned from the function. The P command is convenient to
use when you are debugging the main algorithm and it is irrational to be distracted by
tracing each procedure.
Note: The T command cannot trace system calls (WIN32 API calls) while in source
mode; to trace them, you need to switch to mixed or code mode.
The Locals Window shows the current stack frame. In our case, it contains local
variables for the WinMain function.
Use the T command to enter the RegisterAppClass function, the Locals Window will
become empty, since local variables have not yet been defined for this function. The
RegisterAppClass function is in the INIT.C file. SoftIce shows the current file in the
upper left corner of the Code Window
Issue the T command again, the Locals Window will contain the parameter passed to
the RegisterAppClass (hInstance) function and the local structure wndClass. There is a
plus sign in front of the structure, which means that there are variables inside that can
be viewed (you can also view string variables and arrays). If you have a Pentium and
use a mouse, then you can view the structure by double-clicking on it with the mouse,
8 of 15 09/06/2024, 15:03
Soft Ice in action https://joj-narod-ru.translate.goog/softice.html?_x_tr_sl=ru&_x_tr_tl=...
the + sign will change to – and you will see the variables that make up the structure.
You can close the structure in the same way (by double-clicking). If you are using a
keyboard, then the sequence of actions is as follows: press Alt-L to go to the Locals
Window, then use the cursor keys to move the glowing strip to the structure that you
want to view and press Enter. If you need to close it, press Enter again.
There are two types of execution breakpoints: simple breakpoints and one-time
breakpoints.
One-time breakpoints:
Go to the Code Window, using the PgDn key, move the cursor to line number 61 (the
same can be done using the U .61 command), this line contains the first call to the
Win32 API RegisterClass function. Using the HERE command (F7 key) execute the
program up to this line.
The HERE command sets a breakpoint in the program to the address or line at which
the cursor is located and executes the program from the current address to the address
at which the cursor is located, i.e. until the breakpoint is triggered, once triggered
SoftIce will automatically disable this breakpoint so that it will not trigger again.
If(!RegisterClass(&wndClass))
Note: The same result could have been achieved if you had given command G .61
(execute program up to line 61).
Regular breakpoints.
The following steps demonstrate the use of normal breakpoints, i.e. those that will work
until you cancel them. Find the next call to the RegisterClass function, located on line
74. Place the cursor on this line and enter the BPX (BreakPoint eXecutable) command
or the F9 key (using this command, the INT3 command is written to memory in place of
the command located under the cursor, but you do not see it: -)). The line should be
highlighted (if you are in code mode, then after the BPX command you must specify the
address of the line on which you want to set a breakpoint). You can remove the
breakpoint by re-entering the same command. If you are the proud owner of a Pentium
processor, then the process of setting and removing a breakpoint is reduced to double-
clicking the left button on the command where you want to set the breakpoint. After
setting a breakpoint, run the program using the G or X command (F5 key). When the
program executes the INT3 instruction, it will transfer control to SoftIce, after which it will
appear in front of you. You can view information about set breakpoints using the BL
(BreakPoint List) command:
:BL
We see that one execution breakpoint is set at address 00402442, with serial number 0.
At this address there is a command located in the current INIT.C file on line 74. You can
9 of 15 09/06/2024, 15:03
Soft Ice in action https://joj-narod-ru.translate.goog/softice.html?_x_tr_sl=ru&_x_tr_tl=...
:? .74
void * = 0x00402442
Since further step-by-step tracing of the RegisterAppClass function does not make
sense for us, let’s return to the place from where this function was called. To do this,
there is a P command with the RET parameter (F12 key). It allows you to execute a
program until it encounters a RET (RETF) command; after executing this command,
SoftIce will exit the subroutine and stop at the line following the call of this subroutine. In
relation to our program: the RegisterAppClass function is called from the WinMain
function, SoftIce stops in the WinMain function on the line following the call to the
RegisterAppClass function, i.e. The line will be highlighted:
Msg.wParam = 1 ;
Use the BC (Breakpoint Clear) command with the breakpoint number that you looked at
using the BL command in order to remove this particular breakpoint, or enter * instead
of the number, then you will remove all breakpoints (you can enter breakpoint numbers
through a comma if you want to remove several periods).
SoftIce has at its disposal many different commands with which you can find out the
status and obtain other information about the operating system and applications running
on it. We will look at only two commands: H (Help) and CLASS. These commands
display quite a lot of information in the Command Window, so it is advisable to increase
the size of this window; to do this, close the Locals Window.
With the H command, you can get help on all SoftIce commands or more detailed
information about a specific command if you enter its name as an argument to the H
command:
:H CLASS
The first line gives a description of the command, the second shows information about
the syntax and arguments that can be used in the command, and the third line contains
an example of how to use the command.
:CLASS GDIDEMO
The result of this command is information about each registered window class. The
10 of 15 09/06/2024, 15:03
Soft Ice in action https://joj-narod-ru.translate.goog/softice.html?_x_tr_sl=ru&_x_tr_tl=...
information includes: the name of the class, the address of the internal data structure
WNDCLASS, the module that registered this class, the address of the procedure that
services this class, and the state of the class style flags. For detailed information, use
the –X switch.
7. Symbolic names.
When you load an application with debugging information, SoftIce automatically creates
a symbolic name table that contains all the names defined in the application. Using the
TABLE command you can see which symbolic name tables are currently loaded:
:TABLE
GDIDEMO[NM32]
The symbol table currently in use is highlighted in color. If the current table of symbolic
names does not match the one referenced by your application, then using the TABLE
command with the name of your application as an argument, you will connect the
desired table (if a table is created for your application):
:TABLE GDIDEMO
Using the SYM command you can view all symbolic names defined in the current table
(displayed by segments, within them in alphabetical order). If you are interested in some
specific names, then use templates:
:SYM w*
On the screen there is a list of all symbolic names starting with the letter w, all of them
are located in the .text segment (the executable segment, it starts at the address
0137:00401000 and has a length of 0145C1H bytes), i.e. these names are the names of
11 of 15 09/06/2024, 15:03
Soft Ice in action https://joj-narod-ru.translate.goog/softice.html?_x_tr_sl=ru&_x_tr_tl=...
functions and procedures included in the GDIDEMO application. The data is in the
.data, .rdata, .idata segments.
8. Conditional breakpoints.
Using the BPX LockWindowInfo command, we will set an execution breakpoint on this
function. Every time information needs to be updated in one of the windows of the
GDIDEMO application, the program will call the LockWindowInfo function, since a
breakpoint is set on this function, SoftIce will be called. Use the BL command to check if
the breakpoint is set. Launch the application with the X or G command. As soon as the
LockWindowInfo function is called, SoftIce will pop up. Since the update occurs
constantly, SoftIce is also constantly called, which is very inconvenient if we are
interested in updating a specific window. To intercept a call to update a specific window,
for example, POLYDEMO, we will use a conditional breakpoint. From the source text of
the program (wininfo.c file) it is clear that the LockWindowInfo function receives as an
input argument one HWND (Handle Window) parameter - a window handle, and returns
one value to the calling function - a pointer to variables for this window. That is, if we
forced the breakpoint to fire only on the POLYDEMO window handler, we would have
achieved our goal. First, we need to find out the descriptor of our window, for this we will
use the command:
:HWND GDIDEMO
the POLYDEMO window descriptor has the value 0730, if you do not see the required
window in the list, then launch the application with the X or G key, the breakpoint will
work again, check whether the window has been created, if not, repeat the last steps.
Now you can stop program execution only if the value 0730 is used as a parameter for
the LockWindowInfo function. In Windows, parameters for a function are usually passed
on the stack. When stopped in the LockWindowInfo function, the stack will look like this
(you can view the contents of the stack by moving the cursor to the ESP register, right-
clicking, calling up the context menu and selecting the Display command; it would also
be a good idea to change the data output format in the DataWindow to display double
words with the DD command, so as our application is 32-bit):
12 of 15 09/06/2024, 15:03
Soft Ice in action https://joj-narod-ru.translate.goog/softice.html?_x_tr_sl=ru&_x_tr_tl=...
ESP = 0055FC00
the number 00404852 is the address to which the program will go after our function
completes (return address);
the number 00000730 is the POLYDEMO window descriptor (actually what interests
us);
Now knowing where and what is being passed into the function, we can set a
conditional breakpoint. To do this, let's call the breakpoint set on the LockWindowInfo
function for editing:
:BPE 0
The following line will appear at the bottom line of the command window:
:BPX LockWindowInfo
and the cursor is positioned at the end of the line, now you can edit the breakpoint at
your discretion, add the following condition at the end of the line: IF ESP->4 ==
00000730 and press Enter.
That is, it will fire when the double word at address ESP+4 is equal to the number
00000730, which corresponds to the POLYDEMO window handle. Check with the BL
command whether the breakpoint matches the specified one, run the application and
make sure that this whole structure works great.
Let's set a breakpoint on access to the first double word of data in the POLYWINDOW
instance. As noted above, the input argument to the LockWindowInfo function is a
window handle, the output address of the window instance data. After triggering a
breakpoint set on the LockWindowInfo function with a parameter corresponding to the
POLYWINDOW window descriptor, at the output of the function we will have the data
address of the POLYWINDOW window instance, at this address we will set a breakpoint
on memory access.
In order to get the data address of the window instance, we execute the program up to
line number 57 (in the WININFO.C file):
:G .57
the function returns a 32-bit value (in our case, the address) in the EAX register, so you
can use the BPMD (BreakPoint Memory Dword) command and the address value in the
EAX register to set a breakpoint on access to the first word of data of the POLYDEMO
window instance:
:BPMD EAX
13 of 15 09/06/2024, 15:03
Soft Ice in action https://joj-narod-ru.translate.goog/softice.html?_x_tr_sl=ru&_x_tr_tl=...
This command uses the hardware debug registers built into the processor to monitor
reads and writes of a doubleword at a specified linear address. In this case, it is the first
data word of the POLYDEMO window instance. Use the BL command to check that the
breakpoint is set correctly.
:BL
:BD 0
:BL
Start SoftIce with the X or G commands, when the POLYDEMO window tries to access
the first dword of the window data instance, a breakpoint will be hit and SoftIce will pop
up, this will happen in the PolyRedraw and PolyDrawBez functions. These functions
access the nBezTotal field, which is located at offset zero in the POLYDEMO window
instance data. The value of this field specifies the number of curves simultaneously
displayed in the POLYDEMO window.
Epilogue
If you have reached these lines, then you have debugged your first program using the
wonderful SOFTICE debugger. But SoftIce's capabilities are not limited to those that we
briefly reviewed here, they are much more.
When debugging a program in Code Window, jump commands (JXX SHORT) are
disassembled incorrectly, i.e. the arrow shows the direction of the transition
14 of 15 09/06/2024, 15:03
Soft Ice in action https://joj-narod-ru.translate.goog/softice.html?_x_tr_sl=ru&_x_tr_tl=...
correctly, but in the mnemonic it is 100H more (or less - I don’t remember).
The author expresses her deep gratitude to all those who helped in writing this opus: my
beloved girl Nadenka; Ernest Poletaev - for bringing into divine form what I wrote and
for detailed consultations; Mikhailov Anton and Makhonin Yura - for the idea and the
possibility of its implementation; Dima Petras - for being my fidish boss; and to all those
who are not here :-))).
15 of 15 09/06/2024, 15:03