# Summarize (rev)

{% file src="<https://243380073-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FBIa0PcXGYuP6chd3J1Ry%2Fuploads%2Fa12iRYfSNu5LNi4iGAzT%2Fsummarize?alt=media&token=1d22ba41-e672-4328-99e7-bc1a875d60c0>" %}

This challenge is a crackme, which requires us to correctly enter 6 numbers in order for the flag to be shown.&#x20;

The main function given by Ghidra looks like this:<br>

```c
undefined8 main(void)
{
  char cVar1;
  long in_FS_OFFSET;
  uint local_60;
  uint local_5c;
  uint local_58;
  uint local_54;
  uint local_50;
  uint local_4c;
  char local_48 [56];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  puts(
      "To get the flag, you must correctly enter six 9-digit positive integers: a, b, c, d, e, and f ."
      );
  putchar(10);
  printf("a = ");
  __isoc99_scanf(&DAT_0040206c,&local_60);
  printf("b = ");
  __isoc99_scanf(&DAT_0040206c,&local_5c);
  printf("c = ");
  __isoc99_scanf(&DAT_0040206c,&local_58);
  printf("d = ");
  __isoc99_scanf(&DAT_0040206c,&local_54);
  printf("e = ");
  __isoc99_scanf(&DAT_0040206c,&local_50);
  printf("f = ");
  __isoc99_scanf(&DAT_0040206c,&local_4c);
  cVar1 = check(local_60,local_5c,local_58,local_54,local_50,local_4c);
  if (cVar1 == '\0') {
    puts("Wrong.");
  }
  else {
    puts("Correct.");
    sprintf(local_48,"uiuctf{%x%x%x%x%x%x}",(ulong)local_60,(ulong)local_5c,(ulong)local_58,
            (ulong)local_54,(ulong)local_50,(ulong)local_4c);
    puts(local_48);
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}
```

Our 6 inputs are first checked using `check()`. If it returns 1 we have passed the check, and we obtain the flag. Looking at the `check()` function:

```c
undefined4 check(uint param_1,uint param_2,uint param_3,uint param_4,uint param_5,uint param_6)
{
  undefined4 uVar1;
  uint uVar2;
  uint uVar3;
  undefined4 uVar4;
  uint uVar5;
  uint uVar6;
  uint uVar7;
  uint uVar8;
  uint uVar9;
  uint uVar10;
  ulong uVar11;
  
  if (((((param_1 < 0x5f5e101) || (param_2 < 0x5f5e101)) || (param_3 < 0x5f5e101)) ||
      ((param_4 < 0x5f5e101 || (param_5 < 0x5f5e101)))) || (param_6 < 0x5f5e101)) {
    uVar1 = 0;
  }
  else if (((param_1 < 1000000000) && (param_2 < 1000000000)) &&
          ((param_3 < 1000000000 &&
           (((param_4 < 1000000000 && (param_5 < 1000000000)) && (param_6 < 1000000000)))))) {
    uVar1 = fun1_2(param_1,param_2);
    uVar2 = fun1(uVar1,param_3);
    uVar3 = fun1(param_1,param_2);
    uVar1 = fun2(2,param_2);
    uVar4 = fun2(3,param_1);
    uVar5 = fun1_2(uVar4,uVar1);
    uVar6 = fun3(param_1,param_4);
    uVar1 = fun1(param_3,param_1);
    uVar7 = fun4(param_2,uVar1);
    uVar11 = fun1(param_2,param_4);
    uVar1 = fun1(param_4,param_6);
    uVar8 = fun3(param_3,uVar1);
    uVar9 = fun1_2(param_5,param_6);
    uVar10 = fun1(param_5,param_6);
    if ((((uVar2 % 0x10ae961 == 0x3f29b9) && (uVar3 % 0x1093a1d == 0x8bdcd2)) &&
        ((uVar5 % uVar6 == 0x212c944d &&
         ((uVar7 % 0x6e22 == 0x31be && ((int)((uVar11 & 0xffffffff) % (ulong)param_1) == 0x2038c4 3c)
          ))))) &&
       ((uVar8 % 0x1ce628 == 0x1386e2 &&
        ((uVar9 % 0x1172502 == 0x103cf4f && (uVar10 % 0x2e16f83 == 0x16ab0d7)))))) {
      uVar1 = 1;
    }
    else {
      uVar1 = 0;
    }
  }
  else {
    uVar1 = 0;
  }
  return uVar1;
}
```

