2021. október 19., kedd

A Wi-Fi hálózat jelszava

Az alábbiakban a saját mobil hotspot-om jelszavát töröm fel a Kali Linux és az aircrack-ng segítségével.

Elfelejtett jelszó lekérdezése

Az operációs rendszerek külön profilt készítenek minden Wi-Fi hálózatnak, amire rácsatlakoztak és ez nem veszik el hacsak manuálisan el nem felejtettjük az adott hálózatot. A profilokból kideríthető többek között a jelszó is.


Windows
  • Indítsunk el parancssort adminisztrátor módban, majd gépeljük be a netsh wlan show profile parancsot. Ez egy Network Shell utasítás, ami kilistázza a Wi-Fi hálózati profilokat. A profil neve a hálózat nevével egyezik.

  • A kilistázott profilok közül válasszuk ki azt a hálózatot, amelyik jelszavára szükség van, például „KnetAP”, majd gépeljük be a netsh wlan show profile name="KnetAP" key=clear parancsot. Ez az utasítás kilistázza a kiválasztott profil beállításait, köztük a jelszót olvasható formában.

  • A megjelenített adatok közül a Key Content mező tartalmazza a jelszót.
Linux
  • Indítsunk el parancssort, majd gépeljük be az ls /etc/NetworkManager/system-connections parancsot. Ez kilistázza a system-connections könyvtárban lévő fájlokat. Mindenik fájl egy hálózati konfigurációs fájl, ami az eddig használt hálózatok beállításait tartalmazza. A fájlok neve a hálózat nevével egyezik.

  • A fájlok közül válasszuk ki azt, amelyik nevében szereplő hálózatot jelszavára szükség van, például „KnetAP”, majd gépeljük be a sudo cat /etc/NetworkManager/system-connections/KnetAP.nmconnection parancsot. Ez az utasítás megjeleníti a kiválasztott fájl tartalmát
  • A megjelenített adatok közül a psk mező tartalmazza a jelszót.
Az egyik legmegbízhatóbb módszert a számítógépes hálózatokba való behatoláshoz a Kali Linux operációs rendszer nyújtja, pontosabban a vele érkező programcsomagok sokasága. Az egyszerűség kedvéért egy előre elkészített VMware képfájlt töltöttem le a kali.org oldalról, amit a szintén ingyenes VMware Playerrel lehet megnyitni. A boot-olást követően a kali felhasználónévvel és jelszóval lehet belépni az operációs rendszerbe.


Mielőtt bármit is tennénk frissítsük a rendszer komponenseit a következő parancsokkal:,
  • sudo apt update
  • sudo apt upgrade

Jelszó megfejtése bruteforce módszerrel


1. A környező Wi-Fi hálózatok felderítése


Első lépésként azt kell kideríteni, hogy a feltörendő Wi-Fi hálózatra rá van-e már valamilyen eszköz csatlakozva. Ez csak akkor látszik, ha van adatforgalom. A Kali Linux egyik legerősebb eszköztára az Aircrack-ng, mely kifejezetten Wi-Fi hálózatok támadására lett tervezve.
  • iwconfig - kilistázza az vezetéknélküli hálózati adaptereket. A wlan0 egy USB Wi-Fi adapter.
  • iw list – kilistázza a kártya képességeit. A Supported interface modes alatt szerepelnie kell a monitor módnak.
  • sudo airmon-ng check kill - megtisztítja az Aircrack-ng környezetét a zavaró folyamatoktól.
  • sudo airmon-ng start wlan0 utasítás monitor módba kapcsolja a wlan0 hálózati kártyát. Egyes esetekben wlan0 itt wlan0mon-ra vagy mon0-ra változik a parancs után:
  • sudo airodump-ng wlan0mon - kilistázza az elérhető Wi-Fi hálózatokat és a rájuk csatlakozó eszközök fizikai címeit. A folyamatból Ctrl+C-vel lehet kilépni.
  • A felső táblázatból a következő adatok fontosak:
    • A KnetAP router fizikai címe (BSSID): EC:10:7B:0D:C5:2B
      • Ha az ESSID üres, akkor rejtett az adott hálózat.
    • A router csatornája (CH): 6
  • sudo airodump-ng wlan0mon --bssid [router MAC] --channel 6  - kilistázza a routerre csatlakozott klienseket, melyek adatcserét folytatnak a 6-os csatornán.
  • A második táblázatból derül ki, hogy a 00:0C:F1:2F:D5:C2 a KnetAP-re csatlakozik.
    • Ha netalán MAC szűrés van a hálózaton, akkor már tudjuk, hogy ez a MAC cím hozzáférhet a hálózathoz
    • Ha a MAC nem volt manuálisan megváltoztatva, akkor egy Online MAC lookup oldalon ki lehet deríteni, hogy milyen gyártmányú készülékről van szó. Ezáltal kiszemelhető egy kevésbé érzékeny készülék, aminek nem probléma ha egy pillanatra lecsatlakozik a hálózatról.

