In this series of blog posts, I will be discussing some of my favorite topics in math, It mostly falls under the domains of mathematical logic and computability theory.
Diophantus was a Greek mathematician that flourished around 250 AD, he is most famous for his book “Arithmetica” in which he had compiled 130 algebraic problems among thirteen books where most of their solutions were positive integers, and that’s why mathematicians call such algebraic equations as “Diophantine Equations”.
It is said that he wrote his book “Arithmetica” as a distraction from his son’s death, and it turned out to be one of the most brilliant books that discussed algebra and he was amongst the first to use symbolic representations for real-life problems, he was also the first to use powers of more than 2 and 3 which had no physical meaning unlike the square and the cube, his solutions also never involved zeroes or negative numbers.
Diophantus had a brilliant way of solving problems involving multiple variables using only a single variable, he did it in such a way that he finds relations that represent the other variables in terms of the first variable.
For Example, In his first problem of the fourth book:
“Divide a given number into two cubes such that the sum of their sides is a given number”.
He then provides two numbers:
- Given Number: 370
- Sum of Sides: 10
We can visualize the problem Geomterically as follows: Let’s solve it using second grade algebra!
The two sides ($x$ and $y$) add up to 10 and the sum of their cubes ($x^3$ and $y^3$) is 370.
$x + y = 10$
$x^3 + y^3 = 370$
$x = 10 - y$
$(10 - y)^3 + y^3 = 370$
$(1000 + 30y^2 - 300y - y^3) + y^3 = 370$
$30y^2 - 300y + 630 = 0$
$y^2 - 10y + 21 = 0$
$(y - 7)(y - 3) = 0$
y = 7, y = 3
I didn’t want to bother with a detailed explanation of the steps above (I hope all of those who are reading this know some basic algebra), here is how Diophantus solved such problems.
He would first express the two variables $x$ and $y$ as two relations in terms of one variable so his $x$ will be $(5+x)$ and his $y$ will be $(5-x)$.
$(5 + x) + (5 - x) = 10$
These indeed satisfy the first equation and will yield 10 upon adding them together, and for the second equation, he describes it as follows:
$(5 + x)^3 + (5 - x)^3 = 370$
It will seem a bit odd and crazy at first, but once we start expanding these cubes, terms will start canceling out like crazy until we are left with:
$30x^2 + 250 = 370$
Which will yield:
$x^2 = 4$
Finally we arrive at the same solution:
$x = 2$
$(x + 5) = {\bf 7}$
$(x - 5) = {\bf 3}$
his problems may seem plain and simple at the first glance but they sometimes become very tough and hard to attack, a famous quote by the German mathematician Hermann Hankel regarding the diophantine problems, “Every question requires a quite special method, which often will not serve even for the most closely allied problems. It is on that account difficult for a modern mathematician even after studying 100 Diophantine solutions to solve the 101st problem”
Pierre de Fermat was a famous 17th-century French mathematician, he had a copy of Diophantus’s Arithmetica and had filled its margins extensively with notes.
At one of the problems that involved a solution in the form of $x^2\ +\ b^2\ =\ c^2$, Fermat wrote:
“On the contrary, it is impossible to separate a cube into two cubes, a fourth power into two fourth powers, or generally any power above the second into two powers of the same degree. I have discovered a truly marvelous demonstration which this margin is too narrow to contain.”
This nonexistent proof became knows as “Fermat’s Last Theorem”, It was later solved by the British Number Theorist Andrew Wiles in 1993 after three and a half centuries, he then received the Abel Prize in 2016 for his solution.
]]>We can think of buffers as containers to hold our data for later use, it’s not really something specific to computer science; In fact, we have buffers in electronics, mechanics, chemistry and even politics! We can say that the word buffer is just a fancy name for a placeholder (temporary storage)!
Now, it’s time to get more technical and get to know buffers more in computer science!
Online video streaming is a pretty good application of buffering, back in the old days we would see YouTube showing that nostalgic loading bar while waiting the video to load and that’s exactly why we use buffers! We need to store video data in someplace so that when we need that data we can load it directly from the computer’s memory since we can’t really have our internet connection playback videos in realtime without having some temporarily storage for data to reside in, and that’s the reason why livestreams never sync with realtime and have a couple seconds delay.
here is a more lower level representation of a buffer:
#define BUFSIZE 1024
char buffer[BUFSIZE];
size_t len;
// ... later
while((len=read(STDIN, &buffer, BUFSIZE)) > 0)
write(STDOUT, buffer, len);
This snippet basically check for data available then reads it from the standard input (command line) and writes that data back to the standard output (command line too).
It’s time for the good stuff now!
Let’s say we have an arbitrary variable of size 5 bytes, and we stored some value in that variable which exceeds 5 bytes, that’s a simple buffer overflow!
char someVariable[5];
/* we are trying to copy 15 bytes to a 5 bytes sized variable */
memcpy(someVariable, "Hello, world!\n", 15);
printf(someVariable);
We encounter buffer overflows more frequently than we imagine, ever seen Segmentation Fault
appear in your C program out of nowhere? That’s also a buffer overflow but your computer has prevented it from causing any damage and stopped the program from running.
Let’s get back to our 5 bytes overflown variable example. What happens to the overflown data? Where is it’s destination? Does it get lost in the void of nothingness?
Nope, in fact we just overwrite other data when we overflow any variable, and that’s the core of our problem. We can’t just overwrite data and act as if nothing happened. What if we overwrite important data? What if we can overwrite data related to process memory and already running programs?
These are all possibilities which are just a mere consequence of buffer overflows and can cause great damage to computers
A basic technique used by hackers when exploiting buffer overflow vulnerabilities is determining the place where data gets overwritten and handcrafting the overflown data to inject arbitrary code in the program, imagine if that program runs at elevated privileges and a hacker takes control of it’s execution flow, you get pwned.
And that’s just the basic technique in abusing buffer overflow vulnerabilities.
Sadly, yes you are safe.
Buffer overflows aren’t as popular in 2020 (as of writing this article) as they used to be back in the old days of computers, all modern processors/operating systems have strict rules and mitigations for all the paradigms and techniques of buffer overflows.
Some of these famous mitigations are:
ASLR (Address Space Layout Randomization)
Basically randomizes all the memory addresses at runtime whenever you execute any program so it makes it impossible to hardcode memory addresses for exploiting buffer overflow attacks
i.e: the exploit needs to be completely dynamic
NX Bit
Basically marks areas of memory as non-executable, prevents hackers from injecting malicious code into areas of memory in an attempt to execute it.
Stack Canaries
Basically generates some random value in memory at runtime and checks before returning from any function if that random value got overwritten or not, if yes then program exits.
And these are just some of the basic mitigation used in 2020.
But again, these doesn’t really make your computer invincible as much as making it just harder for an attacker to compromise your system using buffer overflow attacks.
Hackers still can bypass these mitigations using underhanded methods such as bruteforce and leaking addresses from memory but using these mitigations correctly can make buffer overflows nearly useless.
It’s also worthy to mention that buffer overflows can be prevented from the programmers side by following the coding best practices and avoiding unsanitized input etc…
]]>Since a couple months ago, i have decided to try and switch fully to linux and ditch that piece of junk they call windows.
Now, that’s a bit misleading since i have been using windows for the past 12-13 years of my keyboard smashing journey but i don’t really feel any guilt doing this, in fact windows is nothing more of a bottleneck to me or so have i figured later.
enough ranting, maybe this needs it’s own blog post after all or maybe not, it’s a highly controversial topic whatsoever. Just believe me windows is pure evil.
I have tried tons of Linux distrois but i always kept jumping from one to the next, never settled on a distro, at first i wanted to use it mainly for my cybersecurity hobby so i thought Kali would be a pretty nice choice…
but guess what, it wasn’t…
I really think that kali is a pretty good OS but not that good for using as a daily driver system, it’s just bloated with a tons of security tools that you will never get chance to use and maybe you don’t even know of their existence.
so i started trying different operating systems ranging from the classic debian to using arch linux which actually was the thing i liked the most!
beside the fact that you get to customize your operating system to the max level, you also get the Arch User Repository (AUR) which contains 64,441 packages as of writing this, it’s a rolling release distro which basically gives you gradual little updates as they are released unlike other stable release distro that give you a bulky big update that sometimes forces you to reinstall your os.
Arch is a bit tougher than any other OS though, since they don’t provide any graphical installer like most distros and you have to configure it yourself from the groundup, a pretty awesome arch based distro is Manjaro which is bascially a simpified version of arch with all Arch’s perks and that’s what i have setteled on!
So we now have an arch distro that is lightweigt, fully customizable and contains an awesome package manager with the arch user repositoty, and that’s not even the end! we can add to our manjaro setup the blackarch repo which contains as of writing this article a whooping 7030 security tool ready for installing at your fingertips!
now we can customize our own distro and bundle it with the tools we only need!
Manjaro comes in differnt desktop enviroments, but i have actually tried all of them inclduing XFCE, KDE and MATE and the one that sucked the least was XFCE…
but that was until i discovered the heaven of tiling window managers, which basically can speed up your keyboard smashing habits by 10x if you invested the little time into making it your own!
here are some pics of my Manjaro boxes!
]]>Recursion is a pretty improtant topic in programming and it’s not that hard to grasp or even implement, but how about actually using it correctly?
In this blog post i will try to explain the basic concept of recursion and then show why recursion can be so inefficient and how to optimize it using Call Tail Optimization!
Most of us tech nerds have already dealt with the good ‘ol recursion, let’s refresh our understanding using the iconic factorial program. $$0! = 1$$ $$n! = n (n-1)!$$ Python Implementation:
def fact(x):
if (x==0):
return 1
else:
return x * fact(x-1)
But python is just too mainstream and overrated, let’s use Lisp!
(define (fact x)
(if (= x 0)
1
(* x (fact (- x 1)))))
ain’t Scheme just too beautiful? Now, let’s inspect the program behavior!
let’s say we want to execute (fact 5)
which supposedly evaluates to 120.
here is the trace of the factorial operation:
(fact 5)
(* 5 (fact 4))
(* 5 (* 4 (fact 3)))
(* 5 (* 4 (* 3 (fact 2))))
(* 5 (* 4 (* 3 (* 2 (fact 1)))))
(* 5 (* 4 (* 3 (* 2 (* 1 (fact 0))))))
(* 5 (* 4 (* 3 (* 2 (* 1 1)))))
(* 5 (* 4 (* 3 (* 2 1))))
(* 5 (* 4 (* 3 2)))
(* 5 (* 4 6))
(* 5 24)
120
here’s the pythonic version for those who are struggling with lisp (it’s way easier believe me)
fact(5)
5 * fact(4)
5 * (4 * fact(3))
5 * (4 * (3 * fact(2)))
5 * (4 * (3 * (2 * fact(1))))
5 * (4 * (3 * (2 * (1 * fact(0)))))
5 * (4 * (3 * (2 * (1 * 1))))
5 * (4 * (3 * (2 * 1)))
5 * (4 * (3 * 2))
5 * (4 * 6)
5 * 24
120
Did you figure out the flaw of our simple recursion implementation yet?
It’s pretty simple, the way we expand the factorial on each iteration so that it grows and keeps growing until we fully expand it is just so inefficient and wastes memory space.
The waste of memory space comes from the fact that each call of (fact x)
will allocate a new stack frame to store its data, so we have used around 6 stack frames for this simple calculation, allocating and popping stack frames is a relatively intensive operation for the CPU.
The source of this flaw is the multiplication that we are performing with our recurred call.
So Tail Call Optimization or Tail Recursion are just fancy names for a simple rule we need to follow in order to optimize our recursive functions.
“The recurred call shouldn’t be combined with other operations”
i.e: we need to move the multiplication operator out of the recurred call in the factorial function
let’s rewrite the factorial function in Tail Recursion:
(define (fact-tail x accum)
(if (= x 0) accum
(fact-tail (- x 1) (* x accum))))
(define (fact x) (fact-tail x 1))
def factTail(x, accum):
if (x == 0):
return accum
else:
return factTail(x-1, x*accum)
def fact(x):
return factTail(x, 1)
what we did in that snippet above is pretty simple, we just split the work across two functions, the first function (fact-tail x accum)
will iterate and the second function (fact x)
will call the first function and returns the value of each iteration (we have also moved the multiplication operation to it’s own variable) so we basically have no extra operations going on, in fact calling (fact 0)
is now the same as calling (fact 10000)
in terms of memory size.
let’s step through each iteration and see for ourselves how great is Tail Recursion:
(fact 5)
(fact-tail 5 1)
(fact-tail 4 5)
(fact-tail 3 20)
(fact-tail 2 60)
(fact-tail 1 120)
(fact-tail 0 120)
120
Pythonic Version:
fact(5)
factTail(5, 1)
factTail(4, 5)
factTail(3, 20)
factTail(2, 60)
factTail(1, 120)
factTail(0, 120)
is this even recursion anymore, that’s just fancy iteration!
we have used recursion in such a way that we store all the data to perform our evalutaion in each individual reccured call! All Hail Tail Call Optimization!
here is one more example with the infamous fibonacci function in both normal Recursion and then Tail Recursion:
(you try to implement it in python this time :p)
(define (fib x)
(cond ((= x 0) 0)
((= x 1) 1)
(else (+ (fib (- x 1)) (fib (- x 2))))))
(define (fib x)
(fib-iter x 0 1))
(define (fib-iter x a b)
(cond ((= x 0) a)
((= x 1) b)
(else (fib-iter (- x 1) b (+ a b)))))
All Hail The Tail Recursion
]]>Data:
disas main
to disassemble functionx/50wx $esp
examine 50 words in hex starting from espx/50i $eip
examine 50 instructions from eipx anything
examine address and show it’s contentsinfo variables varname
will show any variable address (for global!)info functions
to list all functionsinfo frame
shows the saved RTN address under “saved eip”info proc map
shows memory mapctx
shows general info about everything (works in gef only)Flow
b *address
or b *function<+number>
j *address
to jumpsi
step instructionni
step instruction and stepover functionsHook Stops we use these to execute certain commands everytime our program flow stops (ex: breakpoints, steps)
define hook-stop
>info registers
>x/24wx $esp
>x/2i $eip
>end
]]>