Let's look at `fun1_2` and `fun1` first.

```c
void fun1_2(undefined4 param_1,int param_2)
{
  fun1(param_1,-param_2);
  return;
}
long fun1(uint param_1,uint param_2)
{
  byte bVar1;
  uint uVar2;
  uint uVar3;
  uint local_30;
  uint local_2c;
  uint local_20;
  long local_10;
  
  local_10 = 0;
  local_20 = 0;
  bVar1 = 0;
  local_2c = param_1;
  for (local_30 = param_2; (local_2c != 0 || (local_30 != 0)); local_30 = local_30 >> 1) {
    uVar2 = local_2c & 1;
    uVar3 = local_30 & 1;
    local_2c = local_2c >> 1;
    local_10 = local_10 + (ulong)((uVar2 ^ uVar3 ^ local_20) << (bVar1 & 0x1f));
    local_20 = uVar3 & local_20 | uVar2 & (uVar3 | local_20);
    bVar1 = bVar1 + 1;
  }
  return local_10 + ((ulong)local_20 << (bVar1 & 0x3f));
}
```

`fun1_2` is simply a wrapper for `fun1`, except that the second parameter is negated before calling `fun1`.&#x20;

`local_2c` and `local_30` are initialised to be `param_1` and `param_2` respectively at first. Then, with each iteration of the for loop, they are each right shifted by 1 bit. `bVar1` is also incremented with each iteration. The loop stops if both of them are equivalent to 0. Within the loop, `uVar2` is the rightmost bit of `local_2c`, while `uVar3` is the rightmost bit of `local_30`. Then, `local_10` is incremented by `(uVar2 ^ uVar3 ^ local_20) << (bVar1 & 0x1f)`. Notice that `(uVar2 ^ uVar3 ^ local_20)` only evaluates to `1` if all 3 variables are `1`, or only one of them is `1`. This looks like how the result bit is obtained from addition of integers!

Let's think about the next line, `local_20 = (uVar3 & local_20) | uVar2 & (uVar3 | local_20)`. In what scenarios would `local_20` be set to `1`?

* Case 1: `local_20` = `1`.`(uVar3 & local_20)` evaluates to `uVar3`, and `(uVar3 | local_20)` evaluates to `1` here, so we are left with `uVar3 | uVar2`.
* Case 2: `local_20` = `0`. `(uVar3 & local_20)` evaluates to `0` and `(uVar3 | local_20)` evaluates to `uVar3`. So we are left with `uVar2 & uVar3`.&#x20;

So if `local_20` was already `1`, as long as either `uVar3` or `uVar2` is `1`, `local_20` will be `1`. If `local_20` was `0`, it will become `1` only if both `uVar2` and `uVar3` are `1`. This reminds us of the carry bit during addition of integers! This can be further confirmed by the fact that `local_20` is added to `local_10` as the most significant bit once the loop exits.

With the above observations, we can deduce that `fun1` is essentially doing integer addition of `param_1` and `param_2` . This means that `fun1_2` would be doing subtraction of `param_2` from `param_1`.

Now let's look at `fun2`.&#x20;

```c
int fun2(uint param_1,int param_2)

{
  byte bVar1;
  uint local_1c;
  int local_14;
  
  local_14 = 0;
  bVar1 = 0;
  for (local_1c = param_1; local_1c != 0; local_1c = local_1c >> 1) {
    local_14 = local_14 + (param_2 << (bVar1 & 0x1f)) * (local_1c & 1);
    bVar1 = bVar1 + 1;
  }
  return local_14;
}
```

