Python Forum
How i Can Reverse This Code To get the key
Thread Rating:
  • 1 Vote(s) - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How i Can Reverse This Code To get the key
#1
Hi can you help me reverse this and get the key.
My apologies if my post goes against the rules.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import string

def big(data):
    o = ''
    f = (data[-1] + data[:-1])[::-1]
    for i in f:
        o += chr(ord(i) ^ 0xb)
    return o[::2]+ o[1::2]

def bang(data,shift):
    enc = ''
    for c in data:
        if c not in string.ascii_letters:
            enc += c
        else:
            if c in string.ascii_lowercase:
                start = ord('a')
            else:
                start = ord('A')
            enc += chr(((ord(c) - start + shift) % 26) + start)

    return enc[-1] + enc[:-1].swapcase()

def check(s):
    
    if big(bang(bang(big(bang(bang(big(s),18),13)),6),11)) != "ZYYKXWAT[6RM@T6?ES>69=?Z6GRE}6WEGNK^>Oa6" :
        print("\nTry harder :)")
    else:
        print("\nYou got me !")

Attached Files

.zip   breakme.zip (Size: 2.41 KB / Downloads: 127)
Reply
#2
To reverse engineer this, do it in small steps and see what each part of the code is doing, then design your code to reverse the operation.

Have an input that is easy to track; as an example:

data = '0123456789'

f = (data[-1] + data[:-1])[::-1]
print('f:',f)

x = data[-1]
y = data[:-1]
z = data[::-1]
print('x:',x)
print('y:',y)
print('z:',z)
... which starts to breakdown of the big() function.

edit done to update code and add...

So, feeding in lleho will output hello; so we've potentially cracked that small part, but always test and retest what you've done so that you don't end off with a major debugging of your own code.

You should be able to see that feeding its output back in again, simply reverses it:

def encode(data):
    f = (data[-1] + data[:-1])[::-1]
    return f
def decode(data):
    f = (data[-1] + data[:-1])[::-1]
    return f

data_in = 'hello'
print(decode(encode(data_in)))
BashBedlam and rachhost like this post
Sig:
>>> import this

The UNIX philosophy: "Do one thing, and do it well."

"The danger of computers becoming like humans is not as great as the danger of humans becoming like computers." :~ Konrad Zuse

"Everything should be made as simple as possible, but not simpler." :~ Albert Einstein
Reply
#3
All the steps can be reversed, even the exclusive or (^ 0xb). If you invert everything and perform the steps in reverse order you can find the starting string. I wrote an rbig() and tested if rbig(big(str)) == str. I did the same for bang(). Of the two, I found bang() easier to reverse, so maybe start with that.

The first thing I did was write a comment to describe each step. For example, this removes that last character, swaps the case of the remaining characters, then inserts the old trailing character as the first character.
enc[-1] + enc[:-1].swapcase()
The reverse would be remove the first character, swap all the remaining characters, then append the old leading character as the last character.

The starting string looks like gibberish, so if you get a gibberish result from your code breaking don't assume it must be wrong. Test your results by running through the check.
rachhost and rob101 like this post
Reply
#4
@rachhost I've reversed the big() function (as @deanhystad has also done, I think) and I'm sure that we'll have different code for this.

The method I've used is to construct three functions, each one taking the input from the preceding one, in the reverse order in which big() encoded the string.

big("Testing this decoder.")
Output:
Big output: yohoxc+en%ndn+blbx_
Output:
Stage one decode output: ynodhno+xbc+lebxn_% Stage two decode output: redoced siht gnitseT. Stage three decode output: Testing this decoder.
So, I'll now work on the rest of this and see if I can achieve the goal.
So, to recap...

def big(data):
    o = ''
    f = (data[-1] + data[:-1])[::-1] # feeding the output back in to this, reverses the operation 
    for i in f:                      # passes each character in f to the function below
        o += chr(ord(i) ^ 0xb)       # chr() is the reverse of ord(), so again feeding the output back in to this, reverses the operation
    return o[::2]+ o[1::2]           # I had to construct a loop routine to reverse this, but there may be other ways.
The only real challenge here (for me), was the return as I could not reverse the o[::2]+ o[1::2] operation without constructing a loop routine, but as this is a reversing operation, it's the first thing that needs to be done in order to reverse the big() function. Again (as in my first post) using a none repeating sequence as the input, one can better understand what is happening:

