0

This is my first time doing RE on a statically linked and stripped binary with ghidra. And I'm having a really hard time analyzing what function does what just by looking the decompiled code present by ghidra.

Ghidra recognised the binary language ID as ARM:LE:32:v8 and I'm especially stuck on this function:

undefined8 ToSolve1(uint *param_1,uint *param_2,int param_3,uint param_4)

{
  uint *puVar1;
  uint *puVar2;
  byte *pbVar3;
  byte *pbVar4;
  uint *puVar5;
  uint *puVar6;
  byte *pbVar7;
  int iVar8;
  int iVar9;
  int iVar10;
  byte bVar11;
  uint uVar12;
  uint uVar13;
  uint uVar14;
  uint uVar15;
  uint uVar16;
  bool bVar17;
  bool bVar18;
  
  if (param_2 < param_1) {
    param_2 = (uint *)((int)param_2 + param_3);
    param_1 = (uint *)((int)param_1 + param_3);
    iVar8 = param_3 + -4;
    iVar10 = iVar8;
    if (param_3 < 4) goto LAB_0003ba2c;
    uVar13 = (uint)param_1 & 3;
    if (uVar13 != 0) {
      bVar11 = *(byte *)(uint *)((int)param_2 + -1);
      *(byte *)(uint *)((int)param_1 + -1) = bVar11;
      puVar2 = (uint *)((int)param_1 + -1);
      puVar1 = (uint *)((int)param_2 + -1);
      if (1 < uVar13) {
        bVar11 = *(byte *)(uint *)((int)param_2 + -2);
        *(byte *)(uint *)((int)param_1 + -2) = bVar11;
        puVar2 = (uint *)((int)param_1 + -2);
        puVar1 = (uint *)((int)param_2 + -2);
      }
      param_2 = puVar1;
      param_1 = puVar2;
      if (2 < uVar13) {
        param_2 = (uint *)((int)param_2 + -1);
        bVar11 = *(byte *)param_2;
      }
      param_4 = (uint)bVar11;
      if (2 < uVar13) {
        param_1 = (uint *)((int)param_1 + -1);
        *(byte *)param_1 = bVar11;
      }
      iVar10 = iVar8 - uVar13;
      bVar17 = iVar8 < (int)uVar13;
      iVar8 = iVar10;
      if (bVar17) goto LAB_0003ba2c;
    }
    uVar13 = (uint)param_2 & 3;
    if (uVar13 == 0) {
      iVar9 = iVar8 + -8;
      if (7 < iVar8) {
        iVar8 = iVar8 + -0x1c;
        iVar10 = iVar8;
        if (0x13 < iVar9) {
          do {
            uVar16 = param_2[-2];
            uVar14 = param_2[-3];
            uVar13 = param_2[-4];
            param_1[-1] = param_2[-1];
            param_1[-2] = uVar16;
            param_1[-3] = uVar14;
            param_1[-4] = uVar13;
            puVar2 = param_2 + -5;
            uVar14 = param_2[-6];
            uVar13 = param_2[-7];
            param_2 = param_2 + -8;
            param_4 = *param_2;
            param_1[-5] = *puVar2;
            param_1[-6] = uVar14;
            param_1[-7] = uVar13;
            param_1 = param_1 + -8;
            *param_1 = param_4;
            iVar8 = iVar10 + -0x20;
            bVar17 = 0x1f < iVar10;
            iVar10 = iVar8;
          } while (bVar17);
        }
        if (iVar8 + 0x10 < 0 == SCARRY4(iVar8,0x10)) {
          puVar2 = param_2 + -1;
          uVar14 = param_2[-2];
          uVar13 = param_2[-3];
          param_2 = param_2 + -4;
          param_4 = *param_2;
          param_1[-1] = *puVar2;
          param_1[-2] = uVar14;
          param_1[-3] = uVar13;
          param_1 = param_1 + -4;
          *param_1 = param_4;
          iVar8 = iVar8 + -0x10;
        }
        iVar9 = iVar8 + 0x14;
        if (iVar9 < 0 == SCARRY4(iVar8,0x14)) {
          puVar2 = param_2 + -1;
          uVar13 = param_2[-2];
          param_2 = param_2 + -3;
          param_4 = *param_2;
          param_1[-1] = *puVar2;
          param_1[-2] = uVar13;
          param_1 = param_1 + -3;
          *param_1 = param_4;
          iVar9 = iVar8 + 8;
        }
      }
      iVar10 = iVar9 + 8;
      if (iVar10 < 0 == SCARRY4(iVar9,8)) {
        if (iVar10 < 4) {
          param_2 = param_2 + -1;
          param_4 = *param_2;
        }
        if (iVar9 + 4 < 0 == SBORROW4(iVar10,4)) {
          puVar2 = param_2 + -1;
          param_2 = param_2 + -2;
          uVar13 = *param_2;
          param_1[-1] = *puVar2;
          param_1 = param_1 + -2;
          *param_1 = uVar13;
          iVar10 = iVar9;
        }
        else {
          param_1 = param_1 + -1;
          *param_1 = param_4;
          iVar10 = iVar9 + 4;
        }
      }
      goto LAB_0003ba2c;
    }
    param_2 = (uint *)((uint)param_2 & 0xfffffffc);
    uVar14 = *param_2;
    if (1 < uVar13) {
      if (uVar13 == 2) {
        if (iVar8 < 0xc) {
LAB_0003bb4c:
          do {
            uVar13 = uVar14 << 0x10;
            param_2 = param_2 + -1;
            uVar14 = *param_2;
            param_1 = param_1 + -1;
            *param_1 = uVar13 | uVar14 >> 0x10;
            iVar10 = iVar8 + -4;
            bVar17 = 3 < iVar8;
            iVar8 = iVar10;
          } while (bVar17);
        }
        else {
          iVar8 = iVar8 + -0xc;
          do {
            iVar9 = iVar8;
            uVar13 = uVar14 << 0x10;
            uVar15 = param_2[-1];
            uVar12 = param_2[-2];
            uVar16 = param_2[-3];
            param_2 = param_2 + -4;
            uVar14 = *param_2;
            param_1[-1] = uVar13 | uVar15 >> 0x10;
            param_1[-2] = uVar15 << 0x10 | uVar12 >> 0x10;
            param_1[-3] = uVar12 << 0x10 | uVar16 >> 0x10;
            param_1 = param_1 + -4;
            *param_1 = uVar16 << 0x10 | uVar14 >> 0x10;
            iVar8 = iVar9 + -0x10;
          } while (0xf < iVar9);
          iVar10 = iVar9 + -4;
          iVar8 = iVar10;
          if (iVar10 < 0 == SCARRY4(iVar9 + -0x10,0xc)) goto LAB_0003bb4c;
        }
        param_2 = (uint *)((int)param_2 + 2);
        goto LAB_0003ba2c;
      }
      if (iVar8 < 0xc) {
LAB_0003bae0:
        do {
          uVar13 = uVar14 << 8;
          param_2 = param_2 + -1;
          uVar14 = *param_2;
          param_1 = param_1 + -1;
          *param_1 = uVar13 | uVar14 >> 0x18;
          iVar10 = iVar8 + -4;
          bVar17 = 3 < iVar8;
          iVar8 = iVar10;
        } while (bVar17);
      }
      else {
        iVar8 = iVar8 + -0xc;
        do {
          iVar9 = iVar8;
          uVar13 = uVar14 << 8;
          uVar15 = param_2[-1];
          uVar12 = param_2[-2];
          uVar16 = param_2[-3];
          param_2 = param_2 + -4;
          uVar14 = *param_2;
          param_1[-1] = uVar13 | uVar15 >> 0x18;
          param_1[-2] = uVar15 << 8 | uVar12 >> 0x18;
          param_1[-3] = uVar12 << 8 | uVar16 >> 0x18;
          param_1 = param_1 + -4;
          *param_1 = uVar16 << 8 | uVar14 >> 0x18;
          iVar8 = iVar9 + -0x10;
        } while (0xf < iVar9);
        iVar10 = iVar9 + -4;
        iVar8 = iVar10;
        if (iVar10 < 0 == SCARRY4(iVar9 + -0x10,0xc)) goto LAB_0003bae0;
      }
      param_2 = (uint *)((int)param_2 + 3);
      goto LAB_0003ba2c;
    }
    if (iVar8 < 0xc) {
LAB_0003bbb8:
      do {
        uVar13 = uVar14 << 0x18;
        param_2 = param_2 + -1;
        uVar14 = *param_2;
        param_1 = param_1 + -1;
        *param_1 = uVar13 | uVar14 >> 8;
        iVar10 = iVar8 + -4;
        bVar17 = 3 < iVar8;
        iVar8 = iVar10;
      } while (bVar17);
    }
    else {
      iVar8 = iVar8 + -0xc;
      do {
        iVar9 = iVar8;
        uVar13 = uVar14 << 0x18;
        uVar15 = param_2[-1];
        uVar12 = param_2[-2];
        uVar16 = param_2[-3];
        param_2 = param_2 + -4;
        uVar14 = *param_2;
        param_1[-1] = uVar13 | uVar15 >> 8;
        param_1[-2] = uVar15 << 0x18 | uVar12 >> 8;
        param_1[-3] = uVar12 << 0x18 | uVar16 >> 8;
        param_1 = param_1 + -4;
        *param_1 = uVar16 << 0x18 | uVar14 >> 8;
        iVar8 = iVar9 + -0x10;
      } while (0xf < iVar9);
      iVar10 = iVar9 + -4;
      iVar8 = iVar10;
      if (iVar10 < 0 == SCARRY4(iVar9 + -0x10,0xc)) goto LAB_0003bbb8;
    }
    param_2 = (uint *)((int)param_2 + 1);
LAB_0003ba2c:
    iVar8 = iVar10 + 4;
    if (iVar8 != 0) {
      bVar18 = SBORROW4(iVar8,2);
      bVar17 = iVar10 + 2 < 0;
      pbVar7 = (byte *)((int)param_2 + -1);
      bVar11 = *pbVar7;
      pbVar3 = (byte *)((int)param_1 + -1);
      *pbVar3 = bVar11;
      if (1 < iVar8) {
        pbVar7 = (byte *)((int)param_2 + -2);
        bVar11 = *pbVar7;
      }
      if (bVar17 == bVar18) {
        pbVar3 = (byte *)((int)param_1 + -2);
        *pbVar3 = bVar11;
      }
      if (iVar8 != 2 && bVar17 == bVar18) {
        pbVar7 = pbVar7 + -1;
        bVar11 = *pbVar7;
      }
      if (iVar8 != 2 && bVar17 == bVar18) {
        pbVar3 = pbVar3 + -1;
        *pbVar3 = bVar11;
      }
      return CONCAT44(pbVar3,pbVar7);
    }
    return CONCAT44(param_1,param_2);
  }
  if (param_2 == param_1) {
    return CONCAT44(param_1,param_2);
  }
  iVar8 = param_3 + -4;
  puVar2 = param_1;
  iVar10 = iVar8;
  if (param_3 < 4) goto LAB_0003b7ec;
  if (((uint)param_1 & 3) == 0) {
    uVar13 = (uint)param_2 & 3;
    puVar1 = param_1;
  }
  else {
    iVar10 = -((uint)param_1 & 3);
    iVar9 = iVar10 + 4;
    bVar18 = SBORROW4(iVar9,2);
    bVar17 = iVar10 + 2 < 0;
    bVar11 = *(byte *)param_2;
    *(byte *)param_1 = bVar11;
    puVar2 = (uint *)((int)param_2 + 1);
    if (1 < iVar9) {
      puVar2 = (uint *)((int)param_2 + 2);
      bVar11 = *(byte *)(uint *)((int)param_2 + 1);
    }
    puVar1 = (uint *)((int)param_1 + 1);
    if (bVar17 == bVar18) {
      puVar1 = (uint *)((int)param_1 + 2);
      *(byte *)(uint *)((int)param_1 + 1) = bVar11;
    }
    param_2 = puVar2;
    if (iVar9 != 2 && bVar17 == bVar18) {
      param_2 = (uint *)((int)puVar2 + 1);
      bVar11 = *(byte *)puVar2;
    }
    param_4 = (uint)bVar11;
    puVar2 = puVar1;
    if (iVar9 != 2 && bVar17 == bVar18) {
      puVar2 = (uint *)((int)puVar1 + 1);
      *(byte *)puVar1 = bVar11;
    }
    iVar10 = iVar8 - iVar9;
    if (iVar8 < iVar9) goto LAB_0003b7ec;
    uVar13 = (uint)param_2 & 3;
    iVar8 = iVar10;
    puVar1 = puVar2;
  }
  if (uVar13 == 0) {
    iVar9 = iVar8 + -8;
    if (7 < iVar8) {
      iVar8 = iVar8 + -0x1c;
      if (0x13 < iVar9) {
        do {
          iVar10 = iVar8;
          puVar5 = param_2;
          puVar2 = puVar1;
          uVar13 = puVar5[1];
          uVar14 = puVar5[2];
          uVar16 = puVar5[3];
          *puVar2 = *puVar5;
          puVar2[1] = uVar13;
          puVar2[2] = uVar14;
          puVar2[3] = uVar16;
          param_4 = puVar5[4];
          uVar13 = puVar5[5];
          uVar14 = puVar5[6];
          uVar16 = puVar5[7];
          param_2 = puVar5 + 8;
          puVar2[4] = param_4;
          puVar2[5] = uVar13;
          puVar2[6] = uVar14;
          puVar2[7] = uVar16;
          puVar1 = puVar2 + 8;
          iVar8 = iVar10 + -0x20;
        } while (0x1f < iVar10);
        if (iVar10 + -0x10 < 0 == SCARRY4(iVar8,0x10)) {
          param_4 = *param_2;
          uVar13 = puVar5[9];
          uVar14 = puVar5[10];
          uVar16 = puVar5[0xb];
          param_2 = puVar5 + 0xc;
          *puVar1 = param_4;
          puVar2[9] = uVar13;
          puVar2[10] = uVar14;
          puVar2[0xb] = uVar16;
          puVar1 = puVar2 + 0xc;
          iVar8 = iVar10 + -0x30;
        }
      }
      bVar18 = SCARRY4(iVar8,0x14);
      iVar9 = iVar8 + 0x14;
      bVar17 = iVar9 < 0;
      do {
        if (bVar17 == bVar18) {
          param_4 = *param_2;
          uVar13 = param_2[1];
          uVar14 = param_2[2];
          param_2 = param_2 + 3;
          *puVar1 = param_4;
          puVar1[1] = uVar13;
          puVar1[2] = uVar14;
          puVar1 = puVar1 + 3;
          bVar18 = SBORROW4(iVar9,0xc);
          iVar9 = iVar9 + -0xc;
          bVar17 = iVar9 < 0;
        }
      } while (bVar17 == bVar18);
    }
    iVar10 = iVar9 + 8;
    puVar2 = puVar1;
    if (iVar10 < 0 == SCARRY4(iVar9,8)) {
      if (iVar10 < 4) {
        param_4 = *param_2;
        param_2 = param_2 + 1;
      }
      if (iVar9 + 4 < 0 == SBORROW4(iVar10,4)) {
        uVar13 = *param_2;
        uVar14 = param_2[1];
        param_2 = param_2 + 2;
        *puVar1 = uVar13;
        puVar1[1] = uVar14;
        puVar2 = puVar1 + 2;
        iVar10 = iVar9;
      }
      else {
        puVar2 = puVar1 + 1;
        *puVar1 = param_4;
        iVar10 = iVar9 + 4;
      }
    }
    goto LAB_0003b7ec;
  }
  puVar5 = (uint *)((uint)param_2 & 0xfffffffc) + 1;
  uVar14 = *(uint *)((uint)param_2 & 0xfffffffc);
  if (uVar13 < 3) {
    if (uVar13 == 2) {
      puVar6 = puVar5;
      if (iVar8 < 0xc) {
LAB_0003b910:
        do {
          uVar13 = uVar14 >> 0x10;
          puVar5 = puVar6 + 1;
          uVar14 = *puVar6;
          puVar2 = puVar1 + 1;
          *puVar1 = uVar13 | uVar14 << 0x10;
          iVar10 = iVar8 + -4;
          bVar17 = 3 < iVar8;
          puVar1 = puVar2;
          puVar6 = puVar5;
          iVar8 = iVar10;
        } while (bVar17);
      }
      else {
        iVar8 = iVar8 + -0xc;
        do {
          iVar9 = iVar8;
          uVar13 = uVar14 >> 0x10;
          uVar16 = *puVar5;
          uVar12 = puVar5[1];
          uVar15 = puVar5[2];
          uVar14 = puVar5[3];
          puVar5 = puVar5 + 4;
          *puVar1 = uVar13 | uVar16 << 0x10;
          puVar1[1] = uVar16 >> 0x10 | uVar12 << 0x10;
          puVar1[2] = uVar12 >> 0x10 | uVar15 << 0x10;
          puVar1[3] = uVar15 >> 0x10 | uVar14 << 0x10;
          puVar1 = puVar1 + 4;
          iVar8 = iVar9 + -0x10;
        } while (0xf < iVar9);
        iVar10 = iVar9 + -4;
        puVar2 = puVar1;
        puVar6 = puVar5;
        iVar8 = iVar10;
        if (iVar10 < 0 == SCARRY4(iVar9 + -0x10,0xc)) goto LAB_0003b910;
      }
      param_2 = (uint *)((int)puVar5 + -2);
      goto LAB_0003b7ec;
    }
    puVar6 = puVar5;
    if (iVar8 < 0xc) {
LAB_0003b8a4:
      do {
        uVar13 = uVar14 >> 8;
        puVar5 = puVar6 + 1;
        uVar14 = *puVar6;
        puVar2 = puVar1 + 1;
        *puVar1 = uVar13 | uVar14 << 0x18;
        iVar10 = iVar8 + -4;
        bVar17 = 3 < iVar8;
        puVar1 = puVar2;
        puVar6 = puVar5;
        iVar8 = iVar10;
      } while (bVar17);
    }
    else {
      iVar8 = iVar8 + -0xc;
      do {
        iVar9 = iVar8;
        uVar13 = uVar14 >> 8;
        uVar16 = *puVar5;
        uVar12 = puVar5[1];
        uVar15 = puVar5[2];
        uVar14 = puVar5[3];
        puVar5 = puVar5 + 4;
        *puVar1 = uVar13 | uVar16 << 0x18;
        puVar1[1] = uVar16 >> 8 | uVar12 << 0x18;
        puVar1[2] = uVar12 >> 8 | uVar15 << 0x18;
        puVar1[3] = uVar15 >> 8 | uVar14 << 0x18;
        puVar1 = puVar1 + 4;
        iVar8 = iVar9 + -0x10;
      } while (0xf < iVar9);
      iVar10 = iVar9 + -4;
      puVar2 = puVar1;
      puVar6 = puVar5;
      iVar8 = iVar10;
      if (iVar10 < 0 == SCARRY4(iVar9 + -0x10,0xc)) goto LAB_0003b8a4;
    }
    param_2 = (uint *)((int)puVar5 + -3);
    goto LAB_0003b7ec;
  }
  puVar6 = puVar5;
  if (iVar8 < 0xc) {
LAB_0003b97c:
    do {
      uVar13 = uVar14 >> 0x18;
      puVar5 = puVar6 + 1;
      uVar14 = *puVar6;
      puVar2 = puVar1 + 1;
      *puVar1 = uVar13 | uVar14 << 8;
      iVar10 = iVar8 + -4;
      bVar17 = 3 < iVar8;
      puVar1 = puVar2;
      puVar6 = puVar5;
      iVar8 = iVar10;
    } while (bVar17);
  }
  else {
    iVar8 = iVar8 + -0xc;
    do {
      iVar9 = iVar8;
      uVar13 = uVar14 >> 0x18;
      uVar16 = *puVar5;
      uVar12 = puVar5[1];
      uVar15 = puVar5[2];
      uVar14 = puVar5[3];
      puVar5 = puVar5 + 4;
      *puVar1 = uVar13 | uVar16 << 8;
      puVar1[1] = uVar16 >> 0x18 | uVar12 << 8;
      puVar1[2] = uVar12 >> 0x18 | uVar15 << 8;
      puVar1[3] = uVar15 >> 0x18 | uVar14 << 8;
      puVar1 = puVar1 + 4;
      iVar8 = iVar9 + -0x10;
    } while (0xf < iVar9);
    iVar10 = iVar9 + -4;
    puVar2 = puVar1;
    puVar6 = puVar5;
    iVar8 = iVar10;
    if (iVar10 < 0 == SCARRY4(iVar9 + -0x10,0xc)) goto LAB_0003b97c;
  }
  param_2 = (uint *)((int)puVar5 + -1);
LAB_0003b7ec:
  iVar8 = iVar10 + 4;
  if (iVar8 != 0) {
    bVar18 = SBORROW4(iVar8,2);
    bVar17 = iVar10 + 2 < 0;
    bVar11 = *(byte *)param_2;
    *(byte *)puVar2 = bVar11;
    pbVar3 = (byte *)((int)param_2 + 1);
    if (1 < iVar8) {
      pbVar3 = (byte *)((int)param_2 + 2);
      bVar11 = *(byte *)((int)param_2 + 1);
    }
    pbVar7 = (byte *)((int)puVar2 + 1);
    if (bVar17 == bVar18) {
      pbVar7 = (byte *)((int)puVar2 + 2);
      *(byte *)((int)puVar2 + 1) = bVar11;
    }
    pbVar4 = pbVar3;
    if (iVar8 != 2 && bVar17 == bVar18) {
      pbVar4 = pbVar3 + 1;
      bVar11 = *pbVar3;
    }
    if (iVar8 != 2 && bVar17 == bVar18) {
      *pbVar7 = bVar11;
    }
    return CONCAT44(param_1,pbVar4);
  }
  return CONCAT44(param_1,param_2);
}