This looks similar to `fun1` in that it is performing some bitwise operation, but our carry flag is gone. We are using `param_1`'s bits, but `param_2` is used without extracting its bits. Our loop is iterating through the bits in `param_1` starting from the rightmost bit. Let's run through an example. Say `param_1 = 2 = 0b10` and `param_2 = 3 = 0b11`.&#x20;

In the first iteration, `local_1c & 1 = 0`, so `local_14` remains as `0`.&#x20;

In the second iteration, `bVar1 = 1`. So `param_2` is left-shifted by 1 bit, and this time `local_1c & 1 = 1`. So we get `local_14 = local_14 + (3 << 1) * 1)`.&#x20;

The result we get is `6`. Notice that this is the result of doing `2 * 3`!

In general, if bit `i` of `param_1` = `1`, then `local_14 = local_14 + (param_2 << i)`, which is equivalent to `local_14 = local_14 + param_2 * 2^i`.

In another example if `param_1 = 3 = 0b11` and `param_2 = x`, then we would get a result of `x * 2^0 + x * 2^1` = `x * 1 + x * 2` = `param_2 * 3`. This should convince you that `fun2` is simply a multiplication function!

Now let's look at `fun3`.&#x20;

```c
int fun3(uint param_1,uint param_2)
{
  byte bVar1;
  uint uVar2;
  uint local_20;
  uint local_1c;
  int local_18;
  
  local_18 = 0;
  bVar1 = 0;
  local_1c = param_1;
  for (local_20 = param_2; (local_1c != 0 || (local_20 != 0)); local_20 = local_20 >> 1) {
    uVar2 = local_1c & 1;
    local_1c = local_1c >> 1;
    local_18 = local_18 + ((uVar2 ^ local_20 & 1) << (bVar1 & 0x1f));
    bVar1 = bVar1 + 1;
  }
  return local_18;
}
```

`fun3` seems very similar to `fun1`, except that our carry bit is gone and now the bit being added in each iteration is given by `(uVar2 ^ local_20 & 1)`. This is essentially an XOR operation between the bit we obtained from `param_1` and the bit we obtained from `param_2`. So `fun3` is an XOR function.

Moving on to `fun4`:

```c
int fun4(uint param_1,uint param_2)
{
  byte bVar1;
  uint uVar2;
  uint local_20;
  uint local_1c;
  int local_18;
  
  local_18 = 0;
  bVar1 = 0;
  local_1c = param_1;
  for (local_20 = param_2; (local_1c != 0 || (local_20 != 0)); local_20 = local_20 >> 1) {
    uVar2 = local_1c & 1;
    local_1c = local_1c >> 1;
    local_18 = local_18 + ((uVar2 & local_20 & 1) << (bVar1 & 0x1f));
    bVar1 = bVar1 + 1;
  }
  return local_18;
}
```

It is similar to `fun3`, except that the bit being added is `(uVar2 & local_20 & 1)`. That is the AND operation between the bit obtained from `param_1` and the bit obtained from `param_2`. So `fun4` is performing an AND operation on `param_1` and `param_2`.

Looking back at the restrictions imposed by `main()`,&#x20;

