📜 ⬆️ ⬇️

Quineas. Indirect creation

While working on the utility for my own use, I encountered one "magic" error. Initially, I intended to make a program with the capabilities of Quine, but this did not work, because even from the line (from Wikipedia):
var s:string='var s:string=;begin insert(#39+s+#39,s,14);write(s)end.';begin insert(#39+s+#39,s,14);write(s)end. 

my brain was rapidly recurring. What to say about more complex programs? I keep quiet about quains in several languages. Therefore, I decided to make a quine indirectly, i.e. which outputs its code, and through workarounds, i.e. There is a base program that needs to be output and a program that outputs. It turned out pseudo-quine. And then came the most real magic, which programmers have often.

At some point I confused the code of the output program and the code of the program that I need to output (I made a mistake in the file name). And got a real quine. I finished a little to a better view and came here to share my story.

My tools:
Free Pascal Compiler 2.6.4
Notepad ++
Accordingly, the programming language FreePascal (mode objFPC)
Note about console output of Russian letters
if you want to see Russian letters in the Windows console, type the code in OEM 866 encoding (in Notepad ++ menu: Encodings-> Encodings-> Cyrillic-> OEM 866) or in the FreePascal environment itself. Works on Russian-language Windows.


First things first. And I will tell you how it was.
')

Part one. "Basic program"


Here a little. We need a program, whose code we will display at first. Anyone can do it, even this one:
Hello, World!
 program MyProgram; begin write('Hello, world'); end. 

I wrote a little more code as a “base program”.

Let's transform our program into an array of HEX code. To do this, use the standard data2inc (supplied with the Free Pascal Compiler). Since we will need to use this utility often, it is better to write such a bat script:

 D:\FPC\2.6.4\bin\i386-win32\data2inc.exe -B -A myProg.pp myProg.inc MyArray 

bat script Argument description
1) full path data2inc.exe (I refused to clog the PATH)
2) -B for perception as an array of bytes of the converted file
3) -A for output as an array of HEX constants
4) myProg.pp our file name
5) myProg.inc where to generate an array
6) MyArray how to call the resulting array


And we put in the folder of our program. After launch, I got the following code for my program:

Code
 const MyArray : array[1..407] of byte=( $2F,$2F,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D, $2D,$2D,$2D,$2D,$2D,$2D,$2D,$0D,$0A,$2F,$2F,$20,$80,$A2,$E2, $AE,$E0,$3A,$20,$49,$76,$61,$6E,$54,$61,$6D,$65,$72,$6C,$61, $6E,$0D,$0A,$2F,$2F,$20,$76,$6B,$2E,$63,$6F,$6D,$2F,$49,$76, $61,$6E,$54,$61,$6D,$65,$72,$6C,$61,$6E,$0D,$0A,$2F,$2F,$2D, $2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D, $2D,$2D,$2D,$2D,$0D,$0A,$70,$72,$6F,$67,$72,$61,$6D,$20,$4D, $79,$51,$75,$69,$6E,$65,$3B,$0D,$0A,$7B,$24,$6D,$6F,$64,$65, $20,$6F,$62,$6A,$66,$70,$63,$7D,$0D,$0A,$76,$61,$72,$20,$69, $3A,$20,$69,$6E,$74,$65,$67,$65,$72,$3B,$0D,$0A,$20,$20,$20, $20,$73,$31,$2C,$20,$73,$32,$3A,$20,$50,$43,$68,$61,$72,$3B, $0D,$0A,$0D,$0A,$66,$75,$6E,$63,$74,$69,$6F,$6E,$20,$4D,$79, $46,$75,$6E,$63,$53,$75,$6D,$28,$61,$2C,$62,$3A,$69,$6E,$74, $65,$67,$65,$72,$29,$3A,$69,$6E,$74,$65,$67,$65,$72,$3B,$0D, $0A,$62,$65,$67,$69,$6E,$0D,$0A,$20,$72,$65,$73,$75,$6C,$74, $3A,$3D,$61,$2B,$62,$3B,$0D,$0A,$65,$6E,$64,$3B,$0D,$0A,$0D, $0A,$62,$65,$67,$69,$6E,$0D,$0A,$20,$69,$3A,$3D,$20,$4D,$79, $46,$75,$6E,$63,$53,$75,$6D,$28,$31,$31,$32,$2C,$31,$30,$30, $35,$30,$30,$29,$3B,$20,$2F,$2F,$20,$AA,$A0,$AA,$A0,$EF,$2D, $E2,$AE,$20,$E4,$E3,$AD,$AA,$E6,$A8,$EF,$2E,$0D,$0A,$20,$73, $31,$3A,$3D,$27,$8A,$A0,$AF,$E0,$A0,$AB,$2C,$20,$AC,$EB,$20, $AF,$E0,$AE,$A8,$A3,$E0,$A0,$AB,$A8,$21,$27,$3B,$0D,$0A,$20, $73,$32,$3A,$3D,$27,$20,$AD,$A0,$AC,$20,$AD,$A5,$20,$E3,$A4, $A0,$AB,$AE,$E1,$EC,$20,$AE,$E2,$AF,$E0,$A0,$A2,$A8,$E2,$EC, $20,$E8,$A8,$E4,$E0,$AE,$A2,$AA,$E3,$20,$AD,$A0,$20,$E5,$A0, $A1,$E0,$A5,$20,$3D,$28,$27,$3B,$0D,$0A,$20,$77,$72,$69,$74, $65,$6C,$6E,$28,$73,$31,$2C,$73,$32,$29,$3B,$0D,$0A,$65,$6E, $64,$2E); 

