summaryrefslogtreecommitdiff
path: root/posts/challenges-re-writeups-2.org
blob: cb185fcbe4f3d12e7443383212f8f2b381c247b5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
#+TITLE: Writeups for Dennis Yurichev's Reverse Engineering Challenges (#12-#22)
#+TAGS: writeup reverse-engineering x86
#+DATE: <2019-05-28 Tue 15:18>

This is the second set of solutions for my self-imposed challenge of completing
at least fifty of the exercises on Dennis Yurichev's [[https://challenges.re][challenges.re]] by the end of
the year. The first set is available [[http:///jakob.space/challenges-re-writeups-1.html][here]].

* Challenge #12

No hints are given for this challenge, but it is the first time a binary is
available in addition to the disassembly. I didn't download the executable, I
was able to gather from the tags that the target is amd64 Linux.

If later challenges also provide executables, I may use it as an opportunity to
explore the NSA's newly-released [[https://www.nsa.gov/resources/everyone/ghidra/][Ghidra]]. At the time of writing this, Ghidra's
source code has yet to be released, so I'll have to pass up the opportunity this
time around.

#+BEGIN_SRC c
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include <utime.h>

int main(int argc, char **argv)
{
        // push rbx
        // mov rbx, rsi
        // sub rsp, 160
        int ret;
        struct stat sbuf;
        struct utimbuf tbuf;

        // cmp edi, 2
        // je .L2
        if (argc != 2) {
                // mov edi, OFFSET FLAT:.LC0
                // call puts
                puts("Usage: ");
        }

        // .L2:
        // mov rsi, QWORD PTR [rbx+8]
        // lea rdx, [rsp+16]
        // mov edi, 1
        // call __xstat
        ret = stat(argv[1], &sbuf);

        // test eax, eax
        // js .L10
        if (ret < 0) {
                // .L10:
                // mov edi, OFFSET FLAT:.LC1
                // call puts
                // xor edi, edi
                // call exit
                puts("error #1!");
                exit(0);
        }

        // mov rax, QWORD PTR [rsp+88]
        // xor edi, edi
        // mov QWORD PTR [rsp], rax
        tbuf.actime = sbuf.st_atim.tv_sec;

        // call time
        /// ...
        // mov QWORD PTR [rsp+8], rax
        tbuf.modtime = time(NULL);

        // mov rdi, QWORD PTR [rbx+8]
        // mov rsi, rsp
        // call utime
        ret = utime(argv[1], &tbuf);

        // test eax, eax
        // js .L11
        if (ret < 0) {
                // .L11:
                // mov edi, OFFSET FLAT:.LC2
                // call puts
                // xor edi, edi
                // call exit
                puts("error #2!");
                exit(0);
        }

        // add rsp, 160
        // xor eax, eax
        // pop rbx
        // ret
        return 0;
}
#+END_SRC

The first thing that may stand out in the disassembly listing is =call __xstat=.
=__xstat= isn't part of the C standard library or POSIX. My understanding of
functions like these is that sometimes an interface like =stat= will result in a
call to some internal libc routine when compiled, but the only time I've seen
this before was with =__printf_chk=. Regardless, search engines are your friend,
and you should have no trouble arriving at the [[http://refspecs.linuxbase.org/LSB_3.0.0/LSB-PDA/LSB-PDA/baselib-xstat-1.html][relevant page]] in the Linux
Standard Base Specification. One interesting thing of note is the comment that
"=ver= shall be =3= or the behavior of these functions is undefined," yet the
disassembly indicates that =ver= is =1=. I'm doubtful that this is part of the
challenge, though.

I also just guessed that =QWORD PTR [rsp+88]= was =sbuf.st_atim.tv_sec=, given the
context that the value is used in. Going from assembly to the corresponding
fields of a struct is a pain without a tool, which is perhaps an indication that
I should've downloaded the binary and used Ghidra^h^h^h^h^h^hradare2 to prod at
it.

Either way, the code updates a file's modification time. It's comparable to
=touch=, but doesn't create the file if it doesn't exist. The strings are
intentionally vague, so here's a cleaned up version:

#+BEGIN_SRC c
#include <errno.h>
#include <string.h>

int main(int argc, char **argv)
{
        struct stat sbuf;
        struct utimbuf tbuf;

        if (argc != 2) {
                printf("Usage: %s [path]\n", argv[0]);
        }

        if (stat(argv[1], &sbuf) < 0) {
                printf("%s: %s\n", argv[1], strerror(errno));
                exit(0);
        }

        tbuf.actime = sbuf.st_atim.tv_sec;
        tbuf.modtime = time(NULL);

        if (utime(argv[1], &tbuf) < 0) {
                printf("utime: %s\n", strerror(errno));
                exit(0);
        }

        return 0;
}
#+END_SRC

* Challenge #13

The question for this exercise is, "[w]hat does this SSE code do?" Uh oh. I
don't know anything about SSE. Not the end of the world, though. I always
appreciate an opportunity to learn. Here are some notes I took on chapter 25 of
Yurichev's book:

_Vectorization_ is the process of taking several arrays as input and producing a
single array as output. SIMD (Single Instruction, Multiple Data) is a way of
optimizing vectorization by doing certain array-level operations in parallel.

Intel's initial implementation of SIMD reused FPU register. SSE added 128-bit
registers (%xmm*) that were separate from the FPU, and AVX added 256-bit
registers that were separate from the FPU.

Well, that doesn't seem too complicated, and the exercise only uses two
instructions: =movdqu=, which loads a 16-byte value from memory into an %xmm*
register, and =pmaxub=, which calculates the maximum values between two %xmm*
registers.

#+BEGIN_SRC c
void f(int *dest, int *a, int *b)
{
        int i;

        // xor rax, rax
        // ...
        // add rax, 16
        // cmp rax, 1024
        // jne .L4
        // ...
        // .L4:
        for (i = 0; i < 256; i++) {
                // movdqu xmm0, XMMWORD PTR [rsi+rax]
                // movdqu xmm1, XMMWORD PTR [rdx+rax]
                // pmaxub xmm0, xmm1
                // movdqu XMMWORD PTR [rdi+rax], xmm0
                dest[i] = a[i] > b[i] ? a[i] : b[i];
        }

        // rep ret
        return;
}
#+END_SRC

=f= will fill an array, =dest=, such that the element at each index contains the
greater value between =a= and =b= for that index.

* Challenge #14

The challenge description explains that, "[n]ow that's easy," and gives both
.NET and Java bytecode disassemblies. I am not familiar with either bytecode
format, but I do know Java (unfortunately), so I went with that.

#+BEGIN_SRC java
public class Challenge14 {
    public static boolean f(char a) {
        //  0: iload_1
        //  1: bipush        97
        //  3: if_icmplt     14
        //  6: iload_1
        //  7: bipush        122
        //  9: if_icmpgt     14
        if (a < 97 || a > 122) {
            // 14: iload_1
            // 15: bipush        65
            // 17: if_icmplt     28
            // 20: iload_1
            // 21: bipush        90
            // 23: if_icmpgt     28
            if (a < 65 || a > 90) {
                // 28: iconst_0
                // 29: ireturn
                return false;
            }

            // 26: iconst_1
            // 27: ireturn
            return true;
        }

        // 12: iconst_1
        // 13: ireturn
        return true;
    }
}
#+END_SRC

I'm not particularly confident in my translation -- the above is the result of
skimming the [[https://en.wikipedia.org/wiki/Java_bytecode][Java bytecode]] and [[https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings][Java bytecode instruction listings]] pages on
Wikipedia -- but that translation does appear to convey a meaningful operation:
telling whether or not =a= is an ASCII letter.

* Challenge #15

The challenge description explains that, "[n]ow that's really easy."

#+BEGIN_SRC c
void f(char *dst)
{
        int i;

        // xorps   %xmm0, %xmm0
        // movups  %xmm0, 240(%rdi)
        // movups  %xmm0, 224(%rdi)
        // movups  %xmm0, 208(%rdi)
        // movups  %xmm0, 192(%rdi)
        // movups  %xmm0, 176(%rdi)
        // movups  %xmm0, 160(%rdi)
        // movups  %xmm0, 144(%rdi)
        // movups  %xmm0, 128(%rdi)
        // movups  %xmm0, 112(%rdi)
        // movups  %xmm0, 96(%rdi)
        // movups  %xmm0, 80(%rdi)
        // movups  %xmm0, 64(%rdi)
        // movups  %xmm0, 48(%rdi)
        // movups  %xmm0, 32(%rdi)
        // movups  %xmm0, 16(%rdi)
        // movups  %xmm0, (%rdi)
        // ret
        for (i = 0; i < 256; i++) {
                dst[i] = '\0';
        }
}
#+END_SRC

I initially read the disassembly for this challenge as if it were Intel syntax,
but it's AT&T. The operation is simple: =f= zeroes out a 256-byte buffer specified
by the first parameter.

* Challenge #16

Only one disassembly is given for this challenge, and the description hints that
it is from Clang: "[n]ow this is getting harder. Clang did a lot of optimization
tricks and this code is heavily optimized for SSE2. Nevertheless, the original
function is tiny and simple. What does it do?"

In all honesty, I don't think that a translation to C is helpful. As the problem
mentioned, there's heavy optimization for SSE2, and the assembly code only
tangentially corresponds to what (I believe) is going on. Instead, I'll attempt
to justify my partial conclusion that =f= sums an array of integers.

Bytes from =rdi= (indexed with =rcx=) are interleaved into =xmm0= and =xmm2= with
=pinsrw=, and continually added into =xmm3= and =xmm4=. Then, =xmm0= and =xmm2= are added,
and =xmm1= is unpacked with =punpckhqdq xmm1, xmm1=. The pseudocode for the
=punpckhqdq= instruction is given as:

#+BEGIN_SRC c
Destination[0..63] = Destination[64..127];
Destination[64..127] = Source[64..127];
#+END_SRC

So it's unusual to see =xmm1= as both the "Source" and "Destination". This is
followed by a =paddq xmm1, xmm0=.

I can't really confirm any of this because of the =movdqa xmm1, xmmword ptr
[rip + .LCPI0_0]= instruction. Some sort of mask is being used in those =pand
xmm0, xmm1= and =pand xmm2, xmm1= instructions, and I suspect that it's one of many
tricks coming together so the function works for an array of _integers_, but we
aren't given =.LCPI0_0=, so I can't tell for sure.

This also means that I can't assemble what's given. If anyone out there is
experienced in SIMD and would to share some tips for making sense of this one,
I'd really appreciate it.

* Challenge #17

The description explains that "[t]his is a quite esoteric piece of code, but
nevertheless, the task it does is very mundane and well-known to anyone. The
function has 4 32-bit arguments and returns a 32-bit one."

#+BEGIN_SRC c
int f(int a, int b, int c, int d)
{
        int tmp1, tmp2;

        // sub edx, edi
        c -= a;

        // mov r8d, ecx
        // ...
        // sub r8d, esi
        tmp1 = d - b;

        // mov ecx, 63
        d = 63;

        // mov eax, edx
        // sar eax, cl
        // and eax, edx
        tmp2 = (c >> (d & 0xff)) & c;

        // mov edx, r8d
        // sar edx, cl
        // ...
        // and edx, r8d
        c = (tmp1 >> (d & 0xff)) & tmp1;

        // add edi, eax
        a += tmp2;

        // add esi, edx
        // sub esi, edi
        b += c - a;

        // mov eax, esi
        // sar eax, cl
        // and eax, esi
        // add eax, edi
        // ret
        return ((b >> (d & 0xff)) & b) + a;
}
#+END_SRC

The initial translation is quite messy, but observe that =d= has a constant value
of =63=, and =63 & 0xff= is just =63=. Still, there are a number of snippets that look
like =(c >> (d & 0xff)) & c=, and it isn't obvious what that does.

#+BEGIN_SRC c
int black_box(int a)
{
        return (a >> 63) & a;
}

int main(void)
{
        int i;

        for (i = 0; i >= 0; i += 1) {
                if (black_box(i) != 0) {
                        printf("black_box(%d) = %d\n", i, black_box(i));
                }
        }

        for (i = 0; i <= 0; i -= 1) {
                if (black_box(i) != i) {
                        printf("black_box(%d) = %d\n", i, black_box(i));
                }
        }


        return 0;
}
#+END_SRC

#+BEGIN_SRC
re.c: In function 'black_box':
re.c:64:13: warning: right shift count >= width of type [-Wshift-count-overflow]
   return (a >> 63) & a;
             ^~
#+END_SRC

I'm not sure if this is the intended behavior, but on amd64, this acts as
\(min(x, 0)\). A first step at simplification can be made.

#+BEGIN_SRC c
#define MIN(a, b) (a < b ? a : b)

int f(int a, int b, int c, int d)
{
        a += MIN(c - a, 0);
        c = MIN(d - b, 0);
        b += c - a;
        return MIN(b, 0) + a;
}
#+END_SRC

And this can be further cleaned up into a one-liner.

#+BEGIN_SRC c
#define MIN(a, b) (a < b ? a : b)

int f(int a, int b, int c, int d)
{
        return a + \
                MIN(b - a + MIN(d - b, 0) - MIN(c - a, 0), 0) + \
                MIN(c - a, 0);
}
#+END_SRC

And this happens to be an interesting implementation of \(min(a, b, c, d)\).

* Challenge #18

For challenges with more complicated control flow, I've been drawing the basic
blocks out on a sheet of paper and drawing arrows between them to identify which
transitions represent loops, and which transitions represent conditionals. That
didn't work particularly well for this challenge, though. The solution instead
came to me instead by just staring at the disassembly for some time.

#+BEGIN_SRC c
#include <ctype.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

int f3(char *a, uint64_t *b, uint64_t *c, uint64_t *d, uint64_t *e, uint64_t *f)
{
        int i;
        char *cur;

        if (strlen(a) != 36) {
                return a;
        }

        cur = a;
        i = 0;

        while (i != 37) {
                if (i == 8 || i == 13 || i == 18 || i == 23) {
                        if (*cur != '-') {
                                return (char *) -1;
                        }
                } else {
                        if (i == 36 && *cur == '\0') {
                                break;
                        }

                        if (!isxdigit(*cur)) {
                                return (char *) -1;
                        }
                }

                i++;
                cur++;
        }

        *b = strtoul(a, NULL, 16);
        *c = strtoul(a + 9, NULL, 16);
        *d = strtoul(a + 14, NULL, 16);
        *e = strtoul(a + 19, NULL, 16);
        *f = strtoull(a + 24, NULL, 16);
        return 0;
}
#+END_SRC

The code appears to implement a [[https://en.wikipedia.org/wiki/Universally_unique_identifier][UUID]] parser.

* Challenge #19

This challenge was particularly difficult. I began by translating the individual
basic blocks to C, and connecting them with =goto=.

#+BEGIN_SRC c
#include <stdlib.h>
#include <stdio.h>

char *f2_bb(char *a, int b, int c, char *d)
{
        // rax <- a
        // rbx <- a

        // QWORD PTR [rsp+24] <- d
        // QWORD PTR [rsp+16] <- b
        // QWORD PTR [rsp+8]  <- c

        int i;
        int j;
        int k;
        int *ret;

        // f2:
        {
                // test rcx, rcx
                // jne .L21
                if (c == 0) {
                        // add rsp, 32
                        // pop rbx
                        // ret
                        return a;
                }

                goto BBL21;
        }

        // .L21:
        {
        BBL21:
                // lea rdi, [4+rcx*4]
                // ...
                // call malloc
                ret = malloc(c * 4 + 4);

                // ...
                // mov DWORD PTR [rax], -1
                ret[0] = -1;

                // ...
                // mov DWORD PTR [rax+4], 0
                ret[1] = 0;

                // mov r9d, 1
                i = 1;

                // ...
                // cmp r9, rcx
                // je .L22
                if (c == 1) {
                        goto BBL22;
                }

                goto BBL8;
        }

        // .L8:
        {
        BBL8:
                // mov edi, DWORD PTR [rax+r9*4]
                // lea r8d, [rdi+1]
                // test r8d, r8d
                // mov DWORD PTR [rax+4+r9*4], r8d
                if ((ret[i + 1] = ret[i] + 1) <= 0) {
                        // jle .L5
                        goto BBL5;
                }

                // movzx r10d, BYTE PTR [rdx+r9]
                // movsx r8, r8d
                // cmp r10b, BYTE PTR [rdx-1+r8]
                if (d[i] != d[ret[i + 1] - 1]) {
                        // jne .L7
                        goto BBL7;
                }

                // jmp .L5
                goto BBL5;
        }

        // .L7:
        {
        BBL7:
                // mov r8d, DWORD PTR [rax-4+r8*4]
                // add r8d, 1
                // test r8d, r8d
                // mov DWORD PTR [rax+4+r9*4], r8d
                if ((ret[i + 1] = ret[ret[i + 1] - 1] + 1) > 0) {
                        // jg .L23
                        goto BBL23;
                }

                goto BBL5;
        }

        // .L23:
        {
        BBL23:
                // movsx r8, r8d
                // cmp BYTE PTR [rdx-1+r8], r10b
                if (d[ret[i + 1] - 1] == ret[i]) {
                        // je .L5
                        goto BBL5;
                }

                goto BBL7;
        }

        // .L5:
        {
        BBL5:
                // add r9, 1
                // cmp r9, rcx
                if (++i != c) {
                        // jne .L8
                        goto BBL8;
                }

                goto BBL22;
        }

        // .L22:
        {
        BBL22:
                // xor r8d, r8d
                // xor r10d, r10d
                // xor edi, edi
                i = j = k = 0;
                goto BBL9;
        }

        // .L9:
        {
        BBL9:
                // cmp rdi, rsi
                // jae .L24
                if (j >= b) {
                        goto BBL24;
                }

                goto BBL14;
        }

        // .L14:
        {
        BBL14:
                // test r8d, r8d
                if (i < 0) {
                        // js .L10
                        goto BBL10;
                }

                // movsx r9, r8d
                // movzx r11d, BYTE PTR [rdx+r9]
                // cmp BYTE PTR [rbx+rdi], r11b
                if (d[i] == a[j]) {
                        // je .L10
                        goto BBL10;
                }

                // cmp rdi, rsi
                // mov r8d, DWORD PTR [rax+r9*4]
                i = ret[i];

                if (j < b) {
                        // jb .L14
                        goto BBL14;
                }

                goto BBL24;
        }

        // .L10:
        {
        BBL10:
                // add r8d, 1
                // add r10d, 1
                // movsx rdi, r8d
                j = ++i;
                k++;

                // cmp rdi, rcx
                if (j == c) {
                        // je .L18
                        goto BBL18;
                }

                // movsx rdi, r10d
                j = k;

                // jmp .L9
                goto BBL9;
        }

        // .L18:
        {
        BBL18:
                // movsx r10, r10d
                // sub r10, rcx
                k -= c;

                // add rbx, r10
                a += k;

                // jmp .L13
                goto BBL13;
        }


        // .L24:
        {
        BBL24:
                // xor ebx, ebx
                a = NULL;
                goto BBL13;
        }

        // .L13:
        {
        BBL13:
                // mov rdi, rax
                // call free
                free(ret);

                // add rsp, 32
                // mov rax, rbx
                // pop rbx
                // ret
                return a;
        }

}
#+END_SRC

#+BEGIN_SRC c
char *f2(char *a, int b, int c, char *d)
{
        int i;
        int j;
        int k;
        int *ret;

        if (c == 0) {
                return a;
        }

        ret = malloc((c + 1) * sizeof(int));
        ret[0] = -1;
        ret[1] = 0;

        i = 1;

        do {
                if ((ret[i + 1] = ret[i] + 1) > 0
                    && d[i] != d[ret[i] - 1]) {
                        while ((ret[i + 1] = ret[ret[i + 1] - 1] + 1) > 0) {
                                if (d[ret[i + 1] - 1] == ret[i]) {
                                        break;
                                }
                        }
                }
        } while (++i < c);

        i = j = k = 0;

        while (j < b) {
                if (i < 0 || d[i] == a[j]) {
                        j = ++i;
                        k++;

                        if (j == c) {
                                free(ret);
                                return a + k - c;
                        }

                        j = k;
                }
                i = ret[i];
        }

        free(ret);
        return NULL;
}
#+END_SRC

This challenge was nightmarishly difficult. I plan to come back to it near the
end of the year, but for now, consider this challenge incomplete. I've been
banging my head against a wall trying to make sense of it for a number of weeks
now, and I still don't have a good answer for what it does.

* Challenge #20

Another challenge described as "easy." This time, it really is.

#+BEGIN_SRC c
#include <stdlib.h>

float f4()
{
    return rand() * ((float) 4.65661287307739257813e-10);
}
#+END_SRC

I did defer to an ISA reference for =cvtsi2ss= and =mulss= as I'm not particularly
familiar with x86's floating point instructions. This challenge also gave me an
opportunity to use [[https://float.exposed/0x30000000][float.exposed]] to turn =.long 805306368= into a floating point
constant, but \(4.65661287307739257813 \cdot 10^{-10}\) isn't any more
comprehensible. The purpose of =f4= is clearer when observing the output.

#+BEGIN_SRC prog
...
0.086556
0.535690
0.176955
0.791683
0.575702
0.418118
0.952373
...
#+END_SRC

=f4= returns a random floating point number on the range \([0, 1]\).

* Challenge #21

I was able to complete the translation for this challenge in under five minutes,
which I'm quite proud of.

#+BEGIN_SRC c
#include <string.h>

int f1(char *a, char *b)
{
        // rbp <- a
        // rbx <- b

        int offset;

        // push r12
        // push rbp
        // mov rbp, rdi
        // push rbx
        // mov rbx, rsi
        // call strlen
        // mov rdi, rbx
        // mov r12, rax
        // call strlen
        // sub r12, rax
        offset = strlen(a) - strlen(b);

        // mov rsi, rbx
        // lea rdi, [rbp+0+r12]
        // call strcmp
        // pop rbx
        // test eax, eax
        // pop rbp
        // sete al
        // pop r12
        // ret
        return strcmp(a + offset, b) ? 1 : 0;
}
#+END_SRC

=f= is a simple "ends with" predicate function. It returns =1= if =a= ends with the
substring =b=.

* Challenge #22

I stopped when I got to the =// ...=. I'd figured it out by then, and =-Os= made the
assembly for this quite messy.

#+BEGIN_SRC c
int f2(int *a, int b, int *c, int d)
{
        // rcx <- a[b]
        // eax <- d + 1
        // ebp <- c[0]

        int i; // r8
        int j;
        int *cur;

        i = d + 1;
        j = 0;

        while (j < i){
                while (c[j + 1] <= c[0] && j < d)
                        j++;

                cur = &a[i - 1];
                while (*cur-- > c[0]);

                if (j < i) {
                        c[j + 1] ^= *(cur + 1);
                        *cur ^= *(cur + 1);
                        c[j + 1] ^= *cur;
                }
        }

        c[0] ^= *(cur + 1);
        // ...

        return 0;
}

void f1(int *a, int b, int *c, int d)
{
        int ret;

        while (b < d) {
                ret = f2(a, b, c, d);
                f1(a, b, c, ret - 1);
                b = ret + 1;
        }
}
#+END_SRC

The hint for this challenge is that "[t]his can be tricky, but the algorithm is
well known and heavily used almost everywhere," which gave it away once I got to
the mess of =xor= instructions. This is the [[https://en.wikipedia.org/wiki/XOR_swap_algorithm][XOR swap algorithm]], extended so that
it reverses the contents of =a= and =c=.

#+BEGIN_EXPORT html
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/latest.js?config=TeX-MML-AM_CHTML"></script>
#+END_EXPORT