d = '0123456789'
x = d[::2]
y = d[1::2]
z = x+y

print('d:',d)
print('x:',x)
print('y:',y)
print('x + y:', z)
Output:
d: 0123456789 x: 02468 y: 13579 x + y: 0246813579
edit done for code comments and typos
Note: This thread may now be a out of sequence, @rachhost has yet to come back with any code, but moving forward...

For this part, I wanted to write a decoder that does not use the string library.

So, if c not in string.ascii_letters: becomes if not c.isalpha(): and if c in string.ascii_lowercase: becomes if c.islower():

def bang(data,shift): # data is a string object and shift is a integer value
    enc = ''
    for c in data:
        if not c.isalpha():
            enc += c
        else:
            if c.islower():
                start = ord('a')
            else:
                start = ord('A')
            enc += chr(((ord(c) - start + shift) % 26) + start)

    return enc[-1] + enc[:-1].swapcase()
That aside...

Again, working in reverse, we first need to deconstruct return enc[-1] + enc[:-1].swapcase()

enc is constructed in the for loop, which in turn, does not alter anything that is not an alpha character, so an easy test for the return, is to pass a none repeating numeric sequence to bang() and see what it does.

d = "0123456789"
s = 0

x = bang(d, s)

print(x)
Output:
9012345678
So, it seems to simply swap the first and last characters of a numeric string; let's test that by doing the same on the input and break apart the return:

d = "1234567890"

x = d[-1]
y = d[:-1]
z = x+y

print('x' ,x)
print('y' ,y)
print('z', z)
Output:
x 0 y 123456789 z 0123456789
Confirmed.

I then did some more tests by adding in a alpha character (both uppercase and lowercase) at various positions, to see what results I got.

<some time later>

While playing around with different inputs, I got to thinking: what if (as before) I simply feed the output back in again, for as many times as there are characters in the input, and see what happens.

In doing that, I discovered that, for an even number of characters, the character case is reversed, but not for an odd number of characters. So it follows that running the routine twice, will always return the input.

data = "This is a line of text."
s = 0

print(f"Length: {len(data)}\n")

data_in = data
print(f"Loop 1 data: {data_in}")
for run in range(len(data)):
    data_out =(bang(data_in, s))
    print(f"run{run}: {data_out}")
    data_in = data_out

data_in = data_out
print(f"\nLoop 2 data: {data_in}")
for run in range(len(data)):
    data_out =(bang(data_in, s))
    print(f"run{run}: {data_out}")
    data_in = data_out

print(data_out))
I'll not post the output here, as it's over 50 lines long. The entire output is only really needed if you want to see what's happening, but simply comment out the print() functions within the loops to reduce that.

So, I think that the bang() function can be used against itself; no need for a rewrite, but I'll continue testing and see if I'm correct.
Now for the 'shift' in the bang() function for c in data: loop.

This puts me in mind of a 'Rotation cipher', which is a very simple form of encryption, which is performed by shifting the alpha characters by a predetermined number of places (search for Rot13 for some details on that). This, together with the XOR operation that we see in the big() function, seems to be the key to how all of this hangs together.

If that's correct, then a simple 52 - the shift, would get us back to the starting point. I see that the first 'shift' is 18 (have a look at the check() function) so 52 - 18 = 34; let's test this by introducing a shift in (by 18) and a shift out (by 34).

data = "This is a line of text."

data_in = data
shift_in = 18

for run in range(len(data)):
    data_out = (bang(data_in, shift_in))
    data_in = data_out

data_in = data_out
for run in range(len(data)):
    data_out = (bang(data_in, shift_in))
    data_in = data_out

print(data_out)

#=======================================#

data_in = data_out
shift_out = 52 - shift_in

for run in range(len(data)):
    data_out = (bang(data_in, shift_out))
    data_in = data_out

data_in = data_out
for run in range(len(data)):
    data_out = (bang(data_in, shift_out))
    data_in = data_out

print(data_out)
Nailed it!

With what we have here, it seems to me that all that remains is to glue these bits together, in the correct order, and it's job done.

I've yet to do this, so it could turn out that I've messed up somewhere; we'll see.