Immediately I say - I forwarded the range from [0..406] to [1..407], so that in the future the file length will not be reduced by one each time. The upper limit was taken out separately in a constant.

Part two. HEX code output


We write the program for outputting the HEX code of the program from the first part.

A couple of conditions - you need to display the code in various ways:

1) Convert an array to text. Get the initial listing of the program.
2) Display the HEX code as a HEX code. Those. with full clearance. In Pascal, for this you need to put down commas and designations of HEX numbers. It is possible for beauty and transfers.

If we have an array length from 1 to N, then we need a number K that satisfies the condition 1 = <K = <N.
Print the first K numbers of the array as text. Then we output from 1 to N a sequence of HEX codes. And after that we complete the output from K + 1 to N.

Numbers K and N to issue in the form of constants in order not to run to edit throughout the listing. They will have to rule sometimes very often.

Listing of my program
 //-------------------- // : IvanTamerlan //-------------------- program myQuine; {$mode objfpc} var i:integer; const N = 00407; K = 00407; MyArray : array[1..N] of byte = ( $2F,$2F,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D, $2D,$2D,$2D,$2D,$2D,$2D,$2D,$0D,$0A,$2F,$2F,$20,$80,$A2,$E2, $AE,$E0,$3A,$20,$49,$76,$61,$6E,$54,$61,$6D,$65,$72,$6C,$61, $6E,$0D,$0A,$2F,$2F,$20,$76,$6B,$2E,$63,$6F,$6D,$2F,$49,$76, $61,$6E,$54,$61,$6D,$65,$72,$6C,$61,$6E,$0D,$0A,$2F,$2F,$2D, $2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D, $2D,$2D,$2D,$2D,$0D,$0A,$70,$72,$6F,$67,$72,$61,$6D,$20,$4D, $79,$51,$75,$69,$6E,$65,$3B,$0D,$0A,$7B,$24,$6D,$6F,$64,$65, $20,$6F,$62,$6A,$66,$70,$63,$7D,$0D,$0A,$76,$61,$72,$20,$69, $3A,$20,$69,$6E,$74,$65,$67,$65,$72,$3B,$0D,$0A,$20,$20,$20, $20,$73,$31,$2C,$20,$73,$32,$3A,$20,$50,$43,$68,$61,$72,$3B, $0D,$0A,$0D,$0A,$66,$75,$6E,$63,$74,$69,$6F,$6E,$20,$4D,$79, $46,$75,$6E,$63,$53,$75,$6D,$28,$61,$2C,$62,$3A,$69,$6E,$74, $65,$67,$65,$72,$29,$3A,$69,$6E,$74,$65,$67,$65,$72,$3B,$0D, $0A,$62,$65,$67,$69,$6E,$0D,$0A,$20,$72,$65,$73,$75,$6C,$74, $3A,$3D,$61,$2B,$62,$3B,$0D,$0A,$65,$6E,$64,$3B,$0D,$0A,$0D, $0A,$62,$65,$67,$69,$6E,$0D,$0A,$20,$69,$3A,$3D,$20,$4D,$79, $46,$75,$6E,$63,$53,$75,$6D,$28,$31,$31,$32,$2C,$31,$30,$30, $35,$30,$30,$29,$3B,$20,$2F,$2F,$20,$AA,$A0,$AA,$A0,$EF,$2D, $E2,$AE,$20,$E4,$E3,$AD,$AA,$E6,$A8,$EF,$2E,$0D,$0A,$20,$73, $31,$3A,$3D,$27,$8A,$A0,$AF,$E0,$A0,$AB,$2C,$20,$AC,$EB,$20, $AF,$E0,$AE,$A8,$A3,$E0,$A0,$AB,$A8,$21,$27,$3B,$0D,$0A,$20, $73,$32,$3A,$3D,$27,$20,$AD,$A0,$AC,$20,$AD,$A5,$20,$E3,$A4, $A0,$AB,$AE,$E1,$EC,$20,$AE,$E2,$AF,$E0,$A0,$A2,$A8,$E2,$EC, $20,$E8,$A8,$E4,$E0,$AE,$A2,$AA,$E3,$20,$AD,$A0,$20,$E5,$A0, $A1,$E0,$A5,$20,$3D,$28,$27,$3B,$0D,$0A,$20,$77,$72,$69,$74, $65,$6C,$6E,$28,$73,$31,$2C,$73,$32,$29,$3B,$0D,$0A,$65,$6E, $64,$2E); MyHEX : array[0..15] of byte=( $30,$31,$32,$33,$34,$35,$36,$37, $38,$39,$41,$42,$43,$44,$45,$46); function ByteToHex (const b:byte):string; begin result := #$24 + char(MyHEX[b div 16]) + char(MyHEX[b mod 16]); end; begin for i:=1 to K do write(char(MyArray[i])); write(#$0D#$0A#$20#$20); for i:=1 to N do begin if ((i mod 10) = 0) then write(#$0D#$0A#$20#$20); write(ByteToHex(MyArray[i])); if i<N then write(', '); end; for i:=K + 1 to N do write(char(MyArray[i])); end. 