2. A Wi-Fi-re csatlakozott eszközök kényszerített lecsatlakoztatása


A harmadik lépés megfigyelni egy hitelesítési adatcserét. Ehhez kényszeríteni lehet az első lépésben kiválasztott klienst, hogy újra kapcsolódjon.
  • sudo aireplay-ng --deauth 0 -c [kliens MAC] -a [router MAC] wlan0mon – folyamatos lecsatlakoztatási parancsot küld a klienseknek és a routernek. Ha a -c [kliens MAC] paraméter hiányzik, akkor minden kliens megkapja a parancsot.

Amint megszűnik a végtelen (0) támadás (Ctrl + C), a kliens azonnal visszacsatlakozik a hálózatra.

Ennél a pontnál kétféleképp lehet folytatni:
  1. Ha nem igényel emberi interakciót a készülék a működéshez (pl. webkamera), akkor a visszacsatlakozásnál elküldött csomagokat kell befogni és valamilyen bruteforce módszerrel feltörni titkosított jelszót.
  2. Számítógép / telefon esetén klónozni lehet a routert és rávenni a klienst, hogy a klónra (Evil twin) csatlakozzon.
    • A jelszót egy hamis bejelentkezési oldallal lehet megszerezni, ami a felhasználótól kéri a Wi-Fi jelszót.
    • Minden további adatforgalmat követni lehet, amíg a kliens a klónra van csatlakozva (=adathalászat)
A második módszer csak emberi beavatkozással működhet és egy hamis weboldal gyanússá válhat a felhasználó számára, ha nem tökéletes, ráadásul fizikailag a közelben kell tartózkodni amíg az események zajlanak. Az első módszernél ezzel ellentétben a befogott csomagokat offline kell feltörni. A jelszó megfejtése azonban hosszabb időbe kerül, a jelszó bonyolultságának függvényében.

3. A hitelesítési adafolyam befogása


Két terminálra van szükség. Az egyikben a csomagokat befogó parancs fut az első lépésből (csak most fájlba menti), a másikban a lecsatlakoztatós támadás a második lépésből (csak most elég lesz 5 támadás).
  • sudo airodump-ng wlan0mon --bssid [router MAC] --channel 6 -w csomagok – a „csomagok” fájlba menti a router 6-os csatornáján közlekedő összes adatcsomagot.
  • (másik terminál) sudo aireplay-ng --deauth 5 -c [kliens MAC] -a [router MAC] wlan0mon – 5 lecsatlakoztatási támadást indít a kliens ellen. Amíg ez fut, a kliens újra csatlakozni próbál, ami megjelenik az első terminál jobb felső sarkában: WPA handshake: kliens MAC
  • A fájlok a Home könyvtárban lesznek, hisz onnan volt a parancs elindítva. Ebből csak a .cap kiterjesztésűre van szükség.
  • sudo aircrack-ng csomagok-01.cap – azonosítja a befogott csomagok közül a handshake-et
  • sudo airmon-ng stop wlan0mon – kikapcsolja a Wi-Fi kártya monitor módját
  • sudo service NetworkManager start – elindítja a Kali Linux hálózatkezelőjét