```c
  if (((((param_1 < 0x5f5e101) || (param_2 < 0x5f5e101)) || (param_3 < 0x5f5e101)) ||
      ((param_4 < 0x5f5e101 || (param_5 < 0x5f5e101)))) || (param_6 < 0x5f5e101)) {
    uVar1 = 0;
  }
  else if (((param_1 < 1000000000) && (param_2 < 1000000000)) &&
          ((param_3 < 1000000000 &&
           (((param_4 < 1000000000 && (param_5 < 1000000000)) && (param_6 < 1000000000)))))) {
    uVar1 = fun1_2(param_1,param_2);
    uVar2 = fun1(uVar1,param_3);
    uVar3 = fun1(param_1,param_2);
    uVar1 = fun2(2,param_2);
    uVar4 = fun2(3,param_1);
    uVar5 = fun1_2(uVar4,uVar1);
    uVar6 = fun3(param_1,param_4);
    uVar1 = fun1(param_3,param_1);
    uVar7 = fun4(param_2,uVar1);
    uVar11 = fun1(param_2,param_4);
    uVar1 = fun1(param_4,param_6);
    uVar8 = fun3(param_3,uVar1);
    uVar9 = fun1_2(param_5,param_6);
    uVar10 = fun1(param_5,param_6);
    if ((((uVar2 % 0x10ae961 == 0x3f29b9) && (uVar3 % 0x1093a1d == 0x8bdcd2)) &&
        ((uVar5 % uVar6 == 0x212c944d &&
         ((uVar7 % 0x6e22 == 0x31be && ((int)((uVar11 & 0xffffffff) % (ulong)param_1) == 0x2038c4 3c)
          ))))) &&
       ((uVar8 % 0x1ce628 == 0x1386e2 &&
        ((uVar9 % 0x1172502 == 0x103cf4f && (uVar10 % 0x2e16f83 == 0x16ab0d7)))))) {
      uVar1 = 1;
    }
```

Our 6 integers are restricted to be within the range `(0x5f5e101, 1000000000)` (which means they must all be positive and 9-digits long), and further modulo restrictions are imposed on the arithmetic results. Recall that `fun1` is addition, `fun1_2` is subtraction, `fun2` is multiplication, `fun3` is XOR, and `fun4` is AND. We can use a z3 script to solve these constraints and obtain the 6 integers.

Below is my script:

```python
from z3 import *

i1, i2, i3, i4, i5, i6 = BitVec('i1', 32), BitVec('i2', 32), BitVec('i3', 32), BitVec('i4', 32), BitVec('i5', 32), BitVec('i6', 32)
i1_cpy, i2_cpy, i3_cpy, i4_cpy, i5_cpy, i6_cpy = i1, i2, i3, i4, i5, i6

s = Solver()
s.add(And(i1_cpy >= 0x5f5e101, i2_cpy >= 0x5f5e101,i3_cpy >= 0x5f5e101,i4_cpy >= 0x5f5e101,i5_cpy >= 0x5f5e101))
s.add(And(i1_cpy < 1000000000, i2_cpy < 1000000000,i3_cpy < 1000000000,i4_cpy < 1000000000,i5_cpy < 1000000000))
uVar1 = i1_cpy - i2_cpy
uVar2 = uVar1 + i3_cpy
uVar3 = i1_cpy + i2_cpy
uVar1 = 2 * i2_cpy
uVar4 = 3 * i1_cpy
uVar5 = uVar4 - uVar1
uVar6 = i1_cpy ^ i4_cpy
uVar1 = i3_cpy + i1_cpy
uVar7 = i2_cpy & uVar1
uVar11 = i2_cpy + i4_cpy
uVar1 = i4_cpy + i6_cpy
uVar8 = i3_cpy ^ uVar1
uVar9 = i5_cpy - i6_cpy
uVar10 = i5_cpy + i6_cpy
s.add(uVar2 % 0x10ae961 == 0x3f29b9)
s.add(uVar3 % 0x1093a1d == 0x8bdcd2)
s.add(uVar5 % uVar6 == 0x212c944d)
s.add(uVar7 % 0x6e22 == 0x31be)
s.add(((uVar11 & 0xffffffff) % i1_cpy) == 0x2038c43c)
s.add(uVar8 % 0x1ce628 == 0x1386e2)
s.add(uVar9 % 0x1172502 == 0x103cf4f)
s.add(uVar10 % 0x2e16f83 == 0x16ab0d7)
print(s.check())
print(s.model())
"""
[i2 = 780663452,
 i5 = 966221407,
 i3 = 341222189,
 i6 = 217433792,
 i1 = 705965527,
 i4 = 465893239]
uiuctf{2a142dd72e87fa9c1456a32d1bc4f77739975e5fcf5c6c0}
"""
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://elijahchia.gitbook.io/ctf-blog/uiuctf-24/summarize-rev.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