My best guess for now is it is doing some kind of encoding and it might be a standard glibc function as it is called many times throughout the code.

This function is usally called with parameter like follows:

ToSolve1(auStack55,(uint *)&DAT_000543f4,0xe,uVar10);
ToSolve1(auStack55,(uint *)&DAT_0005489c,0xe,uVar10);
ToSolve1((uint *)&DAT_0004f602,&local_1c,4,(uint)&DAT_0004f680);
ToSolve1(param_1,&DAT_0004f5f8,0xe,(uint)puVar4);

Hope someone can give me a nudge on what this function is. If possible, I would like to learn about if there are any resources out there that can help me improve the skills of recognizing standard function signatures.

  • 1
    Sometimes dynamic analysis helps here. If you can set a breakpoint when the function is called, examine the inputs, and the examine the results after returning, you may figure out what it is doing without dealing with the above mess – multithr3at3d Jun 27 at 19:09
  • @multithr3at3d Great advice! Unfortunately I'll have to solve this question first because the binary which this function came from is preventing me from accessing system shell. (Kinda like a custom shell thingy) I'm doing RE on this binary hoping to find away to get system shell. – Jimmy.D Jun 28 at 13:03
0

For starters you could start by geting rid of most of the casts then start carving out the stack and heap structs where possible, then try to change the name of variables where possible to more type oriented ones, this will show most of the "duplicate" variables.