4. A hitelesítő adatcsomag megfejtése


Ebben a lépésben már nincs szükség a hálózat lefedettségében lenni. A WPA2 protokoll handshake-je egy négylépéses kulcs csere, aminek folyamata a következő:

  1. ANonce – egy véletlenszerű számsor, amit a hitelesítő küld a kliensnek.
    •  A kliens ezután generál egy SNonce véletlen számot
    • A kliens generál egy PTK-t, ami egy kulcs a további adatforgalom titkosítására.
      •  PTK = PRF(PMK + ANonce + SNonce + MAC(kliens) + MAC(router))
      • PMK – mesterkulcs amit a kliens és a router is ismer. Ezt kell majd visszafejteni.
        • PMK = PBKDF2(HMAC−SHA1, PSK, SSID, 4096, 256)
        • PBKDF2 - Password-based Key Derivation Funtion 2
        • HMAC−SHA1 – titkosító algoritmus
        • PSK – jelszó
        • SSID – a hálózat neve (KnetAP)
        • 4096 – az iterációk száma
        • 256 – a PMK hosszúsága
      • PRF – pszeudo-random függvény
    • A PTK hossza 512 bit, ami fel van darabolva:
      • 128 bit – Key Confirmation Key (KCK) – a MIC létrehozásához szükséges.
      • 128 bit – Key Encryption Key (KEK) – a titkosításhoz szükséges.
      • 128 bit – Temporal Key (TK) – Unicast csomagok titkosítására és visszafejtésére.
      • 128 bit – MIC Tx és Rx – Az unicast csomagokat titkosító TKIP algoritmus használja
  2. SNonce + MIC – ahol a MIC (Message Integrity Check) =  HMAC_SHA1(KCK, SNonce, r)
    • HMAC_SHA1 – titkosító algoritmus
    • KCK – a PTK első 128 bitje
    • r – szekvenciaszám, hogy ugyanazt az üzenetet ne lehessen kétszer felhasználni
  3. GTK + MIC – ahol a MIC a router PTK-jának KCK részét használja a kódoláshoz.
    • GTK (Group Temporal Key) = PRF-256(GMK, “Group key expansion”, MAC(router)||GNonce)
      • PRF-256 – pszeudo-random függvény
      • GMK (Group Master Key) – egy random szám amit a hitelesítő router időnként generál
      • Gnonce – egy random szám amit a router a hitelesítés során generál
    • MIC = HMAC_SHA1(KCK, GTK, r)
  4. ACK – a hitelesítés nyugtázása

A Hitelesítés végső soron a MIC-ek összehasonlításával történik. Ha a kliens és a hitelesítő által generált MIC-ek találnak a második üzenetet követően, akkor a PTK kulcsok is találnak, hisz ezekből vannak származtatva. Ha a PTK egyezik, akkor a PMK is egyezik, ami csak akkor egyezhet, ha a jelszó helyes. Ezután a router elküldi a GTK kulcsot amit a kliens telepít és nyugtáz.

A feltörő program feladata tehát hogy a befogott handshake adatokból (ANonce, SNonce) és egy adott jelszóból előállítsa a PMK-t, a PTK-t majd a MIC-ket és összehasonlítsa a handshake-ben található MIC-kel.

A befogott csomagok-01.cap fájl-ban a 4 adatfolyam a következő:


1. Az első üzenet az ANonce véletlenszámot (WPA Key Nonce) mutatja:


2. A következő üzenet az SNonce + MIC