I'll refrain from posting any more code, unless I've made a huge error which would be too misleading for this thread to be of any help, but I'll try and answer any questions, should there be any.



I may have got a little ahead of myself here, as the decode of bang() with a shift seems to be broken, but it's close. I may simply re-code it, rather than try to fudge a fix.
rachhost and BashBedlam like this post
Sig:
>>> import this

The UNIX philosophy: "Do one thing, and do it well."

"The danger of computers becoming like humans is not as great as the danger of humans becoming like computers." :~ Konrad Zuse

"Everything should be made as simple as possible, but not simpler." :~ Albert Einstein
Reply
#5
-shift works fine since any roll over, positive or negative, is corrected by the % 52. (x -18) % 52 == (x + 52 -18) % 52
Reply
#6
(Aug-09-2022, 06:31 PM)deanhystad Wrote: -shift works fine since any roll over, positive or negative, is corrected by the % 52. (x -18) % 52 == (x + 52 -18) % 52

Thanks for that. I think I've been at this too long and need a break, as I'm getting myself confused now.

I'm close, but there seems to be an issue with reversing either the .swapcase() or the shift, or maybe it's a combination of the two and I've got the sequence wrong.

I've a good idea where I've messed up, but I need to come back to this fresh and spend some dedicated time on it, rather than dipping in as and when I've time between doing other things.

I really like solving puzzles and I know it's right there, in the code, and by doing this I'll also improve on my coding skills.
Sig:
>>> import this

The UNIX philosophy: "Do one thing, and do it well."

"The danger of computers becoming like humans is not as great as the danger of humans becoming like computers." :~ Konrad Zuse

"Everything should be made as simple as possible, but not simpler." :~ Albert Einstein
Reply
#7
The bang function cannot be used to reverse itself. The Caeser's cipher can be reversed using -shift, but undoing "enc[-1] + enc[:-1].swapcase()" requires that the three steps (slice, swapcase, concatenate) be performed in a different order, plus you need to do those steps before the cipher to undo bang().

Undoing the cipher is easy. Undoing "enc[-1] + enc[:-1].swapcase()" is easy if you look at the steps instead of treating it like a black box. The same goes for "f = (data[-1] + data[:-1])[::-1]" (slice, concatenate, reverse). Reversing the exclusive or step results in a fun discovery, or a forehead slapper depending on how long you've been programming.

That leaves "return o[::2]+ o[1::2]" as probably the most challenging step to reverse. The logic is pretty straight-forward, but coding, at least coding in a few lines, is challenging.
rob101 likes this post
Reply
#8
(Aug-10-2022, 11:16 AM)deanhystad Wrote: That leaves "return o[::2]+ o[1::2]" as probably the most challenging step to reverse. The logic is pretty straight-forward, but coding, at least coding in a few lines, is challenging.

I think that I have the big() function decode working, as a stand-alone, least ways.

Output:
>>> decode = bigd("ndn+b+c+enyoholinlbx_") >>> decode Testing the big decoder >>>
n.b. highlight, copy and paste the string, as there's a character in there that does not display.

If I have, and you have, then in theory the above should decode the same at your side.

Thanks for the advice and pointers. I'll get there, but if I do get stuck in a hole, I trust that you'll help me out.

Not too sure if the OP has abandoned this, or if he's simply watching at a distance.

Cheers.
@deanhystad

I've just done my first successful decode of a string encode with bang(big(s),18), so I think that I'm on the home straight now!

Thanks for the pointers: I got myself locked into a mindset about how to tackle the bang() reversal, which cost me time, but it payed back experience, so lesson learned.

Cracked it!

So, to anyone else that's interested in this, I'm not what you'd call an experienced Python code, but I've some skills and know enough to be able to craft a decoder for this. I've maybe done things a little different to someone with more advanced skills than I, but results count, as does the journey, from which one can gain more experience.

I don't think that there's any major gaps in the process here, but if you get stuck, simply ask for help, but please don't ask for or post the full solution.

Edit to add...

I've done some searching, but I can't find where this challenge has come from. Anyone know please?
Sig:
>>> import this

The UNIX philosophy: "Do one thing, and do it well."

"The danger of computers becoming like humans is not as great as the danger of humans becoming like computers." :~ Konrad Zuse

"Everything should be made as simple as possible, but not simpler." :~ Albert Einstein
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020