Jump to content

Shrinavat

Members
  • Content Count

    72
  • Joined

  • Last visited

  • Days Won

    4

Shrinavat last won the day on December 25 2024

Shrinavat had the most liked content!

Community Reputation

17 Good

Recent Profile Visitors

The recent visitors block is disabled and is not being shown to other users.

  1. Shrinavat

    RSA decryption from JavaScript

    Hi Kas Ob., Thank you so much for your detailed response and for taking the time to look into this. I really appreciate your help and insights! You're right, the main issue was loading the private key correctly, as TurboPower LockBox 3 doesn't seem to have built-in ASN.1 decoding capabilities. After considering your suggestions, I decided to switch from TurboPower LockBox 3 and use an OpenSSL wrapper instead, along with libeay32.dll. This approach allows me to properly handle the PEM-encoded private key and perform the RSA decryption. I've managed to get it working, and it seems to be decrypting the string as expected. For anyone interested, I've attached a zip file with a sample project demonstrating the solution. Please note that the code is still a bit rough around the edges and could use some cleaning up. Thanks again for your guidance! src.zip
  2. Shrinavat

    RSA decryption from JavaScript

    Hey everyone, I’m trying to reverse engineer a piece of JavaScript code found within an HTML file. It has this get_cookie_spsc_encrypted_part function, which contains an RSA private key embedded within a comment, and also an encrypted string. It uses some JavaScript crypto lib, KJUR.crypto.Cipher.decrypt, to decrypt this string. Here’s the Javascript code: function get_cookie_spsc_encrypted_part() { let func = function () {/*-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQDFoP5AJIv1KFGRpv/Uw7drFXjWbZG6wNsO7P58ocZIcxyKGU6u TgXw8N1IvTmd9yXRSdcb2fCWB7J/QUQDJQ3YuuXSOQCVOdi8Wy9UoZ5jNdqtZ6CM CvnK/v4Wy38ZhrB0CRkeiuyjmUdfQhe8mh3pE3iFBusYd1TVCxQt3VBkqQIDAQAB AoGAaYBaeo+ID6YodWL7a+/XeNkLmxz/EP1nc/5clNgf7AlXkPmVoUORtGBBIVWy 7ntDuwh6Ryn/X3hYd8q1riAX1UwVuUduOENmgyzmO1rRIoB/17vzYwVMYOB2h+qb xEqjg4dUfk/1occyDwpehWel+1NIgvQLNYLcn2JXxkAyrMkCQQD37+3Y8sjYxwAp giIClsCjrla73cS/QwzArGEnOjBs86LyzCc0pNzmP2OD0a9VlD3k6dMnhT2Oj+2k nZs8dUlHAkEAzA4/mQeFvdiKIkzUBECn3w9Ylu2IfpKnQt/0EFUENxS9ONZ1jj4p zDBfZosgwnE1GiECELM3R/6Pzl+uIGrajwJBALm5HG3az+CykMiHFnrh+kOiII5x vSOYUkEx30THLecvSeyeSPACXwaKjTz9IV31wbdsACQmhsn3vogFF3feU5kCQARP 9MYeI5RshBbPeteQKjwLjfq6kFzkaoZ+RyElOs6TMKCH37oe1DFNgGahYBLb45xm wC1sLCnoVk+tM/fZaj8CQGQyIlxwbgNBBdV3wnmtX9yPDflOsjpo3FuBMOu3nZAD KEpmTXFgdwP4oMMbCmDvH3dav92LE5JN1cPik9z0Piw= -----END RSA PRIVATE KEY----- */}; let pem = func.toString().match(/[^]*\/\*([^]*)\*\/\}$/)[1]; return KJUR.crypto.Cipher.decrypt("82a4fa1da959701407ae4a80b48c0dca57d68fb5e54df1794418b03ba43ef9022328e4be4011ffbeb74d9d22769634a21537ba32788342f8abe6cfddc22512a39373ea2488af389740ac4f93699da92fcd86895c64bd6760a5aea27e172052947361177f3674ec68c52480744a75163c892556ec526387c92c0b50ab0b3a4ea5", KEYUTIL.getKey(pem)); } I’ve managed to replicate the decryption in C# using System.Security.Cryptography. Here’s the C# code: using System.Text.RegularExpressions; using System.Security.Cryptography; var pemKey = Regex.Match(html, "-----BEGIN RSA PRIVATE KEY-----[\\s\\S]*?-----END RSA PRIVATE KEY-----").Value; var encryptedDataHex = Regex.Match(html, @"KJUR.crypto.Cipher.decrypt\(""(.*)""").Groups[1].Value; using var rsa = RSA.Create(); rsa.ImportFromPem(pemKey); var decryptedData = rsa.Decrypt(Convert.FromHexString(encryptedDataHex), RSAEncryptionPadding.Pkcs1); // Perform Decryption Now, I’m trying to do the same in Delphi, and I’d like to use TurboPower LockBox 3. Unfortunately, the official documentation seems to be offline. I am struggling to understand how to use LockBox 3, or other suitable free libraries for this purpose. Could anyone provide some Delphi code examples and guidance on how to do the RSA decryption, using LockBox or any other free library? Any help would be greatly appreciated! Here's my code: program RSA_test; {$APPTYPE CONSOLE} uses System.SysUtils, System.Classes, System.RegularExpressions, uTPLb_Codec, uTPLb_CryptographicLibrary, uTPLb_BaseNonVisualComponent, uTPLb_Signatory, uTPLb_Constants, uTPLb_Asymetric, uTPLb_RSA_Engine, uTPLb_OpenSSL; const HTML = '<html><head><script>function get_cookie_spsc_encrypted_part() {' + ' let func = function () {/*-----BEGIN RSA PRIVATE KEY-----' + 'MIICXAIBAAKBgQDFoP5AJIv1KFGRpv/Uw7drFXjWbZG6wNsO7P58ocZIcxyKGU6u' + 'TgXw8N1IvTmd9yXRSdcb2fCWB7J/QUQDJQ3YuuXSOQCVOdi8Wy9UoZ5jNdqtZ6CM' + 'CvnK/v4Wy38ZhrB0CRkeiuyjmUdfQhe8mh3pE3iFBusYd1TVCxQt3VBkqQIDAQAB' + 'AoGAaYBaeo+ID6YodWL7a+/XeNkLmxz/EP1nc/5clNgf7AlXkPmVoUORtGBBIVWy' + '7ntDuwh6Ryn/X3hYd8q1riAX1UwVuUduOENmgyzmO1rRIoB/17vzYwVMYOB2h+qb' + 'xEqjg4dUfk/1occyDwpehWel+1NIgvQLNYLcn2JXxkAyrMkCQQD37+3Y8sjYxwAp' + 'giIClsCjrla73cS/QwzArGEnOjBs86LyzCc0pNzmP2OD0a9VlD3k6dMnhT2Oj+2k' + 'nZs8dUlHAkEAzA4/mQeFvdiKIkzUBECn3w9Ylu2IfpKnQt/0EFUENxS9ONZ1jj4p' + 'zDBfZosgwnE1GiECELM3R/6Pzl+uIGrajwJBALm5HG3az+CykMiHFnrh+kOiII5x' + 'vSOYUkEx30THLecvSeyeSPACXwaKjTz9IV31wbdsACQmhsn3vogFF3feU5kCQARP' + '9MYeI5RshBbPeteQKjwLjfq6kFzkaoZ+RyElOs6TMKCH37oe1DFNgGahYBLb45xm' + 'wC1sLCnoVk+tM/fZaj8CQGQyIlxwbgNBBdV3wnmtX9yPDflOsjpo3FuBMOu3nZAD' + 'KEpmTXFgdwP4oMMbCmDvH3dav92LE5JN1cPik9z0Piw=' + '-----END RSA PRIVATE KEY-----' + '*/};' + ' let pem = func.toString().match(/[^]*\\/\\*([^]*)\\*\\/\\}$/)[1];' + ' return KJUR.crypto.Cipher.decrypt("82a4fa1da959701407ae4a80b48c0dca57d68fb5e54df1794418b03ba43ef9022328e4be4011ffbeb74d9d22769634a21537ba32788342f8abe6cfddc22512a39373ea2488af389740ac4f93699da92fcd86895c64bd6760a5aea27e172052947361177f3674ec68c52480744a75163c892556ec526387c92c0b50ab0b3a4ea5", KEYUTIL.getKey(pem));' + '} </script></head><body></body></html>'; var PEMKey, EncryptedDataHex, DecryptedData: string; Codec: TCodec; CryptoLib: TCryptographicLibrary; Signatory: TSignatory; EncryptedBytes, DecryptedBytes: TBytes; MemStream: TMemoryStream; StringStream: TStringStream; begin try Codec := TCodec.Create(nil); CryptoLib := TCryptographicLibrary.Create(nil); Signatory := TSignatory.Create(nil); try with TRegEx.Match(HTML, '-----BEGIN RSA PRIVATE KEY-----[\s\S]*?-----END RSA PRIVATE KEY-----') do if Success then PEMKey := Value; with TRegEx.Match(HTML, 'KJUR\.crypto\.Cipher\.decrypt\("(.*)\"') do if Success then EncryptedDataHex := Groups[1].Value; // Convert base64-string to bytes EncryptedBytes := TNetEncoding.Base64.DecodeStringToBytes(EncryptedDataHex); // Setup encryption components Codec.CryptoLibrary := CryptoLib; Codec.StreamCipherId := 'native.RSA'; Codec.ChainModeId := 'native.CBC'; Codec.AsymetricKeySizeInBits := 1024; // ??? StringStream := TStringStream.Create(PEMKey); try Signatory.Codec := Codec; Signatory.LoadKeysFromStream(StringStream, [partPrivate]); // !!! raised exception class EReadError with message 'Stream read error' finally StringStream.Free; end; // Decrypt data MemStream := TMemoryStream.Create; try MemStream.WriteBuffer(EncryptedBytes[0], Length(EncryptedBytes)); MemStream.Position := 0; Codec.DecryptStream(MemStream, MemStream); // In-place decryption // Get decrypted data SetLength(DecryptedBytes, MemStream.Size); MemStream.Position := 0; MemStream.ReadBuffer(DecryptedBytes[0], MemStream.Size); DecryptedData := TEncoding.UTF8.GetString(DecryptedBytes); finally MemStream.Free; end; finally Codec.Free; CryptoLib.Free; Signatory.Free; end; except on E: Exception do WriteLn(E.ClassName, ': ', E.Message); end; ReadLn; end.
  3. Thank you for the suggestion! Using a sorted CSV file with a dedicated comparison tool sounds like a very interesting and potentially robust approach. I'll definitely explore this option further. Thanks for sharing the XML example. I'm already familiar with saving the configuration in XML format 🙂. However, my main challenge lies in efficiently comparing two XML files to identify meaningful changes. I'm open to suggestions on how to best approach this comparison aspect. That's an interesting idea using SQLite. My main concern with a database approach is the unknown and variable number of fields. I'm not sure how to design the database structure to accommodate this and still allow for easy comparison between different snapshots. Could you elaborate on how you envision structuring the database to handle this dynamic nature and facilitate efficient comparisons?
  4. Hello everyone, I'm working on a utility that gathers PC configuration information and displays it in a TTree component. I want to be able to save this information to a file so that it can be compared with a similar file created at a later time. This would allow me to detect any changes in the PC's configuration, such as adding or removing hardware components. My main challenge is deciding on the best format to store this data for easy comparison and change detection. I've considered options like JSON, XML, and plain text, but I'm unsure which would be most suitable. A simple line-by-line comparison of a text file might not be reliable, as the order of components could change without signifying an actual configuration change (e.g., reordering hard drives). Could anyone offer advice on the best approach for storing and comparing this type of data? Are there any specific technologies or libraries that would be helpful for this task? Any guidance would be greatly appreciated!
  5. Thanks for the feedback! I ended up implementing a modified ROT13 algorithm with a custom alphabet, as shown in the code below. Initial tests indicate that it meets my requirements effectively. const ALL_CHARACTERS_CONSTANT = '!#$%&()*+,-./:;<=>?@[\]^_`{|}~0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя'; function ROT13Encrypt(const Input: string): string; var idx, newIdx: Integer; halfLength: Integer; begin Result := ''; halfLength := Length(ALL_CHARACTERS_CONSTANT) div 2; for var i := 1 to Length(Input) do begin idx := Pos(Input[i], ALL_CHARACTERS_CONSTANT); if idx > 0 then begin newIdx := ((idx - 1 + halfLength) mod Length(ALL_CHARACTERS_CONSTANT)) + 1; Result := Result + ALL_CHARACTERS_CONSTANT[newIdx]; end else Result := Result + Input[i]; end; end; function ROT13Decrypt(const Input: string): string; begin // ROT13 decryption is the same as encryption in this case Result := ROT13Encrypt(Input); end;
  6. Thank you for your insights. I understand the challenges involved in meeting both requirements simultaneously. I'm willing to remove the constraint that "The obfuscated string must contain only Latin letters and digits." Revised requirements: The obfuscated string can contain any printable characters. The obfuscated string must have the same length as the original string. Strong cryptographic security is not required. The input strings will generally not exceed 30 characters in length. With this revised requirement, are there any suitable algorithms or approaches you could suggest for implementing the obfuscation and deobfuscation functions?
  7. How can I implement functions for basic string obfuscation and deobfuscation in Delphi, where the input string can contain Latin and Cyrillic letters, digits, and special characters !@#$%^&*()_+-=[]{}\|;':",./<>? Requirements: The obfuscated string must contain only Latin letters and digits. The obfuscated string must have the same length as the original string. Strong cryptographic security is not required. I'm looking for a simple solution that can handle a variety of characters and maintain the original string length. Any suggestions or code examples would be greatly appreciated.
  8. The issue stemmed from how variables were captured within the closure passed to Parallel.Async. The loop variable i was not correctly captured, leading to incorrect values being used in the background tasks. The following code demonstrates my working solution: procedure Test; var arr_id: TArray<LongWord>; arr_ip: TArray<string>; procedure CreatePingTask(const id: LongWord; const ip: string); begin Parallel.Async( procedure (const task: IOmniTask) var PingResult: Boolean; begin // background thread // Simulate ping PingResult := Random(2) = 1; // random result for demonstration Sleep(Random(5000)); // simulate ping delay // in the main thread task.Invoke( procedure begin mmoLog.Lines.Add(Format('PingResult=%s, rec_id=%d, rec_ip=%s', [PingResult.ToString, id, ip])); end); end); end; begin arr_id := [1,2,3,4,5,6,7]; arr_ip := ['192.168.16.1','192.168.16.2','192.168.16.3','192.168.16.4','192.168.16.5','192.168.16.6','192.168.16.7']; for var i := Low(arr_id) to High(arr_id) do CreatePingTask(arr_id[i], arr_ip[i]); end; Explanation: To ensure each background task receives the correct ID and IP, we pass them as parameters to the CreatePingTask procedure. This creates a separate copy of the variables for each task. task.Invoke is used to synchronize with the main thread and update the UI with the ping results. This approach ensures that each host is pinged exactly once and the results are processed correctly in the main thread. I'd love to get some expert opinions on my solution. Are there any "gotchas" I should be watching out for?
  9. I'm using the Async abstraction from the OmniThreadLibrary to ping multiple hosts in background threads (one thread per host). In the main thread, I want to perform some actions with the ping results based on the host ID. Here's a simplified version of my code: procedure Test; var arr_id: TArray<LongWord>; arr_ip: TArray<string>; begin arr_id := [1,2,3,4,5,6,7]; arr_ip := ['192.168.16.1','192.168.16.2','192.168.16.3','192.168.16.4','192.168.16.5','192.168.16.6','192.168.16.7']; for var i := Low(arr_id) to High(arr_id) do begin Parallel.Async( procedure (const task: IOmniTask) var PingResult: Boolean; rec_id: LongWord; rec_ip: string; begin // background thread rec_id := arr_id[i]; rec_ip := arr_ip[i]; // PingResult := PingHost(rec_ip); // in the main thread task.Invoke( procedure begin // some actions with ping results based on host ID mmoLog.Lines.Add(Format('rec_id=%d, rec_ip=%s', [rec_id, rec_ip])); end); end); end; end; I'm getting inconsistent results. For example: rec_id=3, rec_ip=192.168.16.3 rec_id=3, rec_ip=192.168.16.3 rec_id=135299280, rec_ip=烬Ŧ rec_id=135299280, rec_ip=烬Ŧ rec_id=135299280, rec_ip=烬Ŧ rec_id=135299280, rec_ip=烬Ŧ rec_id=135299280, rec_ip=烬Ŧ or rec_id=2, rec_ip=192.168.16.2 rec_id=3, rec_ip=192.168.16.3 rec_id=4, rec_ip=192.168.16.4 rec_id=7, rec_ip=192.168.16.7 rec_id=7, rec_ip=192.168.16.7 rec_id=7, rec_ip=192.168.16.7 rec_id=135299280, rec_ip=烬Ŧ Not all IPs are being pinged, and some are pinged multiple times. Also, I get garbage data in some cases. How can I achieve the desired behavior, where each IP is pinged once and the results are processed correctly in the main thread? Alternatively, how can I rewrite the code using TTask?
  10. Shrinavat

    Allow tabs to use custom colors

    Oh, now it's clear. We can't manually set the color of the tabs. How quaint.
  11. Shrinavat

    Allow tabs to use custom colors

    Maybe the 'Allow tabs to use custom colors' option is like the Force in Star Wars - a mystical power that only a few chosen ones can truly understand and control. May the colors be with you, young Padawan. ✨ ...on a more serious note, has anyone cracked the code on how to actually use this mysterious option? Does anyone actually know how to change the tab colors?
  12. What is the purpose of this feature? How can I change the color of the tab in the code editor? I am unable to find an option to modify the color in the tab settings.
  13. Shrinavat

    The GetIt server is back online - With the 12.0 Patch 1

    (deletia)R120.patch1-20240131.zip
  14. Shrinavat

    Delphi 12 Component Palette Panel Shift Issue

    Yes, I have different Desktops. *.DST files do not have component palette properties. Deleting both your own Desktops and the default ones did not affect the offset position of the component palette :-(
  15. Shrinavat

    Delphi 12 Component Palette Panel Shift Issue

    Uninstalling CnPack had no effect, the panel appeared to be offset as well.
×