Ez a két üzenet már elegendő információt biztosít, hogy a helyes MIC kulcsot ki lehessen számítani különböző jelszavak próbálgatásával.

 Az aircrack-ng egy előre meghatározott jelszólistából próbálgatja végig a jelszavakat. A jelszólistát le lehet tölteni nagyméretű fájl formájában, amely a leggyakrabban használt jelszavakat tartalmazza.  Ezen kívül használható olyan segédprogram melyek jelszólistát generál a hálózat gazdájának személyes adatai vagy megadott karakterek alapján. A Kali Linuxban ilyen a crunch. Ismerve e jelszót alkotó 9 karaktert, az egyszerűség kedvéért kizárólag ezek ismétléses variációit (9^9) generáltam le a crunch programmal, de így is 384 millió jelszó készült. A fájl mérete 3,6GB.


A keresett jelszó benne van, tehát működni fog a lista.
  • aircrack-ng -a2 -b [router MAC] -w jelszavak.txt csomagok-01.cap
    • a1 – WEP protokoll, a2 – WPA/WPA2 protokoll

A Kali Linux virtuális gépként való futásának az a hátránya, hogy kizárólag a CPU teljesítményét képes használni a számításokra. A program egy Intel Core i7-10850H @ 2.7GHz (12CPU) processzorral 2,5 óra alatt próbált végig 87 millió kombinációt mire eljutott a helyes jelszóig. Egy GeForce GTX 1080 GPU ezt néhány másodperc alatt végrehajthatta volna.

A kadon2184 jelszó bár nem tartalmaz speciális karaktereket, mégis erősnek számit, mert nem tartalmaz értelmes szót vagy személyes adatot. Sok jelszó esetén a speciális karakterek illetve számok értelmes szavakban helyettesítenek betűket, mint az a=@, i=1, e=3, s=$, i=!, n=^ amiket ismernek a jelszógeneráló programok is. A bruteforce legnagyobb ellensége a hosszú jelszó, illetve az azt értelmetlenné tévő automatikus beillesztések – amikor a begépelt jelszóhoz a felhasználói felület karaktereket illeszt különböző helyekre mielőtt elküldené a szervernek (pl. ka%2fdo%8dn2%4s18%2f4).

A bruteforce a legcsendesebb módszer, mert kényszerített lecsatlakoztatás nélkül, egy passzívan hallgatózó eszköz kivárhatja, míg valaki önszántából kapcsolódik a hálózatra, majd a handshake rögzítése után offline megfejti a jelszót. Az egész folyamat észrevétlen marad a feltört eszköz szempontjából.


Jelszó megfejtése a WPS PIN megfejtésével


Bármilyen bonyolult is a jelszó, ha a routeren be van kapcsolva a WPS (vagy QSS), akkor egy 8 számjegyből álló PIN-t kell csak bruteforce módszerrel kitalálni, ahhoz, hogy végül megkapjuk a jelszót. Ez a funkció arra jó, hogy a bonyolult WiFi jelszó helyett a felhasználónak csak meg kell nyomnia a készüléken a WPS/QSS gombot, és egy 8 számjegyű PIN kódot megadva kapcsolódhat a hálózathoz. A Kali linux tartalmazza a reaver szoftvert, ami képes a hitelesítési folyamatot kikényszeríteni akkor is, ha a gomb nem volt megnyomva a routeren. A 8 számjegyű kód utolsó számjegye az ellnőrző összeg, tehát csak 7 számjegyet kell kitalálni. A 7 számjegy azonban két részben van ellenőrizve: az első 4, majd a következő 3. Emiatt a lehetőségek száma 10^4 + 10^3 = 11000. Ezt eltart 3-10 óráig végig próbálni, ugyanis a router pár sikertelen próbálkozás után lezárja néhány percre a kapcsolódás lehetőségét.

1. A környező Wi-Fi hálózatok felderítése

  • sudo airmon-ng start wlan0 - monitor módba kapcsolja a wlan0 hálókártyát.
  • sudo wash -i wlan0 - kilistázza azokat a hálózatokat, melyeken aktív a WPS funkció.