Then start following the main flow following labels and maybe change their name to what specific position mignt represet, like fail_exit or success_exit etc

Allso, is allways good to have the basic std structs defined if the source was compiled with c++.

e.g

struct std_vector{
  void* begin;
  void* end;
  void* capacity;
} 

struct std_tree_node_base{
       std_tree_node_base* left;
       std_tree_node_base* parent;
       std_tree_node_base* right;
       char color;
       char isNil;
       short pad1;
       //here will be the payload of the node, padded if needed
 }

 //red-black tree used to implement std::map, std::set
 struct std_tree{
      std_tree_node_base*    head;
      __int64                size;
 }

 struct std_list_node{
      std_list_node* next;
      std_list_node* prev;
      //node payload here
 }

 struct std_list{
      std_list_node* head;
      __int64        size;
 }

 //same for std::wstring but using wchar_t* 
 struct std_string {
       char*    _Begin;
       char*    _End;
       __int64  _MySize;
       __int64  _MyReserved;
 }

And maybe switch to IDA as its has a lot better decompiler than Ghidra. I think you can get the freeware version on their website, should suffice.

Good Luck!

| improve this answer | |
  • 1
    Whether IDA has a better decompiler is rather subjective in this case. Also, don't think the free version includes an ARM decompiler. – multithr3at3d Jun 27 at 19:08
  • 1
    @multithr3at3d IDA does not include any decompiler. You can purchase it as a plugin and it costs approximately twice as much as the basic product. Personally I have found decompilers more confusing than helpful. They help in very specific circumstances and when they are interactive enough, but oftentimes I'll be faster sifting through assembly and only when I find an algorithm it pays off to have a decompiler at hand. – 0xC0000022L Jun 27 at 20:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.