We compile, run, the program displays the text of the “base” program, and then its HEX sequence. Pseudo-quine is ready!

Part Three Magic!


We are interested in these 2 constants:
N = 00407;
K = 00407;
Now we will need to change these 2 numbers. Must be at least 5 characters. If not enough - add zeros. They are needed, if we change the file size, then the bit width changes and we have to resend the file again. In order not to go into recursion, it is better to do it right away so that changing the bit does not affect the main program.
Remove everything between the brackets in the array, leaving only this:

MyArray: array [1..N] of byte = ();
In the file properties you can see the number of bytes. Write it to N. Save the file.

Everything from the beginning to the opening bracket is copied to the new file and see how many bytes it weighs (this new file will no longer be useful to us).

Write the length of the truncated file in K.

Now, using data2inc (at the beginning of the article I told you how to work with it), we convert the file into a HEX array. New HEX-array set, where once upon a time was different.

Compile and get real quine.

Conclusion


To write a quine, it is not necessary to write a quine. You can write a program that displays a document, I specifically encrypted it in the form of a HEX sequence. Can be replaced by other encoding methods. This eliminates the need to escape service characters.

PS Personally, I did not come out the first time, and this instruction was not. So I had to transcode and edit the HEX code several times. In this manual I got rid of HEX code editing.

Source: https://habr.com/ru/post/253653/


All Articles