Csak azok a készülékek kerülnek ebbe a listába, amelyeken aktív a WPS/QSS fuknció. Az Lck oszlop azt jelzi, hogy a kapcsolódási lehetőség zárolva van-e, vagy sem.

2. A reaver támadás elindítása

  • sudo reaver -i wlan0 -c 6 -b E8:DE:27:DE:5C:7C -vv (--no-nacks)

A -c a csatorna szána (ch oszlop), a -b a BSSID, a -vv pedig a verbose, vagyis a tevékenység részletezése. A próbálkozások órákig eltarthatnak, de végül a helyes PIN-nel együtt a jelszót is megjeleníti:


A WiFi jelszó E$zk1m02625!

Ez a módszer sokkal "zajosabb" az előzőnél, ugyanis folyamatos kapcsolatot igényel az AP-vel (routerrel). Ez azt jelenti, hogy az adminisztrátor rögtön észlelheti, hogy valaki WPS-en csatlakozott még akkor is, ha elsőre sikerül. Minden próbálkozás nagy adatforgalommal jár, amit érzékelhet egy behatolásgátló rendszer (IDS = Intrusion Prevention System). Ezen kívül, ha a sikertelen próbálkozások száma eléri a küszöböt és a WPS lezáródik, a rendszer adminisztrátora értesítést kaphat az eseményről, vagy bárkinek feltűnhet, aki éppen akkor próbál kapcsolódni.

2016. december 6., kedd

Kifejezések

Reguláris kifejezések

      A reguláris vagy szabályos kifejezések bizonyos szabályokat követő karaktersorozatok. Arra szolgálnak, hogy megkönnyítsék a szöveggel való munkát, mint a keresés, helyettesítés, ellenőrzés, stb. Az ilyen kifejezések sok programozási nyelvbe be vannak építve, főleg a szövegek feldolgozására specializálódott nyelvek, mint az XML vagy a HTML. A C++11 is szabványosította ezeket a kifejezéseket a <regex> könyvtár révén. A reguláris kifejezések szabályai eltérőek lehetnek, ezért több szabályzat  (szintaxis) sorolható fel. Ezek közül a C++11 az ECMAScript, az alap és bővített POSIX, az awk, a grep és az egrep szabálygyűjteményeit támogatja. A következő példákban az ECMAScript szintaxisát fogjuk használni.

      A reguláris kifejezéseket fokozatosan kell felépíteni, akár egy programot. Először az alap dolgok működjenek, aztán jöhetnek a különböző megszorítások:
  1. Legyen egy program, ami számokat fogad el bemenetnek. Az ECMAScript a számokat a [:digit:] vagy [:d:] szintaxissal azonosítja: regex r("[[:digit:]]");
  2. A fenti kifejezés csak egyjegyű számokat fogad el. Több számjegy esetén a végére kell írni egy + jelt: regex r("[[:digit:]]+");
  3. Ha tesztelni szeretnénk, hogy a beolvasott szám nem-e negatív, akkor a kifejezés elejébe a –? kombinációt kell tenni: regex r("-?[[:digit:]]+");
  4. Ha a felhasználó netán a + karaktert is használja a számok előtt, akkor erre is fel kell készíteni a fordítót, ugyanis ez egy speciális karakter az ECMAScript szintaxisában. Hogy figyelmen kívül hagyja ezt a karaktert, a \\+? jelt kell a kifejezés elejébe írni: regex r("(\\+?[[:digit:]]+");
  5. A kifejezés előtt lévő ellenőrzéseke kombinálni kell egy „vagy” kapuval, mert vagy az egyik, vagy a másik fordulhat elő: regex r("(\\+|-)?[[:digit:]]+");

