A couple months ago, Matasano Security published a set of cryptography challenges. I’ve been interested in crypto for awhile, so I decided to give them a go. I’ve also been wanting to get into lower level programming, and I took this as a chance to learn C a little better.
I’ve done a little bit of C here and there in the past (read through K&R, implemented some toy exercises), but I am by no means familiar. The following are a couple things that I’ve learned while working on these crypto challenges. Most of them are fairly obvious, but it was cool to see them play out anyway.
- C is fast. Really fast. There was a particular problem that I had difficulty solving in C, so I coded up a solution in Ruby so I could solve the challenge without worrying about using an unfamiliar tool-chain. The solution I wrote in Ruby takes 1.7 seconds to run. The solution I wrote in C takes 0.02.
- Managing memory is hard. Without language-level tools to guarantee that you’re not overrunning your buffers, it’s really easy to write to memory that you’re not supposed to. The symptoms of an overflowed buffer may not appear in obvious places, either. I had an issue where I got segmentation faults whenever I tried to malloc more memory. I was a little confused why a call to malloc would cause a segfault; it took awhile for me to realize that I had overflown a buffer further up. Valgrind, a debugging tool and memory leak detector, was particularly helpful for these sorts of issues.
- gdb is awesome. I’d used gdb a little before when going through some other security related exercises, but I hadn’t used it more extensively until now. I’m used to writing Ruby or Python where an exception or failure results in an immediate stacktrace being printed to the console. C programs don’t do that for you unless you run them through gdb. It’s also good for looking at things in memory at breakpoints that you can set, although this is not much different from Python or Ruby debuggers.
- A lot of the difficulties in learning a new programming language are not actually related to the language itself, but the tool-chain. C is no different. I was not very familiar with GNU Make, so it took a little while to get used to the somewhat confusing syntax to get my project to work right. Building and Extending Gaim had a section on build tools that was quite instructive.
- As a Ruby web developer, I’m used to a very test-driven style of development. I couldn’t find standard unit testing tools for C that work similarly to how rspec or test/unit does, so I straight up did not write tests. This proved to be a little difficult to deal with, especially in an unfamiliar programming environment. I had a lot of bugs in random functions in disparate files that I didn’t know about until I ran them in specific scenarios. I ended up adding function calls to my main just to “unit test” my functions to see if they worked right, before deleting them again so as to actually run the code. In problem I mentioned above where I solved the challenge in Ruby first, I definitely wrote with TDD. It felt so much more secure than cowboy-coding in C.
C has been a great adventure so far; two more challenges before I finish the first set!