Ezt a felépített kifejezést alkalmazzuk a következő programban. Ha a bevitt számjegy nem felel meg a reguláris kifejezés szabályainak, akkor a „Nem helyes!” üzenet jelenik meg. A program a q billentyű beviteléig fut.

#include <iostream>
#include <regex>
#include <string>

using namespace std;

int main()
{
      string input;
      regex r("(\\+|-)?[[:digit:]]+");

      while(true) //végtelen ciklus
      {
            cout << "Irj be egy egesz szamot: ";
            cin >> input;
            if(input=="q") break; // q esetén kilép
            if(regex_match(input,r))
                  cout << "Helyes!" << endl;
            else
                  cout << "Nem helyes!" << endl;
      }
}

A reguláris kifejezés helyességét az std::regex_match() függvény ellenőrzi. A kimenet:

Irj be egy egesz szamot: 23
Helyes!
Irj be egy egesz szamot: +51
Helyes!
Irj be egy egesz szamot: -79
Helyes!
Irj be egy egesz szamot: 6.7
Nem helyes!
Irj be egy egesz szamot: +-41
Nem helyes!
Irj be egy egesz szamot: abc
Nem helyes!
Irj be egy egesz szamot: q

Ha valós számokra is hasonlóképp kell felépíteni a reguláris kifejezést. Ott a tizedespont utáni rész opcionális, ezért a ([[:digit:]]+)? kifejezéssel jelöljük. Ugyanígy a tizedespont is opcionális ha nincs utána semmi, így az egész második részt (\\.)? közé lehet tenni: regex rr("((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?");

Ha a feladat, hogy tudományos formátumú számokat lehessen beírni, mint pl. -1.23e+06 vagy 1E3, akkor azzal bővítjük az előző kifejezést, hogy az exponenciális tag is opcionális részese lehet a számnak. Az exponenciális részt a ((e|E)((\\+|-)?)[[:digit:]]+)? kifejezés jelenti: regex rrr("((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?((e|E)((\\+|-)?)[[:digit:]]+)?");

Az alapértelmezettként használt ECMAScript logikája hamar elsajátítható, de beállítható más szintaxis is, például a POSIX grep:

#include <iostream>
#include <string>
#include <regex>

int main()
{
    std::string str = "zzxayyzz";
    std::regex re1(".*(xay)"); // ECMAScript
    std::regex re2(".*(xay)", std::regex::grep); // POSIX grep

    std::cout << "Kereses 'xay' utan az 'zzxayyzz' szovegben:\n";
    std::smatch m;
    std::regex_search(str, m, re1);
    std::cout << " ECMAScript: " << m[0] << '\n';
    std::regex_search(str, m, re2);
    std::cout << " POSIX grep: " << m[0] << '\n';
}

A fenti program egy keresést végez az str stringben. A keresést az std::regex_search() függvény végzi, melynek első paramétere egy string amiben a keresés zajlik, a második egy konténer, ami a találatokat tartalmazza, a harmadik pedig a keresés reguláris kifejezése. Az eredmény:

Kereses 'xayy' utan az 'zzxayyzz' szovegben:
 ECMAScript: zzxay
 POSIX grep: zzxay

Raw stringek

A raw stringek olyan stringek, melyekben a feloldójelek (pl. \n, \t, \) nem érvényesek. Szintaxisa: R”(szoveg)”. A következő program jól szemlélteti a hagyományos és a raw string közti különbséget:


#include <iostream>
#include <string>

using namespace std;

int main()
{
    string normal_str="Elso sor.\nMasodik sor.\nUzenet vege.\n";
    string raw_str=R"(Elso sor.\nMasodik sor.\nUzenet vege.\n)";
    cout<<normal_str<<endl;
    cout<<raw_str<<endl;
    return(0);
}

A kimenet:

Elso sor.
Masodik sor.
Uzenet vege.

Elso sor.\nMasodik sor.\nUzenet vege.\n

A raw string egyik célja, hogy valamennyire egyszerűsítse a reguláris kifejezések használatát. Ezzel közvetlen bevihetők az ECMAScript kifejezések anélkül, hogy a feloldójeleket a fordító felismerné. Egy korábbi példában a bemenő számok helyességét a következőképp ellenőriztük:

regex r("(\\+|-)?[[:digit:]]+");

Ugyanez raw stringgel:

regex integer(R"((\+|-)?[[:digit:]]+)");

Lambda függvények és kifejezések

A reguláris kifejezések és a raw stringek mellett egy másik hasznos nyelvezet a lambda. A lambda kifejezések lehetővé teszik, hogy a név nélkül definiáljunk és használjunk függvényeket. Használható a függvényobjektumok helyett, így nem kell külön osztályt és tagfüggvényt definiálni. A következő lambda kifejezés egy olyan függvényt definiál, amelyik egy számot térít vissza:

[]() -> int { return 4; }();

A fenti függvény igazából semmi hasznosat nem tesz, csupán visszatéríti a 4-et, ami például kiírásra használható:

int result = []() -> int { return 4; }();
cout << result << endl;

Kicsit hasznosabb a következő:

int result = [](int input) -> int { return 2 * input; }(10);
cout << result << endl;

A fenti kifejezésnek van agy bemenő paramétere (input) és visszatéríti ennek dupláját. A függvény rögtön a deklarálás után megkapja a 10 paramétert, így a kiírt érték 20. Hasonlóképp felírható két szám összege:

int result = [](int a,int b) -> int { return a + b; }(2,4);
cout << result << endl;

Akár a reguláris kifejezések, a lambda kifejezések is kombinálhatók, egymásba építhetők:

int result = [](int m, int n) -> int { return m + n; } ([](int a) -> int { return a; }(2),[](int a) -> int { return a; }(3));
cout << result << endl;

A fenti lambda függvényben két másik lambda függvény szerepel, egyik 2-t, másik 3-at térít vissza, a fő függvény pedig összeadja ezeket, mint bemenő paramétereket. A függvénymutatók működnek a lambda függvényekre is:

auto func = [](int a, int b) -> int { return a+b; };
cout << func(2, 3) << endl;

Az auto típus a func változót a lambda kifejezésre mutató mutatóként fogja deklarálni. Tegyük fel, hogy egész számokkal megpakolt vektor tartalmát kell rendezni a sort() algoritmussal. Ez a hagyományos C++98 fordítóval a következőképp néz ki:

#include <iostream>
#include <vector>
#include <algorithm>

bool compare(int i,int j)
{
      return (i<j);
}

using namespace std;

int main()
{
      vector<int> v {3,1,7,4,8,9,5,6,2,10};
      for(int i = 0; i < v.size(); i++) cout << v[i] << " "; cout << endl;
      sort(v.begin(), v.end(), compare); //rendezés
      for(int i = 0; i < v.size(); i++) cout << v[i] << " "; cout << endl;

      return 0;
}

Lambda kifejezéssel ugyanez elérhető, megspórolva a compare() függvény deklarálását:

sort(v.begin(), v.end(), [](int i, int j) -> bool{ return (i < j);}); //rendezés


Függvénymutatók

A függvénymutatók is a programozás egyszerűsítésére szolgálnak. Egy függvény nem más, mint egy memóriacím, ahonnan az utasítások kezdődnek. A függvény hívásához elegendő ezt a címet ismerni.

A következő függvénymutató olyan függvényre mutat, melynek paramétere két char, visszatérített típusa pedig int. A mutató neve func, amit bármilyen függvényhez lehet használni, melynek ilyen a felépítése.

      int (*func)(char,char) = NULL;

A függvénymutatók az osztályok tagfüggvényeire is mutathatnak.

      int (MyClass::*Classfunc)(char,char) = NULL;

Mindkét mutató NULL mutatónak van inicializálva, de lehet rögtön a függvény címét is adni (pl. &fuggveny). Ha a mutató megkapta a függvény címét, a függvény rajta keresztül hívható:

int result = func(10,2);
int result = (obj.*Classfunc)(10,2);

Mivel mutatóról van szó, ez paraméterként is átadható egy másik függvénynek.

void PassPtr(int (*funcptr)(char, char))
{
   int result = (*funcptr)(20,3);     // call using function pointer
   cout << "PassPtr: " << result << endl;
}

A függvényt a következőképp lehet meghívni: PassPtr(func);

A függvénymutató egy függvény visszatérített értéke is lehet:

int (*ReturnPtr(char muvelet))(char, char)
{
   if(muvelet == '+')
      return &Plus;
   else
      return &Minus;
}

Ebben az esetben a Plus() és Minus() két hasonló függvény. A következő példában összesítve vannak az eddig felsorolt példák:

#include <iostream>
using namespace std;

class MyClass
{
      public:
            int TagOsszeg(char a, char b) //tagfüggvény
            {
                  return a+b;
            }
            int TagKulonbseg(char a, char b) //tagfüggvény
            {
                  return a-b;
            }
};

int Osszeg (char a, char b) //sima függvény
{
      return a+b;
}

int Kulonbseg (char a, char b) //sima függvény
{
      return a-b;
}

void PassPtr(int (*funcptr)(char, char))
{
   int result = (*funcptr)(20,3);
   cout << "PassPtr(20,3): " << result << endl;
}

int (*ReturnPtr(char muvelet))(char, char)
{
   if(muvelet == '+')
      return &Osszeg;
   else
      return &Kulonbseg;
}


int main()
{
      int (*func)(char, char) = NULL; //függvénymutató egy sima függvényre
      int (MyClass::*Classfunc)(char, char) = NULL; //függvénymutató a tagfüggvényre
      int result;
     
      func = &Osszeg;
      result = func(12,3);
      cout << "Osszeg(12,3): " << result << endl;
     
      func = &Kulonbseg;
      result = (*func)(12,3);
      cout << "Kulonbseg(12,3): " << result << endl;
     
      MyClass obj1;
      Classfunc = &MyClass::TagOsszeg;
      result = (obj1.*Classfunc)(10,2);
      cout << "TagOsszeg(10,2): " << result << endl;
     
      MyClass *obj2 = new MyClass;
      Classfunc = &MyClass::TagKulonbseg;
      result = (obj2->*Classfunc)(10,2);
      cout << "TagKulonbseg(10,2): " << result << endl;
     
      PassPtr(func);
     
      result = (*ReturnPtr('+'))(3,7);
      cout << "(*ReturnPtr('+'))(3,7): " << result << endl;
     
     
      return 0;
}

A kimenet:

Osszeg(12,3): 15
Kulonbseg(12,3): 9
TagOsszeg(10,2): 12
TagKulonbseg(10,2): 8
PassPtr(20,3): 17
(*ReturnPtr('+'))(3,7): 10

A függvénymutatók legnagyobb hasznát igazából a tömbben való alkalmazáskor lehet érezni. Ilyenkor ugyanis a függvényeket indexelni lehet.

#include <iostream>
using namespace std;

int Osszeg (char a, char b)
{
      return a+b;
}

int Kulonbseg (char a, char b)
{
      return a-b;
}

int Szorzat (char a, char b)
{
      return a*b;
}

int Hatvany (char a, char b)
{
      int h=1;
      for(int i=1; i<=b; i++)
            h=h*a;
      return h;
}

int main()
{
   int (*tomb[4])(char, char) = {NULL}; //10 darab függvénymutató

   tomb[0]=&Osszeg;
   tomb[1]=&Kulonbseg;
   tomb[2]=&Szorzat;
   tomb[3]=&Hatvany;

   for(int i=0; i<4; i++)
            cout << tomb[i](6,2) << endl;
  
   return 0;
}

A kimenet:

8
4
12
36